aboutsummaryrefslogtreecommitdiff
path: root/src/IO
diff options
context:
space:
mode:
Diffstat (limited to 'src/IO')
-rw-r--r--src/IO/IO.c412
-rw-r--r--src/IO/IO.h45
-rw-r--r--src/IO/Makefile.am14
-rw-r--r--src/IO/Url.h40
-rw-r--r--src/IO/about.c344
-rw-r--r--src/IO/dpi.c779
-rw-r--r--src/IO/http.c494
-rw-r--r--src/IO/iowatch.cc35
-rw-r--r--src/IO/iowatch.hh25
-rw-r--r--src/IO/mime.c152
-rw-r--r--src/IO/mime.h58
-rw-r--r--src/IO/proto.c13
12 files changed, 2411 insertions, 0 deletions
diff --git a/src/IO/IO.c b/src/IO/IO.c
new file mode 100644
index 00000000..060fe200
--- /dev/null
+++ b/src/IO/IO.c
@@ -0,0 +1,412 @@
+/*
+ * File: IO.c
+ *
+ * Copyright (C) 2000-2006 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+/*
+ * Dillo's event driven IO engine
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/socket.h>
+#include "../msg.h"
+#include "../chain.h"
+#include "../klist.h"
+#include "../list.h"
+#include "IO.h"
+#include "iowatch.hh"
+
+#define DEBUG_LEVEL 5
+//#define DEBUG_LEVEL 1
+#include "../debug.h"
+
+/*
+ * Symbolic defines for shutdown() function
+ * (Not defined in the same header file, for all distros --Jcid)
+ */
+#define IO_StopRd 0
+#define IO_StopWr 1
+#define IO_StopRdWr 2
+
+
+typedef struct {
+ int Key; /* Primary Key (for klist) */
+ int Op; /* IORead | IOWrite */
+ int FD; /* Current File Descriptor */
+ int Flags; /* Flag array (look definitions above) */
+ int Status; /* errno code */
+ Dstr *Buf; /* Internal buffer */
+
+ void *Info; /* CCC Info structure for this IO */
+ int events; /* FLTK events for this IO */
+} IOData_t;
+
+
+/*
+ * Local data
+ */
+static Klist_t *ValidIOs = NULL; /* Active IOs list. It holds pointers to
+ * IOData_t structures. */
+
+/*
+ * Forward declarations
+ */
+void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info,
+ void *Data1, void *Data2);
+
+
+/* IO API - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/*
+ * Return a newly created, and initialized, 'io' struct
+ */
+static IOData_t *IO_new(int op, int fd)
+{
+ IOData_t *io = dNew0(IOData_t, 1);
+ io->Op = op;
+ io->FD = fd;
+ io->Flags = 0;
+ io->Key = 0;
+ io->Buf = dStr_sized_new(IOBufLen);
+
+ return io;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/*
+ * Register an IO in ValidIOs
+ */
+static void IO_ins(IOData_t *io)
+{
+ if (io->Key == 0) {
+ io->Key = a_Klist_insert(&ValidIOs, io);
+ }
+}
+
+/*
+ * Remove an IO from ValidIOs
+ */
+static void IO_del(IOData_t *io)
+{
+ if (io->Key != 0) {
+ a_Klist_remove(ValidIOs, io->Key);
+ }
+ io->Key = 0;
+ _MSG(" -->ValidIOs: %d\n", a_Klist_length(ValidIOs));
+}
+
+/*
+ * Return a io by its Key (NULL if not found)
+ */
+static IOData_t *IO_get(int Key)
+{
+ return (IOData_t *)a_Klist_get_data(ValidIOs, Key);
+}
+
+/*
+ * Free an 'io' struct
+ */
+static void IO_free(IOData_t *io)
+{
+ dStr_free(io->Buf, 1);
+ dFree(io);
+}
+
+/*
+ * Close an open FD, and remove io controls.
+ * (This function can be used for Close and Abort operations)
+ */
+static void IO_close_fd(IOData_t *io, int CloseCode)
+{
+ int st;
+
+ /* With HTTP, if we close the writing part, the reading one also gets
+ * closed! (other clients may set 'IOFlag_ForceClose') */
+ if ((io->Flags & IOFlag_ForceClose) || (CloseCode == IO_StopRdWr)) {
+ do
+ st = close(io->FD);
+ while (st < 0 && errno == EINTR);
+ }
+ /* Remove this IOData_t reference, from our ValidIOs list
+ * We don't deallocate it here, just remove from the list.*/
+ IO_del(io);
+
+ /* Stop the polling on this FD */
+ a_IOwatch_remove_fd(io->FD, io->events);
+}
+
+/*
+ * Read data from a file descriptor into a specific buffer
+ */
+static bool_t IO_read(IOData_t *io)
+{
+ char Buf[IOBufLen];
+ ssize_t St;
+ bool_t ret = FALSE;
+
+ DEBUG_MSG(3, " IO_read\n");
+
+ /* this is a new read-buffer */
+ dStr_truncate(io->Buf, 0);
+ io->Status = 0;
+
+ while (1) {
+ St = read(io->FD, Buf, IOBufLen);
+ if (St > 0) {
+ dStr_append_l(io->Buf, Buf, St);
+ continue;
+ } else if (St < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else if (errno == EAGAIN) {
+ ret = TRUE;
+ break;
+ } else {
+ io->Status = errno;
+ break;
+ }
+ } else { /* St == 0 */
+ break;
+ }
+ }
+
+ if (io->Buf->len > 0) {
+ /* send what we've got so far */
+ a_IO_ccc(OpSend, 2, FWD, io->Info, io, NULL);
+ }
+ if (St == 0) {
+ /* All data read (EOF) */
+ a_IO_ccc(OpEnd, 2, FWD, io->Info, io, NULL);
+ }
+ return ret;
+}
+
+/*
+ * Write data, from a specific buffer, into a file descriptor
+ */
+static bool_t IO_write(IOData_t *io)
+{
+ ssize_t St;
+ bool_t ret = FALSE;
+
+ DEBUG_MSG(3, " IO_write\n");
+ io->Status = 0;
+
+ while (1) {
+ St = write(io->FD, io->Buf->str, io->Buf->len);
+ if (St < 0) {
+ /* Error */
+ if (errno == EINTR) {
+ continue;
+ } else if (errno == EAGAIN) {
+ ret = TRUE;
+ break;
+ } else {
+ io->Status = errno;
+ break;
+ }
+ } else if (St < io->Buf->len) {
+ /* Not all data written */
+ dStr_erase (io->Buf, 0, St);
+ } else {
+ /* All data in buffer written */
+ dStr_truncate(io->Buf, 0);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Handle background IO for a given FD (reads | writes)
+ * (This function gets called when there's activity in the FD)
+ */
+static int IO_callback(int fd, IOData_t *io)
+{
+ bool_t ret = FALSE;
+
+ _MSG("IO_callback:: (%s) FD = %d\n",
+ (io->Op == IORead) ? "IORead" : "IOWrite", io->FD);
+
+ if (io->Op == IORead) { /* Read */
+ ret = IO_read(io);
+ } else if (io->Op == IOWrite) { /* Write */
+ ret = IO_write(io);
+ }
+ return (ret) ? 1 : 0;
+}
+
+/*
+ * Handle the READ event of a FD.
+ */
+static void IO_fd_read_cb(int fd, void *data)
+{
+ int io_key = (int)data;
+ IOData_t *io = IO_get(io_key);
+
+ /* There should be no more events on already closed FDs --Jcid */
+ if (io == NULL) {
+ MSG_ERR("IO_fd_read_cb: call on already closed io!\n");
+ a_IOwatch_remove_fd(fd, DIO_READ);
+
+ } else {
+ if (IO_callback(fd, io) == 0)
+ a_IOwatch_remove_fd(fd, DIO_READ);
+ }
+}
+
+/*
+ * Handle the WRITE event of a FD.
+ */
+static void IO_fd_write_cb(int fd, void *data)
+{
+ int io_key = (int)data;
+ IOData_t *io = IO_get(io_key);
+
+ if (io == NULL) {
+ /* There must be no more events on already closed FDs --Jcid */
+ MSG_ERR("IO_fd_write_cb: call on already closed io!\n");
+ a_IOwatch_remove_fd(fd, DIO_WRITE);
+
+ } else {
+ if (IO_callback(fd, io) == 0)
+ a_IOwatch_remove_fd(fd, DIO_WRITE);
+ }
+}
+
+/*
+ * Receive an IO request (IORead | IOWrite),
+ * Set a watch for it, and let it flow!
+ */
+static void IO_submit(IOData_t *r_io)
+{
+ /* Insert this IO in ValidIOs */
+ IO_ins(r_io);
+
+ _MSG("IO_submit:: (%s) FD = %d\n",
+ (io->Op == IORead) ? "IORead" : "IOWrite", io->FD);
+
+ /* Set FD to background and to close on exec. */
+ fcntl(r_io->FD, F_SETFL, O_NONBLOCK | fcntl(r_io->FD, F_GETFL));
+ fcntl(r_io->FD, F_SETFD, FD_CLOEXEC | fcntl(r_io->FD, F_GETFD));
+
+ if (r_io->Op == IORead) {
+ r_io->events = DIO_READ;
+ a_IOwatch_add_fd(r_io->FD, r_io->events,
+ IO_fd_read_cb, (void*)(r_io->Key));
+
+ } else if (r_io->Op == IOWrite) {
+ r_io->events = DIO_WRITE;
+ a_IOwatch_add_fd(r_io->FD, r_io->events,
+ IO_fd_write_cb, (void*)(r_io->Key));
+ }
+}
+
+/*
+ * CCC function for the IO module
+ * ( Data1 = IOData_t* ; Data2 = NULL )
+ */
+void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info,
+ void *Data1, void *Data2)
+{
+ IOData_t *io;
+ DataBuf *dbuf;
+
+ a_Chain_debug_msg("a_IO_ccc", Op, Branch, Dir);
+
+ if (Branch == 1) {
+ if (Dir == BCK) {
+ /* Write data using select */
+ switch (Op) {
+ case OpStart:
+ io = IO_new(IOWrite, *(int*)Data1); /* SockFD */
+ Info->LocalKey = io;
+ break;
+ case OpSend:
+ io = Info->LocalKey;
+ dbuf = Data1;
+ dStr_append_l(io->Buf, dbuf->Buf, dbuf->Size);
+ IO_submit(io);
+ break;
+ case OpEnd:
+ case OpAbort:
+ io = Info->LocalKey;
+ if (io->Buf->len > 0) {
+ MSG_WARN("IO_write, closing with pending data not sent\n");
+ MSG_WARN(" \"%s\"\n", io->Buf->str);
+ }
+ /* close FD, remove from ValidIOs and remove its watch */
+ IO_close_fd(io, IO_StopRdWr);
+ IO_free(io);
+ dFree(Info);
+ break;
+ default:
+ MSG_WARN("Unused CCC\n");
+ break;
+ }
+ } else { /* FWD */
+ /* Write-data status */
+ switch (Op) {
+ default:
+ MSG_WARN("Unused CCC\n");
+ break;
+ }
+ }
+
+ } else if (Branch == 2) {
+ if (Dir == BCK) {
+ /* This part catches the reader's messages */
+ switch (Op) {
+ case OpStart:
+ io = IO_new(IORead, *(int*)Data2); /* SockFD */
+ Info->LocalKey = io;
+ io->Info = Info;
+ IO_submit(io);
+ break;
+ case OpAbort:
+ io = Info->LocalKey;
+ IO_close_fd(io, IO_StopRdWr);
+ IO_free(io);
+ dFree(Info);
+ break;
+ default:
+ MSG_WARN("Unused CCC\n");
+ break;
+ }
+ } else { /* FWD */
+ /* Send read-data */
+ io = Data1;
+ switch (Op) {
+ case OpSend:
+ dbuf = a_Chain_dbuf_new(io->Buf->str, io->Buf->len, 0);
+ a_Chain_fcb(OpSend, Info, dbuf, NULL);
+ dFree(dbuf);
+ break;
+ case OpEnd:
+ a_Chain_fcb(OpEnd, Info, NULL, NULL);
+ IO_close_fd(io, IO_StopRdWr);
+ IO_free(io);
+ dFree(Info);
+ break;
+ default:
+ MSG_WARN("Unused CCC\n");
+ break;
+ }
+ }
+ }
+}
+
diff --git a/src/IO/IO.h b/src/IO/IO.h
new file mode 100644
index 00000000..71ed25b4
--- /dev/null
+++ b/src/IO/IO.h
@@ -0,0 +1,45 @@
+#ifndef __IO_H__
+#define __IO_H__
+
+#include <unistd.h>
+#include <sys/uio.h>
+
+#include "d_size.h"
+#include "../../dlib/dlib.h"
+#include "../chain.h"
+#include "iowatch.hh"
+
+/*
+ * IO Operations
+ */
+#define IORead 0
+#define IOWrite 1
+#define IOClose 2
+#define IOAbort 3
+
+/*
+ * IO Flags (unused)
+ */
+#define IOFlag_ForceClose (1 << 1)
+#define IOFlag_SingleWrite (1 << 2)
+
+/*
+ * IO constants
+ */
+#define IOBufLen 8192
+
+
+/*
+ * Exported functions
+ */
+/* Note: a_IO_ccc() is defined in Url.h together with the *_ccc() set */
+
+
+/*
+ * Exported data
+ */
+extern const char *AboutSplash;
+
+
+#endif /* __IO_H__ */
+
diff --git a/src/IO/Makefile.am b/src/IO/Makefile.am
new file mode 100644
index 00000000..bff4667f
--- /dev/null
+++ b/src/IO/Makefile.am
@@ -0,0 +1,14 @@
+noinst_LIBRARIES = libDiof.a
+
+libDiof_a_SOURCES = \
+ mime.c \
+ mime.h \
+ about.c \
+ Url.h \
+ proto.c \
+ http.c \
+ dpi.c \
+ IO.c \
+ iowatch.cc \
+ iowatch.hh \
+ IO.h
diff --git a/src/IO/Url.h b/src/IO/Url.h
new file mode 100644
index 00000000..91a9c1bd
--- /dev/null
+++ b/src/IO/Url.h
@@ -0,0 +1,40 @@
+#ifndef __IO_URL_H__
+#define __IO_URL_H__
+
+#include "../chain.h"
+#include "../url.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * External functions
+ */
+extern void a_Http_freeall(void);
+int a_Http_init(void);
+int a_Http_proxy_auth(void);
+void a_Http_set_proxy_passwd(char *str);
+char *a_Http_make_query_str(const DilloUrl *url, bool_t use_proxy);
+
+void a_Http_ccc (int Op, int Branch, int Dir, ChainLink *Info,
+ void *Data1, void *Data2);
+void a_About_ccc(int Op, int Branch, int Dir, ChainLink *Info,
+ void *Data1, void *Data2);
+void a_IO_ccc (int Op, int Branch, int Dir, ChainLink *Info,
+ void *Data1, void *Data2);
+void a_Dpi_ccc (int Op, int Branch, int Dir, ChainLink *Info,
+ void *Data1, void *Data2);
+
+char *a_Dpi_send_blocking_cmd(const char *server_name, const char *cmd);
+void a_Dpi_bye_dpid(void);
+void a_Dpi_init(void);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __IO_URL_H__ */
+
diff --git a/src/IO/about.c b/src/IO/about.c
new file mode 100644
index 00000000..a3a0c420
--- /dev/null
+++ b/src/IO/about.c
@@ -0,0 +1,344 @@
+/*
+ * File: about.c
+ *
+ * Copyright (C) 1999-2006 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <config.h>
+
+/*
+ * HTML text for startup screen
+ */
+const char *AboutSplash=
+"<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
+"<html>\n"
+"<head>\n"
+"<title>Splash screen for dillo-" VERSION "</title>\n"
+"</head>\n"
+"<body bgcolor='#778899' text='#000000' link='#000000' vlink='#000000'>\n"
+"\n"
+"\n"
+"<!-- the head of the page -->\n"
+"\n"
+"<table width='100%' border='0' cellspacing='1' cellpadding='3'>\n"
+" <tr><td>\n"
+" <table border='1' cellspacing='1' cellpadding='0'>\n"
+" <tr>\n"
+" <td bgcolor='#000000'>\n"
+" <table width='100%' border='0' bgcolor='#ffffff'>\n"
+" <tr>\n"
+" <td valign='top' align='left'>\n"
+" <h1>&nbsp;Welcome to Dillo " VERSION "&nbsp;</h1>\n"
+" </table>\n"
+" </table>\n"
+"</table>\n"
+"\n"
+"<br>\n"
+"\n"
+"\n"
+"<!-- the main layout table, definition -->\n"
+"\n"
+"<table width='100%' border='0' cellspacing='0' cellpadding='0'>\n"
+"<tr><td valign='top' width='150' align='center'>\n"
+"\n"
+"\n"
+"<!-- The navigation bar -->\n"
+"\n"
+"<table border='0' cellspacing='0' cellpadding='0' width='140' bgcolor='#000000'>\n"
+"<tr>\n"
+" <td>\n"
+" <table width='100%' border='0' cellspacing='1' cellpadding='3'>\n"
+" <tr>\n"
+" <td colspan='1' bgcolor='#CCCCCC'>Dillo\n"
+" <tr>\n"
+" <td bgcolor='#FFFFFF'>\n"
+" <table border='0' cellspacing='0' cellpadding='5'><tr><td>\n"
+" <table border='0' cellspacing='0' cellpadding='2'><tr>\n"
+" <td>\n"
+" <td>\n"
+" <a href='http://www.dillo.org/dillo-help.html'>\n"
+" Help</a>\n"
+" <tr>\n"
+" <td>&nbsp;&nbsp;\n"
+" <td>\n"
+" <a href='http://www.dillo.org/'>Home</a>\n"
+" <tr>\n"
+" <td>&nbsp;&nbsp;\n"
+" <td>\n"
+" <a href='http://www.dillo.org/funding/objectives.html'>\n"
+" Objectives</a>\n"
+" <tr>\n"
+" <td>&nbsp;&nbsp;\n"
+" <td>\n"
+" <a href='http://www.dillo.org/ChangeLog.html'>\n"
+" ChangeLog</a>\n"
+" <tr>\n"
+" <td>&nbsp;&nbsp;\n"
+" <td>\n"
+" <a href='http://www.dillo.org/interview.html'>\n"
+" Interview</a>\n"
+" <tr>\n"
+" <td>&nbsp;&nbsp;\n"
+" <td>\n"
+" <a href='http://www.dillo.org/D_authors.html'>\n"
+" Authors</a>\n"
+" <tr>\n"
+" <td>&nbsp;&nbsp;\n"
+" <td>\n"
+" <a href='http://www.dillo.org/donations.html'>\n"
+" Donate</a>\n"
+" </table>\n"
+" </table>\n"
+" </table>\n"
+"</table>\n"
+"\n"
+"<br>\n"
+"\n"
+"<table border='0' cellspacing='0' cellpadding='0' width='140' bgcolor='#000000'>\n"
+"<tr>\n"
+" <td>\n"
+" <table width='100%' border='0' cellspacing='1' cellpadding='3'>\n"
+" <tr>\n"
+" <td colspan='1' bgcolor='#CCCCCC'>Magazines\n"
+"\n"
+" <tr>\n"
+" <td bgcolor='#FFFFFF'>\n"
+" <table border='0' cellspacing='0' cellpadding='5'><tr><td>\n"
+" <table border='0' cellpadding='2'>\n"
+" <tr>\n"
+" <td>&nbsp;&nbsp;\n"
+" <td>\n"
+" <a href='http://lwn.net/'>LWN</a>\n"
+" <tr>\n"
+" <td>&nbsp;&nbsp;\n"
+" <td>\n"
+" <a href='http://slashdot.org/'>Slashdot</a>\n"
+" <tr>\n"
+" <td>&nbsp;&nbsp;\n"
+" <td>\n"
+" <a href='http://www.kuro5hin.org/?op=section;section=__all__'>KuroShin</a>\n"
+" <tr>\n"
+" <td>&nbsp;&nbsp;\n"
+" <td>\n"
+" <a href='http://www.nexusmagazine.com/'>Nexus&nbsp;M.</a>\n"
+" <tr>\n"
+" <td>&nbsp;&nbsp;\n"
+" <td>\n"
+" <a href='http://www.gnu-darwin.org/update.html'>Monster News</a>\n"
+" <tr>\n"
+" <td>&nbsp;&nbsp;\n"
+" <td>\n"
+" <a href='http://www.theregister.co.uk/index.html'>The Register</a>\n"
+" </table>\n"
+" </table>\n"
+" </table>\n"
+"</table>\n"
+"\n"
+"<br>\n"
+"\n"
+"<table border='0' cellspacing='0' cellpadding='0' width='140' bgcolor='#000000'>\n"
+"<tr>\n"
+" <td>\n"
+" <table width='100%' border='0' cellspacing='1' cellpadding='3'>\n"
+" <tr>\n"
+" <td colspan='1' bgcolor='#CCCCCC'>Additional Stuff\n"
+"\n"
+" <tr>\n"
+" <td bgcolor='#FFFFFF'>\n"
+" <table border='0' cellspacing='0' cellpadding='5'><tr><td>\n"
+" <table border='0' cellpadding='2'><tr>\n"
+" <td>&nbsp;&nbsp;\n"
+" <td><a href='http://www.google.com/'>Google</a>\n"
+" <tr>\n"
+" <td>&nbsp;&nbsp;\n"
+" <td><a href='http://www.wikipedia.org/'>Wikipedia</a>\n"
+" <tr>\n"
+" <td>&nbsp;&nbsp;\n"
+" <td><a href='http://www.gutenberg.org/'>P. Gutenberg</a>\n"
+" <tr>\n"
+" <td>&nbsp;&nbsp;\n"
+" <td><a href='http://freshmeat.net/'>FreshMeat</a>\n"
+" <tr>\n"
+" <td>&nbsp;&nbsp;\n"
+" <td><a href='http://www.gnu.org/gnu/thegnuproject.html'>GNU\n"
+" project</a>\n"
+" <tr>\n"
+" <td>&nbsp;&nbsp;\n"
+" <td><a href='http://www.linuxfund.org/'>LinuxFund</a>\n"
+" </table>\n"
+" </table>\n"
+" </table>\n"
+"</table>\n"
+"\n"
+"<br>\n"
+"\n"
+"<table border='0' cellspacing='0' cellpadding='0' width='140' bgcolor='#000000'>\n"
+"<tr>\n"
+" <td>\n"
+" <table width='100%' border='0' cellspacing='1' cellpadding='3'>\n"
+" <tr>\n"
+" <td colspan='1' bgcolor='#CCCCCC'>Essential Readings\n"
+"\n"
+" <tr>\n"
+" <td bgcolor='#FFFFFF'>\n"
+" <table border='0' cellspacing='0' cellpadding='5'><tr><td>\n"
+" <table border='0' cellpadding='2'>\n"
+" <tr><td>&nbsp;&nbsp;\n"
+" <td><a href='http://www.violence.de'>Peace&amp;Violence</a>\n"
+" <tr><td>&nbsp;&nbsp;\n"
+" <td><a href='http://www.fsf.org/philosophy/right-to-read.html'>"
+" Right to Read</a>\n"
+" </table>\n"
+" </table>\n"
+" </table>\n"
+"</table>\n"
+"\n"
+"<table border='0' width='100%' cellpadding='0' cellspacing='0'><tr><td height='10'></table>\n"
+"\n"
+"\n"
+"<!-- the main layout table, a small vertical spacer -->\n"
+"\n"
+"<td width='20'><td valign='top'>\n"
+"\n"
+"\n"
+"<!-- Main Part of the page -->\n"
+"\n"
+"<table border='0' cellpadding='0' cellspacing='0' align='center' bgcolor='#000000' width='100%'><tr><td>\n"
+"<table border='0' cellpadding='5' cellspacing='1' width='100%'>\n"
+"<tr>\n"
+" <td bgcolor='#CCCCCC'>\n"
+" <h4>Free Software</h4>\n"
+"<tr>\n"
+" <td bgcolor='#FFFFFF'>\n"
+" <table border='0' cellspacing='0' cellpadding='5'><tr><td>\n"
+" <p>\n"
+" Dillo is Free Software in the terms of the GPL.\n"
+" This means you have four basic freedoms:\n"
+" <ul>\n"
+" <li>Freedom to use the program any way you see fit.\n"
+" <li>Freedom to study and modify the source code.\n"
+" <li>Freedom to make backup copies.\n"
+" <li>Freedom to redistribute it.\n"
+" </ul>\n"
+" The <a href='http://www.gnu.org/licenses/gpl.html'>GPL</a>\n"
+" is the legal mechanism that gives you these freedoms.\n"
+" It also protects them from being taken away: any derivative work\n"
+" based on the program must be under the GPL.<br>\n"
+" </table>\n"
+"</table>\n"
+"</table>\n"
+"\n"
+"<br>\n"
+"\n"
+"<table border='0' cellpadding='0' cellspacing='0' align='center' bgcolor='#000000' width='100%'><tr><td>\n"
+"<table border='0' cellpadding='5' cellspacing='1' width='100%'>\n"
+"<tr>\n"
+" <td bgcolor='#CCCCCC'>\n"
+" <h4>Release overview</h4>\n"
+" ??, 2005\n"
+"<tr>\n"
+" <td bgcolor='#FFFFFF'>\n"
+" <table border='0' cellspacing='0' cellpadding='5'>\n"
+" <tr>\n"
+" <td>\n"
+"<p>\n"
+"[...]\n"
+"<p>\n"
+"Remember that dillo project uses a release model where every new\n"
+"browser shall be better than the former.\n"
+"<EM>Keep up with the latest one!</EM>\n"
+" </table>\n"
+"</table>\n"
+"</table>\n"
+"\n"
+"<br>\n"
+"\n"
+"<table border='0' cellpadding='0' cellspacing='0' align='center' bgcolor='#000000' width='100%'><tr><td>\n"
+"<table border='0' cellpadding='5' cellspacing='1' width='100%'>\n"
+"<tr>\n"
+" <td bgcolor='#CCCCCC'>\n"
+" <h4>ChangeLog highlights</h4>\n"
+" (Extracted from the\n"
+" <a href='http://www.dillo.org/ChangeLog.html'>full\n"
+" ChangeLog</a>)\n"
+"<tr>\n"
+" <td bgcolor='#FFFFFF'>\n"
+" <table border='0' cellspacing='0' cellpadding='5'>\n"
+" <tr>\n"
+" <td>\n"
+"<ul>\n"
+"<li>[...]\n"
+"</ul>\n"
+" </table>\n"
+"</table>\n"
+"</table>\n"
+"\n"
+"<br>\n"
+"\n"
+"<table border='0' cellpadding='0' cellspacing='0' align='center' bgcolor='#000000' width='100%'><tr><td>\n"
+"<table border='0' cellpadding='5' cellspacing='1' width='100%'>\n"
+"<tr>\n"
+" <td bgcolor='#CCCCCC'>\n"
+" <h4>Notes</h4>\n"
+"<tr>\n"
+" <td bgcolor='#FFFFFF'>\n"
+" <table border='0' cellspacing='0' cellpadding='5'>\n"
+" <tr>\n"
+" <td>\n"
+"<ul>\n"
+" <li> There's a\n"
+" <a href='http://www.dillo.org/dillorc'>dillorc</a>\n"
+" (readable config) file within the tarball; It is well commented\n"
+" and has plenty of options to customize dillo, so <STRONG>copy\n"
+" it</STRONG> to your <STRONG>~/.dillo/</STRONG> directory, and\n"
+" modify to your taste.\n"
+" <li> There's documentation for developers in the <CODE>/doc</CODE>\n"
+" dir within the tarball; you can find directions on everything\n"
+" else at the home page.\n"
+" <li> Dillo has context sensitive menus using the\n"
+" right mouse button (available on pages, links, images,\n"
+" the Back and Forward buttons, and bug meter).\n"
+" <li> Dillo behaves very nicely when browsing local files, images, and HTML.\n"
+" It's also very good for Internet searching (try Google!).\n"
+" <li> This release is mainly intended <strong>for developers</strong>\n"
+" and <em>advanced users</em>.\n"
+" <li> Frames, Java and Javascript are not supported.\n"
+"</ul>\n"
+"<br>\n"
+" </table>\n"
+"</table>\n"
+"</table>\n"
+"\n"
+"<table border='0' width='100%' cellpadding='0' cellspacing='0'><tr><td height='10'></table>\n"
+"\n"
+"\n"
+"<!-- the main layout table, a small vertical spacer -->\n"
+"\n"
+"<td width='20'>\n"
+"\n"
+"\n"
+"\n"
+"<!-- The right column (info) -->\n"
+"<td valign='top' align='center'>\n"
+"\n"
+"\n"
+"\n"
+"<!-- end of the main layout table -->\n"
+"\n"
+"\n"
+"</table>\n"
+"\n"
+"<!-- footnotes -->\n"
+"\n"
+"<br><br><center>\n"
+"<hr size='2'>\n"
+"<hr size='2'>\n"
+"</center>\n"
+"</body>\n"
+"</html>\n";
+
diff --git a/src/IO/dpi.c b/src/IO/dpi.c
new file mode 100644
index 00000000..13cd1f74
--- /dev/null
+++ b/src/IO/dpi.c
@@ -0,0 +1,779 @@
+/*
+ * File: dpi.c
+ *
+ * Copyright (C) 2002-2006 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+/*
+ * Dillo plugins (small programs that interact with dillo)
+ *
+ * Dillo plugins are designed to handle:
+ * bookmarks, cookies, FTP, downloads, files, preferences, https,
+ * datauri and a lot of any-to-html filters.
+ */
+
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h> /* for errno */
+
+#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "../msg.h"
+#include "../klist.h"
+#include "IO.h"
+#include "Url.h"
+#include "../misc.h"
+#include "../../dpip/dpip.h"
+
+/* #define DEBUG_LEVEL 2 */
+#define DEBUG_LEVEL 4
+#include "../debug.h"
+
+/* This one is tricky, some sources state it should include the byte
+ * for the terminating NULL, and others say it shouldn't. */
+# define D_SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
+ + strlen ((ptr)->sun_path))
+
+/* Solaris may not have this one... */
+#ifndef AF_LOCAL
+#define AF_LOCAL AF_UNIX
+#endif
+
+
+typedef struct {
+ int InTag;
+ int Send2EOF;
+
+ int DataTotalSize;
+ int DataRecvSize;
+
+ Dstr *Buf;
+
+ int BufIdx;
+ int TokIdx;
+ int TokSize;
+ int TokIsTag;
+
+ ChainLink *InfoRecv;
+ int Key;
+} dpi_conn_t;
+
+
+/*
+ * Local data
+ */
+static Klist_t *ValidConns = NULL; /* Active connections list. It holds
+ * pointers to dpi_conn_t structures. */
+
+
+/*
+ * Initialize local data
+ */
+void a_Dpi_init(void)
+{
+ /* empty */
+}
+
+/*
+ * Close a FD handling EINTR
+ */
+static void Dpi_close_fd(int fd)
+{
+ int st;
+
+ do
+ st = close(fd);
+ while (st < 0 && errno == EINTR);
+}
+
+/*
+ * Create a new connection data structure
+ */
+static dpi_conn_t *Dpi_conn_new(ChainLink *Info)
+{
+ dpi_conn_t *conn = dNew0(dpi_conn_t, 1);
+
+ conn->Buf = dStr_sized_new(8*1024);
+ conn->InfoRecv = Info;
+ conn->Key = a_Klist_insert(&ValidConns, conn);
+
+ return conn;
+}
+
+/*
+ * Free a connection data structure
+ */
+static void Dpi_conn_free(dpi_conn_t *conn)
+{
+ a_Klist_remove(ValidConns, conn->Key);
+ dStr_free(conn->Buf, 1);
+ dFree(conn);
+}
+
+/*
+ * Check whether a conn is still valid.
+ * Return: 1 if found, 0 otherwise
+ */
+int Dpi_conn_valid(int key)
+{
+ return (a_Klist_get_data(ValidConns, key)) ? 1 : 0;
+}
+
+/*
+ * Append the new buffer in 'dbuf' to Buf in 'conn'
+ */
+static void Dpi_append_dbuf(dpi_conn_t *conn, DataBuf *dbuf)
+{
+ if (dbuf->Code == 0 && dbuf->Size > 0) {
+ dStr_append_l(conn->Buf, dbuf->Buf, dbuf->Size);
+ }
+}
+
+/*
+ * Split the data stream into tokens.
+ * Here, a token is either:
+ * a) a dpi tag
+ * b) a raw data chunk
+ *
+ * Return Value: 0 upon a new token, -1 on not enough data.
+ *
+ * TODO: define an API and move this function into libDpip.a.
+*/
+static int Dpi_get_token(dpi_conn_t *conn)
+{
+ int i, resp = -1;
+ char *buf = conn->Buf->str;
+
+ if (conn->BufIdx == conn->Buf->len) {
+ dStr_truncate(conn->Buf, 0);
+ conn->BufIdx = 0;
+ return resp;
+ }
+
+ if (conn->Send2EOF) {
+ conn->TokIdx = conn->BufIdx;
+ conn->TokSize = conn->Buf->len - conn->BufIdx;
+ conn->BufIdx = conn->Buf->len;
+ return 0;
+ }
+
+ _MSG("conn->BufIdx = %d; conn->Buf->len = %d\nbuf: [%s]\n",
+ conn->BufIdx,conn->Buf->len, conn->Buf->str + conn->BufIdx);
+
+ if (!conn->InTag) {
+ /* search for start of tag */
+ while (conn->BufIdx < conn->Buf->len && buf[conn->BufIdx] != '<')
+ ++conn->BufIdx;
+ if (conn->BufIdx < conn->Buf->len) {
+ /* found */
+ conn->InTag = 1;
+ conn->TokIdx = conn->BufIdx;
+ } else {
+ MSG_ERR("[Dpi_get_token] Can't find token start\n");
+ }
+ }
+
+ if (conn->InTag) {
+ /* search for end of tag (EOT=" '>") */
+ for (i = conn->BufIdx; i < conn->Buf->len; ++i)
+ if (buf[i] == '>' && i >= 2 && buf[i-1] == '\'' && buf[i-2] == ' ')
+ break;
+ conn->BufIdx = i;
+
+ if (conn->BufIdx < conn->Buf->len) {
+ /* found EOT */
+ conn->TokIsTag = 1;
+ conn->TokSize = conn->BufIdx - conn->TokIdx + 1;
+ ++conn->BufIdx;
+ conn->InTag = 0;
+ resp = 0;
+ }
+ }
+
+ return resp;
+}
+
+/*
+ * Parse a dpi tag and take the appropriate actions
+ */
+static void Dpi_parse_token(dpi_conn_t *conn)
+{
+ char *tag, *cmd, *msg, *urlstr;
+ DataBuf *dbuf;
+ char *Tok = conn->Buf->str + conn->TokIdx;
+
+ if (conn->Send2EOF) {
+ /* we're receiving data chunks from a HTML page */
+ dbuf = a_Chain_dbuf_new(Tok, conn->TokSize, 0);
+ a_Chain_fcb(OpSend, conn->InfoRecv, dbuf, "send_page_2eof");
+ dFree(dbuf);
+ return;
+ }
+
+ tag = dStrndup(Tok, (size_t)conn->TokSize);
+ _MSG("Dpi_parse_token: {%s}\n", tag);
+
+ cmd = a_Dpip_get_attr(Tok, conn->TokSize, "cmd");
+ if (strcmp(cmd, "send_status_message") == 0) {
+ msg = a_Dpip_get_attr(Tok, conn->TokSize, "msg");
+ a_Chain_fcb(OpSend, conn->InfoRecv, msg, cmd);
+ dFree(msg);
+
+ } else if (strcmp(cmd, "chat") == 0) {
+ msg = a_Dpip_get_attr(Tok, conn->TokSize, "msg");
+ a_Chain_fcb(OpSend, conn->InfoRecv, msg, cmd);
+ dFree(msg);
+
+ } else if (strcmp(cmd, "dialog") == 0) {
+ /* For now will send the dpip tag... */
+ a_Chain_fcb(OpSend, conn->InfoRecv, tag, cmd);
+
+ } else if (strcmp(cmd, "start_send_page") == 0) {
+ conn->Send2EOF = 1;
+ urlstr = a_Dpip_get_attr(Tok, conn->TokSize, "url");
+ a_Chain_fcb(OpSend, conn->InfoRecv, urlstr, cmd);
+ dFree(urlstr);
+ /* todo: a_Dpip_get_attr(Tok, conn->TokSize, "send_mode") */
+
+ } else if (strcmp(cmd, "reload_request") == 0) {
+ urlstr = a_Dpip_get_attr(Tok, conn->TokSize, "url");
+ a_Chain_fcb(OpSend, conn->InfoRecv, urlstr, cmd);
+ dFree(urlstr);
+ }
+ dFree(cmd);
+
+ dFree(tag);
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/*
+ * Get a new data buffer (within a 'dbuf'), save it into local data,
+ * split in tokens and parse the contents.
+ */
+static void Dpi_process_dbuf(int Op, void *Data1, dpi_conn_t *conn)
+{
+ DataBuf *dbuf = Data1;
+ int key = conn->Key;
+
+ /* Very useful for debugging: show the data stream as received. */
+ /* fwrite(dbuf->Buf, dbuf->Size, 1, stdout); */
+
+ if (Op == IORead) {
+ Dpi_append_dbuf(conn, dbuf);
+ /* 'conn' has to be validated because Dpi_parse_token() MAY call abort */
+ while (Dpi_conn_valid(key) && Dpi_get_token(conn) != -1) {
+ Dpi_parse_token(conn);
+ }
+
+ } else if (Op == IOClose) {
+ /* unused */
+ }
+}
+
+/*
+ * Start dpid.
+ * Return: 0 starting now, 1 Error.
+ */
+static int Dpi_start_dpid(void)
+{
+ pid_t pid;
+ int st_pipe[2], n, ret = 1;
+ char buf[16];
+
+ /* create a pipe to track our child's status */
+ if (pipe(st_pipe))
+ return 1;
+
+ pid = fork();
+ if (pid == 0) {
+ /* This is the child process. Execute the command. */
+ char *path1 = dStrconcat(dGethomedir(), "/.dillo/dpid", NULL);
+ Dpi_close_fd(st_pipe[0]);
+ if (execl(path1, "dpid", NULL) == -1) {
+ dFree(path1);
+ if (execlp("dpid", "dpid", NULL) == -1) {
+ DEBUG_MSG(4, "Dpi_start_dpid (child): %s\n", dStrerror(errno));
+ do
+ n = write(st_pipe[1], "ERROR", 5);
+ while (n == -1 && errno == EINTR);
+ Dpi_close_fd(st_pipe[1]);
+ _exit (EXIT_FAILURE);
+ }
+ }
+ } else if (pid < 0) {
+ /* The fork failed. Report failure. */
+ DEBUG_MSG(4, "Dpi_start_dpid: %s\n", dStrerror(errno));
+ /* close the unused pipe */
+ Dpi_close_fd(st_pipe[0]);
+ Dpi_close_fd(st_pipe[1]);
+
+ } else {
+ /* This is the parent process, check our child status... */
+ Dpi_close_fd(st_pipe[1]);
+ do
+ n = read(st_pipe[0], buf, 16);
+ while (n == -1 && errno == EINTR);
+ DEBUG_MSG(2, "Dpi_start_dpid: n = %d\n", n);
+ if (n != 5) {
+ ret = 0;
+ } else {
+ DEBUG_MSG(4, "Dpi_start_dpid: %s\n", dStrerror(errno));
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Make a connection test for a UDS.
+ * Return: 0 OK, 1 Not working.
+ */
+static int Dpi_check_uds(char *uds_name)
+{
+ struct sockaddr_un pun;
+ int SockFD, ret = 1;
+
+ if (access(uds_name, W_OK) == 0) {
+ /* socket connection test */
+ memset(&pun, 0, sizeof(struct sockaddr_un));
+ pun.sun_family = AF_LOCAL;
+ strncpy(pun.sun_path, uds_name, sizeof (pun.sun_path));
+
+ if ((SockFD = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1 ||
+ connect(SockFD, (void*)&pun, D_SUN_LEN(&pun)) == -1) {
+ DEBUG_MSG(4, "Dpi_check_uds: %s %s\n", dStrerror(errno), uds_name);
+ } else {
+ Dpi_close_fd(SockFD);
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+/*
+ * Return the directory where the UDS are in,
+ * NULL if it can't be found.
+ */
+static char *Dpi_get_dpid_uds_dir(void)
+{
+ FILE *in;
+ char *saved_name_filename; /* :) */
+ char dpid_uds_dir[256], *p = NULL;
+
+ saved_name_filename =
+ dStrconcat(dGethomedir(), "/.dillo/dpi_socket_dir", NULL);
+ in = fopen(saved_name_filename, "r");
+ dFree(saved_name_filename);
+
+ if (in != NULL) {
+ fgets(dpid_uds_dir, 256, in);
+ fclose(in);
+ if ((p = strchr(dpid_uds_dir, '\n'))) {
+ *p = 0;
+ }
+ if (access(dpid_uds_dir, F_OK) == 0) {
+ p = dStrdup(dpid_uds_dir);
+ _MSG("Dpi_get_dpid_uds_dir:: %s\n", p);
+ }
+ }
+
+ _MSG("Dpi_get_dpid_uds_dir: %s \n", dStrerror(errno));
+ return p;
+}
+
+/*
+ * Return the dpid's UDS name, NULL on failure.
+ */
+static char *Dpi_get_dpid_uds_name(void)
+{
+ char *dpid_uds_dir, *dpid_uds_name = NULL;
+
+ if ((dpid_uds_dir = Dpi_get_dpid_uds_dir()) != NULL)
+ dpid_uds_name= dStrconcat(dpid_uds_dir, "/", "dpid.srs", NULL);
+
+ dFree(dpid_uds_dir);
+ return dpid_uds_name;
+}
+
+/*
+ * Confirm that the dpid is running. If not, start it.
+ * Return: 0 running OK, 1 starting (EAGAIN), 2 Error.
+ */
+static int Dpi_check_dpid(int num_tries)
+{
+ static int starting = 0;
+ char *dpid_uds_name;
+ int check_st = 1, ret = 2;
+
+ if ((dpid_uds_name = Dpi_get_dpid_uds_name()))
+ check_st = Dpi_check_uds(dpid_uds_name);
+
+ _MSG("Dpi_check_dpid: dpid_uds_name=%s, check_st=%d\n",
+ dpid_uds_name, check_st);
+
+ if (check_st == 0) {
+ /* connection test with dpi server passed */
+ starting = 0;
+ ret = 0;
+ } else if (!dpid_uds_name || check_st) {
+ if (!starting) {
+ /* start dpid */
+ if (Dpi_start_dpid() == 0) {
+ starting = 1;
+ ret = 1;
+ }
+ } else if (++starting < num_tries) {
+ ret = 1;
+ } else {
+ /* we waited too much, report an error... */
+ starting = 0;
+ }
+ }
+
+ dFree(dpid_uds_name);
+ DEBUG_MSG(2, "Dpi_check_dpid:: %s\n",
+ (ret == 0) ? "OK" : (ret == 1 ? "EAGAIN" : "ERROR"));
+ return ret;
+}
+
+/*
+ * Confirm that the dpid is running. If not, start it.
+ * Return: 0 running OK, 2 Error.
+ */
+static int Dpi_blocking_start_dpid(void)
+{
+ int cst, try = 0,
+ n_tries = 12; /* 3 seconds */
+
+ /* test the dpid, and wait a bit for it to start if necessary */
+ while ((cst = Dpi_check_dpid(n_tries)) == 1) {
+ MSG("Dpi_blocking_start_dpid: try %d\n", ++try);
+ usleep(250000); /* 1/4 sec */
+ }
+ return cst;
+}
+
+/*
+ * Return the UDS name of a dpi server.
+ * (A query is sent to dpid and then its answer parsed)
+ * note: as the available servers and/or the dpi socket directory can
+ * change at any time, we'll ask each time. If someday we find
+ * that connecting each time significantly degrades performance,
+ * an optimized approach can be tried.
+ */
+static char *Dpi_get_server_uds_name(const char *server_name)
+{
+ char *dpid_uds_dir, *dpid_uds_name = NULL,
+ *server_uds_name = NULL;
+ int st;
+
+ dReturn_val_if_fail (server_name != NULL, NULL);
+ DEBUG_MSG(2, "Dpi_get_server_uds_name:: server_name = [%s]\n", server_name);
+
+ dpid_uds_dir = Dpi_get_dpid_uds_dir();
+ if (dpid_uds_dir) {
+ struct sockaddr_un dpid;
+ int sock, req_sz, rdlen;
+ char buf[128], *cmd, *request, *rply;
+ size_t buflen;
+
+ /* Get the server's uds name from dpid */
+ sock = socket(AF_LOCAL, SOCK_STREAM, 0);
+ dpid.sun_family = AF_LOCAL;
+ dpid_uds_name = dStrconcat(dpid_uds_dir, "/", "dpid.srs", NULL);
+ _MSG("dpid_uds_name = [%s]\n", dpid_uds_name);
+ strncpy(dpid.sun_path, dpid_uds_name, sizeof(dpid.sun_path));
+
+ if (connect(sock, (struct sockaddr *) &dpid, sizeof(dpid)) == -1)
+ perror("connect");
+ /* ask dpid to check the server plugin and send its UDS name back */
+ request = a_Dpip_build_cmd("cmd=%s msg=%s", "check_server", server_name);
+ DEBUG_MSG(2, "[%s]\n", request);
+ do
+ st = write(sock, request, strlen(request));
+ while (st < 0 && errno == EINTR);
+ if (st < 0 && errno != EINTR)
+ perror("writing request");
+ dFree(request);
+ shutdown(sock, 1); /* signals no more writes to dpid */
+
+ /* Get the reply */
+ rply = NULL;
+ buf[0] = '\0';
+ buflen = sizeof(buf)/sizeof(buf[0]);
+ for (req_sz = 0; (rdlen = read(sock, buf, buflen)) != 0;
+ req_sz += rdlen) {
+ if (rdlen == -1 && errno == EINTR)
+ continue;
+ if (rdlen == -1) {
+ perror(" ** Dpi_get_server_uds_name **");
+ break;
+ }
+ rply = dRealloc(rply, (uint_t)(req_sz + rdlen + 1));
+ if (req_sz == 0)
+ rply[0] = '\0';
+ strncat(rply, buf, (size_t)rdlen);
+ }
+ Dpi_close_fd(sock);
+ DEBUG_MSG(2, "rply = [%s]\n", rply);
+
+ /* Parse reply */
+ if (rdlen == 0 && rply) {
+ cmd = a_Dpip_get_attr(rply, (int)strlen(rply), "cmd");
+ if (strcmp(cmd, "send_data") == 0)
+ server_uds_name = a_Dpip_get_attr(rply, (int)strlen(rply), "msg");
+ dFree(cmd);
+ dFree(rply);
+ }
+ }
+ dFree(dpid_uds_dir);
+ dFree(dpid_uds_name);
+ DEBUG_MSG(2, "Dpi_get_server_uds_name:: %s\n", server_uds_name);
+ return server_uds_name;
+}
+
+
+/*
+ * Connect a socket to a dpi server and return the socket's FD.
+ * We have to ask 'dpid' (dpi daemon) for the UDS of the target dpi server.
+ * Once we have it, then the proper file descriptor is returned (-1 on error).
+ */
+static int Dpi_connect_socket(const char *server_name, int retry)
+{
+ char *server_uds_name;
+ struct sockaddr_un pun;
+ int SockFD, err;
+
+ /* Query dpid for the UDS name for this server */
+ server_uds_name = Dpi_get_server_uds_name(server_name);
+ DEBUG_MSG(2, "server_uds_name = [%s]\n", server_uds_name);
+
+ if (access(server_uds_name, F_OK) != 0) {
+ MSG("server socket was NOT found\n");
+ return -1;
+ }
+
+ /* connect with this server's socket */
+ memset(&pun, 0, sizeof(struct sockaddr_un));
+ pun.sun_family = AF_LOCAL;
+ strncpy(pun.sun_path, server_uds_name, sizeof (pun.sun_path));
+ dFree(server_uds_name);
+
+ if ((SockFD = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1)
+ perror("[dpi::socket]");
+ else if (connect(SockFD, (void*)&pun, D_SUN_LEN(&pun)) == -1) {
+ err = errno;
+ SockFD = -1;
+ MSG("[dpi::connect] errno:%d %s\n", errno, dStrerror(errno));
+ if (retry) {
+ switch (err) {
+ case ECONNREFUSED: case EBADF: case ENOTSOCK: case EADDRNOTAVAIL:
+ /* the server may crash and its socket name survive */
+ unlink(pun.sun_path);
+ SockFD = Dpi_connect_socket(server_name, FALSE);
+ break;
+ }
+ }
+ }
+
+ return SockFD;
+}
+
+
+/*
+ * CCC function for the Dpi module
+ */
+void a_Dpi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
+ void *Data1, void *Data2)
+{
+ dpi_conn_t *conn;
+ int SockFD = -1, st;
+
+ a_Chain_debug_msg("a_Dpi_ccc", Op, Branch, Dir);
+
+ if (Branch == 1) {
+ if (Dir == BCK) {
+ /* Send commands to dpi-server */
+ switch (Op) {
+ case OpStart:
+ if ((st = Dpi_blocking_start_dpid()) == 0) {
+ SockFD = Dpi_connect_socket(Data1, TRUE);
+ if (SockFD != -1) {
+ int *fd = dNew(int, 1);
+ *fd = SockFD;
+ Info->LocalKey = fd;
+ a_Chain_link_new(Info, a_Dpi_ccc, BCK, a_IO_ccc, 1, 1);
+ a_Chain_bcb(OpStart, Info, Info->LocalKey, NULL);
+ /* tell the capi to start the receiving branch */
+ a_Chain_fcb(OpSend, Info, Info->LocalKey, "SockFD");
+ }
+ }
+
+ if (st == 0 && SockFD != -1) {
+ a_Chain_fcb(OpSend, Info, NULL, "DpidOK");
+ } else {
+ MSG_ERR("dpi.c: can't start dpi daemon\n");
+ a_Dpi_ccc(OpAbort, 1, FWD, Info, NULL, "DpidERROR");
+ }
+ break;
+ case OpSend:
+ a_Chain_bcb(OpSend, Info, Data1, NULL);
+ break;
+ case OpEnd:
+ a_Chain_bcb(OpEnd, Info, NULL, NULL);
+ dFree(Info->LocalKey);
+ dFree(Info);
+ break;
+ default:
+ MSG_WARN("Unused CCC\n");
+ break;
+ }
+ } else { /* FWD */
+ /* Send commands to dpi-server (status) */
+ switch (Op) {
+ case OpAbort:
+ a_Chain_fcb(OpAbort, Info, NULL, Data2);
+ dFree(Info);
+ break;
+ default:
+ MSG_WARN("Unused CCC\n");
+ break;
+ }
+ }
+
+ } else if (Branch == 2) {
+ if (Dir == FWD) {
+ /* Receiving from server */
+ switch (Op) {
+ case OpSend:
+ /* Data1 = dbuf */
+ Dpi_process_dbuf(IORead, Data1, Info->LocalKey);
+ break;
+ case OpEnd:
+ a_Chain_fcb(OpEnd, Info, NULL, NULL);
+ Dpi_conn_free(Info->LocalKey);
+ dFree(Info);
+ break;
+ default:
+ MSG_WARN("Unused CCC\n");
+ break;
+ }
+ } else { /* BCK */
+ switch (Op) {
+ case OpStart:
+ conn = Dpi_conn_new(Info);
+ Info->LocalKey = conn;
+
+ /* Hack: for receiving HTTP through the DPI framework */
+ if (strcmp(Data2, "http") == 0) {
+ conn->Send2EOF = 1;
+ }
+
+ a_Chain_link_new(Info, a_Dpi_ccc, BCK, a_IO_ccc, 2, 2);
+ a_Chain_bcb(OpStart, Info, NULL, Data1); /* IORead, SockFD */
+ break;
+ case OpAbort:
+ a_Chain_bcb(OpAbort, Info, NULL, NULL);
+ Dpi_conn_free(Info->LocalKey);
+ dFree(Info);
+ break;
+ default:
+ MSG_WARN("Unused CCC\n");
+ break;
+ }
+ }
+ }
+}
+
+/*! Send DpiBye to dpid
+ * Note: currently disabled. Maybe it'd be better to have a
+ * dpid_idle_timeout variable in the config file.
+ */
+void a_Dpi_bye_dpid()
+{
+ char *DpiBye_cmd;
+ struct sockaddr_un sa;
+ size_t sun_path_len, addr_len;
+ char *srs_name;
+ int new_socket;
+
+ srs_name = Dpi_get_dpid_uds_name();
+ sun_path_len = sizeof(sa.sun_path);
+ addr_len = sizeof(sa);
+
+ sa.sun_family = AF_LOCAL;
+
+ if ((new_socket = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) {
+ DEBUG_MSG(4, "a_Dpi_bye_dpid: %s\n", dStrerror(errno));
+ }
+ strncpy(sa.sun_path, srs_name, sizeof (sa.sun_path));
+ if (connect(new_socket, (struct sockaddr *) &sa, addr_len) == -1) {
+ DEBUG_MSG(4, "a_Dpi_bye_dpid: %s\n", dStrerror(errno));
+ MSG("%s\n", sa.sun_path);
+ }
+ DpiBye_cmd = a_Dpip_build_cmd("cmd=%s", "DpiBye");
+ (void) write(new_socket, DpiBye_cmd, strlen(DpiBye_cmd));
+ dFree(DpiBye_cmd);
+ Dpi_close_fd(new_socket);
+}
+
+
+/*
+ * Send a command to a dpi server, and block until the answer is got.
+ * Return value: the dpip tag answer as an string, NULL on error.
+ */
+char *a_Dpi_send_blocking_cmd(const char *server_name, const char *cmd)
+{
+ int cst, SockFD;
+ ssize_t st;
+ char buf[16384], *retval = NULL;
+
+ /* test the dpid, and wait a bit for it to start if necessary */
+ if ((cst = Dpi_blocking_start_dpid()) != 0) {
+ return retval;
+ }
+
+ SockFD = Dpi_connect_socket(server_name, TRUE);
+ if (SockFD != -1) {
+ /* todo: handle the case of (st < strlen(cmd)) */
+ do
+ st = write(SockFD, cmd, strlen(cmd));
+ while (st == -1 && errno == EINTR);
+
+ /* todo: if the answer is too long... */
+ do
+ st = read(SockFD, buf, 16384);
+ while (st < 0 && errno == EINTR);
+
+ if (st == -1)
+ perror("[a_Dpi_send_blocking_cmd]");
+ else if (st > 0)
+ retval = dStrndup(buf, (size_t)st);
+
+ Dpi_close_fd(SockFD);
+
+ } else {
+ perror("[a_Dpi_send_blocking_cmd]");
+ }
+
+ return retval;
+}
+
diff --git a/src/IO/http.c b/src/IO/http.c
new file mode 100644
index 00000000..f9b483fa
--- /dev/null
+++ b/src/IO/http.c
@@ -0,0 +1,494 @@
+/*
+ * File: http.c
+ *
+ * Copyright (C) 2000, 2001 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+/*
+ * HTTP connect functions
+ */
+
+
+#include <config.h>
+
+#include <unistd.h>
+#include <errno.h> /* for errno */
+#include <stdlib.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <sys/socket.h> /* for lots of socket stuff */
+#include <netinet/in.h> /* for ntohl and stuff */
+#include <arpa/inet.h> /* for inet_ntop */
+
+#include "IO.h"
+#include "Url.h"
+#include "../msg.h"
+#include "../klist.h"
+#include "../dns.h"
+#include "../cache.h"
+#include "../web.hh"
+#include "../cookies.h"
+#include "../prefs.h"
+#include "../misc.h"
+
+#include "../uicmd.hh"
+
+/* Used to send a message to the bw's status bar */
+#define MSG_BW(web, root, ...) \
+D_STMT_START { \
+ if (a_Web_valid((web)) && (!(root) || (web)->flags & WEB_RootUrl)) \
+ a_UIcmd_set_msg((web)->bw, __VA_ARGS__); \
+} D_STMT_END
+
+#define _MSG_BW(web, root, ...)
+
+#define DEBUG_LEVEL 5
+#include "../debug.h"
+
+
+
+/* 'Url' and 'web' are just references (no need to deallocate them here). */
+typedef struct {
+ int SockFD;
+ const DilloUrl *Url; /* reference to original URL */
+ uint_t port; /* need a separate port in order to support PROXY */
+ bool_t use_proxy; /* indicates whether to use proxy or not */
+ DilloWeb *web; /* reference to client's web structure */
+ Dlist *addr_list; /* Holds the DNS answer */
+ int Err; /* Holds the errno of the connect() call */
+ ChainLink *Info; /* Used for CCC asynchronous operations */
+} SocketData_t;
+
+
+/*
+ * Local data
+ */
+static Klist_t *ValidSocks = NULL; /* Active sockets list. It holds pointers to
+ * SocketData_t structures. */
+
+static DilloUrl *HTTP_Proxy = NULL;
+static char *HTTP_Proxy_Auth_base64 = NULL;
+
+/*
+ * Initialize proxy vars.
+ */
+int a_Http_init(void)
+{
+ char *env_proxy = getenv("http_proxy");
+
+ if (env_proxy && strlen(env_proxy))
+ HTTP_Proxy = a_Url_new(env_proxy, NULL, 0, 0, 0);
+ if (!HTTP_Proxy && prefs.http_proxy)
+ HTTP_Proxy = a_Url_dup(prefs.http_proxy);
+
+/* This allows for storing the proxy password in "user:passwd" format
+ * in dillorc, but as this constitutes a security problem, it was disabled.
+ *
+ if (HTTP_Proxy && prefs.http_proxyuser && strchr(prefs.http_proxyuser, ':'))
+ HTTP_Proxy_Auth_base64 = a_Misc_encode_base64(prefs.http_proxyuser);
+ */
+ return 0;
+}
+
+/*
+ * Tell whether the proxy auth is already set (user:password)
+ * Return: 1 Yes, 0 No
+ */
+int a_Http_proxy_auth(void)
+{
+ return (HTTP_Proxy_Auth_base64 ? 1 : 0);
+}
+
+/*
+ * Activate entered proxy password for HTTP.
+ */
+void a_Http_set_proxy_passwd(char *str)
+{
+ char *http_proxyauth = dStrconcat(prefs.http_proxyuser, ":", str, NULL);
+ HTTP_Proxy_Auth_base64 = a_Misc_encode_base64(http_proxyauth);
+ dFree(http_proxyauth);
+}
+
+/*
+ * Create and init a new SocketData_t struct, insert into ValidSocks,
+ * and return a primary key for it.
+ */
+static int Http_sock_new(void)
+{
+ SocketData_t *S = dNew0(SocketData_t, 1);
+ return a_Klist_insert(&ValidSocks, S);
+}
+
+/*
+ * Free SocketData_t struct
+ */
+static void Http_socket_free(int SKey)
+{
+ SocketData_t *S;
+
+ if ((S = a_Klist_get_data(ValidSocks, SKey))) {
+ a_Klist_remove(ValidSocks, SKey);
+ dFree(S);
+ }
+}
+
+/*
+ * Close the socket's FD
+ */
+static void Http_socket_close(SocketData_t *S)
+{
+ int st;
+ do
+ st = close(S->SockFD);
+ while (st < 0 && errno == EINTR);
+}
+
+/*
+ * Make the http query string
+ */
+char *a_Http_make_query_str(const DilloUrl *url, bool_t use_proxy)
+{
+ char *str, *ptr, *cookies;
+ Dstr *s_port = dStr_new(""),
+ *query = dStr_new(""),
+ *full_path = dStr_new(""),
+ *proxy_auth = dStr_new("");
+
+ /* Sending the default port in the query may cause a 302-answer. --Jcid */
+ if (URL_PORT(url) && URL_PORT(url) != DILLO_URL_HTTP_PORT)
+ dStr_sprintfa(s_port, ":%d", URL_PORT(url));
+
+ if (use_proxy) {
+ dStr_sprintfa(full_path, "%s%s",
+ URL_STR(url),
+ (URL_PATH_(url) || URL_QUERY_(url)) ? "" : "/");
+ if ((ptr = strrchr(full_path->str, '#')))
+ dStr_truncate(full_path, ptr - full_path->str);
+ if (HTTP_Proxy_Auth_base64)
+ dStr_sprintf(proxy_auth, "Proxy-Authorization: Basic %s\r\n",
+ HTTP_Proxy_Auth_base64);
+ } else {
+ dStr_sprintfa(full_path, "%s%s%s%s",
+ URL_PATH(url),
+ URL_QUERY_(url) ? "?" : "",
+ URL_QUERY(url),
+ (URL_PATH_(url) || URL_QUERY_(url)) ? "" : "/");
+ }
+
+ cookies = a_Cookies_get(url);
+ if (URL_FLAGS(url) & URL_Post) {
+ dStr_sprintfa(
+ query,
+ "POST %s HTTP/1.0\r\n"
+ "Accept-Charset: utf-8, iso-8859-1\r\n"
+ "Host: %s%s\r\n"
+ "%s"
+ "User-Agent: Dillo/%s\r\n"
+ "Cookie2: $Version=\"1\"\r\n"
+ "%s"
+ "Content-type: application/x-www-form-urlencoded\r\n"
+ "Content-length: %ld\r\n"
+ "\r\n"
+ "%s",
+ full_path->str, URL_HOST(url), s_port->str,
+ proxy_auth->str, VERSION, cookies,
+ (long)strlen(URL_DATA(url)),
+ URL_DATA(url));
+
+ } else {
+ dStr_sprintfa(
+ query,
+ "GET %s HTTP/1.0\r\n"
+ "%s"
+ "Accept-Charset: utf-8, iso-8859-1\r\n"
+ "Host: %s%s\r\n"
+ "%s"
+ "User-Agent: Dillo/%s\r\n"
+ "Cookie2: $Version=\"1\"\r\n"
+ "%s"
+ "\r\n",
+ full_path->str,
+ (URL_FLAGS(url) & URL_E2EReload) ?
+ "Cache-Control: no-cache\r\nPragma: no-cache\r\n" : "",
+ URL_HOST(url), s_port->str,
+ proxy_auth->str,
+ VERSION,
+ cookies);
+ }
+ dFree(cookies);
+
+ str = query->str;
+ dStr_free(query, FALSE);
+ dStr_free(s_port, TRUE);
+ dStr_free(full_path, TRUE);
+ dStr_free(proxy_auth, TRUE);
+ DEBUG_MSG(4, "Query:\n%s", str);
+ return str;
+}
+
+/*
+ * Create and submit the HTTP query to the IO engine
+ */
+static void Http_send_query(ChainLink *Info, SocketData_t *S)
+{
+ char *query;
+ DataBuf *dbuf;
+
+ /* Create the query */
+ query = a_Http_make_query_str(S->Url, S->use_proxy);
+ dbuf = a_Chain_dbuf_new(query, (int)strlen(query), 0);
+
+ /* actually this message is sent too early.
+ * It should go when the socket is ready for writing (i.e. connected) */
+ _MSG_BW(S->web, 1, "Sending query to %s...", URL_HOST_(S->Url));
+
+ /* send query */
+ a_Chain_link_new(Info, a_Http_ccc, BCK, a_IO_ccc, 1, 1);
+ a_Chain_bcb(OpStart, Info, &S->SockFD, NULL);
+ a_Chain_bcb(OpSend, Info, dbuf, NULL);
+ dFree(dbuf);
+ dFree(query);
+
+ /* Tell the cache to start the receiving CCC for the answer */
+ a_Chain_fcb(OpSend, Info, &S->SockFD, "SockFD");
+}
+
+/*
+ * This function gets called after the DNS succeeds solving a hostname.
+ * Task: Finish socket setup and start connecting the socket.
+ * Return value: 0 on success; -1 on error.
+ */
+static int Http_connect_socket(ChainLink *Info)
+{
+ int status;
+#ifdef ENABLE_IPV6
+ struct sockaddr_in6 name;
+#else
+ struct sockaddr_in name;
+#endif
+ SocketData_t *S;
+ DilloHost *dh;
+ socklen_t socket_len = 0;
+
+ S = a_Klist_get_data(ValidSocks, VOIDP2INT(Info->LocalKey));
+
+ /* TODO: iterate this address list until success, or end-of-list */
+ dh = dList_nth_data(S->addr_list, 0);
+
+ if ((S->SockFD = socket(dh->af, SOCK_STREAM, IPPROTO_TCP)) < 0) {
+ S->Err = errno;
+ DEBUG_MSG(5, "Http_connect_socket ERROR: %s\n", dStrerror(errno));
+ return -1;
+ }
+ /* set NONBLOCKING and close on exec. */
+ fcntl(S->SockFD, F_SETFL, O_NONBLOCK | fcntl(S->SockFD, F_GETFL));
+ fcntl(S->SockFD, F_SETFD, FD_CLOEXEC | fcntl(S->SockFD, F_GETFD));
+
+ /* Some OSes require this... */
+ memset(&name, 0, sizeof(name));
+ /* Set remaining parms. */
+ switch (dh->af) {
+ case AF_INET:
+ {
+ struct sockaddr_in *sin = (struct sockaddr_in *)&name;
+ socket_len = sizeof(struct sockaddr_in);
+ sin->sin_family = dh->af;
+ sin->sin_port = S->port ? htons(S->port) : htons(DILLO_URL_HTTP_PORT);
+ memcpy(&sin->sin_addr, dh->data, (size_t)dh->alen);
+ if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl))
+ DEBUG_MSG(5, "Connecting to %s\n", inet_ntoa(sin->sin_addr));
+ break;
+ }
+#ifdef ENABLE_IPV6
+ case AF_INET6:
+ {
+ char buf[128];
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&name;
+ socket_len = sizeof(struct sockaddr_in6);
+ sin6->sin6_family = dh->af;
+ sin6->sin6_port = S->port ? htons(S->port) : htons(DILLO_URL_HTTP_PORT);
+ memcpy(&sin6->sin6_addr, dh->data, dh->alen);
+ inet_ntop(dh->af, dh->data, buf, sizeof(buf));
+ if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl))
+ DEBUG_MSG(5, "Connecting to %s\n", buf);
+ break;
+ }
+#endif
+ }
+
+ MSG_BW(S->web, 1, "Contacting host...");
+ status = connect(S->SockFD, (struct sockaddr *)&name, socket_len);
+ if (status == -1 && errno != EINPROGRESS) {
+ S->Err = errno;
+ Http_socket_close(S);
+ DEBUG_MSG(5, "Http_connect_socket ERROR: %s\n", dStrerror(S->Err));
+ return -1;
+ } else {
+ Http_send_query(S->Info, S);
+ }
+
+ return 0; /* Success */
+}
+
+/*
+ * Test proxy settings and check the no_proxy domains list
+ * Return value: whether to use proxy or not.
+ */
+static int Http_must_use_proxy(const DilloUrl *url)
+{
+ char *np, *p, *tok;
+ int ret = 0;
+
+ if (HTTP_Proxy) {
+ ret = 1;
+ if (prefs.no_proxy) {
+ np = dStrdup(prefs.no_proxy);
+ for (p = np; (tok = dStrsep(&p, " ")); ) {
+ if (dStristr(URL_AUTHORITY(url), tok)) {
+ ret = 0;
+ break;
+ }
+ }
+ dFree(np);
+ }
+ }
+ return ret;
+}
+
+/*
+ * Callback function for the DNS resolver.
+ * Continue connecting the socket, or abort upon error condition.
+ */
+void a_Http_dns_cb(int Status, Dlist *addr_list, void *data)
+{
+ int SKey = VOIDP2INT(data);
+ SocketData_t *S;
+
+ S = a_Klist_get_data(ValidSocks, SKey);
+ if (S) {
+ if (Status == 0 && addr_list) {
+ /* Successful DNS answer; save the IP */
+ S->addr_list = addr_list;
+ /* start connecting the socket */
+ if (Http_connect_socket(S->Info) < 0) {
+ MSG_BW(S->web, 1, "ERROR: %s", dStrerror(S->Err));
+ a_Chain_fcb(OpAbort, S->Info, NULL, NULL);
+ dFree(S->Info);
+ Http_socket_free(SKey);
+ }
+
+ } else {
+ /* DNS wasn't able to resolve the hostname */
+ MSG_BW(S->web, 0, "ERROR: Dns can't resolve %s",
+ (S->use_proxy) ? URL_HOST_(HTTP_Proxy) : URL_HOST_(S->Url));
+ a_Chain_fcb(OpAbort, S->Info, NULL, NULL);
+ dFree(S->Info);
+ Http_socket_free(SKey);
+ }
+ }
+}
+
+/*
+ * Asynchronously create a new http connection for 'Url'
+ * We'll set some socket parameters; the rest will be set later
+ * when the IP is known.
+ * ( Data1 = Web structure )
+ * Return value: 0 on success, -1 otherwise
+ */
+static int Http_get(ChainLink *Info, void *Data1)
+{
+ SocketData_t *S;
+ char *hostname;
+
+ S = a_Klist_get_data(ValidSocks, VOIDP2INT(Info->LocalKey));
+ /* Reference Web data */
+ S->web = Data1;
+ /* Reference URL data */
+ S->Url = S->web->url;
+ /* Reference Info data */
+ S->Info = Info;
+
+ /* Proxy support */
+ if (Http_must_use_proxy(S->Url)) {
+ hostname = dStrdup(URL_HOST(HTTP_Proxy));
+ S->port = URL_PORT(HTTP_Proxy);
+ S->use_proxy = TRUE;
+ } else {
+ hostname = dStrdup(URL_HOST(S->Url));
+ S->port = URL_PORT(S->Url);
+ S->use_proxy = FALSE;
+ }
+
+ /* Let the user know what we'll do */
+ MSG_BW(S->web, 1, "DNS resolving %s", URL_HOST_(S->Url));
+
+ /* Let the DNS engine resolve the hostname, and when done,
+ * we'll try to connect the socket from the callback function */
+ a_Dns_resolve(hostname, a_Http_dns_cb, Info->LocalKey);
+
+ dFree(hostname);
+ return 0;
+}
+
+/*
+ * CCC function for the HTTP module
+ */
+void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info,
+ void *Data1, void *Data2)
+{
+ int SKey = VOIDP2INT(Info->LocalKey);
+
+ a_Chain_debug_msg("a_Http_ccc", Op, Branch, Dir);
+
+ if (Branch == 1) {
+ if (Dir == BCK) {
+ /* HTTP query branch */
+ switch (Op) {
+ case OpStart:
+ /* ( Data1 = Web ) */
+ SKey = Http_sock_new();
+ Info->LocalKey = INT2VOIDP(SKey);
+ Http_get(Info, Data1);
+ break;
+ case OpEnd:
+ /* finished the HTTP query branch */
+ a_Chain_bcb(OpEnd, Info, NULL, NULL);
+ Http_socket_free(SKey);
+ dFree(Info);
+ break;
+ case OpAbort:
+ /* something bad happened... */
+ a_Chain_bcb(OpAbort, Info, NULL, NULL);
+ Http_socket_free(SKey);
+ dFree(Info);
+ break;
+ }
+ } else { /* FWD */
+ /* HTTP send-query status branch */
+ switch (Op) {
+ default:
+ MSG_WARN("Unused CCC\n");
+ break;
+ }
+ }
+ }
+}
+
+
+
+/*
+ * Deallocate memory used by http module
+ * (Call this one at exit time)
+ */
+void a_Http_freeall(void)
+{
+ a_Klist_free(&ValidSocks);
+ a_Url_free(HTTP_Proxy);
+ dFree(HTTP_Proxy_Auth_base64);
+}
diff --git a/src/IO/iowatch.cc b/src/IO/iowatch.cc
new file mode 100644
index 00000000..2c1465ba
--- /dev/null
+++ b/src/IO/iowatch.cc
@@ -0,0 +1,35 @@
+/*
+ * File: iowatch.cc
+ *
+ * Copyright (C) 2005 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+// Simple ADT for watching file descriptor activity
+
+#include <fltk/run.h>
+#include "iowatch.hh"
+
+using namespace fltk;
+
+
+//
+// Hook a Callback for a certain activities in a FD
+//
+void a_IOwatch_add_fd(int fd, int when, FileHandler Callback, void *usr_data=0)
+{
+ add_fd(fd, when, Callback, usr_data);
+}
+
+//
+// Remove a Callback for a given FD (or just remove some events)
+//
+void a_IOwatch_remove_fd(int fd, int when)
+{
+ remove_fd(fd, when);
+}
+
diff --git a/src/IO/iowatch.hh b/src/IO/iowatch.hh
new file mode 100644
index 00000000..681d0080
--- /dev/null
+++ b/src/IO/iowatch.hh
@@ -0,0 +1,25 @@
+#ifndef __IO_WATCH_H__
+#define __IO_WATCH_H__
+
+/*
+ * BUG: enum {READ = 1, WRITE = 4, EXCEPT = 8} borrowed from fltk/run.h
+ */
+#define DIO_READ 1
+#define DIO_WRITE 4
+#define DIO_EXCEPT 8
+
+typedef void (*CbFunction_t)(int fd, void *data);
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void a_IOwatch_add_fd(int fd,int when,CbFunction_t Callback,void *usr_data);
+void a_IOwatch_remove_fd(int fd,int when);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __IO_WATCH_H__ */
+
diff --git a/src/IO/mime.c b/src/IO/mime.c
new file mode 100644
index 00000000..3a8040ae
--- /dev/null
+++ b/src/IO/mime.c
@@ -0,0 +1,152 @@
+/*
+ * File: mime.c
+ *
+ * Copyright (C) 2000 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "mime.h"
+#include "../msg.h"
+#include "../list.h"
+
+
+typedef struct {
+ const char *Name; /* MIME type name */
+ Viewer_t Data; /* Pointer to a function */
+} MimeItem_t;
+
+
+/*
+ * Local data
+ */
+static int MimeMinItemsSize = 0, MimeMinItemsMax = 8;
+static MimeItem_t *MimeMinItems = NULL;
+
+static int MimeMajItemsSize = 0, MimeMajItemsMax = 8;
+static MimeItem_t *MimeMajItems = NULL;
+
+
+/*
+ * Add a specific MIME type (as "image/png") to our viewer list
+ * 'Key' is the content-type string that identifies the MIME type
+ * 'Method' is the function that handles it
+ */
+static int Mime_add_minor_type(const char *Key, Viewer_t Method)
+{
+ a_List_add(MimeMinItems, MimeMinItemsSize, MimeMinItemsMax);
+ MimeMinItems[MimeMinItemsSize].Name = Key;
+ MimeMinItems[MimeMinItemsSize].Data = Method;
+ MimeMinItemsSize++;
+ return 0;
+}
+
+/*
+ * Add a major MIME type (as "text") to our viewer list
+ * 'Key' is the content-type string that identifies the MIME type
+ * 'Method' is the function that handles it
+ */
+static int Mime_add_major_type(const char *Key, Viewer_t Method)
+{
+ a_List_add(MimeMajItems, MimeMajItemsSize, MimeMajItemsMax);
+ MimeMajItems[MimeMajItemsSize].Name = Key;
+ MimeMajItems[MimeMajItemsSize].Data = Method;
+ MimeMajItemsSize++;
+ return 0;
+}
+
+/*
+ * Search the list of specific MIME viewers, for a Method that matches 'Key'
+ * 'Key' is the content-type string that identifies the MIME type
+ */
+static Viewer_t Mime_minor_type_fetch(const char *Key, uint_t Size)
+{
+ int i;
+
+ if (Size) {
+ for ( i = 0; i < MimeMinItemsSize; ++i )
+ if (dStrncasecmp(Key, MimeMinItems[i].Name, Size) == 0)
+ return MimeMinItems[i].Data;
+ }
+ return NULL;
+}
+
+/*
+ * Search the list of major MIME viewers, for a Method that matches 'Key'
+ * 'Key' is the content-type string that identifies the MIME type
+ */
+static Viewer_t Mime_major_type_fetch(const char *Key, uint_t Size)
+{
+ int i;
+
+ if (Size) {
+ for ( i = 0; i < MimeMajItemsSize; ++i )
+ if (dStrncasecmp(Key, MimeMajItems[i].Name, Size) == 0)
+ return MimeMajItems[i].Data;
+ }
+ return NULL;
+}
+
+
+/*
+ * Initializes Mime module and, sets the supported Mime types.
+ */
+void a_Mime_init()
+{
+#ifdef ENABLE_GIF
+ Mime_add_minor_type("image/gif", a_Gif_image);
+#endif
+#ifdef ENABLE_JPEG
+ Mime_add_minor_type("image/jpeg", a_Jpeg_image);
+ Mime_add_minor_type("image/pjpeg", a_Jpeg_image);
+ Mime_add_minor_type("image/jpg", a_Jpeg_image);
+#endif
+#ifdef ENABLE_PNG
+ Mime_add_minor_type("image/png", a_Png_image);
+ Mime_add_minor_type("image/x-png", a_Png_image); /* deprecated */
+#endif
+ Mime_add_minor_type("text/html", a_Html_text);
+
+ /* Add a major type to handle all the text stuff */
+ Mime_add_major_type("text", a_Plain_text);
+}
+
+
+/*
+ * Call the handler for the MIME type to set Call and Data as appropriate
+ *
+ * Return Value:
+ * On success: a new Dw (and Call and Data properly set).
+ * On failure: NULL (and Call and Data untouched).
+ */
+void *a_Mime_set_viewer(const char *content_type, void *Ptr,
+ CA_Callback_t *Call, void **Data)
+{
+
+ Viewer_t viewer;
+ uint_t MinSize, MajSize, i;
+ const char *str = content_type;
+
+ MajSize = 0;
+ for (i = 0; str[i] && str[i] != ' ' && str[i] != ';'; ++i) {
+ if (str[i] == '/' && !MajSize)
+ MajSize = i;
+ }
+ MinSize = i;
+
+ /* Try minor type */
+ viewer = Mime_minor_type_fetch(content_type, MinSize);
+ if (viewer)
+ return viewer(content_type, Ptr, Call, Data);
+
+ /* Try major type */
+ viewer = Mime_major_type_fetch(content_type, MajSize);
+ if (viewer)
+ return viewer(content_type, Ptr, Call, Data);
+
+ /* Type not handled */
+ return NULL;
+}
diff --git a/src/IO/mime.h b/src/IO/mime.h
new file mode 100644
index 00000000..0f51a1e2
--- /dev/null
+++ b/src/IO/mime.h
@@ -0,0 +1,58 @@
+/*
+ * File: mime.h
+ *
+ * Copyright (C) 2005 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __MIME_H__
+#define __MIME_H__
+
+#include <config.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#include "../cache.h"
+
+typedef void* (*Viewer_t) (const char*, void*, CA_Callback_t*, void**);
+
+/*
+ * Function prototypes defined elsewhere
+ */
+void *a_Html_text (const char *Type,void *web, CA_Callback_t *Call,
+ void **Data);
+void *a_Plain_text(const char *Type,void *web, CA_Callback_t *Call,
+ void **Data);
+#ifdef ENABLE_JPEG
+void *a_Jpeg_image(const char *Type,void *web, CA_Callback_t *Call,
+ void **Data);
+#endif
+#ifdef ENABLE_PNG
+void *a_Png_image (const char *Type,void *web, CA_Callback_t *Call,
+ void **Data);
+#endif
+#ifdef ENABLE_GIF
+void *a_Gif_image (const char *Type,void *web, CA_Callback_t *Call,
+ void **Data);
+#endif
+
+/*
+ * Functions defined inside Mime module
+ */
+void a_Mime_init(void);
+void *a_Mime_set_viewer(const char *content_type, void *Ptr,
+ CA_Callback_t *Call, void **Data);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __MIME_H__ */
diff --git a/src/IO/proto.c b/src/IO/proto.c
new file mode 100644
index 00000000..3a9e2177
--- /dev/null
+++ b/src/IO/proto.c
@@ -0,0 +1,13 @@
+/*
+ * File: proto.c
+ *
+ * Copyright (C) 2003 Jorge Arellano Cid <jcid@dillo.org>,
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+/* This module may be programmed to manage dpi-programs. */
+