aboutsummaryrefslogtreecommitdiff
path: root/dpi
diff options
context:
space:
mode:
Diffstat (limited to 'dpi')
-rw-r--r--dpi/Makefile.am7
-rw-r--r--dpi/bookmarks.c286
-rw-r--r--dpi/cookies.c1613
-rw-r--r--dpi/datauri.c191
-rw-r--r--dpi/downloads.cc108
-rw-r--r--dpi/dpiutil.c149
-rw-r--r--dpi/dpiutil.h45
-rw-r--r--dpi/file.c827
-rw-r--r--dpi/ftp.c110
-rw-r--r--dpi/hello.c81
-rw-r--r--dpi/https.c247
-rw-r--r--dpi/vsource.c239
12 files changed, 2220 insertions, 1683 deletions
diff --git a/dpi/Makefile.am b/dpi/Makefile.am
index 43101359..34e07483 100644
--- a/dpi/Makefile.am
+++ b/dpi/Makefile.am
@@ -3,6 +3,7 @@ downloadsdir = $(libdir)/dillo/dpi/downloads
ftpdir = $(libdir)/dillo/dpi/ftp
httpsdir = $(libdir)/dillo/dpi/https
hellodir = $(libdir)/dillo/dpi/hello
+vsourcedir = $(libdir)/dillo/dpi/vsource
filedir = $(libdir)/dillo/dpi/file
cookiesdir = $(libdir)/dillo/dpi/cookies
datauridir = $(libdir)/dillo/dpi/datauri
@@ -11,6 +12,7 @@ downloads_PROGRAMS = downloads.dpi
ftp_PROGRAMS = ftp.filter.dpi
https_PROGRAMS = https.filter.dpi
hello_PROGRAMS = hello.filter.dpi
+vsource_PROGRAMS = vsource.filter.dpi
file_PROGRAMS = file.dpi
cookies_PROGRAMS = cookies.dpi
datauri_PROGRAMS = datauri.filter.dpi
@@ -20,11 +22,11 @@ downloads_dpi_LDADD = @LIBFLTK_LIBS@ ../dpip/libDpip.a ../dlib/libDlib.a
ftp_filter_dpi_LDADD = ../dpip/libDpip.a ../dlib/libDlib.a
https_filter_dpi_LDADD = @LIBSSL_LIBS@ ../dpip/libDpip.a ../dlib/libDlib.a
hello_filter_dpi_LDADD = ../dpip/libDpip.a ../dlib/libDlib.a
-file_dpi_LDADD = @LIBPTHREAD_LIBS@ ../dpip/libDpip.a ../dlib/libDlib.a
+vsource_filter_dpi_LDADD = ../dpip/libDpip.a ../dlib/libDlib.a
+file_dpi_LDADD = ../dpip/libDpip.a ../dlib/libDlib.a
cookies_dpi_LDADD = ../dpip/libDpip.a ../dlib/libDlib.a
datauri_filter_dpi_LDADD = ../dpip/libDpip.a ../dlib/libDlib.a
-file_dpi_LDFLAGS = @LIBPTHREAD_LDFLAGS@
downloads_dpi_CXXFLAGS = @LIBFLTK_CXXFLAGS@
bookmarks_dpi_SOURCES = bookmarks.c dpiutil.c dpiutil.h
@@ -32,6 +34,7 @@ downloads_dpi_SOURCES = downloads.cc dpiutil.c dpiutil.h
ftp_filter_dpi_SOURCES = ftp.c dpiutil.c dpiutil.h
https_filter_dpi_SOURCES = https.c dpiutil.c dpiutil.h
hello_filter_dpi_SOURCES = hello.c dpiutil.c dpiutil.h
+vsource_filter_dpi_SOURCES = vsource.c dpiutil.c dpiutil.h
file_dpi_SOURCES = file.c dpiutil.c dpiutil.h
cookies_dpi_SOURCES = cookies.c dpiutil.c dpiutil.h
datauri_filter_dpi_SOURCES = datauri.c dpiutil.c dpiutil.h
diff --git a/dpi/bookmarks.c b/dpi/bookmarks.c
index e5447331..58fc59f5 100644
--- a/dpi/bookmarks.c
+++ b/dpi/bookmarks.c
@@ -44,11 +44,6 @@
#define _MSG(...)
#define MSG(...) printf("[bookmarks dpi]: " __VA_ARGS__)
-/* 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))
-
#define DOCTYPE \
"<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
@@ -101,13 +96,13 @@ DOCTYPE
"<head>\n"
"<title>Bookmarks</title>\n"
"</head>\n"
-"<body bgcolor='#778899' link='black' vlink='brown'>\n"
+"<body id='dillo_bm' bgcolor='#778899' link='black' vlink='brown'>\n"
"<table border='1' cellpadding='0' width='100%'>\n"
" <tr><td>\n"
" <table width='100%' bgcolor='#b4b4b4'>\n"
" <tr>\n"
-" <td>&nbsp;Bookmarks::</td>\n"
-" <td width='100%' align='right'>\n"
+" <td> Bookmarks :: </td>\n"
+" <td align='right'>\n"
" [<a href='dpi:/bm/modify'>modify</a>]\n"
" </td></tr>\n"
" </table></td></tr>\n"
@@ -120,34 +115,36 @@ DOCTYPE
"<head>\n"
"<title>Bookmarks</title>\n"
"</head>\n"
-"<body bgcolor='#778899' link='black' vlink='brown'>\n"
+"<body id='dillo_bm' bgcolor='#778899' link='black' vlink='brown'>\n"
"<table border='1' cellpadding='0' width='100%'>\n"
" <tr><td>\n"
" <table width='100%' bgcolor='#b4b4b4'>\n"
" <tr>\n"
-" <td>&nbsp;Bookmarks :: modify</td></tr>\n"
+" <td> Bookmarks :: modify</td>\n"
+" <td align='right'>\n"
+" [<a href='dpi:/bm/'>cancel</a>]\n"
+" </td>\n"
+" </tr>\n"
" </table></td></tr> \n"
"</table> \n"
"\n"
-"<form>\n"
+"<form action='modify'>\n"
"<table width='100%' border='1' cellpadding='0'>\n"
-" <tr><td>\n"
-" <table width='100%' bgcolor='teal'>\n"
-" <tr>\n"
-" <td><b>Select&nbsp;an&nbsp;operation&nbsp;</b></td>\n"
-" <td><select name='operation'>\n"
-" <option value='none' selected>--\n"
-" <option value='delete'>Delete\n"
-" <option value='move'>Move\n"
-" <option value='modify'>Modify\n"
-" <option value='add_sec'>Add Section\n"
-" <option value='add_url'>Add URL\n"
-" </select></td>\n"
-" <td><b>,&nbsp;mark&nbsp;its&nbsp;operands,&nbsp;and&nbsp;</b></td>\n"
-" <td><input type='submit' name='submit' value='submit.'></td>\n"
-" <td width='100%'></td>\n"
-" </tr>\n"
-" </table></td></tr>\n"
+" <tr style='background-color: teal'>\n"
+" <td>\n"
+" <b>Select an operation</b>\n"
+" <select name='operation'>\n"
+" <option value='none' selected>--\n"
+" <option value='delete'>Delete\n"
+" <option value='move'>Move\n"
+" <option value='modify'>Modify\n"
+" <option value='add_sec'>Add Section\n"
+" <option value='add_url'>Add URL\n"
+" </select>\n"
+" <b>, mark its operands, and</b>\n"
+" <input type='submit' name='submit' value='submit.'>\n"
+" </td>\n"
+" </tr>\n"
"</table>\n";
static const char *mainpage_sections_header =
@@ -248,15 +245,22 @@ DOCTYPE
"<head>\n"
"<title>Bookmarks</title>\n"
"</head>\n"
-"<body bgcolor='#778899' link='black' vlink='brown'>\n"
+"<body id='dillo_bm' bgcolor='#778899' link='black' vlink='brown'>\n"
"<table border='1' cellpadding='0' width='100%'>\n"
" <tr><td colspan='2'>\n"
" <table bgcolor='#b4b4b4' width='100%'>\n"
-" <tr><td bgcolor='#b4b4b4'>&nbsp;Modify bookmarks:: add section\n"
-" </td></tr></table></td></tr>\n"
+" <tr>\n"
+" <td bgcolor='#b4b4b4'>\n"
+" Modify bookmarks :: add section\n"
+" </td>\n"
+" <td align='right'>\n"
+" [<a href='dpi:/bm/'>cancel</a>]\n"
+" </td>\n"
+" </tr>\n"
+" </table></td></tr>\n"
"</table>\n"
"<br>\n"
-"<form>\n"
+"<form action='modify'>\n"
" <input type='hidden' name='operation' value='add_section'>\n"
"<table border='1' width='100%'>\n"
" <tr>\n"
@@ -288,15 +292,20 @@ DOCTYPE
"<head>\n"
"<title>Bookmarks</title>\n"
"</head>\n"
-"<body bgcolor='#778899' link='black' vlink='brown'>\n"
+"<body id='dillo_bm' bgcolor='#778899' link='black' vlink='brown'>\n"
"<table border='1' cellpadding='0' width='100%'>\n"
" <tr><td colspan='2'>\n"
" <table bgcolor='#b4b4b4' width='100%'>\n"
-" <tr><td bgcolor='#b4b4b4'>&nbsp;Modify bookmarks:: update\n"
-" </td></tr></table></td></tr>\n"
+" <tr><td bgcolor='#b4b4b4'> Modify bookmarks :: update\n"
+" </td>\n"
+" <td align='right'>\n"
+" [<a href='dpi:/bm/'>cancel</a>]\n"
+" </td>\n"
+" </tr>\n"
+" </table></td></tr>\n"
"</table>\n"
"<br>\n"
-"<form>\n"
+"<form action='modify'>\n"
"<input type='hidden' name='operation' value='modify2'>\n";
static const char *modifypage_update_title =
@@ -351,15 +360,20 @@ DOCTYPE
"<head>\n"
"<title>Bookmarks</title>\n"
"</head>\n"
-"<body bgcolor='#778899' link='black' vlink='brown'>\n"
+"<body id='dillo_bm' bgcolor='#778899' link='black' vlink='brown'>\n"
"<table border='1' cellpadding='0' width='100%'>\n"
" <tr><td colspan='2'>\n"
" <table bgcolor='#b4b4b4' width='100%'>\n"
-" <tr><td bgcolor='#b4b4b4'>&nbsp;Modify bookmarks:: add url\n"
-" </td></tr></table></td></tr>\n"
+" <tr><td bgcolor='#b4b4b4'> Modify bookmarks :: add url\n"
+" </td>\n"
+" <td align='right'>\n"
+" [<a href='dpi:/bm/'>cancel</a>]\n"
+" </td>\n"
+" </tr>\n"
+" </table></td></tr>\n"
"</table>\n"
"<br>\n"
-"<form>\n"
+"<form action='modify'>\n"
"<input type='hidden' name='operation' value='add_url2'>\n"
"<table border='1' width='100%'>\n"
" <tr>\n"
@@ -446,13 +460,13 @@ static void Unencode_str(char *e_str)
/*
* Send a short message to dillo's status bar.
*/
-static int Bmsrv_dpi_send_status_msg(SockHandler *sh, char *str)
+static int Bmsrv_dpi_send_status_msg(Dsh *sh, char *str)
{
int st;
char *d_cmd;
d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "send_status_message", str);
- st = sock_handler_write_str(sh, 1, d_cmd);
+ st = a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
return st;
}
@@ -673,15 +687,29 @@ static void Bms_check_import(void)
"grep -i \"href\" %s | "
"sed -e 's/<li><A HREF=\"/s0 /' -e 's/\">/ /' -e 's/<.*$//' >> %s";
Dstr *dstr = dStr_new("");
+ int rc;
if (access(BmFile, F_OK) != 0) {
OldBmFile = dStrconcat(dGethomedir(), "/.dillo/bookmarks.html", NULL);
if (access(OldBmFile, F_OK) == 0) {
dStr_sprintf(dstr, cmd1, BmFile);
- system(dstr->str);
+ rc = system(dstr->str);
+ if (rc == 127) {
+ MSG("Bookmarks: /bin/sh could not be executed\n");
+ } else if (rc == -1) {
+ MSG("Bookmarks: process creation failure: %s\n",
+ dStrerror(errno));
+ }
dStr_sprintf(dstr, cmd2, OldBmFile, BmFile);
- system(dstr->str);
+ rc = system(dstr->str);
+ if (rc == 127) {
+ MSG("Bookmarks: /bin/sh could not be executed\n");
+ } else if (rc == -1) {
+ MSG("Bookmarks: process creation failure: %s\n",
+ dStrerror(errno));
+ }
+
dStr_free(dstr, TRUE);
dFree(OldBmFile);
}
@@ -835,7 +863,7 @@ static int Bms_save(void)
/*
* Add a new bookmark to DB :)
*/
-static int Bmsrv_add_bm(SockHandler *sh, char *url, char *title)
+static int Bmsrv_add_bm(Dsh *sh, char *url, char *title)
{
char *u_title;
char *msg="Added bookmark!";
@@ -883,13 +911,13 @@ static void Bmsrv_count_urls_and_sections(char *url, int *n_sec, int *n_url)
* Send a dpi reload request
* Return code: { 0:OK, 1:Abort, 2:Close }
*/
-static int Bmsrv_send_reload_request(SockHandler *sh, char *url)
+static int Bmsrv_send_reload_request(Dsh *sh, char *url)
{
int st;
char *d_cmd;
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "reload_request", url);
- st = sock_handler_write_str(sh, 1, d_cmd) ? 1 : 0;
+ st = a_Dpip_dsh_write_str(sh, 1, d_cmd) ? 1 : 0;
dFree(d_cmd);
return st;
}
@@ -898,7 +926,7 @@ static int Bmsrv_send_reload_request(SockHandler *sh, char *url)
* Send the HTML for the modify page
* Return code: { 0:OK, 1:Abort, 2:Close }
*/
-static int Bmsrv_send_modify_page(SockHandler *sh)
+static int Bmsrv_send_modify_page(Dsh *sh)
{
static Dstr *dstr = NULL;
char *l_title;
@@ -910,25 +938,25 @@ static int Bmsrv_send_modify_page(SockHandler *sh)
dstr = dStr_new("");
/* send modify page header */
- if (sock_handler_write_str(sh, 0, modifypage_header))
+ if (a_Dpip_dsh_write_str(sh, 0, modifypage_header))
return 1;
/* write sections header */
- if (sock_handler_write_str(sh, 0, modifypage_sections_header))
+ if (a_Dpip_dsh_write_str(sh, 0, modifypage_sections_header))
return 1;
/* write sections */
for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
dStr_sprintf(dstr, modifypage_sections_item,
sec_node->section, sec_node->section, sec_node->title);
- if (sock_handler_write_str(sh, 0, dstr->str))
+ if (a_Dpip_dsh_write_str(sh, 0, dstr->str))
return 1;
}
/* write sections footer */
- if (sock_handler_write_str(sh, 0, modifypage_sections_footer))
+ if (a_Dpip_dsh_write_str(sh, 0, modifypage_sections_footer))
return 1;
/* send page middle */
- if (sock_handler_write_str(sh, 0, modifypage_middle1))
+ if (a_Dpip_dsh_write_str(sh, 0, modifypage_middle1))
return 1;
/* send bookmark cards */
@@ -938,7 +966,7 @@ static int Bmsrv_send_modify_page(SockHandler *sh)
dStr_sprintf(dstr, modifypage_section_card_header,
sec_node->section, l_title);
dFree(l_title);
- if (sock_handler_write_str(sh, 0, dstr->str))
+ if (a_Dpip_dsh_write_str(sh, 0, dstr->str))
return 1;
/* send section's bookmarks */
@@ -946,18 +974,18 @@ static int Bmsrv_send_modify_page(SockHandler *sh)
if (bm_node->section == sec_node->section) {
dStr_sprintf(dstr, modifypage_section_card_item,
bm_node->key, bm_node->url, bm_node->title);
- if (sock_handler_write_str(sh, 0, dstr->str))
+ if (a_Dpip_dsh_write_str(sh, 0, dstr->str))
return 1;
}
}
/* send card footer */
- if (sock_handler_write_str(sh, 0, modifypage_section_card_footer))
+ if (a_Dpip_dsh_write_str(sh, 0, modifypage_section_card_footer))
return 1;
}
/* finish page */
- if (sock_handler_write_str(sh, 1, modifypage_footer))
+ if (a_Dpip_dsh_write_str(sh, 1, modifypage_footer))
return 1;
return 2;
@@ -967,10 +995,10 @@ static int Bmsrv_send_modify_page(SockHandler *sh)
* Send the HTML for the modify page for "add section"
* Return code: { 0:OK, 1:Abort, 2:Close }
*/
-static int Bmsrv_send_modify_page_add_section(SockHandler *sh)
+static int Bmsrv_send_modify_page_add_section(Dsh *sh)
{
/* send modify page2 */
- if (sock_handler_write_str(sh, 1, modifypage_add_section_page))
+ if (a_Dpip_dsh_write_str(sh, 1, modifypage_add_section_page))
return 1;
return 2;
@@ -980,9 +1008,9 @@ static int Bmsrv_send_modify_page_add_section(SockHandler *sh)
* Send the HTML for the modify page for "add url"
* Return code: { 0:OK, 1:Abort, 2:Close }
*/
-static int Bmsrv_send_modify_page_add_url(SockHandler *sh)
+static int Bmsrv_send_modify_page_add_url(Dsh *sh)
{
- if (sock_handler_write_str(sh, 1, modifypage_add_url))
+ if (a_Dpip_dsh_write_str(sh, 1, modifypage_add_url))
return 1;
return 2;
}
@@ -994,7 +1022,7 @@ static int Bmsrv_send_modify_page_add_url(SockHandler *sh)
* - send the modify page for the marked urls and sections
* Return code: { 0:OK, 1:Abort, 2:Close }
*/
-static int Bmsrv_send_modify_update(SockHandler *sh, char *url)
+static int Bmsrv_send_modify_update(Dsh *sh, char *url)
{
static char *url1 = NULL;
static Dstr *dstr = NULL;
@@ -1016,7 +1044,7 @@ static int Bmsrv_send_modify_update(SockHandler *sh, char *url)
}
/* send HTML here */
- if (sock_handler_write_str(sh, 0, modifypage_update_header))
+ if (a_Dpip_dsh_write_str(sh, 0, modifypage_update_header))
return 1;
/* Count number of marked urls and sections */
@@ -1024,8 +1052,8 @@ static int Bmsrv_send_modify_update(SockHandler *sh, char *url)
if (n_sec) {
dStr_sprintf(dstr, modifypage_update_title, "Update&nbsp;sections:");
- sock_handler_write_str(sh, 0, dstr->str);
- sock_handler_write_str(sh, 0, modifypage_update_item_header);
+ a_Dpip_dsh_write_str(sh, 0, dstr->str);
+ a_Dpip_dsh_write_str(sh, 0, modifypage_update_item_header);
/* send items here */
p = strchr(url1, '?');
for (q = p; (q = strstr(q, "&s")); ++q) {
@@ -1035,17 +1063,17 @@ static int Bmsrv_send_modify_update(SockHandler *sh, char *url)
if ((sec_node = Bms_get_sec(key))) {
dStr_sprintf(dstr, modifypage_update_item2,
sec_node->section, sec_node->title);
- sock_handler_write_str(sh, 0, dstr->str);
+ a_Dpip_dsh_write_str(sh, 0, dstr->str);
}
}
}
- sock_handler_write_str(sh, 0, modifypage_update_item_footer);
+ a_Dpip_dsh_write_str(sh, 0, modifypage_update_item_footer);
}
if (n_url) {
dStr_sprintf(dstr, modifypage_update_title, "Update&nbsp;titles:");
- sock_handler_write_str(sh, 0, dstr->str);
- sock_handler_write_str(sh, 0, modifypage_update_item_header);
+ a_Dpip_dsh_write_str(sh, 0, dstr->str);
+ a_Dpip_dsh_write_str(sh, 0, modifypage_update_item_header);
/* send items here */
p = strchr(url1, '?');
for (q = p; (q = strstr(q, "&url")); ++q) {
@@ -1055,13 +1083,13 @@ static int Bmsrv_send_modify_update(SockHandler *sh, char *url)
bm_node = Bms_get(key);
dStr_sprintf(dstr, modifypage_update_item,
bm_node->key, bm_node->title, bm_node->url);
- sock_handler_write_str(sh, 0, dstr->str);
+ a_Dpip_dsh_write_str(sh, 0, dstr->str);
}
}
- sock_handler_write_str(sh, 0, modifypage_update_item_footer);
+ a_Dpip_dsh_write_str(sh, 0, modifypage_update_item_footer);
}
- sock_handler_write_str(sh, 1, modifypage_update_footer);
+ a_Dpip_dsh_write_str(sh, 1, modifypage_update_footer);
return 2;
}
@@ -1070,19 +1098,19 @@ static int Bmsrv_send_modify_update(SockHandler *sh, char *url)
* Make the modify-page and send it back
* Return code: { 0:OK, 1:Abort, 2:Close }
*/
-static int Bmsrv_send_modify_answer(SockHandler *sh, char *url)
+static int Bmsrv_send_modify_answer(Dsh *sh, char *url)
{
char *d_cmd;
int st;
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
- st = sock_handler_write_str(sh, 1, d_cmd);
+ st = a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
if (st != 0)
return 1;
/* Send HTTP header */
- if (sock_handler_write_str(sh, 0, Header) != 0) {
+ if (a_Dpip_dsh_write_str(sh, 0, Header) != 0) {
return 1;
}
@@ -1275,7 +1303,7 @@ static int Bmsrv_modify_add_section(char *url)
* Parse an "add url" request, and update the bm file.
* Return code: { 0:OK, 1:Abort }
*/
-static int Bmsrv_modify_add_url(SockHandler *sh, char *s_url)
+static int Bmsrv_modify_add_url(Dsh *sh, char *s_url)
{
char *p, *q, *title, *u_title, *url;
int i;
@@ -1328,7 +1356,7 @@ static int Bmsrv_modify_add_url(SockHandler *sh, char *s_url)
* when it's wrong.
* Return code: { 0:OK, 2:Close }
*/
-static int Bmsrv_check_modify_request(SockHandler *sh, char *url)
+static int Bmsrv_check_modify_request(Dsh *sh, char *url)
{
char *p, *msg;
int n_sec, n_url;
@@ -1393,7 +1421,7 @@ static int Bmsrv_check_modify_request(SockHandler *sh, char *url)
* Parse a and process a modify request.
* Return code: { 0:OK, 1:Abort, 2:Close }
*/
-static int Bmsrv_process_modify_request(SockHandler *sh, char *url)
+static int Bmsrv_process_modify_request(Dsh *sh, char *url)
{
/* check the provided parameters */
if (Bmsrv_check_modify_request(sh, url) != 0)
@@ -1459,7 +1487,7 @@ static int Bmsrv_process_modify_request(SockHandler *sh, char *url)
/*
* Send the current bookmarks page (in HTML)
*/
-static int send_bm_page(SockHandler *sh)
+static int send_bm_page(Dsh *sh)
{
static Dstr *dstr = NULL;
char *l_title;
@@ -1470,25 +1498,25 @@ static int send_bm_page(SockHandler *sh)
if (!dstr)
dstr = dStr_new("");
- if (sock_handler_write_str(sh, 0, mainpage_header))
+ if (a_Dpip_dsh_write_str(sh, 0, mainpage_header))
return 1;
/* write sections header */
- if (sock_handler_write_str(sh, 0, mainpage_sections_header))
+ if (a_Dpip_dsh_write_str(sh, 0, mainpage_sections_header))
return 1;
/* write sections */
for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
dStr_sprintf(dstr, mainpage_sections_item,
sec_node->section, sec_node->title);
- if (sock_handler_write_str(sh, 0, dstr->str))
+ if (a_Dpip_dsh_write_str(sh, 0, dstr->str))
return 1;
}
/* write sections footer */
- if (sock_handler_write_str(sh, 0, mainpage_sections_footer))
+ if (a_Dpip_dsh_write_str(sh, 0, mainpage_sections_footer))
return 1;
/* send page middle */
- if (sock_handler_write_str(sh, 0, mainpage_middle1))
+ if (a_Dpip_dsh_write_str(sh, 0, mainpage_middle1))
return 1;
/* send bookmark cards */
@@ -1498,7 +1526,7 @@ static int send_bm_page(SockHandler *sh)
dStr_sprintf(dstr, mainpage_section_card_header,
sec_node->section, l_title);
dFree(l_title);
- if (sock_handler_write_str(sh, 0, dstr->str))
+ if (a_Dpip_dsh_write_str(sh, 0, dstr->str))
return 1;
/* send section's bookmarks */
@@ -1506,18 +1534,18 @@ static int send_bm_page(SockHandler *sh)
if (bm_node->section == sec_node->section) {
dStr_sprintf(dstr, mainpage_section_card_item,
bm_node->url, bm_node->title);
- if (sock_handler_write_str(sh, 0, dstr->str))
+ if (a_Dpip_dsh_write_str(sh, 0, dstr->str))
return 1;
}
}
/* send card footer */
- if (sock_handler_write_str(sh, 0, mainpage_section_card_footer))
+ if (a_Dpip_dsh_write_str(sh, 0, mainpage_section_card_footer))
return 1;
}
/* finish page */
- if (sock_handler_write_str(sh, 1, mainpage_footer))
+ if (a_Dpip_dsh_write_str(sh, 1, mainpage_footer))
return 1;
return 0;
@@ -1528,13 +1556,13 @@ static int send_bm_page(SockHandler *sh)
/*
* Parse a data stream (dpi protocol)
- * Note: Buf is a zero terminated string
+ * Note: Buf is a dpip token (zero terminated string)
* Return code: { 0:OK, 1:Abort, 2:Close }
*/
-static int Bmsrv_parse_buf(SockHandler *sh, char *Buf)
+static int Bmsrv_parse_token(Dsh *sh, char *Buf)
{
static char *msg1=NULL, *msg2=NULL, *msg3=NULL;
- char *p, *cmd, *d_cmd, *url, *title, *msg;
+ char *cmd, *d_cmd, *url, *title, *msg;
size_t BufSize;
int st;
@@ -1545,29 +1573,28 @@ static int Bmsrv_parse_buf(SockHandler *sh, char *Buf)
msg3 = a_Dpip_build_cmd("cmd=%s msg=%s", "chat", "Ok, send it");
}
- if (!(p = strchr(Buf, '>'))) {
- /* Haven't got a full tag */
- MSG("Haven't got a full tag!\n");
+ if (sh->mode & DPIP_RAW) {
+ MSG("ERROR: Unhandled DPIP_RAW mode!\n");
return 1;
}
BufSize = strlen(Buf);
- cmd = a_Dpip_get_attr(Buf, BufSize, "cmd");
+ cmd = a_Dpip_get_attr_l(Buf, BufSize, "cmd");
if (cmd && strcmp(cmd, "chat") == 0) {
dFree(cmd);
- msg = a_Dpip_get_attr(Buf, BufSize, "msg");
+ msg = a_Dpip_get_attr_l(Buf, BufSize, "msg");
if (*msg == 'H') {
/* "Hi server" */
- if (sock_handler_write_str(sh, 1, msg1))
+ if (a_Dpip_dsh_write_str(sh, 1, msg1))
return 1;
} else if (*msg == 'I') {
/* "I want to set abookmark" */
- if (sock_handler_write_str(sh, 1, msg2))
+ if (a_Dpip_dsh_write_str(sh, 1, msg2))
return 1;
} else if (*msg == 'S') {
/* "Sure" */
- if (sock_handler_write_str(sh, 1, msg3))
+ if (a_Dpip_dsh_write_str(sh, 1, msg3))
return 1;
}
dFree(msg);
@@ -1583,8 +1610,8 @@ static int Bmsrv_parse_buf(SockHandler *sh, char *Buf)
} else if (cmd && strcmp(cmd, "add_bookmark") == 0) {
dFree(cmd);
- url = a_Dpip_get_attr(Buf, BufSize, "url");
- title = a_Dpip_get_attr(Buf, BufSize, "title");
+ url = a_Dpip_get_attr_l(Buf, BufSize, "url");
+ title = a_Dpip_get_attr_l(Buf, BufSize, "title");
if (strlen(title) == 0) {
dFree(title);
title = dStrdup("(Untitled)");
@@ -1597,7 +1624,7 @@ static int Bmsrv_parse_buf(SockHandler *sh, char *Buf)
} else if (cmd && strcmp(cmd, "open_url") == 0) {
dFree(cmd);
- url = a_Dpip_get_attr(Buf, BufSize, "url");
+ url = a_Dpip_get_attr_l(Buf, BufSize, "url");
if (strcmp(url, "dpi:/bm/modify") == 0) {
st = Bmsrv_send_modify_answer(sh, url);
@@ -1611,13 +1638,13 @@ static int Bmsrv_parse_buf(SockHandler *sh, char *Buf)
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
- st = sock_handler_write_str(sh, 1, d_cmd);
+ st = a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
if (st != 0)
return 1;
/* Send HTTP header */
- if (sock_handler_write_str(sh, 1, Header) != 0) {
+ if (a_Dpip_dsh_write_str(sh, 1, Header) != 0) {
return 1;
}
@@ -1625,8 +1652,9 @@ static int Bmsrv_parse_buf(SockHandler *sh, char *Buf)
if (st != 0) {
char *err =
DOCTYPE
- "<HTML><body> Error on the bookmarks server...</body></html>";
- if (sock_handler_write_str(sh, 1, err) != 0) {
+ "<HTML><body id='dillo_bm'> Error on the bookmarks server..."
+ " </body></html>";
+ if (a_Dpip_dsh_write_str(sh, 1, err) != 0) {
return 1;
}
}
@@ -1658,13 +1686,12 @@ static void termination_handler(int signum)
/*
* -- MAIN -------------------------------------------------------------------
*/
-int main (void) {
+int main(void) {
struct sockaddr_un spun;
- int temp_sock_descriptor;
+ int sock_fd, code;
socklen_t address_size;
- char *buf;
- int code;
- SockHandler *sh;
+ char *tok;
+ Dsh *sh;
/* Arrange the cleanup function for terminations via exit() */
atexit(cleanup);
@@ -1677,6 +1704,9 @@ int main (void) {
if (signal (SIGTERM, termination_handler) == SIG_IGN)
signal (SIGTERM, SIG_IGN);
+ /* We may receive SIGPIPE (e.g. socket is closed early by our client) */
+ signal(SIGPIPE, SIG_IGN);
+
/* Initialize local data */
B_bms = dList_new(512);
B_secs = dList_new(32);
@@ -1687,30 +1717,40 @@ int main (void) {
MSG("(v.13): accepting connections...\n");
while (1) {
- temp_sock_descriptor =
- accept(STDIN_FILENO, (struct sockaddr *)&spun, &address_size);
- if (temp_sock_descriptor == -1) {
+ sock_fd = accept(STDIN_FILENO, (struct sockaddr *)&spun, &address_size);
+ if (sock_fd == -1) {
perror("[accept]");
exit(1);
}
- /* create the SockHandler structure */
- sh = sock_handler_new(temp_sock_descriptor,temp_sock_descriptor,8*1024);
+ /* create the Dsh structure */
+ sh = a_Dpip_dsh_new(sock_fd, sock_fd, 8*1024);
+
+ /* Authenticate our client... */
+ if (!(tok = a_Dpip_dsh_read_token(sh, 1)) ||
+ a_Dpip_check_auth(tok) < 0) {
+ MSG("can't authenticate request: %s\n", dStrerror(errno));
+ a_Dpip_dsh_close(sh);
+ exit(1);
+ }
+ dFree(tok);
while (1) {
code = 1;
- if ((buf = sock_handler_read(sh)) != NULL) {
+ if ((tok = a_Dpip_dsh_read_token(sh, 1)) != NULL) {
/* Let's see what we fished... */
- code = Bmsrv_parse_buf(sh, buf);
+ code = Bmsrv_parse_token(sh, tok);
}
- if (code == 1)
- exit(1);
- else if (code == 2)
+ dFree(tok);
+
+ if (code != 0) {
+ /* socket is not operative (e.g. closed by client) */
break;
+ }
}
- sock_handler_close(sh);
- sock_handler_free(sh);
+ a_Dpip_dsh_close(sh);
+ a_Dpip_dsh_free(sh);
}/*while*/
}
diff --git a/dpi/cookies.c b/dpi/cookies.c
index b7641372..a5142224 100644
--- a/dpi/cookies.c
+++ b/dpi/cookies.c
@@ -13,19 +13,13 @@
*
*/
-/* Handling of cookies takes place here.
- * This implementation aims to follow RFC 2965:
- * http://www.ietf.org/rfc/rfc2965.txt
- */
-
-/*
- * TODO: Cleanup this code. Shorten some functions, order things,
- * add comments, remove leaks, etc.
- */
-
-/* TODO: this server is not assembling the received packets.
- * This means it currently expects dillo to send full dpi tags
- * within the socket; if that fails, everything stops.
+/* This is written to follow the HTTP State Working Group's
+ * draft-ietf-httpstate-cookie-01.txt.
+ *
+ * Info on cookies in the wild:
+ * http://www.ietf.org/mail-archive/web/http-state/current/msg00078.html
+ * And dates specifically:
+ * http://www.ietf.org/mail-archive/web/http-state/current/msg00128.html
*/
#ifdef DISABLE_COOKIES
@@ -42,6 +36,7 @@ int main(void)
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
+#include <netinet/in.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
@@ -63,13 +58,6 @@ int main(void)
#define _MSG(...)
#define MSG(...) printf("[cookies dpi]: " __VA_ARGS__)
-
-/* 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))
-
-
/*
* a_List_add()
*
@@ -90,6 +78,9 @@ int main(void)
/* The maximum length of a line in the cookie file */
#define LINE_MAXLEN 4096
+#define MAX_DOMAIN_COOKIES 20
+#define MAX_TOTAL_COOKIES 1200
+
typedef enum {
COOKIE_ACCEPT,
COOKIE_ACCEPT_SESSION,
@@ -103,8 +94,8 @@ typedef struct {
typedef struct {
char *domain;
- Dlist *dlist;
-} CookieNode;
+ Dlist *cookies;
+} DomainNode;
typedef struct {
char *name;
@@ -112,20 +103,25 @@ typedef struct {
char *domain;
char *path;
time_t expires_at;
- uint_t version;
- char *comment;
- char *comment_url;
+ bool_t host_only;
bool_t secure;
bool_t session_only;
- Dlist *ports;
+ long last_used;
} CookieData_t;
+typedef struct {
+ Dsh *sh;
+ int status;
+} ClientInfo;
+
/*
* Local data
*/
-/* List of CookieNode. Each node holds a domain and its list of cookies */
-static Dlist *cookies;
+static Dlist *all_cookies;
+
+/* List of DomainNode. Each node holds a domain and its list of cookies */
+static Dlist *domains;
/* Variables for access control */
static CookieControl *ccontrol = NULL;
@@ -133,13 +129,19 @@ static int num_ccontrol = 0;
static int num_ccontrol_max = 1;
static CookieControlAction default_action = COOKIE_DENY;
+static long cookies_use_counter = 0;
static bool_t disabled;
static FILE *file_stream;
-static char *cookies_txt_header_str =
+static const char *const cookies_txt_header_str =
"# HTTP Cookie File\n"
-"# http://wp.netscape.com/newsref/std/cookie_spec.html\n"
-"# This is a generated file! Do not edit.\n\n";
+"# This is a generated file! Do not edit.\n"
+"# [domain subdomains path secure expiry_time name value]\n\n";
+/* The epoch is Jan 1, 1970. When there is difficulty in representing future
+ * dates, use the (by far) most likely last representable time in Jan 19, 2038.
+ */
+static struct tm cookies_epoch_tm = {0, 0, 0, 1, 0, 70, 0, 0, 0, 0, 0};
+static time_t cookies_epoch_time, cookies_future_time;
/*
* Forward declarations
@@ -147,33 +149,39 @@ static char *cookies_txt_header_str =
static CookieControlAction Cookies_control_check_domain(const char *domain);
static int Cookie_control_init(void);
-static void Cookies_parse_ports(int url_port, CookieData_t *cookie,
- const char *port_str);
-static char *Cookies_build_ports_str(CookieData_t *cookie);
-static char *Cookies_strip_path(const char *path);
static void Cookies_add_cookie(CookieData_t *cookie);
-static void Cookies_remove_cookie(CookieData_t *cookie);
static int Cookies_cmp(const void *a, const void *b);
/*
- * Compare function for searching a cookie node
+ * Compare function for searching a domain node
*/
-static int Cookie_node_cmp(const void *v1, const void *v2)
+static int Domain_node_cmp(const void *v1, const void *v2)
{
- const CookieNode *n1 = v1, *n2 = v2;
+ const DomainNode *n1 = v1, *n2 = v2;
- return strcmp(n1->domain, n2->domain);
+ return dStrcasecmp(n1->domain, n2->domain);
}
/*
- * Compare function for searching a cookie node by domain
+ * Compare function for searching a domain node by domain
*/
-static int Cookie_node_by_domain_cmp(const void *v1, const void *v2)
+static int Domain_node_by_domain_cmp(const void *v1, const void *v2)
{
- const CookieNode *node = v1;
+ const DomainNode *node = v1;
const char *domain = v2;
- return strcmp(node->domain, domain);
+ return dStrcasecmp(node->domain, domain);
+}
+
+/*
+ * Delete node. This will not free any cookies that might be in node->cookies.
+ */
+static void Cookies_delete_node(DomainNode *node)
+{
+ dList_remove(domains, node);
+ dFree(node->domain);
+ dList_free(node->cookies);
+ dFree(node);
}
/*
@@ -181,17 +189,22 @@ static int Cookie_node_by_domain_cmp(const void *v1, const void *v2)
* with the optional 'init_str' as its content.
*/
static FILE *Cookies_fopen(const char *filename, const char *mode,
- char *init_str)
+ const char *init_str)
{
FILE *F_in;
- int fd;
+ int fd, rc;
if ((F_in = fopen(filename, mode)) == NULL) {
/* Create the file */
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (fd != -1) {
- if (init_str)
- write(fd, init_str, strlen(init_str));
+ if (init_str) {
+ rc = write(fd, init_str, strlen(init_str));
+ if (rc == -1) {
+ MSG("Cookies: Could not write initial string to file %s: %s\n",
+ filename, dStrerror(errno));
+ }
+ }
close(fd);
MSG("Created file: %s\n", filename);
@@ -215,71 +228,37 @@ static void Cookies_free_cookie(CookieData_t *cookie)
dFree(cookie->value);
dFree(cookie->domain);
dFree(cookie->path);
- dFree(cookie->comment);
- dFree(cookie->comment_url);
- dList_free(cookie->ports);
dFree(cookie);
}
+static void Cookies_tm_init(struct tm *tm)
+{
+ tm->tm_sec = cookies_epoch_tm.tm_sec;
+ tm->tm_min = cookies_epoch_tm.tm_min;
+ tm->tm_hour = cookies_epoch_tm.tm_hour;
+ tm->tm_mday = cookies_epoch_tm.tm_mday;
+ tm->tm_mon = cookies_epoch_tm.tm_mon;
+ tm->tm_year = cookies_epoch_tm.tm_year;
+ tm->tm_isdst = cookies_epoch_tm.tm_isdst;
+}
+
/*
- * Initialize the cookies module
- * (The 'disabled' variable is writable only within Cookies_init)
+ * Read in cookies from 'stream' (cookies.txt)
*/
-static void Cookies_init()
+static void Cookies_load_cookies(FILE *stream)
{
- CookieData_t *cookie;
- char *filename;
char line[LINE_MAXLEN];
-#ifndef HAVE_LOCKF
- struct flock lck;
-#endif
- FILE *old_cookies_file_stream;
-
- /* Default setting */
- disabled = TRUE;
-
- /* Read and parse the cookie control file (cookiesrc) */
- if (Cookie_control_init() != 0) {
- MSG("Disabling cookies.\n");
- return;
- }
-
- /* Get a stream for the cookies file */
- filename = dStrconcat(dGethomedir(), "/.dillo/cookies.txt", NULL);
- file_stream = Cookies_fopen(filename, "r+", cookies_txt_header_str);
-
- dFree(filename);
-
- if (!file_stream) {
- MSG("ERROR: Can't open ~/.dillo/cookies.txt, disabling cookies\n");
- return;
- }
-
- /* Try to get a lock from the file descriptor */
-#ifdef HAVE_LOCKF
- disabled = (lockf(fileno(file_stream), F_TLOCK, 0) == -1);
-#else /* POSIX lock */
- lck.l_start = 0; /* start at beginning of file */
- lck.l_len = 0; /* lock entire file */
- lck.l_type = F_WRLCK;
- lck.l_whence = SEEK_SET; /* absolute offset */
- disabled = (fcntl(fileno(file_stream), F_SETLK, &lck) == -1);
-#endif
- if (disabled) {
- MSG("The cookies file has a file lock: disabling cookies!\n");
- fclose(file_stream);
- return;
- }
-
- MSG("Enabling cookies as from cookiesrc...\n");
-
- cookies = dList_new(32);
+ all_cookies = dList_new(32);
+ domains = dList_new(32);
/* Get all lines in the file */
- while (!feof(file_stream)) {
+ while (!feof(stream)) {
line[0] = '\0';
- fgets(line, LINE_MAXLEN, file_stream);
+ if ((fgets(line, LINE_MAXLEN, stream) == NULL) && ferror(stream)) {
+ MSG("Error while reading from cookies.txt: %s\n", dStrerror(errno));
+ break; /* bail out */
+ }
/* Remove leading and trailing whitespaces */
dStrstrip(line);
@@ -290,7 +269,7 @@ static void Cookies_init()
* pieces[0] The domain name
* pieces[1] TRUE/FALSE: is the domain a suffix, or a full domain?
* pieces[2] The path
- * pieces[3] Is the cookie unsecure or secure (TRUE/FALSE)
+ * pieces[3] TRUE/FALSE: is the cookie for secure use only?
* pieces[4] Timestamp of expire date
* pieces[5] Name of the cookie
* pieces[6] Value of the cookie
@@ -298,27 +277,37 @@ static void Cookies_init()
CookieControlAction action;
char *piece;
char *line_marker = line;
-
- cookie = dNew0(CookieData_t, 1);
+ CookieData_t *cookie = dNew0(CookieData_t, 1);
cookie->session_only = FALSE;
- cookie->version = 0;
cookie->domain = dStrdup(dStrsep(&line_marker, "\t"));
- dStrsep(&line_marker, "\t"); /* we use domain always as sufix */
+ piece = dStrsep(&line_marker, "\t");
+ if (piece != NULL && piece[0] == 'F')
+ cookie->host_only = TRUE;
cookie->path = dStrdup(dStrsep(&line_marker, "\t"));
piece = dStrsep(&line_marker, "\t");
if (piece != NULL && piece[0] == 'T')
cookie->secure = TRUE;
piece = dStrsep(&line_marker, "\t");
- if (piece != NULL)
- cookie->expires_at = (time_t) strtol(piece, NULL, 10);
+ if (piece != NULL) {
+ /* There is some problem with simply putting the maximum value
+ * into tm.tm_sec (although a value close to it works).
+ */
+ long seconds = strtol(piece, NULL, 10);
+ struct tm tm;
+ Cookies_tm_init(&tm);
+ tm.tm_min += seconds / 60;
+ tm.tm_sec += seconds % 60;
+ cookie->expires_at = mktime(&tm);
+ } else {
+ cookie->expires_at = (time_t) -1;
+ }
cookie->name = dStrdup(dStrsep(&line_marker, "\t"));
- cookie->value = dStrdup(dStrsep(&line_marker, "\t"));
+ cookie->value = dStrdup(line_marker ? line_marker : "");
if (!cookie->domain || cookie->domain[0] == '\0' ||
!cookie->path || cookie->path[0] != '/' ||
- !cookie->name || cookie->name[0] == '\0' ||
- !cookie->value) {
+ !cookie->name || !cookie->value) {
MSG("Malformed line in cookies.txt file!\n");
Cookies_free_cookie(cookie);
continue;
@@ -336,81 +325,63 @@ static void Cookies_init()
Cookies_add_cookie(cookie);
}
}
+ MSG("Cookies loaded: %d.\n", dList_length(all_cookies));
+}
- filename = dStrconcat(dGethomedir(), "/.dillo/cookies", NULL);
- if ((old_cookies_file_stream = fopen(filename, "r")) != NULL) {
- MSG("WARNING: Reading old cookies file ~/.dillo/cookies too\n");
-
- /* Get all lines in the file */
- while (!feof(old_cookies_file_stream)) {
- line[0] = '\0';
- fgets(line, LINE_MAXLEN, old_cookies_file_stream);
-
- /* Remove leading and trailing whitespaces */
- dStrstrip(line);
-
- if (line[0] != '\0') {
- /*
- * Split the row into pieces using a tab as the delimiter.
- * pieces[0] The version this cookie was set as (0 / 1)
- * pieces[1] The domain name
- * pieces[2] A comma separated list of accepted ports
- * pieces[3] The path
- * pieces[4] Is the cookie unsecure or secure (0 / 1)
- * pieces[5] Timestamp of expire date
- * pieces[6] Name of the cookie
- * pieces[7] Value of the cookie
- * pieces[8] Comment
- * pieces[9] Comment url
- */
- CookieControlAction action;
- char *piece;
- char *line_marker = line;
-
- cookie = dNew0(CookieData_t, 1);
-
- cookie->session_only = FALSE;
- piece = dStrsep(&line_marker, "\t");
- if (piece != NULL)
- cookie->version = strtol(piece, NULL, 10);
- cookie->domain = dStrdup(dStrsep(&line_marker, "\t"));
- Cookies_parse_ports(0, cookie, dStrsep(&line_marker, "\t"));
- cookie->path = dStrdup(dStrsep(&line_marker, "\t"));
- piece = dStrsep(&line_marker, "\t");
- if (piece != NULL && piece[0] == '1')
- cookie->secure = TRUE;
- piece = dStrsep(&line_marker, "\t");
- if (piece != NULL)
- cookie->expires_at = (time_t) strtol(piece, NULL, 10);
- cookie->name = dStrdup(dStrsep(&line_marker, "\t"));
- cookie->value = dStrdup(dStrsep(&line_marker, "\t"));
- cookie->comment = dStrdup(dStrsep(&line_marker, "\t"));
- cookie->comment_url = dStrdup(dStrsep(&line_marker, "\t"));
-
- if (!cookie->domain || cookie->domain[0] == '\0' ||
- !cookie->path || cookie->path[0] != '/' ||
- !cookie->name || cookie->name[0] == '\0' ||
- !cookie->value) {
- MSG("Malformed line in cookies file!\n");
- Cookies_free_cookie(cookie);
- continue;
- }
+/*
+ * Initialize the cookies module
+ * (The 'disabled' variable is writeable only within Cookies_init)
+ */
+static void Cookies_init()
+{
+ char *filename;
+#ifndef HAVE_LOCKF
+ struct flock lck;
+#endif
+ struct tm future_tm = {7, 14, 3, 19, 0, 138, 0, 0, 0, 0, 0};
- action = Cookies_control_check_domain(cookie->domain);
- if (action == COOKIE_DENY) {
- Cookies_free_cookie(cookie);
- continue;
- } else if (action == COOKIE_ACCEPT_SESSION) {
- cookie->session_only = TRUE;
- }
+ /* Default setting */
+ disabled = TRUE;
- /* Save cookie in memory */
- Cookies_add_cookie(cookie);
- }
- }
- fclose(old_cookies_file_stream);
+ cookies_epoch_time = mktime(&cookies_epoch_tm);
+ cookies_future_time = mktime(&future_tm);
+
+ /* Read and parse the cookie control file (cookiesrc) */
+ if (Cookie_control_init() != 0) {
+ MSG("Disabling cookies.\n");
+ return;
}
+
+ /* Get a stream for the cookies file */
+ filename = dStrconcat(dGethomedir(), "/.dillo/cookies.txt", NULL);
+ file_stream = Cookies_fopen(filename, "r+", cookies_txt_header_str);
+
dFree(filename);
+
+ if (!file_stream) {
+ MSG("ERROR: Can't open ~/.dillo/cookies.txt; disabling cookies\n");
+ return;
+ }
+
+ /* Try to get a lock from the file descriptor */
+#ifdef HAVE_LOCKF
+ disabled = (lockf(fileno(file_stream), F_TLOCK, 0) == -1);
+#else /* POSIX lock */
+ lck.l_start = 0; /* start at beginning of file */
+ lck.l_len = 0; /* lock entire file */
+ lck.l_type = F_WRLCK;
+ lck.l_whence = SEEK_SET; /* absolute offset */
+
+ disabled = (fcntl(fileno(file_stream), F_SETLK, &lck) == -1);
+#endif
+ if (disabled) {
+ MSG("The cookies file has a file lock; disabling cookies!\n");
+ fclose(file_stream);
+ return;
+ }
+ MSG("Enabling cookies as per cookiesrc...\n");
+
+ Cookies_load_cookies(file_stream);
}
/*
@@ -418,9 +389,10 @@ static void Cookies_init()
*/
static void Cookies_save_and_free()
{
- int i, fd;
- CookieNode *node;
+ int i, fd, saved = 0;
+ DomainNode *node;
CookieData_t *cookie;
+ time_t now;
#ifndef HAVE_LOCKF
struct flock lck;
@@ -429,33 +401,34 @@ static void Cookies_save_and_free()
if (disabled)
return;
+ now = time(NULL);
+
rewind(file_stream);
fd = fileno(file_stream);
- ftruncate(fd, 0);
- fprintf(file_stream, cookies_txt_header_str);
+ if (ftruncate(fd, 0) == -1)
+ MSG("Cookies: Truncate file stream failed: %s\n", dStrerror(errno));
+ fprintf(file_stream, "%s", cookies_txt_header_str);
/* Iterate cookies per domain, saving and freeing */
- while ((node = dList_nth_data(cookies, 0))) {
- for (i = 0; (cookie = dList_nth_data(node->dlist, i)); ++i) {
- if (!cookie->session_only) {
- /* char * ports_str = Cookies_build_ports_str(cookie); */
- fprintf(file_stream, "%s\tTRUE\t%s\t%s\t%ld\t%s\t%s\n",
+ while ((node = dList_nth_data(domains, 0))) {
+ for (i = 0; (cookie = dList_nth_data(node->cookies, i)); ++i) {
+ if (!cookie->session_only && difftime(cookie->expires_at, now) > 0) {
+ fprintf(file_stream, "%s\t%s\t%s\t%s\t%ld\t%s\t%s\n",
cookie->domain,
+ cookie->host_only ? "FALSE" : "TRUE",
cookie->path,
cookie->secure ? "TRUE" : "FALSE",
- (long)cookie->expires_at,
+ (long)difftime(cookie->expires_at, cookies_epoch_time),
cookie->name,
cookie->value);
- /* dFree(ports_str); */
+ saved++;
}
-
Cookies_free_cookie(cookie);
}
- dList_remove(cookies, node);
- dFree(node->domain);
- dList_free(node->dlist);
- dFree(node);
+ Cookies_delete_node(node);
}
+ dList_free(domains);
+ dList_free(all_cookies);
#ifdef HAVE_LOCKF
lockf(fd, F_ULOCK, 0);
@@ -468,34 +441,33 @@ static void Cookies_save_and_free()
fcntl(fileno(file_stream), F_SETLKW, &lck);
#endif
fclose(file_stream);
-}
-static char *months[] =
-{ "",
- "Jan", "Feb", "Mar",
- "Apr", "May", "Jun",
- "Jul", "Aug", "Sep",
- "Oct", "Nov", "Dec"
-};
+ MSG("Cookies saved: %d.\n", saved);
+}
/*
- * Take a months name and return a number between 1-12.
- * E.g. 'April' -> 4
+ * Take a month's name and return a number between 0-11.
+ * E.g. 'April' -> 3
*/
static int Cookies_get_month(const char *month_name)
{
+ static const char *const months[] =
+ { "Jan", "Feb", "Mar",
+ "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep",
+ "Oct", "Nov", "Dec"
+ };
int i;
- for (i = 1; i <= 12; i++) {
+ for (i = 0; i < 12; i++) {
if (!dStrncasecmp(months[i], month_name, 3))
return i;
}
- return 0;
+ return -1;
}
/*
- * Return a local timestamp from a GMT date string
- * Accept: RFC-1123 | RFC-850 | ANSI asctime | Old Netscape format.
+ * Accept: RFC-1123 | RFC-850 | ANSI asctime | Old Netscape format date string.
*
* Wdy, DD-Mon-YY HH:MM:SS GMT
* Wdy, DD-Mon-YYYY HH:MM:SS GMT
@@ -504,633 +476,734 @@ static int Cookies_get_month(const char *month_name)
* Tue May 21 13:46:22 1991\n
* Tue May 21 13:46:22 1991
*
- * (return 0 on malformed date string syntax)
+ * Let's add:
+ * Mon Jan 11 08:00:00 2010 GMT
+ *
+ * Return a pointer to a struct tm, or NULL on error.
+ *
+ * NOTE that the draft spec wants user agents to be more flexible in what
+ * they accept. For now, let's hack in special cases when they're encountered.
+ * Why? Because this function is currently understandable, and I don't want to
+ * abandon that (or at best decrease that -- see section 5.1.1) until there
+ * is known to be good reason.
*/
-static time_t Cookies_create_timestamp(const char *expires)
+static struct tm *Cookies_parse_date(const char *date)
{
- time_t ret;
- int day, month, year, hour, minutes, seconds;
- char *cp;
- char *E_msg =
- "Expire date is malformed!\n"
- " (should be RFC-1123 | RFC-850 | ANSI asctime)\n"
- " Ignoring cookie: ";
-
- cp = strchr(expires, ',');
- if (!cp && (strlen(expires) == 24 || strlen(expires) == 25)) {
+ struct tm *tm;
+ char *cp = strchr(date, ',');
+
+ if (!cp && strlen(date)>20 && date[13] == ':' && date[16] == ':') {
/* Looks like ANSI asctime format... */
- cp = (char *)expires;
- day = strtol(cp + 8, NULL, 10); /* day */
- month = Cookies_get_month(cp + 4); /* month */
- year = strtol(cp + 20, NULL, 10); /* year */
- hour = strtol(cp + 11, NULL, 10); /* hour */
- minutes = strtol(cp + 14, NULL, 10); /* minutes */
- seconds = strtol(cp + 17, NULL, 10); /* seconds */
-
- } else if (cp && (cp - expires == 3 || cp - expires > 5) &&
+ tm = dNew0(struct tm, 1);
+
+ cp = (char *)date;
+ tm->tm_mon = Cookies_get_month(cp + 4);
+ tm->tm_mday = strtol(cp + 8, NULL, 10);
+ tm->tm_hour = strtol(cp + 11, NULL, 10);
+ tm->tm_min = strtol(cp + 14, NULL, 10);
+ tm->tm_sec = strtol(cp + 17, NULL, 10);
+ tm->tm_year = strtol(cp + 20, NULL, 10) - 1900;
+
+ } else if (cp && (cp - date == 3 || cp - date > 5) &&
(strlen(cp) == 24 || strlen(cp) == 26)) {
/* RFC-1123 | RFC-850 format | Old Netscape format */
- day = strtol(cp + 2, NULL, 10);
- month = Cookies_get_month(cp + 5);
- year = strtol(cp + 9, &cp, 10);
- /* TODO: tricky, because two digits for year IS ambiguous! */
- year += (year < 70) ? 2000 : ((year < 100) ? 1900 : 0);
- hour = strtol(cp + 1, NULL, 10);
- minutes = strtol(cp + 4, NULL, 10);
- seconds = strtol(cp + 7, NULL, 10);
+ tm = dNew0(struct tm, 1);
+
+ tm->tm_mday = strtol(cp + 2, NULL, 10);
+ tm->tm_mon = Cookies_get_month(cp + 5);
+ tm->tm_year = strtol(cp + 9, &cp, 10);
+ /* tm_year is the number of years since 1900 */
+ if (tm->tm_year < 70)
+ tm->tm_year += 100;
+ else if (tm->tm_year > 100)
+ tm->tm_year -= 1900;
+ tm->tm_hour = strtol(cp + 1, NULL, 10);
+ tm->tm_min = strtol(cp + 4, NULL, 10);
+ tm->tm_sec = strtol(cp + 7, NULL, 10);
} else {
- MSG("%s%s\n", E_msg, expires);
- return (time_t) 0;
+ tm = NULL;
+ MSG("In date \"%s\", format not understood.\n", date);
}
- /* Error checks --this may be overkill */
- if (!(day > 0 && day < 32 && month > 0 && month < 13 && year > 1970 &&
- hour >= 0 && hour < 24 && minutes >= 0 && minutes < 60 &&
- seconds >= 0 && seconds < 60)) {
- MSG("%s%s\n", E_msg, expires);
- return (time_t) 0;
+ /* Error checks. This may be overkill. */
+ if (tm &&
+ !(tm->tm_mday > 0 && tm->tm_mday < 32 && tm->tm_mon >= 0 &&
+ tm->tm_mon < 12 && tm->tm_year >= 70 && tm->tm_hour >= 0 &&
+ tm->tm_hour < 24 && tm->tm_min >= 0 && tm->tm_min < 60 &&
+ tm->tm_sec >= 0 && tm->tm_sec < 60)) {
+ MSG("Date \"%s\" values not in range.\n", date);
+ dFree(tm);
+ tm = NULL;
}
- /* Calculate local timestamp.
- * [stolen from Lynx... (http://lynx.browser.org)] */
- month -= 3;
- if (month < 0) {
- month += 12;
- year--;
- }
+ return tm;
+}
- day += (year - 1968) * 1461 / 4;
- day += ((((month * 153) + 2) / 5) - 672);
- ret = (time_t)((day * 60 * 60 * 24) +
- (hour * 60 * 60) +
- (minutes * 60) +
- seconds);
+/*
+ * Find the least recently used cookie among those in the provided list.
+ */
+static CookieData_t *Cookies_get_LRU(Dlist *cookies)
+{
+ int i, n = dList_length(cookies);
+ CookieData_t *lru = dList_nth_data(cookies, 0);
- MSG("Expires in %ld seconds, at %s",
- (long)ret - time(NULL), ctime(&ret));
+ for (i = 1; i < n; i++) {
+ CookieData_t *curr = dList_nth_data(cookies, i);
- return ret;
+ if (curr->last_used < lru->last_used)
+ lru = curr;
+ }
+ return lru;
}
/*
- * Parse a string containing a list of port numbers.
+ * Delete expired cookies.
+ * If node is given, only check those cookies.
+ * Note that nodes can disappear if all of their cookies were expired.
+ *
+ * Return the number of cookies that were expired.
*/
-static void Cookies_parse_ports(int url_port, CookieData_t *cookie,
- const char *port_str)
+static int Cookies_rm_expired_cookies(DomainNode *node)
{
- if ((!port_str || !port_str[0]) && url_port != 0) {
- /* There was no list, so only the calling urls port should be allowed. */
- if (!cookie->ports)
- cookie->ports = dList_new(1);
- dList_append(cookie->ports, INT2VOIDP(url_port));
- } else if (port_str[0] == '"' && port_str[1] != '"') {
- char *tok, *str;
- int port;
-
- str = dStrdup(port_str + 1);
- while ((tok = dStrsep(&str, ","))) {
- port = strtol(tok, NULL, 10);
- if (port > 0) {
- if (!cookie->ports)
- cookie->ports = dList_new(1);
- dList_append(cookie->ports, INT2VOIDP(port));
- }
+ Dlist *cookies = node ? node->cookies : all_cookies;
+ int removed = 0;
+ int i = 0, n = dList_length(cookies);
+ time_t now = time(NULL);
+
+ while (i < n) {
+ CookieData_t *c = dList_nth_data(cookies, i);
+
+ if (difftime(c->expires_at, now) < 0) {
+ DomainNode *currnode = node ? node :
+ dList_find_sorted(domains, c->domain, Domain_node_by_domain_cmp);
+ dList_remove(currnode->cookies, c);
+ if (dList_length(currnode->cookies) == 0)
+ Cookies_delete_node(currnode);
+ dList_remove_fast(all_cookies, c);
+ Cookies_free_cookie(c);
+ n--;
+ removed++;
+ } else {
+ i++;
}
- dFree(str);
}
+ return removed;
}
/*
- * Build a string of the ports in 'cookie'.
+ * There are too many cookies. Choose one to remove and delete.
+ * If node is given, select from among its cookies only.
*/
-static char *Cookies_build_ports_str(CookieData_t *cookie)
+static void Cookies_too_many(DomainNode *node)
{
- Dstr *dstr;
- char *ret;
- void *data;
- int i;
-
- dstr = dStr_new("\"");
- for (i = 0; (data = dList_nth_data(cookie->ports, i)); ++i) {
- dStr_sprintfa(dstr, "%d,", VOIDP2INT(data));
- }
- /* Remove any trailing comma */
- if (dstr->len > 1)
- dStr_erase(dstr, dstr->len - 1, 1);
- dStr_append(dstr, "\"");
-
- ret = dstr->str;
- dStr_free(dstr, FALSE);
-
- return ret;
+ CookieData_t *lru = Cookies_get_LRU(node ? node->cookies : all_cookies);
+
+ MSG("Too many cookies!\n"
+ "Removing LRU cookie for \'%s\': \'%s=%s\'\n", lru->domain,
+ lru->name, lru->value);
+ if (!node)
+ node = dList_find_sorted(domains, lru->domain,Domain_node_by_domain_cmp);
+
+ dList_remove(node->cookies, lru);
+ dList_remove_fast(all_cookies, lru);
+ Cookies_free_cookie(lru);
+ if (dList_length(node->cookies) == 0)
+ Cookies_delete_node(node);
}
static void Cookies_add_cookie(CookieData_t *cookie)
{
Dlist *domain_cookies;
CookieData_t *c;
- CookieNode *node;
-
- /* Don't add an expired cookie */
- if (!cookie->session_only && cookie->expires_at < time(NULL)) {
- Cookies_free_cookie(cookie);
- return;
- }
+ DomainNode *node;
- node = dList_find_sorted(cookies, cookie->domain,Cookie_node_by_domain_cmp);
- domain_cookies = (node) ? node->dlist : NULL;
+ node = dList_find_sorted(domains, cookie->domain,Domain_node_by_domain_cmp);
+ domain_cookies = (node) ? node->cookies : NULL;
if (domain_cookies) {
- /* Respect the limit of 20 cookies per domain */
- if (dList_length(domain_cookies) >= 20) {
- MSG("There are too many cookies for this domain (%s)\n",
- cookie->domain);
- Cookies_free_cookie(cookie);
- return;
- }
-
- /* Remove any cookies with the same name and path */
- while ((c = dList_find_custom(domain_cookies, cookie, Cookies_cmp))){
- Cookies_remove_cookie(c);
+ /* Remove any cookies with the same name, path, and host-only values. */
+ while ((c = dList_find_custom(domain_cookies, cookie, Cookies_cmp))) {
+ dList_remove(domain_cookies, c);
+ dList_remove_fast(all_cookies, c);
+ Cookies_free_cookie(c);
}
}
- /* add the cookie into the respective domain list */
- node = dList_find_sorted(cookies, cookie->domain,Cookie_node_by_domain_cmp);
- domain_cookies = (node) ? node->dlist : NULL;
- if (!domain_cookies) {
- domain_cookies = dList_new(5);
- dList_append(domain_cookies, cookie);
- node = dNew(CookieNode, 1);
- node->domain = dStrdup(cookie->domain);
- node->dlist = domain_cookies;
- dList_insert_sorted(cookies, node, Cookie_node_cmp);
+ if ((cookie->expires_at == (time_t) -1) ||
+ (difftime(cookie->expires_at, time(NULL)) <= 0)) {
+ /*
+ * Don't add an expired cookie. Whether expiring now == expired, exactly,
+ * is arguable, but we definitely do not want to add a Max-Age=0 cookie.
+ */
+ _MSG("Goodbye, cookie %s=%s d:%s p:%s\n", cookie->name,
+ cookie->value, cookie->domain, cookie->path);
+ Cookies_free_cookie(cookie);
} else {
- dList_append(domain_cookies, cookie);
+ if (domain_cookies && dList_length(domain_cookies) >=MAX_DOMAIN_COOKIES){
+ int removed = Cookies_rm_expired_cookies(node);
+
+ if (removed == 0) {
+ Cookies_too_many(node);
+ } else if (removed >= MAX_DOMAIN_COOKIES) {
+ /* So many were removed that the node might have been deleted. */
+ node = dList_find_sorted(domains, cookie->domain,
+ Domain_node_by_domain_cmp);
+ domain_cookies = (node) ? node->cookies : NULL;
+ }
+ }
+ if (dList_length(all_cookies) >= MAX_TOTAL_COOKIES) {
+ if (Cookies_rm_expired_cookies(NULL) == 0) {
+ Cookies_too_many(NULL);
+ } else if (domain_cookies) {
+ /* Our own node might have just been deleted. */
+ node = dList_find_sorted(domains, cookie->domain,
+ Domain_node_by_domain_cmp);
+ domain_cookies = (node) ? node->cookies : NULL;
+ }
+ }
+
+ cookie->last_used = cookies_use_counter++;
+
+ /* Actually add the cookie! */
+ dList_append(all_cookies, cookie);
+
+ if (!domain_cookies) {
+ domain_cookies = dList_new(5);
+ dList_append(domain_cookies, cookie);
+ node = dNew(DomainNode, 1);
+ node->domain = dStrdup(cookie->domain);
+ node->cookies = domain_cookies;
+ dList_insert_sorted(domains, node, Domain_node_cmp);
+ } else {
+ dList_append(domain_cookies, cookie);
+ }
}
+ if (domain_cookies && (dList_length(domain_cookies) == 0))
+ Cookies_delete_node(node);
}
/*
- * Remove the cookie from the domain list.
- * If the domain list is empty, remove the node too.
- * Free the cookie.
+ * Return the attribute that is present at *cookie_str.
*/
-static void Cookies_remove_cookie(CookieData_t *cookie)
+static char *Cookies_parse_attr(char **cookie_str)
{
- CookieNode *node;
+ char *str;
+ uint_t len;
- node = dList_find_sorted(cookies, cookie->domain,Cookie_node_by_domain_cmp);
- if (node) {
- dList_remove(node->dlist, cookie);
- if (dList_length(node->dlist) == 0) {
- dList_remove(cookies, node);
- dFree(node->domain);
- dList_free(node->dlist);
- }
- } else {
- MSG("Attempting to remove a cookie that doesn't exist!\n");
- }
+ while (dIsspace(**cookie_str))
+ (*cookie_str)++;
- Cookies_free_cookie(cookie);
+ str = *cookie_str;
+ /* find '=' at end of attr, ';' after attr/val pair, '\0' end of string */
+ len = strcspn(str, "=;");
+ *cookie_str += len;
+
+ while (len && (str[len - 1] == ' ' || str[len - 1] == '\t'))
+ len--;
+ return dStrndup(str, len);
}
/*
- * Return the attribute that is present at *cookie_str. This function
- * will also attempt to advance cookie_str past any equal-sign.
+ * Get the value in *cookie_str.
*/
-static char *Cookies_parse_attr(char **cookie_str)
+static char *Cookies_parse_value(char **cookie_str)
{
- char *str = *cookie_str;
- uint_t i, end = 0;
- bool_t got_attr = FALSE;
-
- for (i = 0; ; i++) {
- switch (str[i]) {
- case ' ':
- case '\t':
- case '=':
- case ';':
- got_attr = TRUE;
- if (end == 0)
- end = i;
- break;
- case ',':
- *cookie_str = str + i;
- return dStrndup(str, i);
- break;
- case '\0':
- if (!got_attr) {
- end = i;
- got_attr = TRUE;
- }
- /* fall through! */
- default:
- if (got_attr) {
- *cookie_str = str + i;
- return dStrndup(str, end);
- }
- break;
- }
+ uint_t len;
+ char *str;
+
+ if (**cookie_str == '=') {
+ (*cookie_str)++;
+ while (dIsspace(**cookie_str))
+ (*cookie_str)++;
+
+ str = *cookie_str;
+ /* finds ';' after attr/val pair or '\0' at end of string */
+ len = strcspn(str, ";");
+ *cookie_str += len;
+
+ while (len && (str[len - 1] == ' ' || str[len - 1] == '\t'))
+ len--;
+ } else {
+ str = *cookie_str;
+ len = 0;
}
+ return dStrndup(str, len);
+}
- return NULL;
+/*
+ * Advance past any value
+ */
+static void Cookies_eat_value(char **cookie_str)
+{
+ if (**cookie_str == '=')
+ *cookie_str += strcspn(*cookie_str, ";");
}
/*
- * Get the value starting at *cookie_str.
- * broken_syntax: watch out for stupid syntax (comma in unquoted string...)
+ * Return the number of seconds by which our clock is ahead of the server's
+ * clock.
*/
-static char *Cookies_parse_value(char **cookie_str,
- bool_t broken_syntax,
- bool_t keep_quotes)
+static double Cookies_server_timediff(const char *server_date)
{
- uint_t i, end;
- char *str = *cookie_str;
-
- for (i = end = 0; !end; ++i) {
- switch (str[i]) {
- case ' ':
- case '\t':
- if (!broken_syntax && str[0] != '\'' && str[0] != '"') {
- *cookie_str = str + i + 1;
- end = 1;
- }
- break;
- case '\'':
- case '"':
- if (i != 0 && str[i] == str[0]) {
- char *tmp = str + i;
+ double ret = 0;
- while (*tmp != '\0' && *tmp != ';' && *tmp != ',')
- tmp++;
+ if (server_date) {
+ struct tm *server_tm = Cookies_parse_date(server_date);
- *cookie_str = (*tmp == ';') ? tmp + 1 : tmp;
+ if (server_tm) {
+ time_t server_time = mktime(server_tm);
- if (keep_quotes)
- i++;
- end = 1;
- }
- break;
- case '\0':
- *cookie_str = str + i;
- end = 1;
- break;
- case ',':
- if (str[0] != '\'' && str[0] != '"' && !broken_syntax) {
- /* A new cookie starts here! */
- *cookie_str = str + i;
- end = 1;
- }
- break;
- case ';':
- if (str[0] != '\'' && str[0] != '"') {
- *cookie_str = str + i + 1;
- end = 1;
- }
- break;
- default:
- break;
+ if (server_time != (time_t) -1)
+ ret = difftime(time(NULL), server_time);
+ dFree(server_tm);
}
}
- /* keep i as an index to the last char */
- --i;
+ return ret;
+}
- if ((str[0] == '\'' || str[0] == '"') && !keep_quotes) {
- return i > 1 ? dStrndup(str + 1, i - 1) : NULL;
- } else {
- return dStrndup(str, i);
+static void Cookies_unquote_string(char *str)
+{
+ if (str && str[0] == '\"') {
+ uint_t len = strlen(str);
+
+ if (len > 1 && str[len - 1] == '\"') {
+ str[len - 1] = '\0';
+ while ((*str = str[1]))
+ str++;
+ }
}
}
/*
- * Parse one cookie...
+ * Parse cookie. A cookie might look something like:
+ * "Name=Val; Domain=example.com; Max-Age=3600; HttpOnly"
*/
-static CookieData_t *Cookies_parse_one(int url_port, char **cookie_str)
+static CookieData_t *Cookies_parse(char *cookie_str, const char *server_date)
{
- CookieData_t *cookie;
- char *str = *cookie_str;
- char *attr;
- char *value;
- int num_attr = 0;
+ CookieData_t *cookie = NULL;
+ char *str = cookie_str;
+ bool_t first_attr = TRUE;
bool_t max_age = FALSE;
- bool_t discard = FALSE;
- bool_t error = FALSE;
-
- cookie = dNew0(CookieData_t, 1);
- cookie->session_only = TRUE;
+ bool_t expires = FALSE;
- /* Iterate until there is nothing left of the string OR we come
- * across a comma representing the start of another cookie */
- while (*str != '\0' && *str != ',') {
- if (error) {
- str++;
- continue;
- }
- /* Skip whitespace */
- while (isspace(*str))
- str++;
+ /* Iterate until there is nothing left of the string */
+ while (*str) {
+ char *attr;
+ char *value;
/* Get attribute */
attr = Cookies_parse_attr(&str);
- if (!attr) {
- MSG("Cannot parse cookie attribute!\n");
- error = TRUE;
- continue;
- }
/* Get the value for the attribute and store it */
- if (num_attr == 0) {
- /* The first attr, which always is the user supplied attr, may
- * have the same name as an ordinary attr. Hence this workaround. */
- cookie->name = dStrdup(attr);
- cookie->value = Cookies_parse_value(&str, FALSE, TRUE);
+ if (first_attr) {
+ if (*str != '=' || *attr == '\0') {
+ /* disregard nameless cookie */
+ dFree(attr);
+ return NULL;
+ }
+ cookie = dNew0(CookieData_t, 1);
+ cookie->name = attr;
+ cookie->value = Cookies_parse_value(&str);
+
+ /* let's arbitrarily initialise with a year for now */
+ time_t now = time(NULL);
+ struct tm *tm = gmtime(&now);
+ ++tm->tm_year;
+ cookie->expires_at = mktime(tm);
+ if (cookie->expires_at == (time_t) -1)
+ cookie->expires_at = cookies_future_time;
} else if (dStrcasecmp(attr, "Path") == 0) {
- value = Cookies_parse_value(&str, FALSE, FALSE);
+ value = Cookies_parse_value(&str);
+ dFree(cookie->path);
cookie->path = value;
} else if (dStrcasecmp(attr, "Domain") == 0) {
- value = Cookies_parse_value(&str, FALSE, FALSE);
+ value = Cookies_parse_value(&str);
+ dFree(cookie->domain);
cookie->domain = value;
- } else if (dStrcasecmp(attr, "Discard") == 0) {
- cookie->session_only = TRUE;
- discard = TRUE;
} else if (dStrcasecmp(attr, "Max-Age") == 0) {
- if (!discard) {
- value = Cookies_parse_value(&str, FALSE, FALSE);
-
- if (value) {
- cookie->expires_at = time(NULL) + strtol(value, NULL, 10);
- cookie->session_only = FALSE;
- max_age = TRUE;
- dFree(value);
- } else {
- MSG("Cannot parse cookie Max-Age value!\n");
- dFree(attr);
- error = TRUE;
- continue;
+ value = Cookies_parse_value(&str);
+ if (isdigit(*value) || *value == '-') {
+ time_t now = time(NULL);
+ long age = strtol(value, NULL, 10);
+ struct tm *tm = gmtime(&now);
+
+ tm->tm_sec += age;
+ cookie->expires_at = mktime(tm);
+ if (age > 0 && cookie->expires_at == (time_t) -1) {
+ cookie->expires_at = cookies_future_time;
}
+ _MSG("Cookie to expire at %s", ctime(&cookie->expires_at));
+ expires = max_age = TRUE;
}
+ dFree(value);
} else if (dStrcasecmp(attr, "Expires") == 0) {
- if (!max_age && !discard) {
- MSG("Old netscape-style cookie...\n");
- value = Cookies_parse_value(&str, TRUE, FALSE);
- if (value) {
- cookie->expires_at = Cookies_create_timestamp(value);
- cookie->session_only = FALSE;
- dFree(value);
+ if (!max_age) {
+ value = Cookies_parse_value(&str);
+ Cookies_unquote_string(value);
+ _MSG("Expires attribute gives %s\n", value);
+ struct tm *tm = Cookies_parse_date(value);
+ if (tm) {
+ tm->tm_sec += Cookies_server_timediff(server_date);
+ cookie->expires_at = mktime(tm);
+ if (cookie->expires_at == (time_t) -1 && tm->tm_year >= 138) {
+ /* Just checking tm_year does not ensure that the problem was
+ * inability to represent a distant date...
+ */
+ cookie->expires_at = cookies_future_time;
+ }
+ _MSG("Cookie to expire at %s", ctime(&cookie->expires_at));
+ dFree(tm);
} else {
- MSG("Cannot parse cookie Expires value!\n");
- dFree(attr);
- error = TRUE;
- continue;
+ cookie->expires_at = (time_t) -1;
}
- }
- } else if (dStrcasecmp(attr, "Port") == 0) {
- value = Cookies_parse_value(&str, FALSE, TRUE);
- Cookies_parse_ports(url_port, cookie, value);
- dFree(value);
- } else if (dStrcasecmp(attr, "Comment") == 0) {
- value = Cookies_parse_value(&str, FALSE, FALSE);
- cookie->comment = value;
- } else if (dStrcasecmp(attr, "CommentURL") == 0) {
- value = Cookies_parse_value(&str, FALSE, FALSE);
- cookie->comment_url = value;
- } else if (dStrcasecmp(attr, "Version") == 0) {
- value = Cookies_parse_value(&str, FALSE, FALSE);
-
- if (value) {
- cookie->version = strtol(value, NULL, 10);
+ expires = TRUE;
dFree(value);
} else {
- MSG("Cannot parse cookie Version value!\n");
- dFree(attr);
- error = TRUE;
- continue;
+ Cookies_eat_value(&str);
}
} else if (dStrcasecmp(attr, "Secure") == 0) {
cookie->secure = TRUE;
+ Cookies_eat_value(&str);
+ } else if (dStrcasecmp(attr, "HttpOnly") == 0) {
+ Cookies_eat_value(&str);
} else {
- /* Oops! this can't be good... */
MSG("Cookie contains unknown attribute: '%s'\n", attr);
- dFree(attr);
- error = TRUE;
- continue;
+ Cookies_eat_value(&str);
}
- dFree(attr);
- num_attr++;
- }
-
- *cookie_str = (*str == ',') ? str + 1 : str;
+ if (first_attr)
+ first_attr = FALSE;
+ else
+ dFree(attr);
- if (!error && (!cookie->name || !cookie->value)) {
- MSG("Cookie missing name and/or value!\n");
- error = TRUE;
- }
- if (error) {
- Cookies_free_cookie(cookie);
- cookie = NULL;
+ if (*str == ';')
+ str++;
}
+ cookie->session_only = expires == FALSE;
return cookie;
}
/*
- * Iterate the cookie string until we catch all cookies.
- * Return Value: a list with all the cookies! (or NULL upon error)
+ * Compare cookies by host_only, name, and path. Return 0 if equal.
*/
-static Dlist *Cookies_parse_string(int url_port, char *cookie_string)
+static int Cookies_cmp(const void *a, const void *b)
{
- CookieData_t *cookie;
- Dlist *ret = NULL;
- char *str = cookie_string;
+ const CookieData_t *ca = a, *cb = b;
- /* The string may contain several cookies separated by comma.
- * We'll iterate until we've caught them all */
- while (*str) {
- cookie = Cookies_parse_one(url_port, &str);
+ return (ca->host_only != cb->host_only) ||
+ (strcmp(ca->name, cb->name) != 0) ||
+ (strcmp(ca->path, cb->path) != 0);
+}
- if (cookie) {
- if (!ret)
- ret = dList_new(4);
- dList_append(ret, cookie);
- } else {
- MSG("Malformed cookie field, ignoring cookie: %s\n", cookie_string);
- }
- }
+/*
+ * Is the domain an IP address?
+ */
+static bool_t Cookies_domain_is_ip(const char *domain)
+{
+ uint_t len;
- return ret;
+ if (!domain)
+ return FALSE;
+
+ len = strlen(domain);
+
+ if (len == strspn(domain, "0123456789.")) {
+ _MSG("an IPv4 address\n");
+ return TRUE;
+ }
+ if (*domain == '[' &&
+ (len == strspn(domain, "0123456789abcdefABCDEF:.[]"))) {
+ /* The precise format is shown in section 3.2.2 of rfc 3986 */
+ _MSG("an IPv6 address\n");
+ return TRUE;
+ }
+ return FALSE;
}
/*
- * Compare cookies by name and path (return 0 if equal)
+ * Check whether url_path path-matches cookie_path
+ *
+ * Note different user agents apparently vary in path-matching behaviour,
+ * but this is the recommended method at the moment.
*/
-static int Cookies_cmp(const void *a, const void *b)
+static bool_t Cookies_path_matches(const char *url_path,
+ const char *cookie_path)
{
- const CookieData_t *ca = a, *cb = b;
- int ret;
+ bool_t ret = TRUE;
+
+ if (!url_path || !cookie_path) {
+ ret = FALSE;
+ } else {
+ uint_t c_len = strlen(cookie_path);
+ uint_t u_len = strlen(url_path);
- if (!(ret = strcmp(ca->name, cb->name)))
- ret = strcmp(ca->path, cb->path);
+ ret = (!strncmp(cookie_path, url_path, c_len) &&
+ ((c_len == u_len) ||
+ (c_len > 0 && cookie_path[c_len - 1] == '/') ||
+ (url_path[c_len] == '/')));
+ }
return ret;
}
/*
- * Validate cookies domain against some security checks.
+ * If cookie path is not properly set, remedy that.
*/
-static bool_t Cookies_validate_domain(CookieData_t *cookie, char *host,
- char *url_path)
+static void Cookies_validate_path(CookieData_t *cookie, const char *url_path)
{
- int dots, diff, i;
- bool_t is_ip;
-
- /* Make sure that the path is set to something */
if (!cookie->path || cookie->path[0] != '/') {
dFree(cookie->path);
- cookie->path = Cookies_strip_path(url_path);
+
+ if (url_path) {
+ uint_t len = strlen(url_path);
+
+ while (len && url_path[len] != '/')
+ len--;
+ cookie->path = dStrndup(url_path, len ? len : 1);
+ } else {
+ cookie->path = dStrdup("/");
+ }
}
+}
- /* If the server never set a domain, or set one without a leading
- * dot (which isn't allowed), we use the calling URL's hostname. */
- if (cookie->domain == NULL || cookie->domain[0] != '.') {
- dFree(cookie->domain);
- cookie->domain = dStrdup(host);
+/*
+ * Check whether host name A domain-matches host name B.
+ */
+static bool_t Cookies_domain_matches(char *A, char *B)
+{
+ int diff;
+
+ if (!A || !*A || !B || !*B)
+ return FALSE;
+
+ if (*B == '.')
+ B++;
+
+ /* Should we concern ourselves with trailing dots in matching (here or
+ * elsewhere)? The HTTP State people have found that most user agents
+ * don't, so: No.
+ */
+
+ if (!dStrcasecmp(A, B))
return TRUE;
- }
- /* Count the number of dots and also find out if it is an IP-address */
- is_ip = TRUE;
- for (i = 0, dots = 0; cookie->domain[i] != '\0'; i++) {
- if (cookie->domain[i] == '.')
- dots++;
- else if (!isdigit(cookie->domain[i]))
- is_ip = FALSE;
- }
+ if (Cookies_domain_is_ip(B))
+ return FALSE;
- /* A valid domain must have at least two dots in it */
- /* NOTE: this breaks cookies on localhost... */
- if (dots < 2) {
+ diff = strlen(A) - strlen(B);
+
+ if (diff > 0) {
+ /* B is the tail of A, and the match is preceded by a '.' */
+ return (dStrcasecmp(A + diff, B) == 0 && A[diff - 1] == '.');
+ } else {
return FALSE;
}
+}
- /* Now see if the url matches the domain */
- diff = strlen(host) - i;
- if (diff > 0) {
- if (dStrcasecmp(host + diff, cookie->domain))
- return FALSE;
-
- if (!is_ip) {
- /* "x.y.test.com" is not allowed to set cookies for ".test.com";
- * only an url of the form "y.test.com" would be. */
- while ( diff-- )
- if (host[diff] == '.')
- return FALSE;
+/*
+ * Based on the host, how many internal dots do we need in a cookie domain
+ * to make it valid? e.g., "org" is not on the list, so dillo.org is a safe
+ * cookie domain, but "uk" is on the list, so ac.uk is not safe.
+ *
+ * This is imperfect, but it's something. Specifically, checking for these
+ * TLDs is the solution that Konqueror used once upon a time, according to
+ * reports.
+ */
+static uint_t Cookies_internal_dots_required(const char *host)
+{
+ uint_t ret = 1;
+
+ if (host) {
+ int start, after, tld_len;
+
+ /* We may be able to trust the format of the host string more than
+ * I am here. Trailing dots and no dots are real possibilities, though.
+ */
+ after = strlen(host);
+ if (after > 0 && host[after - 1] == '.')
+ after--;
+ start = after;
+ while (start > 0 && host[start - 1] != '.')
+ start--;
+ tld_len = after - start;
+
+ if (tld_len > 0) {
+ /* These TLDs were chosen by examining the current publicsuffix list
+ * in January 2010 and picking out those where it was simplest for
+ * them to describe the situation by beginning with a "*.[tld]" rule.
+ */
+ const char *const tlds[] = {"ar","au","bd","bn","bt","ck","cy","do",
+ "eg","er","et","fj","fk","gt","gu","id",
+ "il","jm","ke","kh","kw","ml","mm","mt",
+ "mz","ni","np","nz","om","pg","py","qa",
+ "sv","tr","uk","uy","ve","ye","yu","za",
+ "zm","zw"};
+ uint_t i, tld_num = sizeof(tlds) / sizeof(tlds[0]);
+
+ for (i = 0; i < tld_num; i++) {
+ if (strlen(tlds[i]) == (uint_t) tld_len &&
+ !dStrncasecmp(tlds[i], host + start, tld_len)) {
+ _MSG("TLD code matched %s\n", tlds[i]);
+ ret++;
+ break;
+ }
+ }
}
}
-
- return TRUE;
+ return ret;
}
/*
- * Strip of the filename from a full path
+ * Validate cookies domain against some security checks.
*/
-static char *Cookies_strip_path(const char *path)
+static bool_t Cookies_validate_domain(CookieData_t *cookie, char *host)
{
- char *ret;
- uint_t len;
+ uint_t i, internal_dots;
+
+ if (!cookie->domain) {
+ cookie->domain = dStrdup(host);
+ cookie->host_only = TRUE;
+ return TRUE;
+ }
- if (path) {
- len = strlen(path);
+ if (!Cookies_domain_matches(host, cookie->domain))
+ return FALSE;
- while (len && path[len] != '/')
- len--;
- ret = dStrndup(path, len + 1);
- } else {
- ret = dStrdup("/");
+ internal_dots = 0;
+ for (i = 1; i < strlen(cookie->domain) - 1; i++) {
+ if (cookie->domain[i] == '.')
+ internal_dots++;
}
- return ret;
+ /* All of this dots business is a weak hack.
+ * TODO: accept the publicsuffix.org list as an optional external file.
+ */
+ if (internal_dots < Cookies_internal_dots_required(host)) {
+ MSG("not enough dots in %s\n", cookie->domain);
+ return FALSE;
+ }
+
+ _MSG("host %s and domain %s is all right\n", host, cookie->domain);
+ return TRUE;
}
/*
* Set the value corresponding to the cookie string
+ * Return value: 0 set OK, -1 disabled, -2 denied, -3 rejected.
*/
-static void Cookies_set(char *cookie_string, char *url_host,
- char *url_path, int url_port)
+static int Cookies_set(char *cookie_string, char *url_host,
+ char *url_path, char *server_date)
{
CookieControlAction action;
CookieData_t *cookie;
- Dlist *list;
- int i;
+ int ret = -1;
if (disabled)
- return;
+ return ret;
action = Cookies_control_check_domain(url_host);
if (action == COOKIE_DENY) {
MSG("denied SET for %s\n", url_host);
- return;
- }
+ ret = -2;
- if ((list = Cookies_parse_string(url_port, cookie_string))) {
- for (i = 0; (cookie = dList_nth_data(list, i)); ++i) {
- if (Cookies_validate_domain(cookie, url_host, url_path)) {
+ } else {
+ MSG("%s SETTING: %s\n", url_host, cookie_string);
+ ret = -3;
+ if ((cookie = Cookies_parse(cookie_string, server_date))) {
+ if (Cookies_validate_domain(cookie, url_host)) {
+ Cookies_validate_path(cookie, url_path);
if (action == COOKIE_ACCEPT_SESSION)
cookie->session_only = TRUE;
Cookies_add_cookie(cookie);
+ ret = 0;
} else {
- MSG("Rejecting cookie for %s from host %s path %s\n",
+ MSG("Rejecting cookie for domain %s from host %s path %s\n",
cookie->domain, url_host, url_path);
Cookies_free_cookie(cookie);
}
}
- dList_free(list);
}
+
+ return ret;
}
/*
- * Compare the cookie with the supplied data to see if it matches
+ * Compare the cookie with the supplied data to see whether it matches
*/
-static bool_t Cookies_match(CookieData_t *cookie, int port,
- const char *path, bool_t is_ssl)
+static bool_t Cookies_match(CookieData_t *cookie, const char *url_path,
+ bool_t host_only_val, bool_t is_ssl)
{
- void *data;
- int i;
+ if (cookie->host_only != host_only_val)
+ return FALSE;
/* Insecure cookies matches both secure and insecure urls, secure
cookies matches only secure urls */
if (cookie->secure && !is_ssl)
return FALSE;
- /* Check that the cookie path is a subpath of the current path */
- if (strncmp(cookie->path, path, strlen(cookie->path)) != 0)
- return FALSE;
-
- /* Check if the port of the request URL matches any
- * of those set in the cookie */
- if (cookie->ports) {
- for (i = 0; (data = dList_nth_data(cookie->ports, i)); ++i) {
- if (VOIDP2INT(data) == port)
- return TRUE;
- }
+ if (!Cookies_path_matches(url_path, cookie->path))
return FALSE;
- }
/* It's a match */
return TRUE;
}
+static void Cookies_add_matching_cookies(const char *domain,
+ const char *url_path,
+ bool_t host_only_val,
+ Dlist *matching_cookies,
+ bool_t is_ssl)
+{
+ DomainNode *node = dList_find_sorted(domains, domain,
+ Domain_node_by_domain_cmp);
+ if (node) {
+ int i;
+ CookieData_t *cookie;
+ Dlist *domain_cookies = node->cookies;
+
+ for (i = 0; (cookie = dList_nth_data(domain_cookies, i)); ++i) {
+ /* Remove expired cookie. */
+ if (difftime(cookie->expires_at, time(NULL)) < 0) {
+ _MSG("Goodbye, expired cookie %s=%s d:%s p:%s\n", cookie->name,
+ cookie->value, cookie->domain, cookie->path);
+ dList_remove(domain_cookies, cookie);
+ dList_remove_fast(all_cookies, cookie);
+ Cookies_free_cookie(cookie);
+ --i; continue;
+ }
+ /* Check if the cookie matches the requesting URL */
+ if (Cookies_match(cookie, url_path, host_only_val, is_ssl)) {
+ int j;
+ CookieData_t *curr;
+ uint_t path_length = strlen(cookie->path);
+
+ cookie->last_used = cookies_use_counter;
+
+ /* Longest cookies go first */
+ for (j = 0;
+ (curr = dList_nth_data(matching_cookies, j)) &&
+ strlen(curr->path) >= path_length;
+ j++) ;
+ dList_insert_pos(matching_cookies, cookie, j);
+ }
+ }
+
+ if (dList_length(domain_cookies) == 0)
+ Cookies_delete_node(node);
+ }
+}
+
/*
* Return a string that contains all relevant cookies as headers.
*/
static char *Cookies_get(char *url_host, char *url_path,
- char *url_scheme, int url_port)
+ char *url_scheme)
{
- char *domain_str, *q, *str, *path;
+ char *domain_str, *str;
CookieData_t *cookie;
Dlist *matching_cookies;
- CookieNode *node;
- Dlist *domain_cookies;
- bool_t is_ssl;
+ bool_t is_ssl, is_ip_addr, host_only_val;
+
Dstr *cookie_dstring;
int i;
@@ -1139,27 +1212,47 @@ static char *Cookies_get(char *url_host, char *url_path,
matching_cookies = dList_new(8);
- path = Cookies_strip_path(url_path);
-
/* Check if the protocol is secure or not */
is_ssl = (!dStrcasecmp(url_scheme, "https"));
- for (domain_str = (char *) url_host;
- domain_str != NULL && *domain_str;
- domain_str = strchr(domain_str+1, '.')) {
-
- node = dList_find_sorted(cookies, domain_str, Cookie_node_by_domain_cmp);
- domain_cookies = (node) ? node->dlist : NULL;
-
- for (i = 0; (cookie = dList_nth_data(domain_cookies, i)); ++i) {
- /* Remove expired cookie. */
- if (!cookie->session_only && cookie->expires_at < time(NULL)) {
- Cookies_remove_cookie(cookie);
- --i; continue;
- }
- /* Check if the cookie matches the requesting URL */
- if (Cookies_match(cookie, url_port, path, is_ssl)) {
- dList_append(matching_cookies, cookie);
+ is_ip_addr = Cookies_domain_is_ip(url_host);
+
+ /* If a cookie is set that lacks a Domain attribute, its domain is set to
+ * the server's host and the host_only flag is set for that cookie. Such a
+ * cookie can only be sent back to that host. Cookies with Domain attrs do
+ * not have the host_only flag set, and may be sent to subdomains. Domain
+ * attrs can have leading dots, which should be ignored for matching
+ * purposes.
+ */
+ host_only_val = FALSE;
+ if (!is_ip_addr) {
+ /* e.g., sub.example.com set a cookie with domain ".sub.example.com". */
+ domain_str = dStrconcat(".", url_host, NULL);
+ Cookies_add_matching_cookies(domain_str, url_path, host_only_val,
+ matching_cookies, is_ssl);
+ dFree(domain_str);
+ }
+ host_only_val = TRUE;
+ /* e.g., sub.example.com set a cookie with no domain attribute. */
+ Cookies_add_matching_cookies(url_host, url_path, host_only_val,
+ matching_cookies, is_ssl);
+ host_only_val = FALSE;
+ /* e.g., sub.example.com set a cookie with domain "sub.example.com". */
+ Cookies_add_matching_cookies(url_host, url_path, host_only_val,
+ matching_cookies, is_ssl);
+
+ if (!is_ip_addr) {
+ for (domain_str = strchr(url_host+1, '.');
+ domain_str != NULL && *domain_str;
+ domain_str = strchr(domain_str+1, '.')) {
+ /* e.g., sub.example.com set a cookie with domain ".example.com". */
+ Cookies_add_matching_cookies(domain_str, url_path, host_only_val,
+ matching_cookies, is_ssl);
+ if (domain_str[1]) {
+ domain_str++;
+ /* e.g., sub.example.com set a cookie with domain "example.com".*/
+ Cookies_add_matching_cookies(domain_str, url_path, host_only_val,
+ matching_cookies, is_ssl);
}
}
}
@@ -1167,36 +1260,24 @@ static char *Cookies_get(char *url_host, char *url_path,
/* Found the cookies, now make the string */
cookie_dstring = dStr_new("");
if (dList_length(matching_cookies) > 0) {
- CookieData_t *first_cookie = dList_nth_data(matching_cookies, 0);
dStr_sprintfa(cookie_dstring, "Cookie: ");
- if (first_cookie->version != 0)
- dStr_sprintfa(cookie_dstring, "$Version=\"%d\"; ",
- first_cookie->version);
-
-
for (i = 0; (cookie = dList_nth_data(matching_cookies, i)); ++i) {
- q = (cookie->version == 0 ? "" : "\"");
- dStr_sprintfa(cookie_dstring,
- "%s=%s; $Path=%s%s%s; $Domain=%s%s%s",
- cookie->name, cookie->value,
- q, cookie->path, q, q, cookie->domain, q);
- if (cookie->ports) {
- char *ports_str = Cookies_build_ports_str(cookie);
- dStr_sprintfa(cookie_dstring, "; $Port=%s", ports_str);
- dFree(ports_str);
- }
-
+ dStr_sprintfa(cookie_dstring, "%s=%s", cookie->name, cookie->value);
dStr_append(cookie_dstring,
dList_length(matching_cookies) > i + 1 ? "; " : "\r\n");
}
}
dList_free(matching_cookies);
- dFree(path);
str = cookie_dstring->str;
dStr_free(cookie_dstring, FALSE);
+
+ if (*str)
+ cookies_use_counter++;
+
+ MSG("%s GETTING: %s\n", url_host, str);
return str;
}
@@ -1216,11 +1297,10 @@ static int Cookie_control_init(void)
{
CookieControl cc;
FILE *stream;
- char *filename;
+ char *filename, *rc;
char line[LINE_MAXLEN];
char domain[LINE_MAXLEN];
char rule[LINE_MAXLEN];
- int i, j;
bool_t enabled = FALSE;
/* Get a file pointer */
@@ -1234,28 +1314,31 @@ static int Cookie_control_init(void)
/* Get all lines in the file */
while (!feof(stream)) {
line[0] = '\0';
- fgets(line, LINE_MAXLEN, stream);
+ rc = fgets(line, LINE_MAXLEN, stream);
+ if (!rc && ferror(stream)) {
+ MSG("Error while reading rule from cookiesrc: %s\n",
+ dStrerror(errno));
+ break; /* bail out */
+ }
/* Remove leading and trailing whitespaces */
dStrstrip(line);
if (line[0] != '\0' && line[0] != '#') {
- i = 0;
- j = 0;
+ int i = 0, j = 0;
/* Get the domain */
- while (!isspace(line[i]))
+ while (line[i] != '\0' && !dIsspace(line[i]))
domain[j++] = line[i++];
domain[j] = '\0';
/* Skip past whitespaces */
- i++;
- while (isspace(line[i]))
+ while (dIsspace(line[i]))
i++;
/* Get the rule */
j = 0;
- while (line[i] != '\0' && !isspace(line[i]))
+ while (line[i] != '\0' && !dIsspace(line[i]))
rule[j++] = line[i++];
rule[j] = '\0';
@@ -1277,8 +1360,17 @@ static int Cookie_control_init(void)
default_action = cc.action;
dFree(cc.domain);
} else {
+ int i;
+ uint_t len = strlen(cc.domain);
+
+ /* Insert into list such that longest rules come first. */
a_List_add(ccontrol, num_ccontrol, num_ccontrol_max);
- ccontrol[num_ccontrol++] = cc;
+ for (i = num_ccontrol++;
+ i > 0 && (len > strlen(ccontrol[i-1].domain));
+ i--) {
+ ccontrol[i] = ccontrol[i-1];
+ }
+ ccontrol[i] = cc;
}
if (cc.action != COOKIE_DENY)
@@ -1292,7 +1384,9 @@ static int Cookie_control_init(void)
}
/*
- * Check the rules for an appropriate action for this domain
+ * Check the rules for an appropriate action for this domain.
+ * The rules are ordered by domain length, with longest first, so the
+ * first match is the most specific.
*/
static CookieControlAction Cookies_control_check_domain(const char *domain)
{
@@ -1326,69 +1420,74 @@ static CookieControlAction Cookies_control_check_domain(const char *domain)
* Note: Buf is a zero terminated string
* Return code: { 0:OK, 1:Abort, 2:Close }
*/
-static int srv_parse_buf(SockHandler *sh, char *Buf, size_t BufSize)
+static int srv_parse_tok(Dsh *sh, ClientInfo *client, char *Buf)
{
- char *p, *cmd, *cookie, *host, *path, *scheme;
- int port, ret;
+ char *cmd, *cookie, *host, *path;
+ int ret = 1;
+ size_t BufSize = strlen(Buf);
+
+ cmd = a_Dpip_get_attr_l(Buf, BufSize, "cmd");
+
+ if (!cmd) {
+ /* abort */
+ } else if (client->status == 0) {
+ /* authenticate */
+ if (a_Dpip_check_auth(Buf) == 1) {
+ client->status = 1;
+ ret = 0;
+ }
+ } else if (strcmp(cmd, "DpiBye") == 0) {
+ dFree(cmd);
+ MSG("(pid %d): Got DpiBye.\n", (int)getpid());
+ exit(0);
- if (!(p = strchr(Buf, '>'))) {
- /* Haven't got a full tag */
- MSG("Haven't got a full tag!\n");
- return 1;
- }
+ } else if (strcmp(cmd, "set_cookie") == 0) {
+ int st;
+ char *date;
- cmd = a_Dpip_get_attr(Buf, BufSize, "cmd");
+ cookie = a_Dpip_get_attr_l(Buf, BufSize, "cookie");
+ host = a_Dpip_get_attr_l(Buf, BufSize, "host");
+ path = a_Dpip_get_attr_l(Buf, BufSize, "path");
+ date = a_Dpip_get_attr_l(Buf, BufSize, "date");
- if (cmd && strcmp(cmd, "DpiBye") == 0) {
- dFree(cmd);
- MSG("Cookies dpi (pid %d): Got DpiBye.\n", (int)getpid());
- exit(0);
+ st = Cookies_set(cookie, host, path, date);
- } else if (cmd && strcmp(cmd, "set_cookie") == 0) {
dFree(cmd);
- cookie = a_Dpip_get_attr(Buf, BufSize, "cookie");
- host = a_Dpip_get_attr(Buf, BufSize, "host");
- path = a_Dpip_get_attr(Buf, BufSize, "path");
- p = a_Dpip_get_attr(Buf, BufSize, "port");
- port = strtol(p, NULL, 10);
- dFree(p);
-
- Cookies_set(cookie, host, path, port);
+ cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "set_cookie_answer",
+ st == 0 ? "ok" : "not set");
+ a_Dpip_dsh_write_str(sh, 1, cmd);
+ dFree(date);
dFree(path);
dFree(host);
dFree(cookie);
- return 2;
+ ret = 2;
- } else if (cmd && strcmp(cmd, "get_cookie") == 0) {
- dFree(cmd);
- scheme = a_Dpip_get_attr(Buf, BufSize, "scheme");
- host = a_Dpip_get_attr(Buf, BufSize, "host");
- path = a_Dpip_get_attr(Buf, BufSize, "path");
- p = a_Dpip_get_attr(Buf, BufSize, "port");
- port = strtol(p, NULL, 10);
- dFree(p);
-
- cookie = Cookies_get(host, path, scheme, port);
+ } else if (strcmp(cmd, "get_cookie") == 0) {
+ char *scheme = a_Dpip_get_attr_l(Buf, BufSize, "scheme");
+
+ host = a_Dpip_get_attr_l(Buf, BufSize, "host");
+ path = a_Dpip_get_attr_l(Buf, BufSize, "path");
+
+ cookie = Cookies_get(host, path, scheme);
dFree(scheme);
dFree(path);
dFree(host);
+ dFree(cmd);
cmd = a_Dpip_build_cmd("cmd=%s cookie=%s", "get_cookie_answer", cookie);
- if (sock_handler_write_str(sh, 1, cmd)) {
+ if (a_Dpip_dsh_write_str(sh, 1, cmd)) {
ret = 1;
} else {
- _MSG("sock_handler_write_str: SUCCESS cmd={%s}\n", cmd);
+ _MSG("a_Dpip_dsh_write_str: SUCCESS cmd={%s}\n", cmd);
ret = 2;
}
dFree(cookie);
- dFree(cmd);
-
- return ret;
}
+ dFree(cmd);
- return 0;
+ return ret;
}
/* -- Termination handlers ----------------------------------------------- */
@@ -1415,13 +1514,13 @@ static void termination_handler(int signum)
/*
* -- MAIN -------------------------------------------------------------------
*/
-int main (void) {
- struct sockaddr_un spun;
- int temp_sock_descriptor;
+int main(void) {
+ struct sockaddr_in sin;
socklen_t address_size;
+ ClientInfo *client;
+ int sock_fd, code;
char *buf;
- int code;
- SockHandler *sh;
+ Dsh *sh;
/* Arrange the cleanup function for terminations via exit() */
atexit(cleanup);
@@ -1441,36 +1540,42 @@ int main (void) {
exit(1);
/* some OSes may need this... */
- address_size = sizeof(struct sockaddr_un);
+ address_size = sizeof(struct sockaddr_in);
while (1) {
- temp_sock_descriptor =
- accept(STDIN_FILENO, (struct sockaddr *)&spun, &address_size);
- if (temp_sock_descriptor == -1) {
+ sock_fd = accept(STDIN_FILENO, (struct sockaddr *)&sin, &address_size);
+ if (sock_fd == -1) {
perror("[accept]");
exit(1);
}
- /* create the SockHandler structure */
- sh = sock_handler_new(temp_sock_descriptor,temp_sock_descriptor,8*1024);
+ /* create the Dsh structure */
+ sh = a_Dpip_dsh_new(sock_fd, sock_fd, 8*1024);
+ client = dNew(ClientInfo,1);
+ client->sh = sh;
+ client->status = 0;
while (1) {
code = 1;
- if ((buf = sock_handler_read(sh)) != NULL) {
+ if ((buf = a_Dpip_dsh_read_token(sh, 1)) != NULL) {
/* Let's see what we fished... */
_MSG(" buf = {%s}\n", buf);
- code = srv_parse_buf(sh, buf, strlen(buf));
+ code = srv_parse_tok(sh, client, buf);
+ dFree(buf);
}
+
_MSG(" code = %d %s\n", code, code == 1 ? "EXIT" : "BREAK");
- if (code == 1)
+ if (code == 1) {
exit(1);
- else if (code == 2)
+ } else if (code == 2) {
break;
+ }
}
- _MSG("Closing SockHandler\n");
- sock_handler_close(sh);
- sock_handler_free(sh);
+ _MSG("Closing Dsh\n");
+ a_Dpip_dsh_close(sh);
+ a_Dpip_dsh_free(sh);
+ dFree(client);
}/*while*/
diff --git a/dpi/datauri.c b/dpi/datauri.c
index 5f3c79a7..6d7acfa7 100644
--- a/dpi/datauri.c
+++ b/dpi/datauri.c
@@ -15,6 +15,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <ctype.h>
+#include <errno.h>
#include "../dpip/dpip.h"
#include "dpiutil.h"
@@ -22,75 +24,89 @@
/*
* Debugging macros
*/
+#define SILENT 1
#define _MSG(...)
-#define MSG(...) printf("[datauri dpi]: " __VA_ARGS__)
+#if SILENT
+ #define MSG(...)
+#else
+ #define MSG(...) fprintf(stderr, "[datauri dpi]: " __VA_ARGS__)
+#endif
/*
* Global variables
*/
-static SockHandler *sh = NULL;
+static Dsh *sh = NULL;
+static void b64strip_illegal_chars(unsigned char* str)
+{
+ unsigned char *p, *s = str;
+
+ MSG("len=%d{%s}\n", strlen((char*)str), str);
+
+ for (p = s; (*p = *s); ++s) {
+ if (isalnum(*p) || strchr("+/=", *p))
+ ++p;
+ }
+ MSG("len=%d{%s}\n", strlen((char *)str), str);
+}
static int b64decode(unsigned char* str)
{
- unsigned char *cur, *start;
- int d, dlast, phase;
- unsigned char c;
- static int table[256] = {
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
- 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
- -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
- 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
- -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
- 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
- };
-
- d = dlast = phase = 0;
- start = str;
- for (cur = str; *cur != '\0'; ++cur )
- {
- // jer: treat line endings as physical breaks.
- //if (*cur == '\n' || *cur == '\r'){phase = dlast = 0; continue;}
- d = table[(int)*cur];
- if(d != -1)
- {
- switch(phase)
- {
- case 0:
- ++phase;
- break;
- case 1:
- c = ((dlast << 2) | ((d & 0x30) >> 4));
- *str++ = c;
- ++phase;
- break;
- case 2:
- c = (((dlast & 0xf) << 4) | ((d & 0x3c) >> 2));
- *str++ = c;
- ++phase;
- break;
- case 3:
- c = (((dlast & 0x03 ) << 6) | d);
- *str++ = c;
- phase = 0;
- break;
- }
- dlast = d;
- }
- }
- *str = '\0';
- return str - start;
+ unsigned char *cur, *start;
+ int d, dlast, phase;
+ unsigned char c;
+ static int table[256] = {
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
+ 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
+ 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
+ -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
+ 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
+ };
+
+ d = dlast = phase = 0;
+ start = str;
+ for (cur = str; *cur != '\0'; ++cur ) {
+ // jer: treat line endings as physical breaks.
+ //if (*cur == '\n' || *cur == '\r'){phase = dlast = 0; continue;}
+ d = table[(int)*cur];
+ if (d != -1) {
+ switch(phase) {
+ case 0:
+ ++phase;
+ break;
+ case 1:
+ c = ((dlast << 2) | ((d & 0x30) >> 4));
+ *str++ = c;
+ ++phase;
+ break;
+ case 2:
+ c = (((dlast & 0xf) << 4) | ((d & 0x3c) >> 2));
+ *str++ = c;
+ ++phase;
+ break;
+ case 3:
+ c = (((dlast & 0x03 ) << 6) | d);
+ *str++ = c;
+ phase = 0;
+ break;
+ }
+ dlast = d;
+ }
+ }
+ *str = '\0';
+ return str - start;
}
/* Modified from src/url.c --------------------------------------------------*/
@@ -108,7 +124,7 @@ static int Url_decode_hex_octet(const char *s)
hex[2] = 0;
hex_value = strtol(hex, &tail, 16);
if (tail - hex == 2)
- return hex_value;
+ return hex_value;
}
return -1;
}
@@ -151,16 +167,16 @@ static void send_decoded_data(const char *url, const char *mime_type,
/* Send dpip tag */
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
/* Send HTTP header. */
- sock_handler_write_str(sh, 0, "Content-type: ");
- sock_handler_write_str(sh, 0, mime_type);
- sock_handler_write_str(sh, 1, "\n\n");
+ a_Dpip_dsh_write_str(sh, 0, "Content-type: ");
+ a_Dpip_dsh_write_str(sh, 0, mime_type);
+ a_Dpip_dsh_write_str(sh, 1, "\n\n");
/* Send message */
- sock_handler_write(sh, 0, (char *)data, data_sz);
+ a_Dpip_dsh_write(sh, 0, (char *)data, data_sz);
}
static void send_failure_message(const char *url, const char *mime_type,
@@ -178,24 +194,24 @@ static void send_failure_message(const char *url, const char *mime_type,
/* Send dpip tag */
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
/* Send HTTP header. */
- sock_handler_write_str(sh, 0, "Content-type: ");
- sock_handler_write_str(sh, 0, msg_mime_type);
- sock_handler_write_str(sh, 1, "\n\n");
+ a_Dpip_dsh_write_str(sh, 0, "Content-type: ");
+ a_Dpip_dsh_write_str(sh, 0, msg_mime_type);
+ a_Dpip_dsh_write_str(sh, 1, "\n\n");
/* Send message */
- sock_handler_write_str(sh, 0, msg);
+ a_Dpip_dsh_write_str(sh, 0, msg);
/* send some debug info */
snprintf(buf, 1024, "mime_type: %s<br>data size: %d<br>data: %s<br>",
mime_type, (int)data_sz, data);
- sock_handler_write_str(sh, 0, buf);
+ a_Dpip_dsh_write_str(sh, 0, buf);
/* close page */
- sock_handler_write_str(sh, 0, "</body></html>");
+ a_Dpip_dsh_write_str(sh, 0, "</body></html>");
}
/*
@@ -253,7 +269,8 @@ static unsigned char *datauri_get_data(char *url, size_t *p_sz)
if (p) {
++p;
if (is_base64) {
- data = (unsigned char *)dStrdup(p);
+ data = (unsigned char *)Unescape_uri_str(p);
+ b64strip_illegal_chars(data);
*p_sz = (size_t) b64decode(data);
} else {
data = (unsigned char *)a_Url_decode_hex_str(p, p_sz);
@@ -273,19 +290,33 @@ int main(void)
{
char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *mime_type;
unsigned char *data;
+ int rc;
size_t data_size = 0;
/* Initialize the SockHandler */
- sh = sock_handler_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
+ sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
+
+ rc = chdir("/tmp");
+ if (rc == -1) {
+ MSG("paths: error changing directory to /tmp: %s\n",
+ dStrerror(errno));
+ }
- chdir("/tmp");
+ /* Authenticate our client... */
+ if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
+ a_Dpip_check_auth(dpip_tag) < 0) {
+ MSG("can't authenticate request: %s\n", dStrerror(errno));
+ a_Dpip_dsh_close(sh);
+ return 1;
+ }
+ dFree(dpip_tag);
/* Read the dpi command from STDIN */
- dpip_tag = sock_handler_read(sh);
+ dpip_tag = a_Dpip_dsh_read_token(sh, 1);
MSG("[%s]\n", dpip_tag);
- cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd");
- url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url");
+ cmd = a_Dpip_get_attr(dpip_tag, "cmd");
+ url = a_Dpip_get_attr(dpip_tag, "url");
if (!cmd || !url) {
MSG("Error, cmd=%s, url=%s\n", cmd, url);
exit (EXIT_FAILURE);
@@ -314,8 +345,8 @@ int main(void)
dFree(dpip_tag);
/* Finish the SockHandler */
- sock_handler_close(sh);
- sock_handler_free(sh);
+ a_Dpip_dsh_close(sh);
+ a_Dpip_dsh_free(sh);
return 0;
}
diff --git a/dpi/downloads.cc b/dpi/downloads.cc
index d2f3a54b..5aa7a87c 100644
--- a/dpi/downloads.cc
+++ b/dpi/downloads.cc
@@ -107,7 +107,7 @@ class DLItem {
enum {
ST_newline, ST_number, ST_discard, ST_copy
};
-
+
pid_t mPid;
int LogPipe[2];
char *shortname, *fullname;
@@ -291,10 +291,11 @@ static void prButton_scb(Widget *, void *cb_data)
DLItem::DLItem(const char *full_filename, const char *url, DLAction action)
{
struct stat ss;
- char *p, *esc_url;
+ const char *p;
+ char *esc_url;
if (pipe(LogPipe) < 0) {
- MSG("pipe, %s\n", strerror(errno));
+ MSG("pipe, %s\n", dStrerror(errno));
return;
}
/* Set FD to background */
@@ -513,7 +514,7 @@ void DLItem::log_text_add(const char *buf, ssize_t st)
if (isdigit(*q++ = *p)) {
// keep here
} else if (*p == 'K') {
- for(--q; isdigit(q[-1]); --q); log_state = ST_discard;
+ for (--q; isdigit(q[-1]); --q) ; log_state = ST_discard;
} else {
log_state = ST_copy;
}
@@ -582,7 +583,9 @@ void DLItem::update_size(int new_sz)
prBar->move(1);
} else {
prBar->showtext(true);
- double pos = 100.0 * (double)curr_bytesize / total_bytesize;
+ double pos = 100.0;
+ if (total_bytesize > 0)
+ pos *= (double)curr_bytesize / total_bytesize;
prBar->position(pos);
}
}
@@ -685,7 +688,7 @@ void DLItem::update()
/* Update curr_size */
if (stat(fullname, &ss) == -1) {
- MSG("stat, %s\n", strerror(errno));
+ MSG("stat, %s\n", dStrerror(errno));
return;
}
update_size((int)ss.st_size);
@@ -744,7 +747,7 @@ void DLItem::update()
/*! SIGCHLD handler
*/
static void raw_sigchld(int)
-{
+{
caught_sigchld = 1;
}
@@ -820,29 +823,6 @@ static void update_cb(void *data)
// DLWin ---------------------------------------------------------------------
/*
- * Read a single line from a socket and store it in a Dstr.
- */
-static ssize_t readline(int socket, Dstr ** msg)
-{
- ssize_t st;
- char buf[16384];
-
- /* can't use fread() */
- do
- st = read(socket, buf, 16384);
- while (st < 0 && errno == EINTR);
-
- if (st == -1)
- MSG("readline, %s\n", strerror(errno));
-
- dStr_truncate(*msg, 0);
- if (st > 0)
- dStr_append_l(*msg, buf, (int)st);
-
- return st;
-}
-
-/*
* Make a new name and place it in 'dl_dest'.
*/
static void make_new_name(char **dl_dest, const char *url)
@@ -873,33 +853,48 @@ static void make_new_name(char **dl_dest, const char *url)
*/
static void read_req_cb(int req_fd, void *)
{
- Dstr *tag;
struct sockaddr_un clnt_addr;
- int new_socket;
+ int sock_fd;
socklen_t csz;
struct stat sb;
- char *cmd = NULL, *url = NULL, *dl_dest = NULL;
+ Dsh *sh = NULL;
+ char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *dl_dest = NULL;
DLAction action = DL_ABORT; /* compiler happiness */
/* Initialize the value-result parameter */
csz = sizeof(struct sockaddr_un);
/* accept the request */
do {
- new_socket = accept(req_fd, (struct sockaddr *) &clnt_addr, &csz);
- } while (new_socket == -1 && errno == EINTR);
- if (new_socket == -1) {
- MSG("accept, %s fd=%d\n", strerror(errno), req_fd);
+ sock_fd = accept(req_fd, (struct sockaddr *) &clnt_addr, &csz);
+ } while (sock_fd == -1 && errno == EINTR);
+ if (sock_fd == -1) {
+ MSG("accept, %s fd=%d\n", dStrerror(errno), req_fd);
return;
}
- //sigprocmask(SIG_BLOCK, &blockSC, NULL);
- tag = dStr_sized_new(64);
- readline(new_socket, &tag);
- close(new_socket);
- _MSG("Received tag={%s}\n", tag->str);
+ /* create a sock handler */
+ sh = a_Dpip_dsh_new(sock_fd, sock_fd, 8*1024);
+
+ /* Authenticate our client... */
+ if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
+ a_Dpip_check_auth(dpip_tag) < 0) {
+ MSG("can't authenticate request: %s fd=%d\n", dStrerror(errno), sock_fd);
+ a_Dpip_dsh_close(sh);
+ goto end;
+ }
+ dFree(dpip_tag);
- if ((cmd = a_Dpip_get_attr(tag->str, (size_t)tag->len, "cmd")) == NULL) {
- MSG("Failed to parse 'cmd' in {%s}\n", tag->str);
+ /* Read request */
+ if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1))) {
+ MSG("can't read request: %s fd=%d\n", dStrerror(errno), sock_fd);
+ a_Dpip_dsh_close(sh);
+ goto end;
+ }
+ a_Dpip_dsh_close(sh);
+ _MSG("Received tag={%s}\n", dpip_tag);
+
+ if ((cmd = a_Dpip_get_attr(dpip_tag, "cmd")) == NULL) {
+ MSG("Failed to parse 'cmd' in {%s}\n", dpip_tag);
goto end;
}
if (strcmp(cmd, "DpiBye") == 0) {
@@ -910,12 +905,12 @@ static void read_req_cb(int req_fd, void *)
MSG("unknown command: '%s'. Aborting.\n", cmd);
goto end;
}
- if (!(url = a_Dpip_get_attr(tag->str,(size_t)tag->len, "url"))){
- MSG("Failed to parse 'url' in {%s}\n", tag->str);
+ if (!(url = a_Dpip_get_attr(dpip_tag, "url"))){
+ MSG("Failed to parse 'url' in {%s}\n", dpip_tag);
goto end;
}
- if (!(dl_dest = a_Dpip_get_attr(tag->str,(size_t)tag->len,"destination"))){
- MSG("Failed to parse 'destination' in {%s}\n", tag->str);
+ if (!(dl_dest = a_Dpip_get_attr(dpip_tag, "destination"))){
+ MSG("Failed to parse 'destination' in {%s}\n", dpip_tag);
goto end;
}
/* 'dl_dest' may be a directory */
@@ -934,7 +929,8 @@ end:
dFree(cmd);
dFree(url);
dFree(dl_dest);
- dStr_free(tag, TRUE);
+ dFree(dpip_tag);
+ a_Dpip_dsh_free(sh);
}
/*
@@ -976,7 +972,7 @@ void DLWin::add(const char *full_filename, const char *url, DLAction action)
} else if (f_pid < 0) {
perror("fork, ");
exit(1);
- } else {
+ } else {
/* father */
dl_win->show();
dl_item->pid(f_pid);
@@ -999,7 +995,7 @@ DLAction DLWin::check_filename(char **p_fullname)
return DL_NEWFILE;
ds = dStr_sized_new(128);
- dStr_sprintf(ds,
+ dStr_sprintf(ds,
"The file:\n %s (%d Bytes)\nalready exists. What do we do?",
*p_fullname, (int)ss.st_size);
ch = fltk::choice(ds->str, "Rename", "Continue", "Abort");
@@ -1020,18 +1016,14 @@ DLAction DLWin::check_filename(char **p_fullname)
}
/*
- * Add a new download request to the main window and
- * fork a child to do the job.
+ * Delete a download request from the main window.
*/
void DLWin::del(int n_item)
{
DLItem *dl_item = mDList->get(n_item);
- // Remove the widget from the scroll group
+ // Remove the widget from the packed group
mPG->remove(dl_item->get_widget());
- // Resize the scroll group
- mPG->resize(mWin->w(), 1);
-
mDList->del(n_item);
delete(dl_item);
}
@@ -1093,7 +1085,7 @@ DLWin::DLWin(int ww, int wh) {
mScroll->end();
mScroll->type(ScrollGroup::VERTICAL);
mWin->end();
- mWin->resizable(mPG);
+ mWin->resizable(mScroll);
mWin->callback(dlwin_esc_cb, NULL);
mWin->show();
diff --git a/dpi/dpiutil.c b/dpi/dpiutil.c
index b1affe95..e29d529f 100644
--- a/dpi/dpiutil.c
+++ b/dpi/dpiutil.c
@@ -57,6 +57,28 @@ char *Escape_uri_str(const char *str, const char *p_esc_set)
return p;
}
+/*
+ * Unescape %XX sequences in a string.
+ * Return value: a new unescaped string
+ */
+char *Unescape_uri_str(const char *s)
+{
+ char *p, *buf = dStrdup(s);
+
+ if (strchr(s, '%')) {
+ for (p = buf; (*p = *s); ++s, ++p) {
+ if (*p == '%' && isxdigit(s[1]) && isxdigit(s[2])) {
+ *p = (isdigit(s[1]) ? (s[1] - '0') : toupper(s[1]) - 'A' + 10)*16;
+ *p += isdigit(s[2]) ? (s[2] - '0') : toupper(s[2]) - 'A' + 10;
+ s += 2;
+ }
+ }
+ }
+
+ return buf;
+}
+
+
static const char *unsafe_chars = "&<>\"'";
static const char *unsafe_rep[] =
{ "&amp;", "&lt;", "&gt;", "&quot;", "&#39;" };
@@ -141,130 +163,3 @@ char *Filter_smtp_hack(char *url)
return url;
}
-
-/* Streamed Sockets API (not mandatory) ----------------------------------*/
-
-/*
- * Create and initialize the SockHandler structure
- */
-SockHandler *sock_handler_new(int fd_in, int fd_out, int flush_sz)
-{
- SockHandler *sh = dNew(SockHandler, 1);
-
- /* init descriptors and streams */
- sh->fd_in = fd_in;
- sh->fd_out = fd_out;
- sh->out = fdopen(fd_out, "w");
-
- /* init buffer */
- sh->buf_max = 8 * 1024;
- sh->buf = dNew(char, sh->buf_max);
- sh->buf_sz = 0;
- sh->flush_sz = flush_sz;
-
- return sh;
-}
-
-/*
- * Streamed write to socket
- * Return: 0 on success, 1 on error.
- */
-int sock_handler_write(SockHandler *sh, int flush,
- const char *Data, size_t DataSize)
-{
- int retval = 1;
-
- /* append to buf */
- while (sh->buf_max < sh->buf_sz + DataSize) {
- sh->buf_max <<= 1;
- sh->buf = dRealloc(sh->buf, sh->buf_max);
- }
- memcpy(sh->buf + sh->buf_sz, Data, DataSize);
- sh->buf_sz += DataSize;
-/*
- MSG("sh->buf=%p, sh->buf_sz=%d, sh->buf_max=%d, sh->flush_sz=%d\n",
- sh->buf, sh->buf_sz, sh->buf_max, sh->flush_sz);
-*/
-/**/
-#if 0
-{
- uint_t i;
- /* Test dpip's stream handling by chopping data into characters */
- for (i = 0; i < sh->buf_sz; ++i) {
- fputc(sh->buf[i], sh->out);
- fflush(sh->out);
- usleep(50);
- }
- if (i == sh->buf_sz) {
- sh->buf_sz = 0;
- retval = 0;
- }
-}
-#else
- /* flush data if necessary */
- if (flush || sh->buf_sz >= sh->flush_sz) {
- if (sh->buf_sz && fwrite (sh->buf, sh->buf_sz, 1, sh->out) != 1) {
- perror("[sock_handler_write]");
- } else {
- fflush(sh->out);
- sh->buf_sz = 0;
- retval = 0;
- }
-
- } else {
- retval = 0;
- }
-#endif
- return retval;
-}
-
-/*
- * Convenience function.
- */
-int sock_handler_write_str(SockHandler *sh, int flush, const char *str)
-{
- return sock_handler_write(sh, flush, str, strlen(str));
-}
-
-/*
- * Return a newlly allocated string with the contents read from the socket.
- */
-char *sock_handler_read(SockHandler *sh)
-{
- ssize_t st;
- char buf[16384];
-
- /* can't use fread() */
- do
- st = read(sh->fd_in, buf, 16384);
- while (st < 0 && errno == EINTR);
-
- if (st == -1)
- perror("[sock_handler_read]");
-
- return (st > 0) ? dStrndup(buf, (uint_t)st) : NULL;
-}
-
-/*
- * Close this socket for reading and writing.
- */
-void sock_handler_close(SockHandler *sh)
-{
- /* flush before closing */
- sock_handler_write(sh, 1, "", 0);
-
- fclose(sh->out);
- close(sh->fd_out);
-}
-
-/*
- * Free the SockHandler structure
- */
-void sock_handler_free(SockHandler *sh)
-{
- dFree(sh->buf);
- dFree(sh);
-}
-
-/* ------------------------------------------------------------------------ */
-
diff --git a/dpi/dpiutil.h b/dpi/dpiutil.h
index fa3dfb86..d939ddcb 100644
--- a/dpi/dpiutil.h
+++ b/dpi/dpiutil.h
@@ -5,7 +5,7 @@
*
* 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
+ * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
*/
@@ -28,43 +28,6 @@ extern "C" {
#endif /* __cplusplus */
-#define BUFLEN 256
-#define TOUT 300
-
-
-/* Streamed Sockets API (not mandatory) ----------------------------------*/
-
-typedef struct _SockHandler SockHandler;
-struct _SockHandler {
- int fd_in;
- int fd_out;
- /* FILE *in; --Unused. The stream functions block when reading. */
- FILE *out;
-
- char *buf; /* internal buffer */
- uint_t buf_sz; /* data size */
- uint_t buf_max; /* allocated size */
- uint_t flush_sz; /* max size before flush */
-};
-
-SockHandler *sock_handler_new(int fd_in, int fd_out, int flush_sz);
-int sock_handler_write(SockHandler *sh, int flush,
- const char *Data,size_t DataSize);
-int sock_handler_write_str(SockHandler *sh, int flush, const char *str);
-char *sock_handler_read(SockHandler *sh);
-void sock_handler_close(SockHandler *sh);
-void sock_handler_free(SockHandler *sh);
-
-#define sock_handler_printf(sh, flush, ...) \
- D_STMT_START { \
- Dstr *dstr = dStr_sized_new(128); \
- dStr_sprintf(dstr, __VA_ARGS__); \
- sock_handler_write(sh, flush, dstr->str, dstr->len); \
- dStr_free(dstr, 1); \
- } D_STMT_END
-
-/* ----------------------------------------------------------------------- */
-
/*
* Escape URI characters in 'esc_set' as %XX sequences.
* Return value: New escaped string.
@@ -72,6 +35,12 @@ void sock_handler_free(SockHandler *sh);
char *Escape_uri_str(const char *str, const char *p_esc_set);
/*
+ * Unescape %XX sequences in a string.
+ * Return value: a new unescaped string
+ */
+char *Unescape_uri_str(const char *str);
+
+/*
* Escape unsafe characters as html entities.
* Return value: New escaped string.
*/
diff --git a/dpi/file.c b/dpi/file.c
index 2398590b..1e70e9ef 100644
--- a/dpi/file.c
+++ b/dpi/file.c
@@ -15,14 +15,13 @@
* With new HTML layout.
*/
-#include <pthread.h>
-
#include <ctype.h> /* for tolower */
#include <errno.h> /* for errno */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <sys/select.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -32,6 +31,7 @@
#include <fcntl.h>
#include <time.h>
#include <signal.h>
+#include <netinet/in.h>
#include "../dpip/dpip.h"
#include "dpiutil.h"
@@ -42,16 +42,31 @@
*/
#define _MSG(...)
#define MSG(...) printf("[file dpi]: " __VA_ARGS__)
+#define _MSG_RAW(...)
+#define MSG_RAW(...) printf(__VA_ARGS__)
#define MAXNAMESIZE 30
#define HIDE_DOTFILES TRUE
-enum {
- FILE_OK,
- FILE_NOT_FOUND,
- FILE_NO_ACCESS
-};
+/*
+ * Communication flags
+ */
+#define FILE_AUTH_OK 1 /* Authentication done */
+#define FILE_READ 2 /* Waiting data */
+#define FILE_WRITE 4 /* Sending data */
+#define FILE_DONE 8 /* Operation done */
+#define FILE_ERR 16 /* Operation error */
+
+
+typedef enum {
+ st_start = 10,
+ st_dpip,
+ st_http,
+ st_content,
+ st_done,
+ st_err
+} FileState;
typedef struct {
char *full_path;
@@ -67,42 +82,40 @@ typedef struct {
} DilloDir;
typedef struct {
- SockHandler *sh;
- int status;
+ Dsh *sh;
+ char *orig_url;
+ char *filename;
+ int file_fd;
+ off_t file_sz;
+ DilloDir *d_dir;
+ FileState state;
+ int err_code;
+ int flags;
int old_style;
- pthread_t thrID;
- int done;
} ClientInfo;
/*
* Forward references
*/
static const char *File_content_type(const char *filename);
-static int File_get_file(ClientInfo *Client,
- const char *filename,
- struct stat *sb,
- const char *orig_url);
-static int File_get_dir(ClientInfo *Client,
- const char *DirName,
- const char *orig_url);
/*
* Global variables
*/
-static volatile int DPIBYE = 0;
-static volatile int ThreadRunning = 0;
+static int DPIBYE = 0;
static int OLD_STYLE = 0;
/* A list for the clients we are serving */
static Dlist *Clients;
-/* a mutex for operations on clients */
-static pthread_mutex_t ClMut;
+/* Set of filedescriptors we're working on */
+fd_set read_set, write_set;
+
/*
* Close a file descriptor, but handling EINTR
*/
static void File_close(int fd)
{
- while (close(fd) < 0 && errno == EINTR)
+ while (fd >= 0 && close(fd) < 0 && errno == EINTR)
;
}
@@ -128,7 +141,7 @@ static const char *File_get_content_type_from_data(void *Data, size_t Size)
_MSG("File_get_content_type_from_data:: Size = %d\n", Size);
/* HTML try */
- for (i = 0; i < Size && isspace(p[i]); ++i);
+ for (i = 0; i < Size && dIsspace(p[i]); ++i);
if ((Size - i >= 5 && !dStrncasecmp(p+i, "<html", 5)) ||
(Size - i >= 5 && !dStrncasecmp(p+i, "<head", 5)) ||
(Size - i >= 6 && !dStrncasecmp(p+i, "<title", 6)) ||
@@ -256,6 +269,8 @@ static void File_dillodir_free(DilloDir *Ddir)
int i;
FileInfo *finfo;
+ dReturn_if (Ddir == NULL);
+
for (i = 0; i < dList_length(Ddir->flist); ++i) {
finfo = dList_nth_data(Ddir->flist, i);
dFree(finfo->full_path);
@@ -270,7 +285,7 @@ static void File_dillodir_free(DilloDir *Ddir)
/*
* Output the string for parent directory
*/
-static void File_print_parent_dir(ClientInfo *Client, const char *dirname)
+static void File_print_parent_dir(ClientInfo *client, const char *dirname)
{
if (strcmp(dirname, "/") != 0) { /* Not the root dir */
char *p, *parent, *HUparent, *Uparent;
@@ -284,7 +299,7 @@ static void File_print_parent_dir(ClientInfo *Client, const char *dirname)
Uparent = Escape_uri_str(parent, NULL);
HUparent = Escape_html_str(Uparent);
- sock_handler_printf(Client->sh, 0,
+ a_Dpip_dsh_printf(client->sh, 0,
"<a href='file:%s'>Parent directory</a>", HUparent);
dFree(HUparent);
dFree(Uparent);
@@ -295,20 +310,20 @@ static void File_print_parent_dir(ClientInfo *Client, const char *dirname)
/*
* Given a timestamp, output an HTML-formatted date string.
*/
-static void File_print_mtime(ClientInfo *Client, time_t mtime)
+static void File_print_mtime(ClientInfo *client, time_t mtime)
{
char *ds = ctime(&mtime);
/* Month, day and {hour or year} */
- if (Client->old_style) {
- sock_handler_printf(Client->sh, 0, " %.3s %.2s", ds + 4, ds + 8);
+ if (client->old_style) {
+ a_Dpip_dsh_printf(client->sh, 0, " %.3s %.2s", ds + 4, ds + 8);
if (time(NULL) - mtime > 15811200) {
- sock_handler_printf(Client->sh, 0, " %.4s", ds + 20);
+ a_Dpip_dsh_printf(client->sh, 0, " %.4s", ds + 20);
} else {
- sock_handler_printf(Client->sh, 0, " %.5s", ds + 11);
+ a_Dpip_dsh_printf(client->sh, 0, " %.5s", ds + 11);
}
} else {
- sock_handler_printf(Client->sh, 0,
+ a_Dpip_dsh_printf(client->sh, 0,
"<td>%.3s&nbsp;%.2s&nbsp;%.5s", ds + 4, ds + 8,
/* (more than 6 months old) ? year : hour; */
(time(NULL) - mtime > 15811200) ? ds + 20 : ds + 11);
@@ -318,7 +333,7 @@ static void File_print_mtime(ClientInfo *Client, time_t mtime)
/*
* Return a HTML-line from file info.
*/
-static void File_info2html(ClientInfo *Client, FileInfo *finfo, int n)
+static void File_info2html(ClientInfo *client, FileInfo *finfo, int n)
{
int size;
char *sizeunits;
@@ -361,10 +376,10 @@ static void File_info2html(ClientInfo *Client, FileInfo *finfo, int n)
HUref = Escape_html_str(Uref);
Hname = Escape_html_str(name);
- if (Client->old_style) {
+ if (client->old_style) {
char *dots = ".. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..";
int ndots = MAXNAMESIZE - strlen(name);
- sock_handler_printf(Client->sh, 0,
+ a_Dpip_dsh_printf(client->sh, 0,
"%s<a href='%s'>%s</a>"
" %s"
" %-11s%4d %-5s",
@@ -373,15 +388,15 @@ static void File_info2html(ClientInfo *Client, FileInfo *finfo, int n)
filecont, size, sizeunits);
} else {
- sock_handler_printf(Client->sh, 0,
+ a_Dpip_dsh_printf(client->sh, 0,
"<tr align=center %s><td>%s<td align=left><a href='%s'>%s</a>"
"<td>%s<td>%d&nbsp;%s",
(n & 1) ? "bgcolor=#dcdcdc" : "",
S_ISDIR (finfo->mode) ? ">" : " ", HUref, Hname,
filecont, size, sizeunits);
}
- File_print_mtime(Client, finfo->mtime);
- sock_handler_write_str(Client->sh, 0, "\n");
+ File_print_mtime(client, finfo->mtime);
+ a_Dpip_dsh_write_str(client->sh, 0, "\n");
dFree(Hname);
dFree(HUref);
@@ -389,79 +404,92 @@ static void File_info2html(ClientInfo *Client, FileInfo *finfo, int n)
}
/*
- * Read a local directory and translate it to html.
+ * Send the HTML directory page in HTTP.
*/
-static void File_transfer_dir(ClientInfo *Client,
- DilloDir *Ddir, const char *orig_url)
+static void File_send_dir(ClientInfo *client)
{
int n;
char *d_cmd, *Hdirname, *Udirname, *HUdirname;
+ DilloDir *Ddir = client->d_dir;
- /* Send DPI header */
- d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", orig_url);
- sock_handler_write_str(Client->sh, 1, d_cmd);
- dFree(d_cmd);
-
- /* Send page title */
- Udirname = Escape_uri_str(Ddir->dirname, NULL);
- HUdirname = Escape_html_str(Udirname);
- Hdirname = Escape_html_str(Ddir->dirname);
-
- sock_handler_printf(Client->sh, 0,
- "Content-Type: text/html\n\n"
- "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
- "<HTML>\n<HEAD>\n <BASE href='file:%s'>\n"
- " <TITLE>file:%s</TITLE>\n</HEAD>\n"
- "<BODY><H1>Directory listing of %s</H1>\n",
- HUdirname, Hdirname, Hdirname);
- dFree(Hdirname);
- dFree(HUdirname);
- dFree(Udirname);
-
- if (Client->old_style) {
- sock_handler_write_str(Client->sh, 0, "<pre>\n");
- }
+ if (client->state == st_start) {
+ /* Send DPI command */
+ d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page",
+ client->orig_url);
+ a_Dpip_dsh_write_str(client->sh, 1, d_cmd);
+ dFree(d_cmd);
+ client->state = st_dpip;
+
+ } else if (client->state == st_dpip) {
+ /* send HTTP header and HTML top part */
+
+ /* Send page title */
+ Udirname = Escape_uri_str(Ddir->dirname, NULL);
+ HUdirname = Escape_html_str(Udirname);
+ Hdirname = Escape_html_str(Ddir->dirname);
+
+ a_Dpip_dsh_printf(client->sh, 0,
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Type: text/html\r\n"
+ "\r\n"
+ "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
+ "<HTML>\n<HEAD>\n <BASE href='file:%s'>\n"
+ " <TITLE>file:%s</TITLE>\n</HEAD>\n"
+ "<BODY><H1>Directory listing of %s</H1>\n",
+ HUdirname, Hdirname, Hdirname);
+ dFree(Hdirname);
+ dFree(HUdirname);
+ dFree(Udirname);
+
+ if (client->old_style) {
+ a_Dpip_dsh_write_str(client->sh, 0, "<pre>\n");
+ }
- /* Output the parent directory */
- File_print_parent_dir(Client, Ddir->dirname);
+ /* Output the parent directory */
+ File_print_parent_dir(client, Ddir->dirname);
- /* HTML style toggle */
- sock_handler_write_str(Client->sh, 0,
- "&nbsp;&nbsp;<a href='dpi:/file/toggle'>%</a>\n");
+ /* HTML style toggle */
+ a_Dpip_dsh_write_str(client->sh, 0,
+ "&nbsp;&nbsp;<a href='dpi:/file/toggle'>%</a>\n");
- if (dList_length(Ddir->flist)) {
- if (Client->old_style) {
- sock_handler_write_str(Client->sh, 0, "\n\n");
+ if (dList_length(Ddir->flist)) {
+ if (client->old_style) {
+ a_Dpip_dsh_write_str(client->sh, 0, "\n\n");
+ } else {
+ a_Dpip_dsh_write_str(client->sh, 0,
+ "<br><br>\n"
+ "<table border=0 cellpadding=1 cellspacing=0"
+ " bgcolor=#E0E0E0 width=100%>\n"
+ "<tr align=center>\n"
+ "<td>\n"
+ "<td width=60%><b>Filename</b>"
+ "<td><b>Type</b>"
+ "<td><b>Size</b>"
+ "<td><b>Modified&nbsp;at</b>\n");
+ }
} else {
- sock_handler_write_str(Client->sh, 0,
- "<br><br>\n"
- "<table border=0 cellpadding=1 cellspacing=0"
- " bgcolor=#E0E0E0 width=100%>\n"
- "<tr align=center>\n"
- "<td>\n"
- "<td width=60%><b>Filename</b>"
- "<td><b>Type</b>"
- "<td><b>Size</b>"
- "<td><b>Modified&nbsp;at</b>\n");
+ a_Dpip_dsh_write_str(client->sh, 0, "<br><br>Directory is empty...");
}
- } else {
- sock_handler_write_str(Client->sh, 0, "<br><br>Directory is empty...");
- }
+ client->state = st_http;
- /* Output entries */
- for (n = 0; n < dList_length(Ddir->flist); ++n) {
- File_info2html(Client, dList_nth_data(Ddir->flist,n), n+1);
- }
+ } else if (client->state == st_http) {
+ /* send directories as HTML contents */
+ for (n = 0; n < dList_length(Ddir->flist); ++n) {
+ File_info2html(client, dList_nth_data(Ddir->flist,n), n+1);
+ }
- if (dList_length(Ddir->flist)) {
- if (Client->old_style) {
- sock_handler_write_str(Client->sh, 0, "</pre>\n");
- } else {
- sock_handler_write_str(Client->sh, 0, "</table>\n");
+ if (dList_length(Ddir->flist)) {
+ if (client->old_style) {
+ a_Dpip_dsh_write_str(client->sh, 0, "</pre>\n");
+ } else {
+ a_Dpip_dsh_write_str(client->sh, 0, "</table>\n");
+ }
}
- }
- sock_handler_write_str(Client->sh, 0, "</BODY></HTML>\n");
+ a_Dpip_dsh_write_str(client->sh, 1, "</BODY></HTML>\n");
+ client->state = st_content;
+ client->flags |= FILE_DONE;
+ }
}
/*
@@ -487,6 +515,8 @@ static const char *File_ext(const char *filename)
!dStrcasecmp(e, "htm") ||
!dStrcasecmp(e, "shtml")) {
return "text/html";
+ } else if (!dStrcasecmp(e, "txt")) {
+ return "text/plain";
} else {
return NULL;
}
@@ -517,54 +547,67 @@ static const char *File_content_type(const char *filename)
File_close(fd);
}
}
-
+ _MSG("File_content_type: name=%s ct=%s\n", filename, ct);
return ct;
}
/*
- * Try to stat the file and determine if it's readable.
+ * Send an error page
*/
-static void File_get(ClientInfo *Client, const char *filename,
- const char *orig_url)
+static void File_prepare_send_error_page(ClientInfo *client, int res,
+ const char *orig_url)
{
- int res;
- struct stat sb;
+ client->state = st_err;
+ client->err_code = res;
+ client->orig_url = dStrdup(orig_url);
+ client->flags &= ~FILE_READ;
+ client->flags |= FILE_WRITE;
+}
+
+/*
+ * Send an error page
+ */
+static void File_send_error_page(ClientInfo *client)
+{
+ const char *status;
char *d_cmd;
- Dstr *ds = NULL;
+ Dstr *body = dStr_sized_new(128);
- if (stat(filename, &sb) != 0) {
- /* stat failed, prepare a file-not-found error. */
- res = FILE_NOT_FOUND;
- } else if (S_ISDIR(sb.st_mode)) {
- /* set up for reading directory */
- res = File_get_dir(Client, filename, orig_url);
+ if (client->err_code == EACCES) {
+ status = "403 Forbidden";
+ } else if (client->err_code == ENOENT) {
+ status = "404 Not Found";
} else {
- /* set up for reading a file */
- res = File_get_file(Client, filename, &sb, orig_url);
+ /* good enough */
+ status = "500 Internal Server Error";
}
+ dStr_append(body, status);
+ dStr_append(body, "\n");
+ dStr_append(body, dStrerror(client->err_code));
- if (res == FILE_NOT_FOUND) {
- ds = dStr_sized_new(128);
- dStr_sprintf(ds, "%s Not Found: %s",
- S_ISDIR(sb.st_mode) ? "Directory" : "File", filename);
- } else if (res == FILE_NO_ACCESS) {
- ds = dStr_sized_new(128);
- dStr_sprintf(ds, "Access denied to %s: %s",
- S_ISDIR(sb.st_mode) ? "Directory" : "File", filename);
- }
- if (ds) {
- d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s","send_status_message",ds->str);
- sock_handler_write_str(Client->sh, 1, d_cmd);
- dFree(d_cmd);
- dStr_free(ds, 1);
- }
+ /* Send DPI command */
+ d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page",
+ client->orig_url);
+ a_Dpip_dsh_write_str(client->sh, 0, d_cmd);
+ dFree(d_cmd);
+
+ a_Dpip_dsh_printf(client->sh, 0,
+ "HTTP/1.1 %s\r\n"
+ "Content-Type: text/plain\r\n"
+ "Content-Length: %d\r\n"
+ "\r\n"
+ "%s",
+ status, body->len, body->str);
+ dStr_free(body, TRUE);
+
+ client->flags |= FILE_DONE;
}
/*
- *
+ * Scan the directory, sort and prepare to send it enclosed in HTTP.
*/
-static int File_get_dir(ClientInfo *Client,
- const char *DirName, const char *orig_url)
+static int File_prepare_send_dir(ClientInfo *client,
+ const char *DirName, const char *orig_url)
{
Dstr *ds_dirname;
DilloDir *Ddir;
@@ -578,90 +621,157 @@ static int File_get_dir(ClientInfo *Client,
Ddir = File_dillodir_new(ds_dirname->str);
dStr_free(ds_dirname, TRUE);
if (Ddir) {
- File_transfer_dir(Client, Ddir, orig_url);
- File_dillodir_free(Ddir);
- return FILE_OK;
+ /* looks ok, set things accordingly */
+ client->orig_url = dStrdup(orig_url);
+ client->d_dir = Ddir;
+ client->state = st_start;
+ client->flags &= ~FILE_READ;
+ client->flags |= FILE_WRITE;
+ return 0;
} else
- return FILE_NO_ACCESS;
+ return EACCES;
+}
+
+/*
+ * Prepare to send HTTP headers and then the file itself.
+ */
+static int File_prepare_send_file(ClientInfo *client,
+ const char *filename,
+ const char *orig_url)
+{
+ int fd, res = -1;
+ struct stat sb;
+
+ if (stat(filename, &sb) != 0) {
+ /* prepare a file-not-found error */
+ res = ENOENT;
+ } else if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) < 0) {
+ /* prepare an error message */
+ res = errno;
+ } else {
+ /* looks ok, set things accordingly */
+ client->file_fd = fd;
+ client->file_sz = sb.st_size;
+ client->d_dir = NULL;
+ client->state = st_start;
+ client->filename = dStrdup(filename);
+ client->orig_url = dStrdup(orig_url);
+ client->flags &= ~FILE_READ;
+ client->flags |= FILE_WRITE;
+ res = 0;
+ }
+ return res;
+}
+
+/*
+ * Try to stat the file and determine if it's readable.
+ */
+static void File_get(ClientInfo *client, const char *filename,
+ const char *orig_url)
+{
+ int res;
+ struct stat sb;
+
+ if (stat(filename, &sb) != 0) {
+ /* stat failed, prepare a file-not-found error. */
+ res = ENOENT;
+ } else if (S_ISDIR(sb.st_mode)) {
+ /* set up for reading directory */
+ res = File_prepare_send_dir(client, filename, orig_url);
+ } else {
+ /* set up for reading a file */
+ res = File_prepare_send_file(client, filename, orig_url);
+ }
+ if (res != 0) {
+ File_prepare_send_error_page(client, res, orig_url);
+ }
}
/*
- * Send the MIME content/type and then send the file itself.
+ * Send HTTP headers and then the file itself.
*/
-static int File_get_file(ClientInfo *Client,
- const char *filename,
- struct stat *sb,
- const char *orig_url)
+static int File_send_file(ClientInfo *client)
{
+//#define LBUF 1
#define LBUF 16*1024
const char *ct;
const char *unknown_type = "application/octet-stream";
char buf[LBUF], *d_cmd, *name;
- int fd, st, namelen;
+ int st, st2, namelen;
bool_t gzipped = FALSE;
- if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) < 0)
- return FILE_NO_ACCESS;
-
- /* Send DPI command */
- d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", orig_url);
- sock_handler_write_str(Client->sh, 1, d_cmd);
- dFree(d_cmd);
-
- /* Check for gzipped file */
- namelen = strlen(filename);
- if (namelen > 3 && !dStrcasecmp(filename + namelen - 3, ".gz")) {
- gzipped = TRUE;
- namelen -= 3;
- }
+ if (client->state == st_start) {
+ /* Send DPI command */
+ d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page",
+ client->orig_url);
+ a_Dpip_dsh_write_str(client->sh, 1, d_cmd);
+ dFree(d_cmd);
+ client->state = st_dpip;
- /* Content-Type info is based on filename extension (with ".gz" removed).
- * If there's no known extension, perform data sniffing.
- * If this doesn't lead to a conclusion, use "application/octet-stream".
- */
- name = dStrndup(filename, namelen);
- if (!(ct = File_content_type(name)))
- ct = unknown_type;
- dFree(name);
-
- /* Send HTTP headers */
- if (gzipped) {
- sock_handler_write_str(Client->sh, 0, "Content-Encoding: gzip\n");
- }
- if (!gzipped || strcmp(ct, unknown_type)) {
- sock_handler_printf(Client->sh, 0, "Content-Type: %s\n", ct);
- } else {
- /* If we don't know type for gzipped data, let dillo figure it out. */
- }
- sock_handler_printf(Client->sh, 0, "Content-Length: %ld\n\n", sb->st_size);
+ } else if (client->state == st_dpip) {
+ /* send HTTP header */
- /* Send body -- raw file contents */
- do {
- if ((st = read(fd, buf, LBUF)) > 0) {
- if (sock_handler_write(Client->sh, 0, buf, (size_t)st) != 0)
- break;
- } else if (st < 0) {
- perror("[read]");
- if (errno == EINTR || errno == EAGAIN)
- continue;
+ /* Check for gzipped file */
+ namelen = strlen(client->filename);
+ if (namelen > 3 && !dStrcasecmp(client->filename + namelen - 3, ".gz")) {
+ gzipped = TRUE;
+ namelen -= 3;
+ }
+ /* Content-Type info is based on filename extension (with ".gz" removed).
+ * If there's no known extension, perform data sniffing.
+ * If this doesn't lead to a conclusion, use "application/octet-stream".
+ */
+ name = dStrndup(client->filename, namelen);
+ if (!(ct = File_content_type(name)))
+ ct = unknown_type;
+ dFree(name);
+
+ /* Send HTTP headers */
+ a_Dpip_dsh_write_str(client->sh, 0, "HTTP/1.1 200 OK\r\n");
+ if (gzipped) {
+ a_Dpip_dsh_write_str(client->sh, 0, "Content-Encoding: gzip\r\n");
+ }
+ if (!gzipped || strcmp(ct, unknown_type)) {
+ a_Dpip_dsh_printf(client->sh, 0, "Content-Type: %s\r\n", ct);
+ } else {
+ /* If we don't know type for gzipped data, let dillo figure it out. */
+ }
+ a_Dpip_dsh_printf(client->sh, 1,
+ "Content-Length: %ld\r\n"
+ "\r\n",
+ client->file_sz);
+ client->state = st_http;
+
+ } else if (client->state == st_http) {
+ /* Send body -- raw file contents */
+ if ((st = a_Dpip_dsh_tryflush(client->sh)) < 0) {
+ client->flags |= (st == -3) ? FILE_ERR : 0;
+ } else {
+ /* no pending data, let's send new data */
+ do {
+ st2 = read(client->file_fd, buf, LBUF);
+ } while (st2 < 0 && errno == EINTR);
+ if (st2 < 0) {
+ MSG("\nERROR while reading from file '%s': %s\n\n",
+ client->filename, dStrerror(errno));
+ client->flags |= FILE_ERR;
+ } else if (st2 == 0) {
+ client->state = st_content;
+ client->flags |= FILE_DONE;
+ } else {
+ /* ok to write */
+ st = a_Dpip_dsh_trywrite(client->sh, buf, st2);
+ client->flags |= (st == -3) ? FILE_ERR : 0;
+ }
}
- } while (st > 0);
-
- /* TODO: It may be better to send an error report to dillo instead of
- * calling exit() */
- if (st == -1) {
- MSG("ERROR while reading from file \"%s\", error was \"%s\"\n",
- filename, strerror(errno));
- exit(1);
}
- File_close(fd);
- return FILE_OK;
+ return 0;
}
/*
- * Given an hex octet (e3, 2F, 20), return the corresponding
+ * Given a hex octet (e3, 2F, 20), return the corresponding
* character if the octet is valid, and -1 otherwise
*/
static int File_parse_hex_octet(const char *s)
@@ -688,6 +798,8 @@ static char *File_normalize_path(const char *orig)
{
char *str = (char *) orig, *basename = NULL, *ret = NULL, *p;
+ dReturn_val_if (orig == NULL, ret);
+
/* Make sure the string starts with "file:/" */
if (strncmp(str, "file:/", 5) != 0)
return ret;
@@ -729,15 +841,15 @@ static char *File_normalize_path(const char *orig)
}
/*
- * Set the style flag and ask for a reload, so it shows inmediatly.
+ * Set the style flag and ask for a reload, so it shows immediately.
*/
-static void File_toggle_html_style(ClientInfo *Client)
+static void File_toggle_html_style(ClientInfo *client)
{
char *d_cmd;
OLD_STYLE = !OLD_STYLE;
d_cmd = a_Dpip_build_cmd("cmd=%s", "reload_request");
- sock_handler_write_str(Client->sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(client->sh, 1, d_cmd);
dFree(d_cmd);
}
@@ -746,6 +858,7 @@ static void File_toggle_html_style(ClientInfo *Client)
*/
static void termination_handler(int signum)
{
+ MSG("\nexit(signum), signum=%d\n\n", signum);
exit(signum);
}
@@ -757,170 +870,184 @@ static void termination_handler(int signum)
*/
static ClientInfo *File_add_client(int sock_fd)
{
- ClientInfo *NewClient;
-
- NewClient = dNew(ClientInfo, 1);
- NewClient->sh = sock_handler_new(sock_fd, sock_fd, 8*1024);
- NewClient->status = 0;
- NewClient->done = 0;
- NewClient->old_style = OLD_STYLE;
- pthread_mutex_lock(&ClMut);
- dList_append(Clients, NewClient);
- pthread_mutex_unlock(&ClMut);
- return NewClient;
-}
-
-/*
- * Get client record by number
- */
-static void *File_get_client_n(uint_t n)
-{
- void *client;
-
- pthread_mutex_lock(&ClMut);
- client = dList_nth_data(Clients, n);
- pthread_mutex_unlock(&ClMut);
-
- return client;
+ ClientInfo *new_client;
+
+ new_client = dNew(ClientInfo, 1);
+ new_client->sh = a_Dpip_dsh_new(sock_fd, sock_fd, 8*1024);
+ new_client->orig_url = NULL;
+ new_client->filename = NULL;
+ new_client->file_fd = -1;
+ new_client->file_sz = 0;
+ new_client->d_dir = NULL;
+ new_client->state = 0;
+ new_client->err_code = 0;
+ new_client->flags = FILE_READ;
+ new_client->old_style = OLD_STYLE;
+
+ dList_append(Clients, new_client);
+ return new_client;
}
/*
* Remove a client from the list.
*/
-static void File_remove_client_n(uint_t n)
+static void File_remove_client(ClientInfo *client)
{
- ClientInfo *Client;
-
- pthread_mutex_lock(&ClMut);
- Client = dList_nth_data(Clients, n);
- dList_remove(Clients, (void *)Client);
- pthread_mutex_unlock(&ClMut);
+ dList_remove(Clients, (void *)client);
_MSG("Closing Socket Handler\n");
- sock_handler_close(Client->sh);
- sock_handler_free(Client->sh);
- dFree(Client);
-}
-
-/*
- * Return the number of clients.
- */
-static int File_num_clients(void)
-{
- uint_t n;
-
- pthread_mutex_lock(&ClMut);
- n = dList_length(Clients);
- pthread_mutex_unlock(&ClMut);
-
- return n;
+ a_Dpip_dsh_close(client->sh);
+ a_Dpip_dsh_free(client->sh);
+ File_close(client->file_fd);
+ dFree(client->orig_url);
+ dFree(client->filename);
+ File_dillodir_free(client->d_dir);
+
+ dFree(client);
}
/*
* Serve this client.
- * (this function runs on its own thread)
*/
-static void *File_serve_client(void *data)
+static void File_serve_client(void *data, int f_write)
{
- char *dpip_tag, *cmd = NULL, *url = NULL, *path;
- ClientInfo *Client = data;
-
- /* Read the dpi command */
- dpip_tag = sock_handler_read(Client->sh);
- _MSG("dpip_tag={%s}\n", dpip_tag);
-
- if (dpip_tag) {
- cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd");
- if (cmd) {
- if (strcmp(cmd, "DpiBye") == 0) {
- DPIBYE = 1;
+ char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *path;
+ ClientInfo *client = data;
+ int st;
+
+ while (1) {
+ _MSG("File_serve_client %p, flags=%d state=%d\n",
+ client, client->flags, client->state);
+ if (client->flags & (FILE_DONE | FILE_ERR))
+ break;
+ if (client->flags & FILE_READ) {
+ dpip_tag = a_Dpip_dsh_read_token(client->sh, 0);
+ _MSG("dpip_tag={%s}\n", dpip_tag);
+ if (!dpip_tag)
+ break;
+ }
+
+ if (client->flags & FILE_READ) {
+ if (!(client->flags & FILE_AUTH_OK)) {
+ /* Authenticate our client... */
+ st = a_Dpip_check_auth(dpip_tag);
+ _MSG("a_Dpip_check_auth returned %d\n", st);
+ client->flags |= (st == 1) ? FILE_AUTH_OK : FILE_ERR;
} else {
- url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url");
- if (!url)
- MSG("file.dpi:: Failed to parse 'url'\n");
+ /* Get file request */
+ cmd = a_Dpip_get_attr(dpip_tag, "cmd");
+ url = a_Dpip_get_attr(dpip_tag, "url");
+ path = File_normalize_path(url);
+ if (cmd) {
+ if (strcmp(cmd, "DpiBye") == 0) {
+ DPIBYE = 1;
+ MSG("(pid %d): Got DpiBye.\n", (int)getpid());
+ client->flags |= FILE_DONE;
+ } else if (url && strcmp(url, "dpi:/file/toggle") == 0) {
+ File_toggle_html_style(client);
+ } else if (path) {
+ File_get(client, path, url);
+ } else {
+ client->flags |= FILE_ERR;
+ MSG("ERROR: URL was %s\n", url);
+ }
+ }
+ dFree(path);
+ dFree(url);
+ dFree(cmd);
+ dFree(dpip_tag);
+ break;
}
+ dFree(dpip_tag);
+
+ } else if (f_write) {
+ /* send our answer */
+ if (client->state == st_err)
+ File_send_error_page(client);
+ else if (client->d_dir)
+ File_send_dir(client);
+ else
+ File_send_file(client);
+ break;
}
- }
- dFree(cmd);
- dFree(dpip_tag);
-
- if (!DPIBYE && url) {
- _MSG("url = '%s'\n", url);
-
- path = File_normalize_path(url);
- if (path) {
- _MSG("path = '%s'\n", path);
- File_get(Client, path, url);
- } else if (strcmp(url, "dpi:/file/toggle") == 0) {
- File_toggle_html_style(Client);
- } else {
- MSG("ERROR: URL path was %s\n", url);
- }
- dFree(path);
- }
- dFree(url);
+ } /*while*/
- /* flag the the transfer finished */
- Client->done = 1;
-
- return NULL;
+ client->flags |= (client->sh->status & DPIP_ERROR) ? FILE_ERR : 0;
+ client->flags |= (client->sh->status & DPIP_EOF) ? FILE_DONE : 0;
}
/*
* Serve the client queue.
- * (this function runs on its own thread)
*/
-static void *File_serve_clients(void *client)
+static void File_serve_clients()
{
- /* switch to detached state */
- pthread_detach(pthread_self());
-
- while (File_num_clients()) {
- client = File_get_client_n((uint_t)0);
- File_serve_client(client);
- File_remove_client_n((uint_t)0);
+ int i, f_read, f_write;
+ ClientInfo *client;
+
+ for (i = 0; (client = dList_nth_data(Clients, i)); ++i) {
+ f_read = FD_ISSET(client->sh->fd_in, &read_set);
+ f_write = FD_ISSET(client->sh->fd_out, &write_set);
+ if (!f_read && !f_write)
+ continue;
+ File_serve_client(client, f_write);
+ if (client->flags & (FILE_DONE | FILE_ERR)) {
+ File_remove_client(client);
+ --i;
+ }
}
- ThreadRunning = 0;
-
- return NULL;
}
/* --------------------------------------------------------------------------*/
/*
- * Check a fd for activity, with a max timeout.
+ * Check the fd sets for activity, with a max timeout.
* return value: 0 if timeout, 1 if input available, -1 if error.
*/
-static int File_check_fd(int filedes, unsigned int seconds)
+static int File_check_fds(uint_t seconds)
{
- int st;
- fd_set set;
- struct timeval timeout;
-
- /* Initialize the file descriptor set. */
- FD_ZERO (&set);
- FD_SET (filedes, &set);
-
- /* Initialize the timeout data structure. */
- timeout.tv_sec = seconds;
- timeout.tv_usec = 0;
+ int i, st;
+ ClientInfo *client;
+ struct timeval timeout;
+
+ /* initialize observed file descriptors */
+ FD_ZERO (&read_set);
+ FD_ZERO (&write_set);
+ FD_SET (STDIN_FILENO, &read_set);
+ for (i = 0; (client = dList_nth_data(Clients, i)); ++i) {
+ if (client->flags & FILE_READ)
+ FD_SET (client->sh->fd_in, &read_set);
+ if (client->flags & FILE_WRITE)
+ FD_SET (client->sh->fd_out, &write_set);
+ }
+ _MSG("Watching %d fds\n", dList_length(Clients) + 1);
- do {
- st = select(FD_SETSIZE, &set, NULL, NULL, &timeout);
- } while (st == -1 && errno == EINTR);
+ /* Initialize the timeout data structure. */
+ timeout.tv_sec = seconds;
+ timeout.tv_usec = 0;
- return st;
+ do {
+ st = select(FD_SETSIZE, &read_set, &write_set, NULL, &timeout);
+ } while (st == -1 && errno == EINTR);
+/*
+ MSG_RAW(" (%d%s%s)", STDIN_FILENO,
+ FD_ISSET(STDIN_FILENO, &read_set) ? "R" : "",
+ FD_ISSET(STDIN_FILENO, &write_set) ? "W" : "");
+ for (i = 0; (client = dList_nth_data(Clients, i)); ++i) {
+ MSG_RAW(" (%d%s%s)", client->sh->fd_in,
+ FD_ISSET(client->sh->fd_in, &read_set) ? "R" : "",
+ FD_ISSET(client->sh->fd_out, &write_set) ? "W" : "");
+ }
+ MSG_RAW("\n");
+*/
+ return st;
}
int main(void)
{
- ClientInfo *NewClient;
- struct sockaddr_un spun;
- int temp_sock_descriptor;
- socklen_t address_size;
- int c_st, st = 1;
- uint_t i;
+ struct sockaddr_in sin;
+ socklen_t sin_sz;
+ int sock_fd, c_st, st = 1;
/* Arrange the cleanup function for abnormal terminations */
if (signal (SIGINT, termination_handler) == SIG_IGN)
@@ -930,57 +1057,57 @@ int main(void)
if (signal (SIGTERM, termination_handler) == SIG_IGN)
signal (SIGTERM, SIG_IGN);
- MSG("(v.1) accepting connections...\n");
+ MSG("(v.2) accepting connections...\n");
+ //sleep(20);
- /* initialize mutex */
- pthread_mutex_init(&ClMut, NULL);
+ /* initialize observed file descriptors */
+ FD_ZERO (&read_set);
+ FD_ZERO (&write_set);
+ FD_SET (STDIN_FILENO, &read_set);
+
+ /* Set STDIN socket nonblocking (to ensure accept() never blocks) */
+ fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK | fcntl(STDIN_FILENO, F_GETFL));
/* initialize Clients list */
Clients = dList_new(512);
/* some OSes may need this... */
- address_size = sizeof(struct sockaddr_un);
+ sin_sz = sizeof(sin);
/* start the service loop */
while (!DPIBYE) {
- /* wait for a connection */
+ /* wait for activity */
do {
- c_st = File_check_fd(STDIN_FILENO, 1);
+ c_st = File_check_fds(10);
} while (c_st == 0 && !DPIBYE);
if (c_st < 0) {
- perror("[select]");
+ MSG(" select() %s\n", dStrerror(errno));
break;
}
if (DPIBYE)
break;
- temp_sock_descriptor =
- accept(STDIN_FILENO, (struct sockaddr *)&spun, &address_size);
-
- if (temp_sock_descriptor == -1) {
- perror("[accept]");
- break;
- }
-
- /* Create and initialize a new client */
- NewClient = File_add_client(temp_sock_descriptor);
-
- if (!ThreadRunning) {
- ThreadRunning = 1;
- /* Serve the client from a thread (avoids deadlocks) */
- if (pthread_create(&NewClient->thrID, NULL,
- File_serve_clients, NewClient) != 0) {
- perror("[pthread_create]");
- ThreadRunning = 0;
+ if (FD_ISSET(STDIN_FILENO, &read_set)) {
+ /* accept the incoming connection */
+ do {
+ sock_fd = accept(STDIN_FILENO, (struct sockaddr *)&sin, &sin_sz);
+ } while (sock_fd < 0 && errno == EINTR);
+ if (sock_fd == -1) {
+ if (errno == EAGAIN)
+ continue;
+ MSG(" accept() %s\n", dStrerror(errno));
break;
+ } else {
+ _MSG(" accept() fd=%d\n", sock_fd);
+ /* Set nonblocking */
+ fcntl(sock_fd, F_SETFL, O_NONBLOCK | fcntl(sock_fd, F_GETFL));
+ /* Create and initialize a new client */
+ File_add_client(sock_fd);
}
+ continue;
}
- }
- /* TODO: handle a running thread better. */
- for (i = 0; i < 5 && ThreadRunning; ++i) {
- MSG("sleep i=%u", i);
- sleep(i);
+ File_serve_clients();
}
if (DPIBYE)
diff --git a/dpi/ftp.c b/dpi/ftp.c
index 139444c9..6b1e2f31 100644
--- a/dpi/ftp.c
+++ b/dpi/ftp.c
@@ -56,7 +56,7 @@
/*
* Global variables
*/
-static SockHandler *sh = NULL;
+static Dsh *sh = NULL;
static char **dl_argv = NULL;
/*---------------------------------------------------------------------------*/
@@ -97,7 +97,7 @@ static int a_Misc_get_content_type_from_data2(void *Data, size_t Size,
size_t i, non_ascci;
/* HTML try */
- for (i = 0; i < Size && isspace(p[i]); ++i);
+ for (i = 0; i < Size && dIsspace(p[i]); ++i);
if ((Size - i >= 5 && !dStrncasecmp(p+i, "<html", 5)) ||
(Size - i >= 5 && !dStrncasecmp(p+i, "<head", 5)) ||
(Size - i >= 6 && !dStrncasecmp(p+i, "<title", 6)) ||
@@ -129,7 +129,7 @@ static int a_Misc_get_content_type_from_data2(void *Data, size_t Size,
Size = MIN (Size, 256);
for (i = 0; i < Size; i++) {
ch = (uchar_t) p[i];
- if ((ch < 32 || ch > 126) && !isspace(ch))
+ if ((ch < 32 || ch > 126) && !dIsspace(ch))
++non_ascci;
}
if (Size == 256) {
@@ -171,23 +171,24 @@ static void make_wget_argv(char *url)
/*
* Fork, exec command, get its output and send via stdout.
- * Return: Number of bytes transfered.
+ * Return: Number of bytes transfered, -1 if file-not_found, -2 if aborted.
*/
static int try_ftp_transfer(char *url)
{
-#define MinSZ 256
+#define MIN_SZ 256
+#define READ_SZ 16*1024
ssize_t n;
- int nb, has_mime_type, has_html_header;
+ int nb, has_mime_type, has_html_header, no_such_file, offer_download;
const char *mime_type = "application/octet-stream";
- char buf[4096], *d_cmd;
- Dstr *dbuf = dStr_sized_new(4096);
+ char buf[READ_SZ], *d_cmd;
+ Dstr *dbuf = dStr_sized_new(READ_SZ);
pid_t ch_pid;
int aborted = 0;
int DataPipe[2];
if (pipe(DataPipe) < 0) {
- MSG("pipe, %s\n", strerror(errno));
+ MSG("pipe, %s\n", dStrerror(errno));
return 0;
}
@@ -214,18 +215,24 @@ static int try_ftp_transfer(char *url)
nb = 0;
has_mime_type = 0;
has_html_header = 0;
+ no_such_file = 0;
+ offer_download = 0;
do {
- while ((n = read(DataPipe[0], buf, 4096)) < 0 && errno == EINTR);
+ while ((n = read(DataPipe[0], buf, READ_SZ)) < 0 && errno == EINTR);
if (n > 0) {
dStr_append_l(dbuf, buf, n);
- if (!has_mime_type && dbuf->len < MinSZ)
+ if (!has_mime_type && dbuf->len < MIN_SZ)
continue;
} else if (n < 0)
break;
if (!has_mime_type) {
- if (dbuf->len > 0)
- a_Misc_get_content_type_from_data2(dbuf->str,dbuf->len,&mime_type);
+ if (dbuf->len == 0) {
+ /* When the file doesn't exist, the transfer size is zero */
+ no_such_file = 1;
+ break;
+ }
+ a_Misc_get_content_type_from_data2(dbuf->str, dbuf->len, &mime_type);
has_mime_type = 1;
if (strcmp(mime_type, "application/octet-stream") == 0) {
@@ -233,31 +240,33 @@ static int try_ftp_transfer(char *url)
kill(ch_pid, SIGTERM);
/* The "application/octet-stream" MIME type will be sent and
* Dillo will offer a download dialog */
+ offer_download = 1;
aborted = 1;
}
}
- if (!has_html_header && dbuf->len) {
+ if (offer_download || (!aborted && !has_html_header && dbuf->len)) {
/* Send dpip tag */
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
/* Send HTTP header. */
- sock_handler_write_str(sh, 0, "Content-type: ");
- sock_handler_write_str(sh, 0, mime_type);
- sock_handler_write_str(sh, 1, "\n\n");
+ a_Dpip_dsh_write_str(sh, 0, "Content-type: ");
+ a_Dpip_dsh_write_str(sh, 0, mime_type);
+ a_Dpip_dsh_write_str(sh, 1, "\r\n\r\n");
has_html_header = 1;
}
if (!aborted && dbuf->len) {
- sock_handler_write(sh, 0, dbuf->str, dbuf->len);
+ a_Dpip_dsh_write(sh, 1, dbuf->str, dbuf->len);
nb += dbuf->len;
dStr_truncate(dbuf, 0);
}
} while (n > 0 && !aborted);
- return nb;
+ dStr_free(dbuf, 1);
+ return (no_such_file ? -1 : (aborted ? -2 : nb));
}
/*
@@ -265,47 +274,66 @@ static int try_ftp_transfer(char *url)
*/
int main(int argc, char **argv)
{
+ const char *err_msg = "404 Not Found\nNo such file or directory";
char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *url2 = NULL;
- int nb;
+ int st, rc;
char *p, *d_cmd;
- /* Debugging with a command line argument */
- if (argc == 2)
- dpip_tag = dStrdup(argv[1]);
+ /* wget may need to write a temporary file... */
+ rc = chdir("/tmp");
+ if (rc == -1) {
+ MSG("paths: error changing directory to /tmp: %s\n",
+ dStrerror(errno));
+ }
/* Initialize the SockHandler */
- sh = sock_handler_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
-
- /* wget may need to write a temporary file... */
- chdir("/tmp");
+ sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
- /* Read the dpi command from STDIN */
- if (!dpip_tag)
- dpip_tag = sock_handler_read(sh);
+ if (argc == 2) {
+ /* Debugging with a command line argument */
+ dpip_tag = dStrdup(argv[1]);
+ } else {
+ /* Authenticate our client... */
+ if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
+ a_Dpip_check_auth(dpip_tag) < 0) {
+ MSG("can't authenticate request: %s\n", dStrerror(errno));
+ a_Dpip_dsh_close(sh);
+ return 1;
+ }
+ dFree(dpip_tag);
+ /* Read the dpi command from STDIN */
+ dpip_tag = a_Dpip_dsh_read_token(sh, 1);
+ }
MSG("tag=[%s]\n", dpip_tag);
- cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd");
- url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url");
+ cmd = a_Dpip_get_attr(dpip_tag, "cmd");
+ url = a_Dpip_get_attr(dpip_tag, "url");
if (!cmd || !url) {
MSG("ERROR, cmd=%s, url=%s\n", cmd, url);
exit (EXIT_FAILURE);
}
- if ((nb = try_ftp_transfer(url)) == 0) {
+ if ((st = try_ftp_transfer(url)) == -1) {
/* Transfer failed, the requested file may not exist or be a symlink
* to a directory. Try again... */
if ((p = strrchr(url, '/')) && p[1]) {
url2 = dStrconcat(url, "/", NULL);
- nb = try_ftp_transfer(url2);
+ st = try_ftp_transfer(url2);
}
}
- if (nb == 0) {
+ if (st == -1) {
/* The transfer failed, let dillo know... */
- d_cmd = a_Dpip_build_cmd("cmd=%s to_cmd=%s msg=%s",
- "answer", "open_url", "not a directory");
- sock_handler_write_str(sh, 1, d_cmd);
+ d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
+ a_Dpip_dsh_write_str(sh, 0, d_cmd);
dFree(d_cmd);
+ a_Dpip_dsh_printf(sh, 1,
+ "HTTP/1.1 404 Not Found\r\n"
+ "Content-Type: text/plain\r\n"
+ "Content-Length: %d\r\n"
+ "\r\n"
+ "%s",
+ strlen(err_msg), err_msg);
}
dFree(cmd);
@@ -314,8 +342,8 @@ int main(int argc, char **argv)
dFree(dpip_tag);
/* Finish the SockHandler */
- sock_handler_close(sh);
- sock_handler_free(sh);
+ a_Dpip_dsh_close(sh);
+ a_Dpip_dsh_free(sh);
return 0;
}
diff --git a/dpi/hello.c b/dpi/hello.c
index 250a4a44..4643efc5 100644
--- a/dpi/hello.c
+++ b/dpi/hello.c
@@ -36,7 +36,7 @@
int main(void)
{
FILE *in_stream;
- SockHandler *sh;
+ Dsh *sh;
char *dpip_tag, *cmd = NULL, *url = NULL, *child_cmd = NULL;
char *esc_tag, *d_cmd;
size_t n;
@@ -45,21 +45,46 @@ int main(void)
char *choice[] = {"Window was closed", "Yes", "No",
"Could be", "It's OK", "Cancel"};
/* "Could>be", ">It's OK", "Can'>cel"}; --for testing */
- int choice_num;
+ int choice_num = -1;
MSG("starting...\n");
+ /* sleep(20) */
+
+ /* Initialize the SockHandler.
+ * This means we'll use stdin for input and stdout for output.
+ * In case of a server dpi, we'd use a socket and pass its file descriptor
+ * twice (e.g. a_Dpip_dsh_new(sock_fd, sock_fd, 1024).
+ * (Note: by now the last parameter is not used) */
+ sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 2*1024);
+
+ /* Authenticate our client...
+ * As we're using Internet domain sockets, DPIP checks whether the client
+ * runs with the user's ID, by means of a shared secret. The DPIP API does
+ * the work for us. */
+ if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
+ a_Dpip_check_auth(dpip_tag) < 0) {
+ MSG("can't authenticate request: %s\n", dStrerror(errno));
+ a_Dpip_dsh_close(sh);
+ return 1;
+ }
+ dFree(dpip_tag);
- /* Initialize the SockHandler */
- sh = sock_handler_new(STDIN_FILENO, STDOUT_FILENO, 2*1024);
-
- /* Read the dpi command from STDIN */
- dpip_tag = sock_handler_read(sh);
+ /* Read the dpi command from STDIN
+ * Now we're past the authentication phase, let's see what's dillo
+ * asking from us. a_Dpip_dsh_read_token() will block and return
+ * a full dpip token or null on error (it's commented in dpip.c) */
+ dpip_tag = a_Dpip_dsh_read_token(sh, 1);
MSG("tag = [%s]\n", dpip_tag);
- cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd");
- url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url");
+ /* Now that we have the dpip_tag, let's isolate the command and url */
+ cmd = a_Dpip_get_attr(dpip_tag, "cmd");
+ url = a_Dpip_get_attr(dpip_tag, "url");
/*-- Dialog part */
+/* This is the dialog window. This is an example of interaction with
+ * the user. If you're starting to understand dpis, comment this out
+ * by switching to "#if 0" and the dialog will be disabled. */
+#if 1
{
char *dpip_tag2, *dialog_msg;
@@ -69,15 +94,15 @@ int main(void)
"cmd=%s msg=%s alt1=%s alt2=%s alt3=%s alt4=%s alt5=%s",
"dialog", "Do you want to see the hello page?",
choice[1], choice[2], choice[3], choice[4], choice[5]);
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
/* Get the answer */
- dpip_tag2 = sock_handler_read(sh);
+ dpip_tag2 = a_Dpip_dsh_read_token(sh, 1);
MSG("tag = [%s]\n", dpip_tag2);
/* Get "msg" value */
- dialog_msg = a_Dpip_get_attr(dpip_tag2, strlen(dpip_tag2), "msg");
+ dialog_msg = a_Dpip_get_attr(dpip_tag2, "msg");
choice_num = 0;
if (dialog_msg)
choice_num = *dialog_msg - '0';
@@ -85,14 +110,16 @@ int main(void)
dFree(dialog_msg);
dFree(dpip_tag2);
}
+#endif
/*-- EOD part */
- /* Start sending our answer */
+ /* Start sending our answer.
+ * (You can read the comments for DPIP API functions in dpip/dpip.c) */
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
- sock_handler_write_str(sh, 0, d_cmd);
+ a_Dpip_dsh_write_str(sh, 0, d_cmd);
dFree(d_cmd);
- sock_handler_printf(sh, 0,
+ a_Dpip_dsh_printf(sh, 0,
"Content-type: text/html\n\n"
"<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
"<html>\n"
@@ -100,17 +127,17 @@ int main(void)
"<body><hr><h1>Hello world!</h1><hr>\n<br><br>\n");
/* Show the choice received with the dialog */
- sock_handler_printf(sh, 0,
+ a_Dpip_dsh_printf(sh, 0,
"<hr>\n"
"<table width='100%%' border='1' bgcolor='burlywood'><tr><td>\n"
"<big><em>Dialog question:</em> Do you want to see the hello page?<br>\n"
"<em>Answer received:</em> <b>%s</b></big> </table>\n"
"<hr>\n",
- choice[choice_num]);
+ choice_num < 0 ? "There was NO dialog!" : choice[choice_num]);
/* Show the dpip tag we received */
esc_tag = Escape_html_str(dpip_tag);
- sock_handler_printf(sh, 0,
+ a_Dpip_dsh_printf(sh, 0,
"<h3>dpip tag received:</h3>\n"
"<pre>\n%s</pre>\n"
"<br><small>(<b>dpip:</b> dpi protocol)</small><br><br><br>\n",
@@ -119,7 +146,9 @@ int main(void)
/* Now something more interesting,
- * fork a command and show its feedback */
+ * fork a command and show its feedback.
+ * (An example of generating dynamic content with an external
+ * program). */
if (cmd && url) {
child_cmd = dStrdup("date -R");
MSG("[%s]\n", child_cmd);
@@ -130,15 +159,15 @@ int main(void)
return EXIT_FAILURE;
}
- sock_handler_printf(sh, 0, "<h3>date:</h3>\n");
- sock_handler_printf(sh, 0, "<pre>\n");
+ a_Dpip_dsh_write_str(sh, 0, "<h3>date:</h3>\n");
+ a_Dpip_dsh_write_str(sh, 0, "<pre>\n");
/* Read/Write */
while ((n = fread (buf, 1, 4096, in_stream)) > 0) {
- sock_handler_write(sh, 0, buf, n);
+ a_Dpip_dsh_write(sh, 0, buf, n);
}
- sock_handler_printf(sh, 0, "</pre>\n");
+ a_Dpip_dsh_write_str(sh, 0, "</pre>\n");
if ((ret = pclose(in_stream)) != 0)
MSG("popen: [%d]\n", ret);
@@ -146,15 +175,15 @@ int main(void)
dFree(child_cmd);
}
- sock_handler_printf(sh, 1, "</body></html>\n");
+ a_Dpip_dsh_write_str(sh, 1, "</body></html>\n");
dFree(cmd);
dFree(url);
dFree(dpip_tag);
/* Finish the SockHandler */
- sock_handler_close(sh);
- sock_handler_free(sh);
+ a_Dpip_dsh_close(sh);
+ a_Dpip_dsh_free(sh);
return 0;
}
diff --git a/dpi/https.c b/dpi/https.c
index 00ceb0c1..3f474b81 100644
--- a/dpi/https.c
+++ b/dpi/https.c
@@ -25,7 +25,7 @@
* the https dillo plugin with the OpenSSL project's "OpenSSL"
* library, and distribute the linked executables, without including
* the source code for OpenSSL in the source distribution. You must
- * obey the GNU General Public License, version 2, in all respects
+ * obey the GNU General Public License, version 3, in all respects
* for all of the code used other than "OpenSSL".
*
*/
@@ -64,12 +64,15 @@
/*
* Debugging macros
*/
+#define SILENT 1
#define _MSG(...)
-#define MSG(...) printf("[https dpi]: " __VA_ARGS__)
+#if SILENT
+ #define MSG(...)
+#else
+ #define MSG(...) fprintf(stderr, "[https dpi]: " __VA_ARGS__)
+#endif
-#define ENABLE_SSL
-#undef ENABLE_SSL
#ifdef ENABLE_SSL
#include <openssl/ssl.h>
@@ -88,7 +91,7 @@ static int save_certificate_home(X509 * cert);
* Global variables
*/
static char *root_url = NULL; /*Holds the URL we are connecting to*/
-static SockHandler *sh;
+static Dsh *sh;
#ifdef ENABLE_SSL
@@ -104,8 +107,8 @@ static int dialog_get_answer_number(void)
char *dpip_tag, *response;
/* Read the dpi command from STDIN */
- dpip_tag = sock_handler_read(sh);
- response = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "msg");
+ dpip_tag = a_Dpip_dsh_read_token(sh, 1);
+ response = a_Dpip_get_attr(dpip_tag, "msg");
response_number = (response) ? strtol (response, NULL, 10) : -1;
dFree(dpip_tag);
dFree(response);
@@ -126,9 +129,10 @@ static void yes_ssl_support(void)
SSL_CTX * ssl_context = NULL;
SSL * ssl_connection = NULL;
- char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *http_query = NULL;
+ char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *http_query = NULL,
+ *proxy_url = NULL, *proxy_connect = NULL;
char buf[4096];
- int retval = 0;
+ int ret = 0;
int network_socket = -1;
@@ -189,10 +193,13 @@ static void yes_ssl_support(void)
SSL_set_verify(ssl_connection, SSL_VERIFY_NONE, 0);
/*Get the network address and command to be used*/
- dpip_tag = sock_handler_read(sh);
- cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd");
- url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url");
- http_query = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "query");
+ dpip_tag = a_Dpip_dsh_read_token(sh, 1);
+ cmd = a_Dpip_get_attr(dpip_tag, "cmd");
+ proxy_url = a_Dpip_get_attr(dpip_tag, "proxy_url");
+ proxy_connect =
+ a_Dpip_get_attr(dpip_tag, "proxy_connect");
+ url = a_Dpip_get_attr(dpip_tag, "url");
+ http_query = a_Dpip_get_attr(dpip_tag, "query");
if (cmd == NULL || url == NULL || http_query == NULL){
MSG("***Value of cmd, url or http_query is NULL"
@@ -202,13 +209,66 @@ static void yes_ssl_support(void)
}
if (exit_error == 0){
- network_socket = get_network_connection(url);
+ char *connect_url = proxy_url ? proxy_url : url;
+
+ network_socket = get_network_connection(connect_url);
if (network_socket<0){
MSG("Network socket create error\n");
exit_error = 1;
}
}
+ if (exit_error == 0 && proxy_connect != NULL) {
+ ssize_t St;
+ const char *p = proxy_connect;
+ int writelen = strlen(proxy_connect);
+
+ while (writelen > 0) {
+ St = write(network_socket, p, writelen);
+ if (St < 0) {
+ /* Error */
+ if (errno != EINTR) {
+ MSG("Error writing to proxy.\n");
+ exit_error = 1;
+ break;
+ }
+ } else {
+ p += St;
+ writelen -= St;
+ }
+ }
+ if (exit_error == 0) {
+ const size_t buflen = 200;
+ char buf[buflen];
+ Dstr *reply = dStr_new("");
+
+ while (1) {
+ St = read(network_socket, buf, buflen);
+ if (St > 0) {
+ dStr_append_l(reply, buf, St);
+ if (strstr(reply->str, "\r\n\r\n")) {
+ /* have whole reply header */
+ if (reply->len >= 12 && reply->str[9] == '2') {
+ /* e.g. "HTTP/1.1 200 Connection established[...]" */
+ MSG("CONNECT through proxy succeeded.\n");
+ } else {
+ /* TODO: send reply body to dillo */
+ exit_error = 1;
+ MSG("CONNECT through proxy failed.\n");
+ }
+ break;
+ }
+ } else if (St < 0) {
+ if (errno != EINTR) {
+ exit_error = 1;
+ MSG("Error reading from proxy.\n");
+ break;
+ }
+ }
+ }
+ dStr_free(reply, 1);
+ }
+ }
if (exit_error == 0){
/* Configure SSL to use network file descriptor */
@@ -244,14 +304,14 @@ static void yes_ssl_support(void)
/*Send dpi command*/
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
/*Send remaining data*/
- while ((retval = SSL_read(ssl_connection, buf, 4096)) > 0 ){
+ while ((ret = SSL_read(ssl_connection, buf, 4096)) > 0 ){
/* flush is good for dialup speed */
- sock_handler_write(sh, 1, buf, (size_t)retval);
+ a_Dpip_dsh_write(sh, 1, buf, (size_t)ret);
}
}
@@ -260,6 +320,8 @@ static void yes_ssl_support(void)
dFree(cmd);
dFree(url);
dFree(http_query);
+ dFree(proxy_url);
+ dFree(proxy_connect);
if (network_socket != -1){
close(network_socket);
@@ -288,12 +350,15 @@ static int get_network_connection(char * url)
int s;
int url_offset = 0;
int portnum = 443;
- unsigned int url_look_up_length = 0;
+ uint_t url_look_up_length = 0;
char * url_look_up = NULL;
/*Determine how much of url we chop off as unneeded*/
if (dStrncasecmp(url, "https://", 8) == 0){
url_offset = 8;
+ } else if (dStrncasecmp(url, "http://", 7) == 0) {
+ url_offset = 7;
+ portnum = 80;
}
/*Find end of URL*/
@@ -305,7 +370,7 @@ static int get_network_connection(char * url)
/*Check for port number*/
if (strchr(url+url_offset, ':') ==
(url + url_offset + url_look_up_length)){
- portnum = atoi(url + url_offset + url_look_up_length + 1);
+ portnum = strtol(url + url_offset + url_look_up_length + 1, NULL, 10);
}
} else {
url_look_up = url + url_offset;
@@ -314,7 +379,7 @@ static int get_network_connection(char * url)
root_url = dStrdup(url_look_up);
hp=gethostbyname(url_look_up);
- /*url_look_uip no longer needed, so free if neccessary*/
+ /*url_look_uip no longer needed, so free if necessary*/
if (url_look_up_length != 0){
dFree(url_look_up);
}
@@ -349,9 +414,9 @@ static int get_network_connection(char * url)
static int handle_certificate_problem(SSL * ssl_connection)
{
int response_number;
- int retval = -1;
+ int ret = -1;
long st;
- char *cn, *cn_end;
+ char *cn;
char buf[4096], *d_cmd, *msg;
X509 * remote_cert;
@@ -366,7 +431,7 @@ static int handle_certificate_problem(SSL * ssl_connection)
"This site CAN NOT be trusted. Sending data is NOT SAFE.\n"
"What do I do?",
"Continue", "Cancel");
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
/*Read the user's response*/
@@ -374,7 +439,7 @@ static int handle_certificate_problem(SSL * ssl_connection)
/*Abort on anything but "Continue"*/
if (response_number == 1){
- retval = 0;
+ ret = 0;
}
} else {
@@ -382,36 +447,37 @@ static int handle_certificate_problem(SSL * ssl_connection)
st = SSL_get_verify_result(ssl_connection);
switch (st) {
case X509_V_OK: /*Everything is Kosher*/
- retval = 0;
+ ret = 0;
break;
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
/*Either self signed and untrusted*/
/*Extract CN from certificate name information*/
- cn = strstr(remote_cert->name, "/CN=") + 4;
- if (cn == NULL)
- break;
-
- if ((cn_end = strstr(cn, "/")) == NULL )
- cn_end = cn + strlen(cn);
+ if ((cn = strstr(remote_cert->name, "/CN=")) == NULL) {
+ strcpy(buf, "(no CN given)");
+ } else {
+ char *cn_end;
- strncpy(buf, cn, (size_t) (cn_end - cn));
+ cn += 4;
- /*Add terminating NULL*/
- buf[cn_end - cn] = 0;
+ if ((cn_end = strstr(cn, "/")) == NULL )
+ cn_end = cn + strlen(cn);
+ strncpy(buf, cn, (size_t) (cn_end - cn));
+ buf[cn_end - cn] = '\0';
+ }
msg = dStrconcat("The remote certificate is self-signed and "
"untrusted.\nFor address: ", buf, NULL);
d_cmd = a_Dpip_build_cmd(
"cmd=%s msg=%s alt1=%s alt2=%s alt3=%s",
- "dialog", msg, "Continue", "Cancel", "Trust Certificate");
- sock_handler_write_str(sh, 1, d_cmd);
+ "dialog", msg, "Continue", "Cancel", "Save Certificate");
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
dFree(msg);
response_number = dialog_get_answer_number();
switch (response_number){
case 1:
- retval = 0;
+ ret = 0;
break;
case 2:
break;
@@ -420,7 +486,7 @@ static int handle_certificate_problem(SSL * ssl_connection)
/*Potential security problems because we are writing
*to the filesystem*/
save_certificate_home(remote_cert);
- retval = 1;
+ ret = 1;
break;
default:
break;
@@ -434,12 +500,12 @@ static int handle_certificate_problem(SSL * ssl_connection)
"The issuer for the remote certificate cannot be found\n"
"The authenticity of the remote certificate cannot be trusted",
"Continue", "Cancel");
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
response_number = dialog_get_answer_number();
if (response_number == 1) {
- retval = 0;
+ ret = 0;
}
break;
@@ -453,12 +519,12 @@ static int handle_certificate_problem(SSL * ssl_connection)
"The remote certificate signature could not be read\n"
"or is invalid and should not be trusted",
"Continue", "Cancel");
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
response_number = dialog_get_answer_number();
if (response_number == 1) {
- retval = 0;
+ ret = 0;
}
break;
case X509_V_ERR_CERT_NOT_YET_VALID:
@@ -472,12 +538,12 @@ static int handle_certificate_problem(SSL * ssl_connection)
"presented has a starting validity after today's date\n"
"You should be cautious about using this site",
"Continue", "Cancel");
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
response_number = dialog_get_answer_number();
if (response_number == 1) {
- retval = 0;
+ ret = 0;
}
break;
case X509_V_ERR_CERT_HAS_EXPIRED:
@@ -489,11 +555,11 @@ static int handle_certificate_problem(SSL * ssl_connection)
"wasn't designed to last this long. You should avoid \n"
"this site.",
"Continue", "Cancel");
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
response_number = dialog_get_answer_number();
if (response_number == 1) {
- retval = 0;
+ ret = 0;
}
break;
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
@@ -508,11 +574,11 @@ static int handle_certificate_problem(SSL * ssl_connection)
"making it impossible to determine if the certificate\n"
"is valid. You should not trust this certificate.",
"Continue", "Cancel");
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
response_number = dialog_get_answer_number();
if (response_number == 1) {
- retval = 0;
+ ret = 0;
}
break;
case X509_V_ERR_INVALID_CA:
@@ -528,11 +594,11 @@ static int handle_certificate_problem(SSL * ssl_connection)
"with the remote system. The connection should not\n"
"be trusted",
"Continue", "Cancel");
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
response_number = dialog_get_answer_number();
if (response_number == 1) {
- retval = 0;
+ ret = 0;
}
break;
case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
@@ -543,9 +609,9 @@ static int handle_certificate_problem(SSL * ssl_connection)
"dialog",
"Some of the information presented by the remote system\n"
"does not match other information presented\n"
- "This may be an attempt to evesdrop on communications",
+ "This may be an attempt to eavesdrop on communications",
"Continue", "Cancel");
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
default: /*Need to add more options later*/
snprintf(buf, 80,
@@ -553,19 +619,19 @@ static int handle_certificate_problem(SSL * ssl_connection)
d_cmd = a_Dpip_build_cmd(
"cmd=%s msg=%s alt1=%s alt2=%s",
"dialog", buf, "Continue", "Cancel");
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
response_number = dialog_get_answer_number();
/*abort on anything but "Continue"*/
if (response_number == 1){
- retval = 0;
+ ret = 0;
}
}
X509_free(remote_cert);
remote_cert = 0;
}
- return retval;
+ return ret;
}
/*
@@ -577,8 +643,8 @@ static int save_certificate_home(X509 * cert)
char buf[4096];
FILE * fp = NULL;
- unsigned int i = 0;
- int retval = 1;
+ uint_t i = 0;
+ int ret = 1;
/*Attempt to create .dillo/certs blindly - check later*/
snprintf(buf,4096,"%s/.dillo/", dGethomedir());
@@ -586,7 +652,7 @@ static int save_certificate_home(X509 * cert)
snprintf(buf,4096,"%s/.dillo/certs/", dGethomedir());
mkdir(buf, 01777);
- do{
+ do {
snprintf(buf, 4096, "%s/.dillo/certs/%lx.%u",
dGethomedir(), X509_subject_name_hash(cert), i);
@@ -601,7 +667,7 @@ static int save_certificate_home(X509 * cert)
PEM_write_X509(fp, cert);
fclose(fp);
MSG("Wrote certificate\n");
- retval = 0;
+ ret = 0;
break;
}
} else {
@@ -609,9 +675,9 @@ static int save_certificate_home(X509 * cert)
}
i++;
/*Don't loop too many times - just give up*/
- } while( i < 1024 );
+ } while (i < 1024);
- return retval;
+ return ret;
}
@@ -629,14 +695,14 @@ static void no_ssl_support(void)
char *d_cmd;
/* Read the dpi command from STDIN */
- dpip_tag = sock_handler_read(sh);
+ dpip_tag = a_Dpip_dsh_read_token(sh, 1);
MSG("{In https.filter.dpi}\n");
MSG("no_ssl_support version\n");
- cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd");
- url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url");
- http_query = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "query");
+ cmd = a_Dpip_get_attr(dpip_tag, "cmd");
+ url = a_Dpip_get_attr(dpip_tag, "url");
+ http_query = a_Dpip_get_attr(dpip_tag, "query");
MSG("{ cmd: %s}\n", cmd);
MSG("{ url: %s}\n", url);
@@ -645,33 +711,35 @@ static void no_ssl_support(void)
MSG("{ sending dpip cmd...}\n");
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
MSG("{ dpip cmd sent.}\n");
MSG("{ sending HTML...}\n");
- sock_handler_printf(sh, 1,
+ a_Dpip_dsh_printf(sh, 1,
"Content-type: text/html\n\n"
"<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
- "<html><body><pre>\n"
- "<b>Hi!\n\n"
- " This is the https dpi that just got a request to send\n"
- " the following HTTP query:\n{</b>\n"
- "<code>%s</code>\n"
- "<b>}</b>\n\n"
- " <b>*** Dillo's prototype plugin for https support"
- " is disabled now ***</b>\n\n"
- " If you want to test this <b>alpha</b> support code, just remove\n"
- " line 72 from dpi/https.c, recompile and reinstall.\n\n"
- " (beware that this https support is very limited now)\n\n"
+ "<html><head><title>SSL support is disabled</title></head>\n"
+ "<body>\n"
+ "<p>\n"
+ " The https dpi was unable to send\n"
+ " the following HTTP query:\n"
+ " <blockquote><pre>%s</pre></blockquote>\n"
+ " because Dillo's prototype plugin for https support"
+ " is disabled.\n\n"
+ "<p>\n"
+ " If you want to test this <b>alpha</b> support code,\n"
+ " just reconfigure with <code>--enable-ssl</code>,\n"
+ " recompile and reinstall.\n\n"
+ " (Beware that this https support is very limited now)\n\n"
" To use https and SSL, you must have \n"
" the OpenSSL development libraries installed. Check your\n"
" O/S distribution provider, or check out\n"
- " <a href=\"http://www.openssl.org\">www.openssl.org</a>\n\n"
- " --\n"
- "</pre></body></html>\n",
+ " <a href=\"http://www.openssl.org\">www.openssl.org</a>.\n\n"
+ "</p>\n\n"
+ "</body></html>\n",
http_query
);
MSG("{ HTML content sent.}\n");
@@ -691,8 +759,19 @@ static void no_ssl_support(void)
/*---------------------------------------------------------------------------*/
int main(void)
{
+ char *dpip_tag;
+
/* Initialize the SockHandler for this filter dpi */
- sh = sock_handler_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
+ sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
+
+ /* Authenticate our client... */
+ if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
+ a_Dpip_check_auth(dpip_tag) < 0) {
+ MSG("can't authenticate request: %s\n", dStrerror(errno));
+ a_Dpip_dsh_close(sh);
+ return 1;
+ }
+ dFree(dpip_tag);
#ifdef ENABLE_SSL
yes_ssl_support();
@@ -701,8 +780,8 @@ int main(void)
#endif
/* Finish the SockHandler */
- sock_handler_close(sh);
- sock_handler_free(sh);
+ a_Dpip_dsh_close(sh);
+ a_Dpip_dsh_free(sh);
dFree(root_url);
diff --git a/dpi/vsource.c b/dpi/vsource.c
new file mode 100644
index 00000000..113aaa99
--- /dev/null
+++ b/dpi/vsource.c
@@ -0,0 +1,239 @@
+/*
+ * Dpi for "View source".
+ *
+ * This server is an example. Play with it and modify to your taste.
+ *
+ * Copyright 2010 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 <unistd.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "../dpip/dpip.h"
+#include "dpiutil.h"
+
+/*
+ * Debugging macros
+ */
+#define _MSG(...)
+#define MSG(...) printf("[vsource dpi]: " __VA_ARGS__)
+
+/*---------------------------------------------------------------------------*/
+
+const char *DOCTYPE=
+ "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>";
+
+
+void send_dpip_tag(Dsh *sh, char *dpip_tag)
+{
+ a_Dpip_dsh_write_str(sh, 0, "\nDpip tag received: ");
+ a_Dpip_dsh_write_str(sh, 0, dpip_tag ? dpip_tag : "None");
+ a_Dpip_dsh_write_str(sh, 1, "\n\n");
+}
+
+/*
+ * Send source as plain text
+ */
+void send_plain_text(Dsh *sh, int data_size)
+{
+ int bytes_read = 0;
+ char *src_str;
+
+ /* Send HTTP header for plain text MIME type */
+ a_Dpip_dsh_write_str(sh, 0, "Content-type: text/plain\n\n");
+
+ while (bytes_read < data_size &&
+ (src_str = a_Dpip_dsh_read_token(sh, 1))) {
+ bytes_read += strlen(src_str);
+ a_Dpip_dsh_write_str(sh, 1, src_str);
+ dFree(src_str);
+ }
+}
+
+/*
+ * Send source as plain text with line numbers
+ */
+void send_numbered_text(Dsh *sh, int data_size)
+{
+ int bytes_read = 0, line = 1;
+ char *p, *q, *src_str, line_str[32];
+
+ /* Send HTTP header for plain text MIME type */
+ a_Dpip_dsh_write_str(sh, 0, "Content-type: text/plain\n\n");
+
+ while (bytes_read < data_size &&
+ (src_str = a_Dpip_dsh_read_token(sh, 1))) {
+ bytes_read += strlen(src_str);
+ p = q = src_str;
+
+ while (*p) {
+ snprintf(line_str, 32, "%2d: ", line);
+ a_Dpip_dsh_write_str(sh, 0, line_str);
+ if ((p = strchr(q, '\n'))) {
+ a_Dpip_dsh_write(sh, 0, q, p - q + 1);
+ if (p[1] == '\r')
+ ++p;
+ ++line;
+ } else {
+ a_Dpip_dsh_write_str(sh, 1, q);
+ break;
+ }
+ q = ++p;
+ }
+ dFree(src_str);
+ }
+}
+
+/*
+ * Send source as html text with line numbers
+ */
+void send_html_text(Dsh *sh, const char *url, int data_size)
+{
+ int bytes_read = 0, old_line = 0, line = 1;
+ char *p, *q, *src_str, line_str[128];
+
+ if (strncmp(url, "dpi:/vsource/:", 14) == 0)
+ url += 14;
+
+ /* Send HTTP header for plain text MIME type */
+ a_Dpip_dsh_write_str(sh, 0, "Content-type: text/html\n\n");
+
+ a_Dpip_dsh_write_str(sh, 0, DOCTYPE);
+ a_Dpip_dsh_printf(sh, 0,
+ "\n"
+ "<html><head>\n"
+ "<title>Source for %s</title>\n"
+ "<style type=\"text/css\">PRE {white-space: pre-wrap}\n"
+ "</style>\n"
+ "</head>\n"
+ "<body id=\"dillo_vs\">\n<table cellpadding='0'>\n", url);
+
+ while (bytes_read < data_size &&
+ (src_str = a_Dpip_dsh_read_token(sh, 1))) {
+ bytes_read += strlen(src_str);
+ p = q = src_str;
+
+ while (*p) {
+ if (line > old_line) {
+ snprintf(line_str, 128,
+ "%s<tr><td bgcolor='%s'>%d%s<td><pre>",
+ (line > 1) ? "</pre>" : "",
+ (line & 1) ? "#B87333" : "#DD7F32", line,
+ (line == 1 || (line % 10) == 0) ? "&nbsp;&nbsp;" : "");
+ a_Dpip_dsh_write_str(sh, 0, line_str);
+ old_line = line;
+ }
+ if ((p = strpbrk(q, "\r\n<&"))) {
+ if (*p == '\r' || *p == '\n') {
+ a_Dpip_dsh_write(sh, 0, q, p - q + 1);
+ if (*p == '\r' && p[1] == '\n')
+ p++;
+ ++line;
+ } else {
+ a_Dpip_dsh_write(sh, 0, q, p - q);
+ a_Dpip_dsh_write_str(sh, 0, (*p == '<') ? "&lt;" : "&amp;");
+ }
+ } else {
+ a_Dpip_dsh_write_str(sh, 1, q);
+ break;
+ }
+ q = ++p;
+ }
+ dFree(src_str);
+ }
+
+ if (data_size > 0)
+ a_Dpip_dsh_write_str(sh, 0, "</pre>");
+ a_Dpip_dsh_write_str(sh, 1, "</table></body></html>");
+}
+
+/*
+ *
+ */
+int main(void)
+{
+ Dsh *sh;
+ int data_size;
+ char *dpip_tag, *cmd = NULL, *cmd2 = NULL, *url = NULL, *size_str = NULL;
+ char *d_cmd;
+
+ _MSG("starting...\n");
+ //sleep(20);
+
+ /* Initialize the SockHandler.
+ * This means we'll use stdin for input and stdout for output.
+ * In case of a server dpi, we'd use a socket and pass its file descriptor
+ * twice (e.g. a_Dpip_dsh_new(sock_fd, sock_fd, 1024).
+ * (Note: by now the last parameter is not used) */
+ sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 2*1024);
+
+ /* Authenticate our client...
+ * As we're using Internet domain sockets, DPIP checks whether the client
+ * runs with the user's ID, by means of a shared secret. The DPIP API does
+ * the work for us. */
+ if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
+ a_Dpip_check_auth(dpip_tag) < 0) {
+ MSG("can't authenticate request: %s\n", dStrerror(errno));
+ a_Dpip_dsh_close(sh);
+ return 1;
+ }
+ dFree(dpip_tag);
+
+ /* Read the dpi command from STDIN
+ * Now we're past the authentication phase, let's see what's dillo
+ * asking from us. a_Dpip_dsh_read_token() will block and return
+ * a full dpip token or null on error (it's commented in dpip.c) */
+ dpip_tag = a_Dpip_dsh_read_token(sh, 1);
+ MSG("tag = [%s]\n", dpip_tag);
+
+ /* Now that we have the dpip_tag, let's isolate the command and url */
+ cmd = a_Dpip_get_attr(dpip_tag, "cmd");
+ url = a_Dpip_get_attr(dpip_tag, "url");
+
+ /* Start sending our answer.
+ * (You can read the comments for DPIP API functions in dpip/dpip.c) */
+ d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
+ a_Dpip_dsh_write_str(sh, 0, d_cmd);
+ dFree(d_cmd);
+ dFree(dpip_tag);
+
+ dpip_tag = a_Dpip_dsh_read_token(sh, 1);
+ cmd2 = a_Dpip_get_attr(dpip_tag, "cmd");
+ if (cmd2) {
+ if (strcmp(cmd2, "start_send_page") == 0 &&
+ (size_str = a_Dpip_get_attr(dpip_tag, "data_size"))) {
+ data_size = strtol(size_str, NULL, 10);
+ /* Choose your flavour */
+ //send_plain_text(sh, data_size);
+ //send_numbered_text(sh, data_size);
+ send_html_text(sh, url, data_size);
+ } else if (strcmp(cmd2, "DpiError") == 0) {
+ /* Dillo detected an error (other failures just close the socket) */
+ a_Dpip_dsh_write_str(sh, 0, "Content-type: text/plain\n\n");
+ a_Dpip_dsh_write_str(sh, 1, "[vsource dpi]: "
+ "ERROR: Page not cached.\n");
+ }
+ dFree(cmd2);
+ }
+
+ dFree(cmd);
+ dFree(url);
+ dFree(size_str);
+ dFree(dpip_tag);
+
+ /* Finish the SockHandler */
+ a_Dpip_dsh_close(sh);
+ a_Dpip_dsh_free(sh);
+
+ return 0;
+}
+