diff options
Diffstat (limited to 'dpi')
-rw-r--r-- | dpi/Makefile.am | 7 | ||||
-rw-r--r-- | dpi/bookmarks.c | 286 | ||||
-rw-r--r-- | dpi/cookies.c | 1613 | ||||
-rw-r--r-- | dpi/datauri.c | 191 | ||||
-rw-r--r-- | dpi/downloads.cc | 108 | ||||
-rw-r--r-- | dpi/dpiutil.c | 149 | ||||
-rw-r--r-- | dpi/dpiutil.h | 45 | ||||
-rw-r--r-- | dpi/file.c | 827 | ||||
-rw-r--r-- | dpi/ftp.c | 110 | ||||
-rw-r--r-- | dpi/hello.c | 81 | ||||
-rw-r--r-- | dpi/https.c | 247 | ||||
-rw-r--r-- | dpi/vsource.c | 239 |
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> 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> 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 an operation </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>, mark its operands, and </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'> 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'> 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'> 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 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 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[] = { "&", "<", ">", """, "'" }; @@ -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. */ @@ -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 %.2s %.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 %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, - " <a href='dpi:/file/toggle'>%</a>\n"); + /* HTML style toggle */ + a_Dpip_dsh_write_str(client->sh, 0, + " <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 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 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) @@ -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) ? " " : ""); + 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 == '<') ? "<" : "&"); + } + } 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; +} + |