diff options
Diffstat (limited to 'src/IO')
-rw-r--r-- | src/IO/IO.c | 412 | ||||
-rw-r--r-- | src/IO/IO.h | 45 | ||||
-rw-r--r-- | src/IO/Makefile.am | 14 | ||||
-rw-r--r-- | src/IO/Url.h | 40 | ||||
-rw-r--r-- | src/IO/about.c | 344 | ||||
-rw-r--r-- | src/IO/dpi.c | 779 | ||||
-rw-r--r-- | src/IO/http.c | 494 | ||||
-rw-r--r-- | src/IO/iowatch.cc | 35 | ||||
-rw-r--r-- | src/IO/iowatch.hh | 25 | ||||
-rw-r--r-- | src/IO/mime.c | 152 | ||||
-rw-r--r-- | src/IO/mime.h | 58 | ||||
-rw-r--r-- | src/IO/proto.c | 13 |
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> Welcome to Dillo " VERSION " </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> \n" +" <td>\n" +" <a href='http://www.dillo.org/'>Home</a>\n" +" <tr>\n" +" <td> \n" +" <td>\n" +" <a href='http://www.dillo.org/funding/objectives.html'>\n" +" Objectives</a>\n" +" <tr>\n" +" <td> \n" +" <td>\n" +" <a href='http://www.dillo.org/ChangeLog.html'>\n" +" ChangeLog</a>\n" +" <tr>\n" +" <td> \n" +" <td>\n" +" <a href='http://www.dillo.org/interview.html'>\n" +" Interview</a>\n" +" <tr>\n" +" <td> \n" +" <td>\n" +" <a href='http://www.dillo.org/D_authors.html'>\n" +" Authors</a>\n" +" <tr>\n" +" <td> \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> \n" +" <td>\n" +" <a href='http://lwn.net/'>LWN</a>\n" +" <tr>\n" +" <td> \n" +" <td>\n" +" <a href='http://slashdot.org/'>Slashdot</a>\n" +" <tr>\n" +" <td> \n" +" <td>\n" +" <a href='http://www.kuro5hin.org/?op=section;section=__all__'>KuroShin</a>\n" +" <tr>\n" +" <td> \n" +" <td>\n" +" <a href='http://www.nexusmagazine.com/'>Nexus M.</a>\n" +" <tr>\n" +" <td> \n" +" <td>\n" +" <a href='http://www.gnu-darwin.org/update.html'>Monster News</a>\n" +" <tr>\n" +" <td> \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> \n" +" <td><a href='http://www.google.com/'>Google</a>\n" +" <tr>\n" +" <td> \n" +" <td><a href='http://www.wikipedia.org/'>Wikipedia</a>\n" +" <tr>\n" +" <td> \n" +" <td><a href='http://www.gutenberg.org/'>P. Gutenberg</a>\n" +" <tr>\n" +" <td> \n" +" <td><a href='http://freshmeat.net/'>FreshMeat</a>\n" +" <tr>\n" +" <td> \n" +" <td><a href='http://www.gnu.org/gnu/thegnuproject.html'>GNU\n" +" project</a>\n" +" <tr>\n" +" <td> \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> \n" +" <td><a href='http://www.violence.de'>Peace&Violence</a>\n" +" <tr><td> \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. */ + |