From 2da19eb8eff90f6c9a7cb46ff891643e89d50a55 Mon Sep 17 00:00:00 2001 From: jongfoster Date: Tue, 4 Jun 2002 14:34:21 +0000 Subject: [PATCH] Moving source files to src/ --- src/actionlist.h | 173 ++ src/actions.c | 1660 ++++++++++++++++ src/actions.h | 161 ++ src/amiga.c | 286 +++ src/amiga.h | 126 ++ src/cgi.c | 2178 +++++++++++++++++++++ src/cgi.h | 257 +++ src/cgiedit.c | 4715 ++++++++++++++++++++++++++++++++++++++++++++++ src/cgiedit.h | 157 ++ src/cgisimple.c | 1419 ++++++++++++++ src/cgisimple.h | 152 ++ src/cygwin.h | 76 + src/deanimate.c | 507 +++++ src/deanimate.h | 105 ++ src/encode.c | 486 +++++ src/encode.h | 85 + src/errlog.c | 973 ++++++++++ src/errlog.h | 180 ++ src/filters.c | 1601 ++++++++++++++++ src/filters.h | 285 +++ src/gateway.c | 399 ++++ src/gateway.h | 126 ++ src/jbsockets.c | 820 ++++++++ src/jbsockets.h | 133 ++ src/jcc.c | 2364 +++++++++++++++++++++++ src/jcc.h | 143 ++ src/killpopup.c | 216 +++ src/killpopup.h | 106 ++ src/list.c | 1067 +++++++++++ src/list.h | 167 ++ src/loadcfg.c | 1598 ++++++++++++++++ src/loadcfg.h | 192 ++ src/loaders.c | 1449 ++++++++++++++ src/loaders.h | 238 +++ src/miscutil.c | 1763 +++++++++++++++++ src/miscutil.h | 197 ++ src/parsers.c | 1794 ++++++++++++++++++ src/parsers.h | 234 +++ src/pcrs.c | 942 +++++++++ src/pcrs.h | 171 ++ src/project.h | 1437 ++++++++++++++ src/ssplit.c | 211 +++ src/ssplit.h | 80 + src/urlmatch.c | 812 ++++++++ src/urlmatch.h | 100 + src/w32log.c | 1347 +++++++++++++ src/w32log.h | 164 ++ src/w32res.h | 177 ++ src/w32taskbar.c | 291 +++ src/w32taskbar.h | 78 + src/win32.c | 314 +++ src/win32.h | 90 + 52 files changed, 34802 insertions(+) create mode 100644 src/actionlist.h create mode 100644 src/actions.c create mode 100644 src/actions.h create mode 100644 src/amiga.c create mode 100644 src/amiga.h create mode 100644 src/cgi.c create mode 100644 src/cgi.h create mode 100644 src/cgiedit.c create mode 100644 src/cgiedit.h create mode 100644 src/cgisimple.c create mode 100644 src/cgisimple.h create mode 100644 src/cygwin.h create mode 100644 src/deanimate.c create mode 100644 src/deanimate.h create mode 100644 src/encode.c create mode 100644 src/encode.h create mode 100644 src/errlog.c create mode 100644 src/errlog.h create mode 100644 src/filters.c create mode 100644 src/filters.h create mode 100644 src/gateway.c create mode 100644 src/gateway.h create mode 100644 src/jbsockets.c create mode 100644 src/jbsockets.h create mode 100644 src/jcc.c create mode 100644 src/jcc.h create mode 100644 src/killpopup.c create mode 100644 src/killpopup.h create mode 100644 src/list.c create mode 100644 src/list.h create mode 100644 src/loadcfg.c create mode 100644 src/loadcfg.h create mode 100644 src/loaders.c create mode 100644 src/loaders.h create mode 100644 src/miscutil.c create mode 100644 src/miscutil.h create mode 100644 src/parsers.c create mode 100644 src/parsers.h create mode 100644 src/pcrs.c create mode 100644 src/pcrs.h create mode 100644 src/project.h create mode 100644 src/ssplit.c create mode 100644 src/ssplit.h create mode 100644 src/urlmatch.c create mode 100644 src/urlmatch.h create mode 100644 src/w32log.c create mode 100644 src/w32log.h create mode 100644 src/w32res.h create mode 100644 src/w32taskbar.c create mode 100644 src/w32taskbar.h create mode 100644 src/win32.c create mode 100644 src/win32.h diff --git a/src/actionlist.h b/src/actionlist.h new file mode 100644 index 00000000..c7247f9f --- /dev/null +++ b/src/actionlist.h @@ -0,0 +1,173 @@ +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/actionlist.h,v $ + * + * Purpose : Master list of supported actions. + * Not really a header, since it generates code. + * This is included (3 times!) from actions.c + * Each time, the following macros are defined to + * suitable values beforehand: + * DEFINE_ACTION_MULTI() + * DEFINE_ACTION_STRING() + * DEFINE_ACTION_BOOL() + * DEFINE_ACTION_ALIAS + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: actionlist.h,v $ + * Revision 1.17 2002/05/14 21:25:55 oes + * Renamed prevent-(setting/reading)-cookies to crunch-(incoming/outgoing)-cookies + * + * Revision 1.16 2002/04/24 02:15:18 oes + * Renamed actions as discussed, Aliased old action names to new ones. + * + * Revision 1.15 2002/03/26 22:29:54 swa + * we have a new homepage! + * + * Revision 1.14 2002/03/24 16:32:08 jongfoster + * Removing logo option + * + * Revision 1.13 2002/03/24 15:23:33 jongfoster + * Name changes + * + * Revision 1.12 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.11 2002/03/12 01:42:49 oes + * Introduced modular filters + * + * Revision 1.10 2002/03/08 18:19:14 jongfoster + * Adding +image-blocker{pattern} option to edit interface + * + * Revision 1.9 2001/11/22 21:58:41 jongfoster + * Adding action +no-cookies-keep + * + * Revision 1.8 2001/10/10 16:42:52 oes + * Fixed a bug, Added +limit-connect string action + * + * Revision 1.7 2001/10/07 15:33:59 oes + * Introduced a +no-compression action + * Introduced a +downgrade action + * + * Revision 1.6 2001/09/16 15:47:37 jongfoster + * First version of CGI-based edit interface. This is very much a + * work-in-progress, and you can't actually use it to edit anything + * yet. You must #define FEATURE_CGI_EDIT_ACTIONS for these changes + * to have any effect. + * + * Revision 1.5 2001/07/18 12:27:03 oes + * Changed deanimate-gifs to string action + * + * Revision 1.4 2001/07/13 13:52:12 oes + * - Formatting + * - Introduced new action ACTION_DEANIMATE + * + * Revision 1.3 2001/06/07 23:03:56 jongfoster + * Added standard comment at top of file. + * + * + *********************************************************************/ + + +#if !(defined(DEFINE_ACTION_BOOL) && defined(DEFINE_ACTION_MULTI) && defined(DEFINE_ACTION_STRING)) +#error Please define lots of macros before including "actionlist.h". +#endif /* !defined(all the DEFINE_ACTION_xxx macros) */ + +#ifndef DEFINE_CGI_PARAM_RADIO +#define DEFINE_CGI_PARAM_RADIO(name, bit, index, value, is_default) +#define DEFINE_CGI_PARAM_CUSTOM(name, bit, index, default_val) +#define DEFINE_CGI_PARAM_NO_RADIO(name, bit, index, default_val) +#endif /* ndef DEFINE_CGI_PARAM_RADIO */ + +DEFINE_ACTION_MULTI ("add-header", ACTION_MULTI_ADD_HEADER) +DEFINE_ACTION_BOOL ("block", ACTION_BLOCK) +DEFINE_ACTION_BOOL ("crunch-incoming-cookies", ACTION_NO_COOKIE_SET) +DEFINE_ACTION_BOOL ("crunch-outgoing-cookies", ACTION_NO_COOKIE_READ) +DEFINE_ACTION_STRING ("deanimate-gifs", ACTION_DEANIMATE, ACTION_STRING_DEANIMATE) +DEFINE_CGI_PARAM_RADIO ("deanimate-gifs", ACTION_DEANIMATE, ACTION_STRING_DEANIMATE, "first", 0) +DEFINE_CGI_PARAM_RADIO ("deanimate-gifs", ACTION_DEANIMATE, ACTION_STRING_DEANIMATE, "last", 1) +DEFINE_ACTION_BOOL ("downgrade-http-version", ACTION_DOWNGRADE) +DEFINE_ACTION_BOOL ("fast-redirects", ACTION_FAST_REDIRECTS) +DEFINE_ACTION_MULTI ("filter", ACTION_MULTI_FILTER) +DEFINE_ACTION_BOOL ("handle-as-image", ACTION_IMAGE) +DEFINE_ACTION_BOOL ("hide-forwarded-for-headers", ACTION_HIDE_FORWARDED) +DEFINE_ACTION_STRING ("hide-from-header", ACTION_HIDE_FROM, ACTION_STRING_FROM) +DEFINE_CGI_PARAM_RADIO ("hide-from-header", ACTION_HIDE_FROM, ACTION_STRING_FROM, "block", 1) +DEFINE_CGI_PARAM_CUSTOM ("hide-from-header", ACTION_HIDE_FROM, ACTION_STRING_FROM, "spam_me_senseless@sittingduck.xyz") +DEFINE_ACTION_STRING ("hide-referer", ACTION_HIDE_REFERER, ACTION_STRING_REFERER) +DEFINE_CGI_PARAM_RADIO ("hide-referer", ACTION_HIDE_REFERER, ACTION_STRING_REFERER, "forge", 1) +DEFINE_CGI_PARAM_RADIO ("hide-referer", ACTION_HIDE_REFERER, ACTION_STRING_REFERER, "block", 0) +DEFINE_CGI_PARAM_CUSTOM ("hide-referer", ACTION_HIDE_REFERER, ACTION_STRING_REFERER, "http://www.google.com/") +DEFINE_ACTION_STRING ("hide-user-agent", ACTION_HIDE_USER_AGENT, ACTION_STRING_USER_AGENT) +DEFINE_CGI_PARAM_NO_RADIO("hide-user-agent", ACTION_HIDE_USER_AGENT, ACTION_STRING_USER_AGENT, "Privoxy/3.0 (Anonymous)") +DEFINE_ACTION_BOOL ("kill-popups", ACTION_NO_POPUPS) +DEFINE_ACTION_STRING ("limit-connect", ACTION_LIMIT_CONNECT, ACTION_STRING_LIMIT_CONNECT) +DEFINE_CGI_PARAM_NO_RADIO("limit-connect", ACTION_LIMIT_CONNECT, ACTION_STRING_LIMIT_CONNECT, "443") +DEFINE_ACTION_BOOL ("prevent-compression", ACTION_NO_COMPRESSION) +DEFINE_ACTION_BOOL ("send-vanilla-wafer", ACTION_VANILLA_WAFER) +DEFINE_ACTION_MULTI ("send-wafer", ACTION_MULTI_WAFER) +DEFINE_ACTION_BOOL ("session-cookies-only", ACTION_NO_COOKIE_KEEP) +DEFINE_ACTION_STRING ("set-image-blocker", ACTION_IMAGE_BLOCKER, ACTION_STRING_IMAGE_BLOCKER) +DEFINE_CGI_PARAM_RADIO ("set-image-blocker", ACTION_IMAGE_BLOCKER, ACTION_STRING_IMAGE_BLOCKER, "pattern", 1) +DEFINE_CGI_PARAM_RADIO ("set-image-blocker", ACTION_IMAGE_BLOCKER, ACTION_STRING_IMAGE_BLOCKER, "blank", 0) +DEFINE_CGI_PARAM_CUSTOM ("set-image-blocker", ACTION_IMAGE_BLOCKER, ACTION_STRING_IMAGE_BLOCKER, CGI_PREFIX "show-banner?type=pattern") + + +#if DEFINE_ACTION_ALIAS + +/* + * Alternative spellings + */ +DEFINE_ACTION_BOOL ("kill-popup", ACTION_NO_POPUPS) +DEFINE_ACTION_STRING ("hide-referrer", ACTION_HIDE_REFERER, ACTION_STRING_REFERER) +DEFINE_ACTION_BOOL ("prevent-keeping-cookies", ACTION_NO_COOKIE_KEEP) + +/* + * Pre-3.0 compatibility + */ +DEFINE_ACTION_BOOL ("prevent-reading-cookies", ACTION_NO_COOKIE_READ) +DEFINE_ACTION_BOOL ("prevent-setting-cookies", ACTION_NO_COOKIE_SET) +DEFINE_ACTION_BOOL ("downgrade", ACTION_DOWNGRADE) +DEFINE_ACTION_BOOL ("hide-forwarded", ACTION_HIDE_FORWARDED) +DEFINE_ACTION_STRING ("hide-from", ACTION_HIDE_FROM, ACTION_STRING_FROM) +DEFINE_ACTION_BOOL ("image", ACTION_IMAGE) +DEFINE_ACTION_STRING ("image-blocker", ACTION_IMAGE_BLOCKER, ACTION_STRING_IMAGE_BLOCKER) +DEFINE_ACTION_BOOL ("no-compression", ACTION_NO_COMPRESSION) +DEFINE_ACTION_BOOL ("no-cookies-keep", ACTION_NO_COOKIE_KEEP) +DEFINE_ACTION_BOOL ("no-cookies-read", ACTION_NO_COOKIE_READ) +DEFINE_ACTION_BOOL ("no-cookies-set", ACTION_NO_COOKIE_SET) +DEFINE_ACTION_BOOL ("no-popups", ACTION_NO_POPUPS) +#endif /* if DEFINE_ACTION_ALIAS */ + +#undef DEFINE_ACTION_MULTI +#undef DEFINE_ACTION_STRING +#undef DEFINE_ACTION_BOOL +#undef DEFINE_ACTION_ALIAS +#undef DEFINE_CGI_PARAM_CUSTOM +#undef DEFINE_CGI_PARAM_RADIO +#undef DEFINE_CGI_PARAM_NO_RADIO + diff --git a/src/actions.c b/src/actions.c new file mode 100644 index 00000000..967faad1 --- /dev/null +++ b/src/actions.c @@ -0,0 +1,1660 @@ +const char actions_rcs[] = "$Id: actions.c,v 1.33 2002/05/26 12:14:19 roro Exp $"; +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/actions.c,v $ + * + * Purpose : Declares functions to work with actions files + * Functions declared include: FIXME + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: actions.c,v $ + * Revision 1.33 2002/05/26 12:14:19 roro + * Change unsigned to unsigned long in actions_name struct. This closes + * SourceForge Bug #539284. + * + * Revision 1.32 2002/05/12 21:36:29 jongfoster + * Correcting function comments + * + * Revision 1.31 2002/05/06 07:56:50 oes + * Made actions_to_html independent of FEATURE_CGI_EDIT_ACTIONS + * + * Revision 1.30 2002/04/30 11:14:52 oes + * Made csp the first parameter in *action_to_html + * + * Revision 1.29 2002/04/26 19:30:54 jongfoster + * - current_action_to_html(): Adding help link for the "-" form of + * one-string actions. + * - Some actions had "
-", some "
-" (note the space). + * Standardizing on no space. + * - Greatly simplifying some of the code by using string_join() + * where appropriate. + * + * Revision 1.28 2002/04/26 12:53:15 oes + * - CGI AF editor now writes action lines split into + * single lines with line continuation + * - actions_to_html now embeds each action name in + * link to chapter + * - current_action_to_text is now called current_action_to_html + * and acts like actions_to_html + * + * Revision 1.27 2002/04/24 02:10:31 oes + * - Jon's patch for multiple AFs: + * - split load_actions_file, add load_one_actions_file + * - make csp->actions_list files an array + * - remember file id with each action + * - Copy_action now frees dest action before copying + * + * Revision 1.26 2002/03/26 22:29:54 swa + * we have a new homepage! + * + * Revision 1.25 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.24 2002/03/16 23:54:06 jongfoster + * Adding graceful termination feature, to help look for memory leaks. + * If you enable this (which, by design, has to be done by hand + * editing config.h) and then go to http://i.j.b/die, then the program + * will exit cleanly after the *next* request. It should free all the + * memory that was used. + * + * Revision 1.23 2002/03/07 03:46:16 oes + * Fixed compiler warnings + * + * Revision 1.22 2002/01/21 00:27:02 jongfoster + * Allowing free_action(NULL). + * Moving the functions that #include actionlist.h to the end of the file, + * because the Visual C++ 97 debugger gets extremely confused if you try + * to debug any code that comes after them in the file. + * + * Revision 1.21 2002/01/17 20:54:44 jongfoster + * Renaming free_url to free_url_spec, since it frees a struct url_spec. + * + * Revision 1.20 2001/11/22 21:56:49 jongfoster + * Making action_spec->flags into an unsigned long rather than just an + * unsigned int. + * Fixing a bug in the display of -add-header and -wafer + * + * Revision 1.19 2001/11/13 00:14:07 jongfoster + * Fixing stupid bug now I've figured out what || means. + * (It always returns 0 or 1, not one of it's paramaters.) + * + * Revision 1.18 2001/11/07 00:06:06 steudten + * Add line number in error output for lineparsing for + * actionsfile. + * + * Revision 1.17 2001/10/25 03:40:47 david__schmidt + * Change in porting tactics: OS/2's EMX porting layer doesn't allow multiple + * threads to call select() simultaneously. So, it's time to do a real, live, + * native OS/2 port. See defines for __EMX__ (the porting layer) vs. __OS2__ + * (native). Both versions will work, but using __OS2__ offers multi-threading. + * + * Revision 1.16 2001/10/23 21:30:30 jongfoster + * Adding error-checking to selected functions. + * + * Revision 1.15 2001/10/14 21:58:22 jongfoster + * Adding support for the CGI-based editor: + * - Exported get_actions() + * - Added new function free_alias_list() + * - Added support for {{settings}} and {{description}} blocks + * in the actions file. They are currently ignored. + * - Added restriction to only one {{alias}} block which must appear + * first in the file, to simplify the editor's rewriting rules. + * - Note that load_actions_file() is no longer used by the CGI-based + * editor, but some of the other routines in this file are. + * + * Revision 1.14 2001/09/22 16:36:59 jongfoster + * Removing unused parameter fs from read_config_line() + * + * Revision 1.13 2001/09/16 15:47:37 jongfoster + * First version of CGI-based edit interface. This is very much a + * work-in-progress, and you can't actually use it to edit anything + * yet. You must #define FEATURE_CGI_EDIT_ACTIONS for these changes + * to have any effect. + * + * Revision 1.12 2001/09/16 13:21:27 jongfoster + * Changes to use new list functions. + * + * Revision 1.11 2001/09/14 00:17:32 jongfoster + * Tidying up memory allocation. New function init_action(). + * + * Revision 1.10 2001/09/10 10:14:34 oes + * Removing unused variable + * + * Revision 1.9 2001/07/30 22:08:36 jongfoster + * Tidying up #defines: + * - All feature #defines are now of the form FEATURE_xxx + * - Permanently turned off WIN_GUI_EDIT + * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS + * + * Revision 1.8 2001/06/29 13:19:52 oes + * Removed logentry from cancelled commit + * + * Revision 1.7 2001/06/09 10:55:28 jongfoster + * Changing BUFSIZ ==> BUFFER_SIZE + * + * Revision 1.6 2001/06/07 23:04:34 jongfoster + * Made get_actions() static. + * + * Revision 1.5 2001/06/03 19:11:48 oes + * adapted to new enlist_unique arg format + * + * Revision 1.4 2001/06/01 20:03:42 jongfoster + * Better memory management - current_action->strings[] now + * contains copies of the strings, not the original. + * + * Revision 1.3 2001/06/01 18:49:17 jongfoster + * Replaced "list_share" with "list" - the tiny memory gain was not + * worth the extra complexity. + * + * Revision 1.2 2001/05/31 21:40:00 jongfoster + * Removing some commented out, obsolete blocks of code. + * + * Revision 1.1 2001/05/31 21:16:46 jongfoster + * Moved functions to process the action list into this new file. + * + * + *********************************************************************/ + + +#include "config.h" + +#include +#include +#include +#include + +#include "project.h" +#include "jcc.h" +#include "list.h" +#include "actions.h" +#include "miscutil.h" +#include "errlog.h" +#include "loaders.h" +#include "encode.h" +#include "urlmatch.h" +#include "cgi.h" + +const char actions_h_rcs[] = ACTIONS_H_VERSION; + + +/* + * We need the main list of options. + * + * First, we need a way to tell between boolean, string, and multi-string + * options. For string and multistring options, we also need to be + * able to tell the difference between a "+" and a "-". (For bools, + * the "+"/"-" information is encoded in "add" and "mask"). So we use + * an enumerated type (well, the preprocessor equivalent). Here are + * the values: + */ +#define AV_NONE 0 /* +opt -opt */ +#define AV_ADD_STRING 1 /* +stropt{string} */ +#define AV_REM_STRING 2 /* -stropt */ +#define AV_ADD_MULTI 3 /* +multiopt{string} +multiopt{string2} */ +#define AV_REM_MULTI 4 /* -multiopt{string} -multiopt */ + +/* + * We need a structure to hold the name, flag changes, + * type, and string index. + */ +struct action_name +{ + const char * name; + unsigned long mask; /* a bit set to "0" = remove action */ + unsigned long add; /* a bit set to "1" = add action */ + int takes_value; /* an AV_... constant */ + int index; /* index into strings[] or multi[] */ +}; + +/* + * And with those building blocks in place, here's the array. + */ +static const struct action_name action_names[] = +{ + /* + * Well actually there's no data here - it's in actionlist.h + * This keeps it together to make it easy to change. + * + * Here's the macros used to format it: + */ +#define DEFINE_ACTION_MULTI(name,index) \ + { "+" name, ACTION_MASK_ALL, 0, AV_ADD_MULTI, index }, \ + { "-" name, ACTION_MASK_ALL, 0, AV_REM_MULTI, index }, +#define DEFINE_ACTION_STRING(name,flag,index) \ + { "+" name, ACTION_MASK_ALL, flag, AV_ADD_STRING, index }, \ + { "-" name, ~flag, 0, AV_REM_STRING, index }, +#define DEFINE_ACTION_BOOL(name,flag) \ + { "+" name, ACTION_MASK_ALL, flag }, \ + { "-" name, ~flag, 0 }, +#define DEFINE_ACTION_ALIAS 1 /* Want aliases please */ + +#include "actionlist.h" + +#undef DEFINE_ACTION_MULTI +#undef DEFINE_ACTION_STRING +#undef DEFINE_ACTION_BOOL +#undef DEFINE_ACTION_ALIAS + + { NULL, 0, 0 } /* End marker */ +}; + + +static int load_one_actions_file(struct client_state *csp, int fileid); + + +/********************************************************************* + * + * Function : merge_actions + * + * Description : Merge two actions together. + * Similar to "dest += src". + * + * Parameters : + * 1 : dest = Actions to modify. + * 2 : src = Action to add. + * + * Returns : JB_ERR_OK or JB_ERR_MEMORY + * + *********************************************************************/ +jb_err merge_actions (struct action_spec *dest, + const struct action_spec *src) +{ + int i; + jb_err err; + + dest->mask &= src->mask; + dest->add &= src->mask; + dest->add |= src->add; + + for (i = 0; i < ACTION_STRING_COUNT; i++) + { + char * str = src->string[i]; + if (str) + { + freez(dest->string[i]); + dest->string[i] = strdup(str); + if (NULL == dest->string[i]) + { + return JB_ERR_MEMORY; + } + } + } + + for (i = 0; i < ACTION_MULTI_COUNT; i++) + { + if (src->multi_remove_all[i]) + { + /* Remove everything from dest */ + list_remove_all(dest->multi_remove[i]); + dest->multi_remove_all[i] = 1; + + err = list_duplicate(dest->multi_add[i], src->multi_add[i]); + } + else if (dest->multi_remove_all[i]) + { + /* + * dest already removes everything, so we only need to worry + * about what we add. + */ + list_remove_list(dest->multi_add[i], src->multi_remove[i]); + err = list_append_list_unique(dest->multi_add[i], src->multi_add[i]); + } + else + { + /* No "remove all"s to worry about. */ + list_remove_list(dest->multi_add[i], src->multi_remove[i]); + err = list_append_list_unique(dest->multi_remove[i], src->multi_remove[i]); + if (!err) err = list_append_list_unique(dest->multi_add[i], src->multi_add[i]); + } + + if (err) + { + return err; + } + } + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : copy_action + * + * Description : Copy an action_specs. + * Similar to "dest = src". + * + * Parameters : + * 1 : dest = Destination of copy. + * 2 : src = Source for copy. + * + * Returns : N/A + * + *********************************************************************/ +jb_err copy_action (struct action_spec *dest, + const struct action_spec *src) +{ + int i; + jb_err err = JB_ERR_OK; + + free_action(dest); + memset(dest, '\0', sizeof(*dest)); + + dest->mask = src->mask; + dest->add = src->add; + + for (i = 0; i < ACTION_STRING_COUNT; i++) + { + char * str = src->string[i]; + if (str) + { + str = strdup(str); + if (!str) + { + return JB_ERR_MEMORY; + } + dest->string[i] = str; + } + } + + for (i = 0; i < ACTION_MULTI_COUNT; i++) + { + dest->multi_remove_all[i] = src->multi_remove_all[i]; + err = list_duplicate(dest->multi_remove[i], src->multi_remove[i]); + if (err) + { + return err; + } + err = list_duplicate(dest->multi_add[i], src->multi_add[i]); + if (err) + { + return err; + } + } + return err; +} + + +/********************************************************************* + * + * Function : free_action + * + * Description : Destroy an action_spec. Frees memory used by it, + * except for the memory used by the struct action_spec + * itself. + * + * Parameters : + * 1 : src = Source to free. + * + * Returns : N/A + * + *********************************************************************/ +void free_action (struct action_spec *src) +{ + int i; + + if (src == NULL) + { + return; + } + + for (i = 0; i < ACTION_STRING_COUNT; i++) + { + freez(src->string[i]); + } + + for (i = 0; i < ACTION_MULTI_COUNT; i++) + { + destroy_list(src->multi_remove[i]); + destroy_list(src->multi_add[i]); + } + + memset(src, '\0', sizeof(*src)); +} + + +/********************************************************************* + * + * Function : get_action_token + * + * Description : Parses a line for the first action. + * Modifies it's input array, doesn't allocate memory. + * e.g. given: + * *line=" +abc{def} -ghi " + * Returns: + * *line=" -ghi " + * *name="+abc" + * *value="def" + * + * Parameters : + * 1 : line = [in] The line containing the action. + * [out] Start of next action on line, or + * NULL if we reached the end of line before + * we found an action. + * 2 : name = [out] Start of action name, null + * terminated. NULL on EOL + * 3 : value = [out] Start of action value, null + * terminated. NULL if none or EOL. + * + * Returns : JB_ERR_OK => Ok + * JB_ERR_PARSE => Mismatched {} (line was trashed anyway) + * + *********************************************************************/ +jb_err get_action_token(char **line, char **name, char **value) +{ + char * str = *line; + char ch; + + /* set default returns */ + *line = NULL; + *name = NULL; + *value = NULL; + + /* Eat any leading whitespace */ + while ((*str == ' ') || (*str == '\t')) + { + str++; + } + + if (*str == '\0') + { + return 0; + } + + if (*str == '{') + { + /* null name, just value is prohibited */ + return JB_ERR_PARSE; + } + + *name = str; + + /* parse option */ + while (((ch = *str) != '\0') && + (ch != ' ') && (ch != '\t') && (ch != '{')) + { + if (ch == '}') + { + /* error, '}' without '{' */ + return JB_ERR_PARSE; + } + str++; + } + *str = '\0'; + + if (ch != '{') + { + /* no value */ + if (ch == '\0') + { + /* EOL - be careful not to run off buffer */ + *line = str; + } + else + { + /* More to parse next time. */ + *line = str + 1; + } + return JB_ERR_OK; + } + + str++; + *value = str; + + str = strchr(str, '}'); + if (str == NULL) + { + /* error */ + *value = NULL; + return JB_ERR_PARSE; + } + + /* got value */ + *str = '\0'; + *line = str + 1; + + chomp(*value); + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : get_actions + * + * Description : Parses a list of actions. + * + * Parameters : + * 1 : line = The string containing the actions. + * Will be written to by this function. + * 2 : alias_list = Custom alias list, or NULL for none. + * 3 : cur_action = Where to store the action. Caller + * allocates memory. + * + * Returns : JB_ERR_OK => Ok + * JB_ERR_PARSE => Parse error (line was trashed anyway) + * nonzero => Out of memory (line was trashed anyway) + * + *********************************************************************/ +jb_err get_actions(char *line, + struct action_alias * alias_list, + struct action_spec *cur_action) +{ + jb_err err; + init_action(cur_action); + cur_action->mask = ACTION_MASK_ALL; + + while (line) + { + char * option = NULL; + char * value = NULL; + + err = get_action_token(&line, &option, &value); + if (err) + { + return err; + } + + if (option) + { + /* handle option in 'option' */ + + /* Check for standard action name */ + const struct action_name * action = action_names; + + while ( (action->name != NULL) && (0 != strcmpic(action->name, option)) ) + { + action++; + } + if (action->name != NULL) + { + /* Found it */ + cur_action->mask &= action->mask; + cur_action->add &= action->mask; + cur_action->add |= action->add; + + switch (action->takes_value) + { + case AV_NONE: + /* ignore any option. */ + break; + case AV_ADD_STRING: + { + /* add single string. */ + + if ((value == NULL) || (*value == '\0')) + { + return JB_ERR_PARSE; + } + /* FIXME: should validate option string here */ + freez (cur_action->string[action->index]); + cur_action->string[action->index] = strdup(value); + if (NULL == cur_action->string[action->index]) + { + return JB_ERR_MEMORY; + } + break; + } + case AV_REM_STRING: + { + /* remove single string. */ + + freez (cur_action->string[action->index]); + break; + } + case AV_ADD_MULTI: + { + /* append multi string. */ + + struct list * remove_p = cur_action->multi_remove[action->index]; + struct list * add_p = cur_action->multi_add[action->index]; + + if ((value == NULL) || (*value == '\0')) + { + return JB_ERR_PARSE; + } + + list_remove_item(remove_p, value); + err = enlist_unique(add_p, value, 0); + if (err) + { + return err; + } + break; + } + case AV_REM_MULTI: + { + /* remove multi string. */ + + struct list * remove_p = cur_action->multi_remove[action->index]; + struct list * add_p = cur_action->multi_add[action->index]; + + if ( (value == NULL) || (*value == '\0') + || ((*value == '*') && (value[1] == '\0')) ) + { + /* + * no option, or option == "*". + * + * Remove *ALL*. + */ + list_remove_all(remove_p); + list_remove_all(add_p); + cur_action->multi_remove_all[action->index] = 1; + } + else + { + /* Valid option - remove only 1 option */ + + if ( !cur_action->multi_remove_all[action->index] ) + { + /* there isn't a catch-all in the remove list already */ + err = enlist_unique(remove_p, value, 0); + if (err) + { + return err; + } + } + list_remove_item(add_p, value); + } + break; + } + default: + /* Shouldn't get here unless there's memory corruption. */ + assert(0); + return JB_ERR_PARSE; + } + } + else + { + /* try user aliases. */ + const struct action_alias * alias = alias_list; + + while ( (alias != NULL) && (0 != strcmpic(alias->name, option)) ) + { + alias = alias->next; + } + if (alias != NULL) + { + /* Found it */ + merge_actions(cur_action, alias->action); + } + else + { + /* Bad action name */ + return JB_ERR_PARSE; + } + } + } + } + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : init_current_action + * + * Description : Zero out an action. + * + * Parameters : + * 1 : dest = An uninitialized current_action_spec. + * + * Returns : N/A + * + *********************************************************************/ +void init_current_action (struct current_action_spec *dest) +{ + memset(dest, '\0', sizeof(*dest)); + + dest->flags = ACTION_MOST_COMPATIBLE; +} + + +/********************************************************************* + * + * Function : init_action + * + * Description : Zero out an action. + * + * Parameters : + * 1 : dest = An uninitialized action_spec. + * + * Returns : N/A + * + *********************************************************************/ +void init_action (struct action_spec *dest) +{ + memset(dest, '\0', sizeof(*dest)); +} + + +/********************************************************************* + * + * Function : merge_current_action + * + * Description : Merge two actions together. + * Similar to "dest += src". + * Differences between this and merge_actions() + * is that this one doesn't allocate memory for + * strings (so "src" better be in memory for at least + * as long as "dest" is, and you'd better free + * "dest" using "free_current_action"). + * Also, there is no mask or remove lists in dest. + * (If we're applying it to a URL, we don't need them) + * + * Parameters : + * 1 : dest = Current actions, to modify. + * 2 : src = Action to add. + * + * Returns 0 : no error + * !=0 : error, probably JB_ERR_MEMORY. + * + *********************************************************************/ +jb_err merge_current_action (struct current_action_spec *dest, + const struct action_spec *src) +{ + int i; + jb_err err = JB_ERR_OK; + + dest->flags &= src->mask; + dest->flags |= src->add; + + for (i = 0; i < ACTION_STRING_COUNT; i++) + { + char * str = src->string[i]; + if (str) + { + str = strdup(str); + if (!str) + { + return JB_ERR_MEMORY; + } + freez(dest->string[i]); + dest->string[i] = str; + } + } + + for (i = 0; i < ACTION_MULTI_COUNT; i++) + { + if (src->multi_remove_all[i]) + { + /* Remove everything from dest, then add src->multi_add */ + err = list_duplicate(dest->multi[i], src->multi_add[i]); + if (err) + { + return err; + } + } + else + { + list_remove_list(dest->multi[i], src->multi_remove[i]); + err = list_append_list_unique(dest->multi[i], src->multi_add[i]); + if (err) + { + return err; + } + } + } + return err; +} + + +/********************************************************************* + * + * Function : free_current_action + * + * Description : Free memory used by a current_action_spec. + * Does not free the current_action_spec itself. + * + * Parameters : + * 1 : src = Source to free. + * + * Returns : N/A + * + *********************************************************************/ +void free_current_action (struct current_action_spec *src) +{ + int i; + + for (i = 0; i < ACTION_STRING_COUNT; i++) + { + freez(src->string[i]); + } + + for (i = 0; i < ACTION_MULTI_COUNT; i++) + { + destroy_list(src->multi[i]); + } + + memset(src, '\0', sizeof(*src)); +} + + +static struct file_list *current_actions_file[MAX_ACTION_FILES] = { + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL +}; + + +#ifdef FEATURE_GRACEFUL_TERMINATION +/********************************************************************* + * + * Function : unload_current_actions_file + * + * Description : Unloads current actions file - reset to state at + * beginning of program. + * + * Parameters : None + * + * Returns : N/A + * + *********************************************************************/ +void unload_current_actions_file(void) +{ + if (current_actions_file) + { + current_actions_file->unloader = unload_actions_file; + current_actions_file = NULL; + } +} +#endif /* FEATURE_GRACEFUL_TERMINATION */ + + +/********************************************************************* + * + * Function : unload_actions_file + * + * Description : Unloads an actions module. + * + * Parameters : + * 1 : file_data = the data structure associated with the + * actions file. + * + * Returns : N/A + * + *********************************************************************/ +void unload_actions_file(void *file_data) +{ + struct url_actions * next; + struct url_actions * cur = (struct url_actions *)file_data; + while (cur != NULL) + { + next = cur->next; + free_url_spec(cur->url); + free_action(cur->action); + freez(cur); + cur = next; + } + +} + + +/********************************************************************* + * + * Function : free_alias_list + * + * Description : Free memory used by a list of aliases. + * + * Parameters : + * 1 : alias_list = Linked list to free. + * + * Returns : N/A + * + *********************************************************************/ +void free_alias_list(struct action_alias *alias_list) +{ + while (alias_list != NULL) + { + struct action_alias * next = alias_list->next; + alias_list->next = NULL; + freez(alias_list->name); + free_action(alias_list->action); + free(alias_list); + alias_list = next; + } +} + + +/********************************************************************* + * + * Function : load_actions_file + * + * Description : Read and parse all the action files and add to files + * list. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : 0 => Ok, everything else is an error. + * + *********************************************************************/ +int load_actions_file(struct client_state *csp) +{ + int i; + int result; + + for (i = 0; i < MAX_ACTION_FILES; i++) + { + if (csp->config->actions_file[i]) + { + result = load_one_actions_file(csp, i); + if (result) + { + return result; + } + } + else if (current_actions_file[i]) + { + current_actions_file[i]->unloader = unload_actions_file; + current_actions_file[i] = NULL; + } + } + + return 0; +} + +/********************************************************************* + * + * Function : load_one_actions_file + * + * Description : Read and parse a action file and add to files + * list. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : fileid = File index to load. + * + * Returns : 0 => Ok, everything else is an error. + * + *********************************************************************/ +static int load_one_actions_file(struct client_state *csp, int fileid) +{ + + /* + * Parser mode. + * Note: Keep these in the order they occur in the file, they are + * sometimes tested with <= + */ +#define MODE_START_OF_FILE 1 +#define MODE_SETTINGS 2 +#define MODE_DESCRIPTION 3 +#define MODE_ALIAS 4 +#define MODE_ACTIONS 5 + + int mode = MODE_START_OF_FILE; + + FILE *fp; + struct url_actions *last_perm; + struct url_actions *perm; + char buf[BUFFER_SIZE]; + struct file_list *fs; + struct action_spec * cur_action = NULL; + int cur_action_used = 0; + struct action_alias * alias_list = NULL; + unsigned long linenum = 0; + + if (!check_file_changed(current_actions_file[fileid], csp->config->actions_file[fileid], &fs)) + { + /* No need to load */ + csp->actions_list[fileid] = current_actions_file[fileid]; + return 0; + } + if (!fs) + { + log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': error finding file: %E", + csp->config->actions_file[fileid]); + return 1; /* never get here */ + } + + fs->f = last_perm = (struct url_actions *)zalloc(sizeof(*last_perm)); + if (last_perm == NULL) + { + log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': out of memory!", + csp->config->actions_file[fileid]); + return 1; /* never get here */ + } + + if ((fp = fopen(csp->config->actions_file[fileid], "r")) == NULL) + { + log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': error opening file: %E", + csp->config->actions_file[fileid]); + return 1; /* never get here */ + } + + while (read_config_line(buf, sizeof(buf), fp, &linenum) != NULL) + { + if (*buf == '{') + { + /* It's a header block */ + if (buf[1] == '{') + { + /* It's {{settings}} or {{alias}} */ + int len = strlen(buf); + char * start = buf + 2; + char * end = buf + len - 1; + if ((len < 5) || (*end-- != '}') || (*end-- != '}')) + { + /* too short */ + fclose(fp); + log_error(LOG_LEVEL_FATAL, + "can't load actions file '%s': invalid line (%lu): %s", + csp->config->actions_file[fileid], linenum, buf); + return 1; /* never get here */ + } + + /* Trim leading and trailing whitespace. */ + end[1] = '\0'; + chomp(start); + + if (*start == '\0') + { + /* too short */ + fclose(fp); + log_error(LOG_LEVEL_FATAL, + "can't load actions file '%s': invalid line (%lu): {{ }}", + csp->config->actions_file[fileid], linenum); + return 1; /* never get here */ + } + + /* + * An actionsfile can optionally contain the following blocks. + * They *MUST* be in this order, to simplify processing: + * + * {{settings}} + * name=value... + * + * {{description}} + * ...free text, format TBD, but no line may start with a '{'... + * + * {{alias}} + * name=actions... + * + * The actual actions must be *after* these special blocks. + * None of these special blocks may be repeated. + * + */ + if (0 == strcmpic(start, "settings")) + { + /* it's a {{settings}} block */ + if (mode >= MODE_SETTINGS) + { + /* {{settings}} must be first thing in file and must only + * appear once. + */ + fclose(fp); + log_error(LOG_LEVEL_FATAL, + "can't load actions file '%s': line %lu: {{settings}} must only appear once, and it must be before anything else.", + csp->config->actions_file[fileid], linenum); + } + mode = MODE_SETTINGS; + } + else if (0 == strcmpic(start, "description")) + { + /* it's a {{description}} block */ + if (mode >= MODE_DESCRIPTION) + { + /* {{description}} is a singleton and only {{settings}} may proceed it + */ + fclose(fp); + log_error(LOG_LEVEL_FATAL, + "can't load actions file '%s': line %lu: {{description}} must only appear once, and only a {{settings}} block may be above it.", + csp->config->actions_file[fileid], linenum); + } + mode = MODE_DESCRIPTION; + } + else if (0 == strcmpic(start, "alias")) + { + /* it's an {{alias}} block */ + if (mode >= MODE_ALIAS) + { + /* {{alias}} must be first thing in file, possibly after + * {{settings}} and {{description}} + * + * {{alias}} must only appear once. + * + * Note that these are new restrictions introduced in + * v2.9.10 in order to make actionsfile editing simpler. + * (Otherwise, reordering actionsfile entries without + * completely rewriting the file becomes non-trivial) + */ + fclose(fp); + log_error(LOG_LEVEL_FATAL, + "can't load actions file '%s': line %lu: {{alias}} must only appear once, and it must be before all actions.", + csp->config->actions_file[fileid], linenum); + } + mode = MODE_ALIAS; + } + else + { + /* invalid {{something}} block */ + fclose(fp); + log_error(LOG_LEVEL_FATAL, + "can't load actions file '%s': invalid line (%lu): {{%s}}", + csp->config->actions_file[fileid], linenum, start); + return 1; /* never get here */ + } + } + else + { + /* It's an actions block */ + + char actions_buf[BUFFER_SIZE]; + char * end; + + /* set mode */ + mode = MODE_ACTIONS; + + /* free old action */ + if (cur_action) + { + if (!cur_action_used) + { + free_action(cur_action); + free(cur_action); + } + cur_action = NULL; + } + cur_action_used = 0; + cur_action = (struct action_spec *)zalloc(sizeof(*cur_action)); + if (cur_action == NULL) + { + fclose(fp); + log_error(LOG_LEVEL_FATAL, + "can't load actions file '%s': out of memory", + csp->config->actions_file[fileid]); + return 1; /* never get here */ + } + init_action(cur_action); + + /* trim { */ + strcpy(actions_buf, buf + 1); + + /* check we have a trailing } and then trim it */ + end = actions_buf + strlen(actions_buf) - 1; + if (*end != '}') + { + /* No closing } */ + fclose(fp); + log_error(LOG_LEVEL_FATAL, + "can't load actions file '%s': invalid line (%lu): %s", + csp->config->actions_file[fileid], linenum, buf); + return 1; /* never get here */ + } + *end = '\0'; + + /* trim any whitespace immediately inside {} */ + chomp(actions_buf); + + if (get_actions(actions_buf, alias_list, cur_action)) + { + /* error */ + fclose(fp); + log_error(LOG_LEVEL_FATAL, + "can't load actions file '%s': invalid line (%lu): %s", + csp->config->actions_file[fileid], linenum, buf); + return 1; /* never get here */ + } + } + } + else if (mode == MODE_SETTINGS) + { + /* + * Part of the {{settings}} block. + * Ignore for now, but we may want to read & check permissions + * when we go multi-user. + */ + } + else if (mode == MODE_DESCRIPTION) + { + /* + * Part of the {{description}} block. + * Ignore for now. + */ + } + else if (mode == MODE_ALIAS) + { + /* + * define an alias + */ + char actions_buf[BUFFER_SIZE]; + struct action_alias * new_alias; + + char * start = strchr(buf, '='); + char * end = start; + + if ((start == NULL) || (start == buf)) + { + log_error(LOG_LEVEL_FATAL, + "can't load actions file '%s': invalid alias line (%lu): %s", + csp->config->actions_file[fileid], linenum, buf); + return 1; /* never get here */ + } + + if ((new_alias = zalloc(sizeof(*new_alias))) == NULL) + { + fclose(fp); + log_error(LOG_LEVEL_FATAL, + "can't load actions file '%s': out of memory!", + csp->config->actions_file[fileid]); + return 1; /* never get here */ + } + + /* Eat any the whitespace before the '=' */ + end--; + while ((*end == ' ') || (*end == '\t')) + { + /* + * we already know we must have at least 1 non-ws char + * at start of buf - no need to check + */ + end--; + } + end[1] = '\0'; + + /* Eat any the whitespace after the '=' */ + start++; + while ((*start == ' ') || (*start == '\t')) + { + start++; + } + if (*start == '\0') + { + log_error(LOG_LEVEL_FATAL, + "can't load actions file '%s': invalid alias line (%lu): %s", + csp->config->actions_file[fileid], linenum, buf); + return 1; /* never get here */ + } + + new_alias->name = strdup(buf); + + strcpy(actions_buf, start); + + if (get_actions(actions_buf, alias_list, new_alias->action)) + { + /* error */ + fclose(fp); + log_error(LOG_LEVEL_FATAL, + "can't load actions file '%s': invalid alias line (%lu): %s = %s", + csp->config->actions_file[fileid], linenum, buf, start); + return 1; /* never get here */ + } + + /* add to list */ + new_alias->next = alias_list; + alias_list = new_alias; + } + else if (mode == MODE_ACTIONS) + { + /* it's a URL pattern */ + + /* allocate a new node */ + if ((perm = zalloc(sizeof(*perm))) == NULL) + { + fclose(fp); + log_error(LOG_LEVEL_FATAL, + "can't load actions file '%s': out of memory!", + csp->config->actions_file[fileid]); + return 1; /* never get here */ + } + + /* Save flags */ + copy_action (perm->action, cur_action); + + /* Save the URL pattern */ + if (create_url_spec(perm->url, buf)) + { + fclose(fp); + log_error(LOG_LEVEL_FATAL, + "can't load actions file '%s': line %lu: cannot create URL pattern from: %s", + csp->config->actions_file[fileid], linenum, buf); + return 1; /* never get here */ + } + + /* add it to the list */ + last_perm->next = perm; + last_perm = perm; + } + else if (mode == MODE_START_OF_FILE) + { + /* oops - please have a {} line as 1st line in file. */ + fclose(fp); + log_error(LOG_LEVEL_FATAL, + "can't load actions file '%s': first needed line (%lu) is invalid: %s", + csp->config->actions_file[fileid], linenum, buf); + return 1; /* never get here */ + } + else + { + /* How did we get here? This is impossible! */ + fclose(fp); + log_error(LOG_LEVEL_FATAL, + "can't load actions file '%s': INTERNAL ERROR - mode = %d", + csp->config->actions_file[fileid], mode); + return 1; /* never get here */ + } + } + + fclose(fp); + + free_action(cur_action); + + free_alias_list(alias_list); + + /* the old one is now obsolete */ + if (current_actions_file[fileid]) + { + current_actions_file[fileid]->unloader = unload_actions_file; + } + + fs->next = files->next; + files->next = fs; + current_actions_file[fileid] = fs; + + csp->actions_list[fileid] = fs; + + return(0); + +} + + +/********************************************************************* + * + * Function : actions_to_text + * + * Description : Converts a actionsfile entry from the internal + * structurt into a text line. The output is split + * into one line for each action with line continuation. + * + * Parameters : + * 1 : action = The action to format. + * + * Returns : A string. Caller must free it. + * NULL on out-of-memory error. + * + *********************************************************************/ +char * actions_to_text(struct action_spec *action) +{ + unsigned mask = action->mask; + unsigned add = action->add; + char * result = strdup(""); + struct list_entry * lst; + + /* sanity - prevents "-feature +feature" */ + mask |= add; + + +#define DEFINE_ACTION_BOOL(__name, __bit) \ + if (!(mask & __bit)) \ + { \ + string_append(&result, " -" __name " \\\n"); \ + } \ + else if (add & __bit) \ + { \ + string_append(&result, " +" __name " \\\n"); \ + } + +#define DEFINE_ACTION_STRING(__name, __bit, __index) \ + if (!(mask & __bit)) \ + { \ + string_append(&result, " -" __name " \\\n"); \ + } \ + else if (add & __bit) \ + { \ + string_append(&result, " +" __name "{"); \ + string_append(&result, action->string[__index]); \ + string_append(&result, "} \\\n"); \ + } + +#define DEFINE_ACTION_MULTI(__name, __index) \ + if (action->multi_remove_all[__index]) \ + { \ + string_append(&result, " -" __name " \\\n"); \ + } \ + else \ + { \ + lst = action->multi_remove[__index]->first; \ + while (lst) \ + { \ + string_append(&result, " -" __name "{"); \ + string_append(&result, lst->str); \ + string_append(&result, "} \\\n"); \ + lst = lst->next; \ + } \ + } \ + lst = action->multi_add[__index]->first; \ + while (lst) \ + { \ + string_append(&result, " +" __name "{"); \ + string_append(&result, lst->str); \ + string_append(&result, "} \\\n"); \ + lst = lst->next; \ + } + +#define DEFINE_ACTION_ALIAS 0 /* No aliases for output */ + +#include "actionlist.h" + +#undef DEFINE_ACTION_MULTI +#undef DEFINE_ACTION_STRING +#undef DEFINE_ACTION_BOOL +#undef DEFINE_ACTION_ALIAS + + return result; +} + + +/********************************************************************* + * + * Function : actions_to_html + * + * Description : Converts a actionsfile entry from numeric form + * ("mask" and "add") to a
-seperated HTML string + * in which each action is linked to its chapter in + * the user manual. + * + * Parameters : + * 1 : csp = Client state (for config) + * 2 : action = Action spec to be converted + * + * Returns : A string. Caller must free it. + * NULL on out-of-memory error. + * + *********************************************************************/ +char * actions_to_html(struct client_state *csp, + struct action_spec *action) +{ + unsigned mask = action->mask; + unsigned add = action->add; + char * result = strdup(""); + struct list_entry * lst; + + /* sanity - prevents "-feature +feature" */ + mask |= add; + + +#define DEFINE_ACTION_BOOL(__name, __bit) \ + if (!(mask & __bit)) \ + { \ + string_append(&result, "\n
-"); \ + string_join(&result, add_help_link(__name, csp->config)); \ + } \ + else if (add & __bit) \ + { \ + string_append(&result, "\n
+"); \ + string_join(&result, add_help_link(__name, csp->config)); \ + } + +#define DEFINE_ACTION_STRING(__name, __bit, __index) \ + if (!(mask & __bit)) \ + { \ + string_append(&result, "\n
-"); \ + string_join(&result, add_help_link(__name, csp->config)); \ + } \ + else if (add & __bit) \ + { \ + string_append(&result, "\n
+"); \ + string_join(&result, add_help_link(__name, csp->config)); \ + string_append(&result, "{"); \ + string_join(&result, html_encode(action->string[__index])); \ + string_append(&result, "}"); \ + } + +#define DEFINE_ACTION_MULTI(__name, __index) \ + if (action->multi_remove_all[__index]) \ + { \ + string_append(&result, "\n
-"); \ + string_join(&result, add_help_link(__name, csp->config)); \ + } \ + else \ + { \ + lst = action->multi_remove[__index]->first; \ + while (lst) \ + { \ + string_append(&result, "\n
-"); \ + string_join(&result, add_help_link(__name, csp->config)); \ + string_append(&result, "{"); \ + string_join(&result, html_encode(lst->str)); \ + string_append(&result, "}"); \ + lst = lst->next; \ + } \ + } \ + lst = action->multi_add[__index]->first; \ + while (lst) \ + { \ + string_append(&result, "\n
+"); \ + string_join(&result, add_help_link(__name, csp->config)); \ + string_append(&result, "{"); \ + string_join(&result, html_encode(lst->str)); \ + string_append(&result, "}"); \ + lst = lst->next; \ + } + +#define DEFINE_ACTION_ALIAS 0 /* No aliases for output */ + +#include "actionlist.h" + +#undef DEFINE_ACTION_MULTI +#undef DEFINE_ACTION_STRING +#undef DEFINE_ACTION_BOOL +#undef DEFINE_ACTION_ALIAS + + /* trim leading
*/ + if (result && *result) + { + char * s = result; + result = strdup(result + 5); + free(s); + } + + return result; +} + + +/********************************************************************* + * + * Function : current_actions_to_html + * + * Description : Converts a curren action spec to a
seperated HTML + * text in which each action is linked to its chapter in + * the user manual. + * + * Parameters : + * 1 : csp = Client state (for config) + * 2 : action = Current action spec to be converted + * + * Returns : A string. Caller must free it. + * NULL on out-of-memory error. + * + *********************************************************************/ +char *current_action_to_html(struct client_state *csp, + struct current_action_spec *action) +{ + unsigned long flags = action->flags; + char * result = strdup(""); + struct list_entry * lst; + +#define DEFINE_ACTION_BOOL(__name, __bit) \ + if (flags & __bit) \ + { \ + string_append(&result, "\n
+"); \ + string_join(&result, add_help_link(__name, csp->config)); \ + } \ + else \ + { \ + string_append(&result, "\n
-"); \ + string_join(&result, add_help_link(__name, csp->config)); \ + } + +#define DEFINE_ACTION_STRING(__name, __bit, __index) \ + if (flags & __bit) \ + { \ + string_append(&result, "\n
+"); \ + string_join(&result, add_help_link(__name, csp->config)); \ + string_append(&result, "{"); \ + string_join(&result, html_encode(action->string[__index])); \ + string_append(&result, "}"); \ + } \ + else \ + { \ + string_append(&result, "\n
-"); \ + string_join(&result, add_help_link(__name, csp->config)); \ + } + +#define DEFINE_ACTION_MULTI(__name, __index) \ + lst = action->multi[__index]->first; \ + if (lst == NULL) \ + { \ + string_append(&result, "\n
-"); \ + string_join(&result, add_help_link(__name, csp->config)); \ + } \ + else \ + { \ + while (lst) \ + { \ + string_append(&result, "\n
+"); \ + string_join(&result, add_help_link(__name, csp->config)); \ + string_append(&result, "{"); \ + string_join(&result, html_encode(lst->str)); \ + string_append(&result, "}"); \ + lst = lst->next; \ + } \ + } + +#define DEFINE_ACTION_ALIAS 0 /* No aliases for output */ + +#include "actionlist.h" + +#undef DEFINE_ACTION_MULTI +#undef DEFINE_ACTION_STRING +#undef DEFINE_ACTION_BOOL +#undef DEFINE_ACTION_ALIAS + + return result; +} diff --git a/src/actions.h b/src/actions.h new file mode 100644 index 00000000..07cf2ed2 --- /dev/null +++ b/src/actions.h @@ -0,0 +1,161 @@ +#ifndef ACTIONS_H_INCLUDED +#define ACTIONS_H_INCLUDED +#define ACTIONS_H_VERSION "$Id: actions.h,v 1.12 2002/05/06 07:56:50 oes Exp $" +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/actions.h,v $ + * + * Purpose : Declares functions to work with actions files + * Functions declared include: FIXME + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: actions.h,v $ + * Revision 1.12 2002/05/06 07:56:50 oes + * Made actions_to_html independent of FEATURE_CGI_EDIT_ACTIONS + * + * Revision 1.11 2002/04/30 11:14:52 oes + * Made csp the first parameter in *action_to_html + * + * Revision 1.10 2002/04/26 12:53:33 oes + * - actions_to_html signature change + * - current_action_to_text: renamed to current_action_to_html + * and signature change + * + * Revision 1.9 2002/03/26 22:29:54 swa + * we have a new homepage! + * + * Revision 1.8 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.7 2002/03/16 23:54:06 jongfoster + * Adding graceful termination feature, to help look for memory leaks. + * If you enable this (which, by design, has to be done by hand + * editing config.h) and then go to http://i.j.b/die, then the program + * will exit cleanly after the *next* request. It should free all the + * memory that was used. + * + * Revision 1.6 2001/10/23 21:30:30 jongfoster + * Adding error-checking to selected functions. + * + * Revision 1.5 2001/10/14 21:58:22 jongfoster + * Adding support for the CGI-based editor: + * - Exported get_actions() + * - Added new function free_alias_list() + * - Added support for {{settings}} and {{description}} blocks + * in the actions file. They are currently ignored. + * - Added restriction to only one {{alias}} block which must appear + * first in the file, to simplify the editor's rewriting rules. + * - Note that load_actions_file() is no longer used by the CGI-based + * editor, but some of the other routines in this file are. + * + * Revision 1.4 2001/09/16 15:47:37 jongfoster + * First version of CGI-based edit interface. This is very much a + * work-in-progress, and you can't actually use it to edit anything + * yet. You must #define FEATURE_CGI_EDIT_ACTIONS for these changes + * to have any effect. + * + * Revision 1.3 2001/09/14 00:17:32 jongfoster + * Tidying up memory allocation. New function init_action(). + * + * Revision 1.2 2001/07/29 19:01:11 jongfoster + * Changed _FILENAME_H to FILENAME_H_INCLUDED. + * Added forward declarations for needed structures. + * + * Revision 1.1 2001/05/31 21:16:46 jongfoster + * Moved functions to process the action list into this new file. + * + * + *********************************************************************/ + + +#ifdef __cplusplus +extern "C" { +#endif + + +struct action_spec; +struct current_action_spec; +struct client_state; + + + +/* This structure is used to hold user-defined aliases */ +struct action_alias +{ + const char * name; + struct action_spec action[1]; + struct action_alias * next; +}; + + +extern jb_err get_actions (char *line, + struct action_alias * alias_list, + struct action_spec *cur_action); +extern void free_alias_list(struct action_alias *alias_list); + +extern void init_action(struct action_spec *dest); +extern void free_action(struct action_spec *src); +extern jb_err merge_actions (struct action_spec *dest, + const struct action_spec *src); +extern jb_err copy_action (struct action_spec *dest, + const struct action_spec *src); +extern char * actions_to_text (struct action_spec *action); +extern char * actions_to_html (struct client_state *csp, + struct action_spec *action); +extern void init_current_action (struct current_action_spec *dest); +extern void free_current_action (struct current_action_spec *src); +extern jb_err merge_current_action (struct current_action_spec *dest, + const struct action_spec *src); +extern char * current_action_to_html(struct client_state *csp, + struct current_action_spec *action); + +extern jb_err get_action_token(char **line, char **name, char **value); +extern void unload_actions_file(void *file_data); +extern int load_actions_file(struct client_state *csp); + +#ifdef FEATURE_GRACEFUL_TERMINATION +void unload_current_actions_file(void); +#endif + + +/* Revision control strings from this header and associated .c file */ +extern const char actions_rcs[]; +extern const char actions_h_rcs[]; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ndef ACTIONS_H_INCLUDED */ + +/* + Local Variables: + tab-width: 3 + end: +*/ + diff --git a/src/amiga.c b/src/amiga.c new file mode 100644 index 00000000..6964eabc --- /dev/null +++ b/src/amiga.c @@ -0,0 +1,286 @@ +const char amiga_rcs[] = "$Id: amiga.c,v 1.9 2002/03/26 22:29:54 swa Exp $"; +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/amiga.c,v $ + * + * Purpose : Amiga-specific declarations. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: amiga.c,v $ + * Revision 1.9 2002/03/26 22:29:54 swa + * we have a new homepage! + * + * Revision 1.8 2002/03/25 19:32:15 joergs + * Name in version string changed from junkbuster to Privoxy. + * + * Revision 1.7 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.6 2002/03/09 20:03:52 jongfoster + * - Making various functions return int rather than size_t. + * (Undoing a recent change). Since size_t is unsigned on + * Windows, functions like read_socket that return -1 on + * error cannot return a size_t. + * + * THIS WAS A MAJOR BUG - it caused frequent, unpredictable + * crashes, and also frequently caused JB to jump to 100% + * CPU and stay there. (Because it thought it had just + * read ((unsigned)-1) == 4Gb of data...) + * + * - The signature of write_socket has changed, it now simply + * returns success=0/failure=nonzero. + * + * - Trying to get rid of a few warnings --with-debug on + * Windows, I've introduced a new type "jb_socket". This is + * used for the socket file descriptors. On Windows, this + * is SOCKET (a typedef for unsigned). Everywhere else, it's + * an int. The error value can't be -1 any more, so it's + * now JB_INVALID_SOCKET (which is -1 on UNIX, and in + * Windows it maps to the #define INVALID_SOCKET.) + * + * - The signature of bind_port has changed. + * + * Revision 1.5 2002/03/03 09:18:03 joergs + * Made jumbjuster work on AmigaOS again. + * + * Revision 1.4 2001/10/07 15:35:13 oes + * Replaced 6 boolean members of csp with one bitmap (csp->flags) + * + * Revision 1.3 2001/09/12 22:54:51 joergs + * Stacksize of main thread increased. + * + * Revision 1.2 2001/05/23 00:13:58 joergs + * AmigaOS support fixed. + * + * Revision 1.1.1.1 2001/05/15 13:58:46 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#include "config.h" + +#ifdef AMIGA + +#include +#include + +#include "project.h" + +const char amiga_h_rcs[] = AMIGA_H_VERSION; + +unsigned long __stack = 100*1024; +static char ver[] = "$VER: Privoxy " __AMIGAVERSION__ " (" __AMIGADATE__ ")"; +struct Task *main_task = NULL; +int childs = 0; + +void serve(struct client_state *csp); + +__saveds ULONG server_thread(void) +{ + struct client_state *local_csp; + struct UserData UserData; + struct Task *me=FindTask(NULL); + + Wait(SIGF_SINGLE); + local_csp=(struct client_state *)(me->tc_UserData); + me->tc_UserData=&UserData; + SocketBase=(APTR)OpenLibrary("bsdsocket.library",3); + if(SocketBase) + { + SetErrnoPtr(&(UserData.eno),sizeof(int)); + local_csp->cfd=ObtainSocket(local_csp->cfd, AF_INET, SOCK_STREAM, 0); + if(JB_INVALID_SOCKET!=local_csp->cfd) + { + Signal(main_task,SIGF_SINGLE); + serve((struct client_state *) local_csp); + } else { + local_csp->flags &= ~CSP_FLAG_ACTIVE; + Signal(main_task,SIGF_SINGLE); + } + CloseLibrary(SocketBase); + } else { + local_csp->flags &= ~CSP_FLAG_ACTIVE; + Signal(main_task,SIGF_SINGLE); + } + childs--; + return 0; +} + +static BPTR olddir; + +void amiga_exit(void) +{ + if(SocketBase) + { + CloseLibrary(SocketBase); + } + CurrentDir(olddir); +} + +static struct SignalSemaphore memsem; +static struct SignalSemaphore *memsemptr = NULL; +static struct UserData GlobalUserData; + +void InitAmiga(void) +{ + main_task = FindTask(NULL); + main_task->tc_UserData = &GlobalUserData; + + if (((struct Library *)SysBase)->lib_Version < 39) + { + exit(RETURN_FAIL); + } + + signal(SIGINT,SIG_IGN); + SocketBase = (APTR)OpenLibrary("bsdsocket.library",3); + if (!SocketBase) + { + fprintf(stderr, "Can't open bsdsocket.library V3+\n"); + exit(RETURN_ERROR); + } + SetErrnoPtr(&(GlobalUserData.eno),sizeof(int)); + InitSemaphore(&memsem); + memsemptr = &memsem; + + olddir=CurrentDir(GetProgramDir()); + atexit(amiga_exit); +} + +#ifdef __GNUC__ +#ifdef libnix +/* multithreadingsafe libnix replacements */ +static void *memPool=NULL; + +void *malloc (size_t s) +{ + ULONG *mem; + LONG size = s; + + if (size<=0) + { + return NULL; + } + if (!memPool) + { + if (!(memPool=CreatePool(MEMF_ANY,32*1024,8*1024))) + { + return NULL; + } + } + size += sizeof(ULONG) + MEM_BLOCKMASK; + size &= ~MEM_BLOCKMASK; + if (memsemptr) + { + ObtainSemaphore(memsemptr); + } + if ((mem=AllocPooled(memPool,size))) + { + *mem++=size; + } + if (memsemptr) + { + ReleaseSemaphore(memsemptr); + } + return mem; +} + +void free (void *m) +{ + ULONG *mem = m; + + if(mem && memPool) + { + ULONG size=*--mem; + + if (memsemptr) + { + ObtainSemaphore(memsemptr); + } + FreePooled(memPool,mem,size); + if (memsemptr) + { + ReleaseSemaphore(memsemptr); + } + } +} + +void *realloc (void *old, size_t ns) +{ + void *new; + LONG osize, *o = old; + LONG nsize = ns; + + if (!old) + { + return malloc(nsize); + } + osize = (*(o-1)) - sizeof(ULONG); + if (nsize <= osize) + { + return old; + } + if ((new = malloc(nsize))) + { + ULONG *n = new; + + osize >>= 2; + while(osize--) + { + *n++ = *o++; + } + free(old); + } + return new; +} + +void __memCleanUp (void) +{ + if (memsemptr) + { + ObtainSemaphore(memsemptr); + } + if (memPool) + { + DeletePool(memPool); + } + if (memsemptr) + { + ReleaseSemaphore(memsemptr); + } +} + +#define ADD2LIST(a,b,c) asm(".stabs \"_" #b "\"," #c ",0,0,_" #a ) +#define ADD2EXIT(a,pri) ADD2LIST(a,__EXIT_LIST__,22); \ + asm(".stabs \"___EXIT_LIST__\",20,0,0," #pri "+128") +ADD2EXIT(__memCleanUp,-50); +#elif !defined(ixemul) +#error No libnix and no ixemul!? +#endif /* libnix */ +#else +#error Only GCC is supported, multithreading safe malloc/free required. +#endif /* __GNUC__ */ + +#endif /* def AMIGA */ diff --git a/src/amiga.h b/src/amiga.h new file mode 100644 index 00000000..b52a551c --- /dev/null +++ b/src/amiga.h @@ -0,0 +1,126 @@ +#ifdef AMIGA +#ifndef AMIGA_H_INCLUDED +#define AMIGA_H_INCLUDED +#define AMIGA_H_VERSION "$Id: amiga.h,v 1.9 2002/03/26 22:29:54 swa Exp $" +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/amiga.h,v $ + * + * Purpose : Amiga-specific declarations. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: amiga.h,v $ + * Revision 1.9 2002/03/26 22:29:54 swa + * we have a new homepage! + * + * Revision 1.8 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.7 2002/03/03 09:18:03 joergs + * Made jumbjuster work on AmigaOS again. + * + * Revision 1.6 2001/10/13 12:46:08 joergs + * Added #undef EINTR to avoid warnings + * + * Revision 1.5 2001/07/29 18:43:08 jongfoster + * Changing #ifdef _FILENAME_H to FILENAME_H_INCLUDED, to conform to + * ANSI C rules. + * + * Revision 1.4 2001/05/29 20:05:06 joergs + * Fixed exit() macro not exiting if called before InitAmiga() + * (junkbuster --help and --version). + * + * Revision 1.3 2001/05/25 21:53:27 jongfoster + * Fixing indentation + * + * Revision 1.2 2001/05/23 00:13:58 joergs + * AmigaOS support fixed. + * + * Revision 1.1.1.1 2001/05/15 13:58:46 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#define _KERNEL +#include +#undef _KERNEL + +#define __NOLIBBASE__ +#include +#undef __NOLIBBASE__ + +#define __CONSTLIBBASEDECL__ const +#define DEVICES_TIMER_H +#include +#include +#include +#include + +struct UserData +{ + struct Library *sb; + int eno; +}; + +#define SocketBase ((struct Library *)(((struct UserData *)(FindTask(NULL)->tc_UserData))->sb)) +#define errno (((struct UserData *)(FindTask(NULL)->tc_UserData))->eno) +#define select(a,b,c,d,e) WaitSelect(a,b,c,d,e,NULL) +#define inet_ntoa(x) Inet_NtoA(x.s_addr) + +extern int childs; +extern struct Task *main_task; + +void InitAmiga(void); +void amiga_exit(void); +void __memCleanUp(void); +__saveds ULONG server_thread(void); + +#define exit(x) \ +{ \ + if(main_task) \ + { \ + if(main_task == FindTask(NULL)) \ + { \ + while(childs) Delay(10*TICKS_PER_SECOND); exit(x); \ + } \ + else \ + { \ + CloseLibrary(SocketBase); \ + childs--; \ + RemTask(NULL); \ + } \ + } \ + else \ + { \ + exit(x); \ + } \ +} + +#undef EINTR +#define EINTR 0 + +#endif /* ndef AMIGA_H_INCLUDED */ +#endif /* def AMIGA */ diff --git a/src/cgi.c b/src/cgi.c new file mode 100644 index 00000000..e20d8bf6 --- /dev/null +++ b/src/cgi.c @@ -0,0 +1,2178 @@ +const char cgi_rcs[] = "$Id: cgi.c,v 1.70 2002/05/19 11:33:20 jongfoster Exp $"; +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/cgi.c,v $ + * + * Purpose : Declares functions to intercept request, generate + * html or gif answers, and to compose HTTP resonses. + * This only contains the framework functions, the + * actual handler functions are declared elsewhere. + * + * Functions declared include: + * + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: cgi.c,v $ + * Revision 1.70 2002/05/19 11:33:20 jongfoster + * If a CGI error was not handled, and propogated back to + * dispatch_known_cgi(), then it was assumed to be "out of memory". + * This gave a very misleading error message. + * + * Now other errors will cause a simple message giving the error + * number and asking the user to report a bug. + * + * Bug report: + * http://sourceforge.net/tracker/index.php?func=detail + * &aid=557905&group_id=11118&atid=111118 + * + * Revision 1.69 2002/05/14 21:28:40 oes + * - Fixed add_help_link to link to the (now split) actions + * part of the config chapter + * - Renamed helplink export to actions-help-prefix + * + * Revision 1.68 2002/05/12 21:36:29 jongfoster + * Correcting function comments + * + * Revision 1.67 2002/04/30 12:02:07 oes + * Nit: updated a comment + * + * Revision 1.66 2002/04/26 18:32:57 jongfoster + * Fixing a memory leak on error + * + * Revision 1.65 2002/04/26 12:53:51 oes + * - New function add_help_link + * - default_exports now exports links to the user manual + * and a prefix for links into the config chapter + * + * Revision 1.64 2002/04/24 02:17:21 oes + * - Better descriptions for CGIs + * - Hide edit-actions, more shortcuts + * - Moved get_char_param, get_string_param and get_number_param here + * from cgiedit.c + * + * Revision 1.63 2002/04/15 19:06:43 jongfoster + * Typos + * + * Revision 1.62 2002/04/10 19:59:46 jongfoster + * Fixes to #include in templates: + * - Didn't close main file if loading an included template fails. + * - I'm paranoid and want to disallow "#include /etc/passwd". + * + * Revision 1.61 2002/04/10 13:37:48 oes + * Made templates modular: template_load now recursive with max depth 1 + * + * Revision 1.60 2002/04/08 20:50:25 swa + * fixed JB spelling + * + * Revision 1.59 2002/04/05 15:51:51 oes + * - added send-stylesheet CGI + * - bugfix: error-pages now get correct request protocol + * - fixed + * - kludged CGI descriptions and menu not to break JS syntax + * + * Revision 1.58 2002/03/29 03:33:13 david__schmidt + * Fix Mac OSX compiler warnings + * + * Revision 1.57 2002/03/26 22:29:54 swa + * we have a new homepage! + * + * Revision 1.56 2002/03/24 17:50:46 jongfoster + * Fixing compile error if actions file editor disabled + * + * Revision 1.55 2002/03/24 16:55:06 oes + * Making GIF checkerboard transparent + * + * Revision 1.54 2002/03/24 16:18:15 jongfoster + * Removing old logo + * + * Revision 1.53 2002/03/24 16:06:00 oes + * Correct transparency for checkerboard PNG. Thanks, Magnus! + * + * Revision 1.52 2002/03/24 15:23:33 jongfoster + * Name changes + * + * Revision 1.51 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.50 2002/03/16 23:54:06 jongfoster + * Adding graceful termination feature, to help look for memory leaks. + * If you enable this (which, by design, has to be done by hand + * editing config.h) and then go to http://i.j.b/die, then the program + * will exit cleanly after the *next* request. It should free all the + * memory that was used. + * + * Revision 1.49 2002/03/13 00:27:04 jongfoster + * Killing warnings + * + * Revision 1.48 2002/03/08 17:47:07 jongfoster + * Adding comments + * + * Revision 1.47 2002/03/08 16:41:33 oes + * Added GIF images again + * + * Revision 1.46 2002/03/07 03:48:38 oes + * - Changed built-in images from GIF to PNG + * (with regard to Unisys patent issue) + * - Added a 4x4 pattern PNG which is less intrusive + * than the logo but also clearly marks the deleted banners + * + * Revision 1.45 2002/03/06 22:54:35 jongfoster + * Automated function-comment nitpicking. + * + * Revision 1.44 2002/03/05 22:43:45 david__schmidt + * - Better error reporting on OS/2 + * - Fix double-slash comment (oops) + * + * Revision 1.43 2002/03/05 21:33:45 david__schmidt + * - Re-enable OS/2 building after new parms were added + * - Fix false out of memory report when resolving CGI templates when no IP + * address is available of failed attempt (a la no such domain) + * + * Revision 1.42 2002/01/21 00:33:20 jongfoster + * Replacing strsav() with the safer string_append() or string_join(). + * Adding map_block_keep() to save a few bytes in the edit-actions-list HTML. + * Adding missing html_encode() to error message generators. + * Adding edit-actions-section-swap and many "shortcuts" to the list of CGIs. + * + * Revision 1.41 2002/01/17 20:56:22 jongfoster + * Replacing hard references to the URL of the config interface + * with #defines from project.h + * + * Revision 1.40 2002/01/09 14:26:46 oes + * Added support for thread-safe gmtime_r call. + * + * Revision 1.39 2001/11/16 00:48:13 jongfoster + * Fixing a compiler warning + * + * Revision 1.38 2001/11/13 00:31:21 jongfoster + * - Adding new CGIs for use by non-JavaScript browsers: + * edit-actions-url-form + * edit-actions-add-url-form + * edit-actions-remove-url-form + * - Fixing make_menu()'s HTML generation - it now quotes the href parameter. + * - Fixing || bug. + * + * Revision 1.37 2001/11/01 14:28:47 david__schmidt + * Show enablement/disablement status in almost all templates. + * There is a little trickiness here: apparent recursive resolution of + * @if-enabled-then@ caused the toggle template to show status out-of-phase with + * the actual enablement status. So a similar construct, + * @if-enabled-display-then@, is used to resolve the status display on non-'toggle' + * templates. + * + * Revision 1.36 2001/10/26 17:33:27 oes + * marginal bugfix + * + * Revision 1.35 2001/10/23 21:48:19 jongfoster + * Cleaning up error handling in CGI functions - they now send back + * a HTML error page and should never cause a FATAL error. (Fixes one + * potential source of "denial of service" attacks). + * + * CGI actions file editor that works and is actually useful. + * + * Ability to toggle Junkbuster remotely using a CGI call. + * + * You can turn off both the above features in the main configuration + * file, e.g. if you are running a multi-user proxy. + * + * Revision 1.34 2001/10/18 22:22:09 david__schmidt + * Only show "Local support" on templates conditionally: + * - if either 'admin-address' or 'proxy-info-url' are uncommented in config + * - if not, no Local support section appears + * + * Revision 1.33 2001/10/14 22:28:41 jongfoster + * Fixing stupid typo. + * + * Revision 1.32 2001/10/14 22:20:18 jongfoster + * - Changes to CGI dispatching method to match CGI names exactly, + * rather than doing a prefix match. + * - No longer need to count the length of the CGI handler names by hand. + * - Adding new handler for 404 error when disptching a CGI, if none of + * the handlers match. + * - Adding new handlers for CGI actionsfile editor. + * + * Revision 1.31 2001/10/10 10:56:39 oes + * Failiure to load template now fatal. Before, the user got a hard-to-understand assertion failure from cgi.c + * + * Revision 1.30 2001/10/02 15:30:57 oes + * Introduced show-request cgi + * + * Revision 1.29 2001/09/20 15:47:44 steudten + * + * Fix BUG: Modify int size to size_t size in fill_template() + * - removes big trouble on machines where sizeof(int) != sizeof(size_t). + * + * Revision 1.28 2001/09/19 18:00:37 oes + * - Deletef time() FIXME (Can't fail under Linux either, if + * the argument is guaranteed to be in out address space, + * which it is.) + * - Fixed comments + * - Pointer notation cosmetics + * - Fixed a minor bug in template_fill(): Failiure of + * pcrs_execute() now secure. + * + * Revision 1.27 2001/09/16 17:08:54 jongfoster + * Moving simple CGI functions from cgi.c to new file cgisimple.c + * + * Revision 1.26 2001/09/16 15:47:37 jongfoster + * First version of CGI-based edit interface. This is very much a + * work-in-progress, and you can't actually use it to edit anything + * yet. You must #define FEATURE_CGI_EDIT_ACTIONS for these changes + * to have any effect. + * + * Revision 1.25 2001/09/16 15:02:35 jongfoster + * Adding i.j.b/robots.txt. + * Inlining add_stats() since it's only ever called from one place. + * + * Revision 1.24 2001/09/16 11:38:01 jongfoster + * Splitting fill_template() into 2 functions: + * template_load() loads the file + * template_fill() performs the PCRS regexps. + * This is because the CGI edit interface has a "table row" + * template which is used many times in the page - this + * change means it's only loaded from disk once. + * + * Revision 1.23 2001/09/16 11:16:05 jongfoster + * Better error handling in dispatch_cgi() and parse_cgi_parameters() + * + * Revision 1.22 2001/09/16 11:00:10 jongfoster + * New function alloc_http_response, for symmetry with free_http_response + * + * Revision 1.21 2001/09/13 23:53:03 jongfoster + * Support for both static and dynamically generated CGI pages. + * Correctly setting Last-Modified: and Expires: HTTP headers. + * + * Revision 1.20 2001/09/13 23:40:36 jongfoster + * (Cosmetic only) Indentation correction + * + * Revision 1.19 2001/09/13 23:31:25 jongfoster + * Moving image data to cgi.c rather than cgi.h. + * + * Revision 1.18 2001/08/05 16:06:20 jongfoster + * Modifiying "struct map" so that there are now separate header and + * "map_entry" structures. This means that functions which modify a + * map no longer need to return a pointer to the modified map. + * Also, it no longer reverses the order of the entries (which may be + * important with some advanced template substitutions). + * + * Revision 1.17 2001/08/05 15:57:38 oes + * Adapted finish_http_response to new list_to_text + * + * Revision 1.16 2001/08/01 21:33:18 jongfoster + * Changes to fill_template() that reduce memory usage without having + * an impact on performance. I also renamed some variables so as not + * to clash with the C++ keywords "new" and "template". + * + * Revision 1.15 2001/08/01 21:19:22 jongfoster + * Moving file version information to a separate CGI page. + * + * Revision 1.14 2001/08/01 00:19:03 jongfoster + * New function: map_conditional() for an if-then-else syntax. + * Changing to use new version of show_defines() + * + * Revision 1.13 2001/07/30 22:08:36 jongfoster + * Tidying up #defines: + * - All feature #defines are now of the form FEATURE_xxx + * - Permanently turned off WIN_GUI_EDIT + * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS + * + * Revision 1.12 2001/07/29 18:47:05 jongfoster + * Adding missing #include "loadcfg.h" + * + * Revision 1.11 2001/07/18 17:24:37 oes + * Changed to conform to new pcrs interface + * + * Revision 1.10 2001/07/13 13:53:13 oes + * Removed all #ifdef PCRS and related code + * + * Revision 1.9 2001/06/29 21:45:41 oes + * Indentation, CRLF->LF, Tab-> Space + * + * Revision 1.8 2001/06/29 13:21:46 oes + * - Cosmetics: renamed and reordered functions, variables, + * texts, improved comments etc + * + * - Removed ij_untrusted_url() The relevant + * info is now part of the "untrusted" page, + * which is generated by filters.c:trust_url() + * + * - Generators of content now call finish_http_response() + * themselves, making jcc.c:chat() a little less + * cluttered + * + * - Removed obsolete "Pragma: no-cache" from our headers + * + * - http_responses now know their head length + * + * - fill_template now uses the new interface to pcrs, so that + * - long jobs (like whole files) no longer have to be assembled + * in a fixed size buffer + * - the new T (trivial) option is used, and the replacement may + * contain Perl syntax backrefs without confusing pcrs + * + * - Introduced default_exports() which generates a set of exports + * common to all CGIs and other content generators + * + * - Introduced convenience function map_block_killer() + * + * - Introduced convenience function make_menu() + * + * - Introduced CGI-like function error_response() which generates + * the "No such domain" and "Connect failed" messages using the + * CGI platform + * + * - cgi_show_url_info: + * - adapted to new CGI features + * - form and answers now generated from same template + * - http:// prefix in URL now OK + * + * - cgi_show_status: + * - adapted to new CGI features + * - no longer uses csp->init_proxy_args + * + * - cgi_default: + * - moved menu generation to make_menu() + * + * - add_stats now writes single export map entries instead + * of a fixed string + * + * - Moved redirect_url() to filters.c + * + * - Fixed mem leak in free_http_response(), map_block_killer(), + * + * - Removed logentry from cancelled commit + * + * Revision 1.7 2001/06/09 10:51:58 jongfoster + * Changing "show URL info" handler to new style. + * Changing BUFSIZ ==> BUFFER_SIZE + * + * Revision 1.6 2001/06/07 23:05:19 jongfoster + * Removing code related to old forward and ACL files. + * + * Revision 1.5 2001/06/05 19:59:16 jongfoster + * Fixing multiline character string (a GCC-only "feature"), and snprintf (it's _snprintf under VC++). + * + * Revision 1.4 2001/06/04 10:41:52 swa + * show version string of cgi.h and cgi.c + * + * Revision 1.3 2001/06/03 19:12:16 oes + * introduced new cgi handling + * + * No revisions before 1.3 + * + **********************************************************************/ + + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#define snprintf _snprintf +#endif /* def _WIN32 */ + +#include "project.h" +#include "cgi.h" +#include "list.h" +#include "encode.h" +#include "ssplit.h" +#include "errlog.h" +#include "miscutil.h" +#include "cgisimple.h" +#ifdef FEATURE_CGI_EDIT_ACTIONS +#include "cgiedit.h" +#endif /* def FEATURE_CGI_EDIT_ACTIONS */ +#include "loadcfg.h" +/* loadcfg.h is for g_bToggleIJB only */ + +const char cgi_h_rcs[] = CGI_H_VERSION; + +/* + * List of CGI functions: name, handler, description + * Note: Do NOT use single quotes in the description; + * this will break the dynamic "blocked" template! + */ +static const struct cgi_dispatcher cgi_dispatchers[] = { + { "", + cgi_default, + "Privoxy main page" }, +#ifdef FEATURE_GRACEFUL_TERMINATION + { "die", + cgi_die, + "Shut down - Do not deploy this build in a production environment, " + "this is a one click Denial Of Service attack!!!" }, +#endif + { "show-status", + cgi_show_status, + "View & change the current configuration" }, + { "show-version", + cgi_show_version, + "View the source code version numbers" }, + { "show-request", + cgi_show_request, + "View the request headers." }, + { "show-url-info", + cgi_show_url_info, + "Look up which actions apply to a URL and why" }, +#ifdef FEATURE_CGI_EDIT_ACTIONS + { "toggle", + cgi_toggle, + "Toggle Privoxy on or off" }, + + { "edit-actions", /* Edit the actions list */ + cgi_edit_actions, + NULL }, + { "eaa", /* Shortcut for edit-actions-add-url-form */ + cgi_edit_actions_add_url_form, + NULL }, + { "eau", /* Shortcut for edit-actions-url-form */ + cgi_edit_actions_url_form, + NULL }, + { "ear", /* Shortcut for edit-actions-remove-url-form */ + cgi_edit_actions_remove_url_form, + NULL }, + { "eafu", /* Shortcut for edit-actions-for-url */ + cgi_edit_actions_for_url, + NULL }, + { "eas", /* Shortcut for edit-actions-submit */ + cgi_edit_actions_submit, + NULL }, + { "easa", /* Shortcut for edit-actions-section-add */ + cgi_edit_actions_section_add, + NULL }, + { "easr", /* Shortcut for edit-actions-section-remove */ + cgi_edit_actions_section_remove, + NULL }, + { "eass", /* Shortcut for edit-actions-section-swap */ + cgi_edit_actions_section_swap, + NULL }, + { "edit-actions-for-url", + cgi_edit_actions_for_url, + NULL /* Edit the actions for (a) specified URL(s) */ }, + { "edit-actions-list", + cgi_edit_actions_list, + NULL /* Edit the actions list */ }, + { "edit-actions-submit", + cgi_edit_actions_submit, + NULL /* Change the actions for (a) specified URL(s) */ }, + { "edit-actions-url", + cgi_edit_actions_url, + NULL /* Change a URL pattern in the actionsfile */ }, + { "edit-actions-url-form", + cgi_edit_actions_url_form, + NULL /* Form to change a URL pattern in the actionsfile */ }, + { "edit-actions-add-url", + cgi_edit_actions_add_url, + NULL /* Add a URL pattern to the actionsfile */ }, + { "edit-actions-add-url-form", + cgi_edit_actions_add_url_form, + NULL /* Form to add a URL pattern to the actionsfile */ }, + { "edit-actions-remove-url", + cgi_edit_actions_remove_url, + NULL /* Remove a URL pattern from the actionsfile */ }, + { "edit-actions-remove-url-form", + cgi_edit_actions_remove_url_form, + NULL /* Form to remove a URL pattern from the actionsfile */ }, + { "edit-actions-section-add", + cgi_edit_actions_section_add, + NULL /* Remove a section from the actionsfile */ }, + { "edit-actions-section-remove", + cgi_edit_actions_section_remove, + NULL /* Remove a section from the actionsfile */ }, + { "edit-actions-section-swap", + cgi_edit_actions_section_swap, + NULL /* Swap two sections in the actionsfile */ }, +#endif /* def FEATURE_CGI_EDIT_ACTIONS */ + { "robots.txt", + cgi_robots_txt, + NULL /* Sends a robots.txt file to tell robots to go away. */ }, + { "send-banner", + cgi_send_banner, + NULL /* Send a built-in image */ }, + { "send-stylesheet", + cgi_send_stylesheet, + NULL /* Send templates/cgi-style.css */ }, + { "t", + cgi_transparent_image, + NULL /* Send a transparent image (short name) */ }, + { NULL, /* NULL Indicates end of list and default page */ + cgi_error_404, + NULL /* Unknown CGI page */ } +}; + + +/* + * Bulit-in images for ad replacement + * + * Hint: You can encode your own images like this: + * cat your-image | perl -e 'while (read STDIN, $c, 1) { printf("\\%.3o", unpack("C", $c)); }' + */ + +#ifdef FEATURE_NO_GIFS + +/* + * Checkerboard pattern, as a PNG. + */ +const char image_pattern_data[] = + "\211\120\116\107\015\012\032\012\000\000\000\015\111\110\104" + "\122\000\000\000\004\000\000\000\004\010\002\000\000\000\046" + "\223\011\051\000\000\000\006\142\113\107\104\000\310\000\310" + "\000\310\052\045\225\037\000\000\000\032\111\104\101\124\170" + "\332\143\070\161\342\304\377\377\377\041\044\003\234\165\342" + "\304\011\006\234\062\000\125\200\052\251\125\174\360\223\000" + "\000\000\000\111\105\116\104\256\102\140\202"; + +/* + * 1x1 transparant PNG. + */ +const char image_blank_data[] = + "\211\120\116\107\015\012\032\012\000\000\000\015\111\110\104\122" + "\000\000\000\004\000\000\000\004\010\006\000\000\000\251\361\236" + "\176\000\000\000\007\164\111\115\105\007\322\003\013\020\073\070" + "\013\025\036\203\000\000\000\011\160\110\131\163\000\000\013\022" + "\000\000\013\022\001\322\335\176\374\000\000\000\004\147\101\115" + "\101\000\000\261\217\013\374\141\005\000\000\000\033\111\104\101" + "\124\170\332\143\070\161\342\304\207\377\377\377\347\302\150\006" + "\144\016\210\146\040\250\002\000\042\305\065\221\270\027\131\110" + "\000\000\000\000\111\105\116\104\256\102\140\202"; +#else + +/* + * Checkerboard pattern, as a GIF. + */ +const char image_pattern_data[] = + "\107\111\106\070\071\141\004\000\004\000\200\000\000\310\310" + "\310\377\377\377\041\376\016\111\040\167\141\163\040\141\040" + "\142\141\156\156\145\162\000\041\371\004\001\012\000\001\000" + "\054\000\000\000\000\004\000\004\000\000\002\005\104\174\147" + "\270\005\000\073"; + +/* + * 1x1 transparant GIF. + */ +const char image_blank_data[] = + "GIF89a\001\000\001\000\200\000\000\377\377\377\000\000" + "\000!\371\004\001\000\000\000\000,\000\000\000\000\001" + "\000\001\000\000\002\002D\001\000;"; +#endif + +const size_t image_pattern_length = sizeof(image_pattern_data) - 1; +const size_t image_blank_length = sizeof(image_blank_data) - 1; + + +static struct http_response cgi_error_memory_response[1]; + +static struct http_response *dispatch_known_cgi(struct client_state * csp, + const char * path); +static struct map *parse_cgi_parameters(char *argstring); + + +/********************************************************************* + * + * Function : dispatch_cgi + * + * Description : Checks if a request URL has either the magical + * hostname CGI_SITE_1_HOST (usually http://p.p/) or + * matches CGI_SITE_2_HOST CGI_SITE_2_PATH (usually + * http://config.privoxy.org/). If so, it passes + * the (rest of the) path onto dispatch_known_cgi, which + * calls the relevant CGI handler function. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : http_response if match, NULL if nonmatch or handler fail + * + *********************************************************************/ +struct http_response *dispatch_cgi(struct client_state *csp) +{ + const char *host = csp->http->host; + const char *path = csp->http->path; + + /* + * Should we intercept ? + */ + + /* Note: "example.com" and "example.com." are equivalent hostnames. */ + + /* Either the host matches CGI_SITE_1_HOST ..*/ + if ( ( (0 == strcmpic(host, CGI_SITE_1_HOST)) + || (0 == strcmpic(host, CGI_SITE_1_HOST "."))) + && (path[0] == '/') ) + { + /* ..then the path will all be for us. Remove leading '/' */ + path++; + } + /* Or it's the host part CGI_SITE_2_HOST, and the path CGI_SITE_2_PATH */ + else if ( ( (0 == strcmpic(host, CGI_SITE_2_HOST )) + || (0 == strcmpic(host, CGI_SITE_2_HOST ".")) ) + && (0 == strncmpic(path, CGI_SITE_2_PATH, strlen(CGI_SITE_2_PATH))) ) + { + /* take everything following CGI_SITE_2_PATH */ + path += strlen(CGI_SITE_2_PATH); + if (*path == '/') + { + /* skip the forward slash after CGI_SITE_2_PATH */ + path++; + } + else if (*path != '\0') + { + /* + * wierdness: URL is /configXXX, where XXX is some string + * Do *NOT* intercept. + */ + return NULL; + } + } + else + { + /* Not a CGI */ + return NULL; + } + + /* + * This is a CGI call. + */ + + return dispatch_known_cgi(csp, path); +} + + +/********************************************************************* + * + * Function : dispatch_known_cgi + * + * Description : Processes a CGI once dispatch_cgi has determined that + * it matches one of the magic prefixes. Parses the path + * as a cgi name plus query string, prepares a map that + * maps CGI parameter names to their values, initializes + * the http_response struct, and calls the relevant CGI + * handler function. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : path = Path of CGI, with the CGI prefix removed. + * Should not have a leading "/". + * + * Returns : http_response, or NULL on handler failure or out of + * memory. + * + *********************************************************************/ +static struct http_response *dispatch_known_cgi(struct client_state * csp, + const char * path) +{ + const struct cgi_dispatcher *d; + struct map *param_list; + struct http_response *rsp; + char *query_args_start; + char *path_copy; + jb_err err; + + if (NULL == (path_copy = strdup(path))) + { + return cgi_error_memory(); + } + + query_args_start = path_copy; + while (*query_args_start && *query_args_start != '?') + { + query_args_start++; + } + if (*query_args_start == '?') + { + *query_args_start++ = '\0'; + } + + if (NULL == (param_list = parse_cgi_parameters(query_args_start))) + { + free(path_copy); + return cgi_error_memory(); + } + + + /* + * At this point: + * path_copy = CGI call name + * param_list = CGI params, as map + */ + + /* Get mem for response or fail*/ + if (NULL == (rsp = alloc_http_response())) + { + free(path_copy); + free_map(param_list); + return cgi_error_memory(); + } + + log_error(LOG_LEVEL_GPC, "%s%s cgi call", csp->http->hostport, csp->http->path); + log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 3", + csp->ip_addr_str, csp->http->cmd); + + /* Find and start the right CGI function*/ + d = cgi_dispatchers; + for (;;) + { + if ((d->name == NULL) || (strcmp(path_copy, d->name) == 0)) + { + err = (d->handler)(csp, rsp, param_list); + free(path_copy); + free_map(param_list); + if (err == JB_ERR_CGI_PARAMS) + { + err = cgi_error_bad_param(csp, rsp); + } + if (err && (err != JB_ERR_MEMORY)) + { + /* Unexpected error! Shouldn't get here */ + log_error(LOG_LEVEL_ERROR, "Unexpected CGI error %d in top-level handler. Please file a bug report!", err); + err = cgi_error_unknown(csp, rsp, err); + } + if (!err) + { + /* It worked */ + return finish_http_response(rsp); + } + else + { + /* Error in handler, probably out-of-memory */ + free_http_response(rsp); + return cgi_error_memory(); + } + } + d++; + } +} + + +/********************************************************************* + * + * Function : parse_cgi_parameters + * + * Description : Parse a URL-encoded argument string into name/value + * pairs and store them in a struct map list. + * + * Parameters : + * 1 : argstring = string to be parsed. Will be trashed. + * + * Returns : pointer to param list, or NULL if out of memory. + * + *********************************************************************/ +static struct map *parse_cgi_parameters(char *argstring) +{ + char *p; + char *vector[BUFFER_SIZE]; + int pairs, i; + struct map *cgi_params; + + if (NULL == (cgi_params = new_map())) + { + return NULL; + } + + pairs = ssplit(argstring, "&", vector, SZ(vector), 1, 1); + + for (i = 0; i < pairs; i++) + { + if ((NULL != (p = strchr(vector[i], '='))) && (*(p+1) != '\0')) + { + *p = '\0'; + if (map(cgi_params, url_decode(vector[i]), 0, url_decode(++p), 0)) + { + free_map(cgi_params); + return NULL; + } + } + } + + return cgi_params; + +} + + +/********************************************************************* + * + * Function : get_char_param + * + * Description : Get a single-character parameter passed to a CGI + * function. + * + * Parameters : + * 1 : parameters = map of cgi parameters + * 2 : param_name = The name of the parameter to read + * + * Returns : Uppercase character on success, '\0' on error. + * + *********************************************************************/ +char get_char_param(const struct map *parameters, + const char *param_name) +{ + char ch; + + assert(parameters); + assert(param_name); + + ch = *(lookup(parameters, param_name)); + if ((ch >= 'a') && (ch <= 'z')) + { + ch = ch - 'a' + 'A'; + } + + return ch; +} + + +/********************************************************************* + * + * Function : get_string_param + * + * Description : Get a string paramater, to be used as an + * ACTION_STRING or ACTION_MULTI paramater. + * Validates the input to prevent stupid/malicious + * users from corrupting their action file. + * + * Parameters : + * 1 : parameters = map of cgi parameters + * 2 : param_name = The name of the parameter to read + * 3 : pparam = destination for paramater. Allocated as + * part of the map "parameters", so don't free it. + * Set to NULL if not specified. + * + * Returns : JB_ERR_OK on success, or if the paramater + * was not specified. + * JB_ERR_MEMORY on out-of-memory. + * JB_ERR_CGI_PARAMS if the paramater is not valid. + * + *********************************************************************/ +jb_err get_string_param(const struct map *parameters, + const char *param_name, + const char **pparam) +{ + const char *param; + const char *s; + char ch; + + assert(parameters); + assert(param_name); + assert(pparam); + + *pparam = NULL; + + param = lookup(parameters, param_name); + if (!*param) + { + return JB_ERR_OK; + } + + if (strlen(param) >= CGI_PARAM_LEN_MAX) + { + /* + * Too long. + * + * Note that the length limit is arbitrary, it just seems + * sensible to limit it to *something*. There's no + * technical reason for any limit at all. + */ + return JB_ERR_CGI_PARAMS; + } + + /* Check every character to see if it's legal */ + s = param; + while ((ch = *s++) != '\0') + { + if ( ((unsigned char)ch < (unsigned char)' ') + || (ch == '}') ) + { + /* Probable hack attempt, or user accidentally used '}'. */ + return JB_ERR_CGI_PARAMS; + } + } + + /* Success */ + *pparam = param; + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : get_number_param + * + * Description : Get a non-negative integer from the parameters + * passed to a CGI function. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : parameters = map of cgi parameters + * 3 : name = Name of CGI parameter to read + * 4 : pvalue = destination for value. + * Set to -1 on error. + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory + * JB_ERR_CGI_PARAMS if the parameter was not specified + * or is not valid. + * + *********************************************************************/ +jb_err get_number_param(struct client_state *csp, + const struct map *parameters, + char *name, + unsigned *pvalue) +{ + const char *param; + char ch; + unsigned value; + + assert(csp); + assert(parameters); + assert(name); + assert(pvalue); + + *pvalue = 0; + + param = lookup(parameters, name); + if (!*param) + { + return JB_ERR_CGI_PARAMS; + } + + /* We don't use atoi because I want to check this carefully... */ + + value = 0; + while ((ch = *param++) != '\0') + { + if ((ch < '0') || (ch > '9')) + { + return JB_ERR_CGI_PARAMS; + } + + ch -= '0'; + + /* Note: + * + * defines UINT_MAX + * + * (UINT_MAX - ch) / 10 is the largest number that + * can be safely multiplied by 10 then have ch added. + */ + if (value > ((UINT_MAX - (unsigned)ch) / 10U)) + { + return JB_ERR_CGI_PARAMS; + } + + value = value * 10 + ch; + } + + /* Success */ + *pvalue = value; + + return JB_ERR_OK; + +} + + +/********************************************************************* + * + * Function : error_response + * + * Description : returns an http_response that explains the reason + * why a request failed. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : templatename = Which template should be used for the answer + * 3 : sys_err = system error number + * + * Returns : A http_response. If we run out of memory, this + * will be cgi_error_memory(). + * + *********************************************************************/ +struct http_response *error_response(struct client_state *csp, + const char *templatename, + int sys_err) +{ + jb_err err; + struct http_response *rsp; + struct map * exports = default_exports(csp, NULL); + if (exports == NULL) + { + return cgi_error_memory(); + } + + if (NULL == (rsp = alloc_http_response())) + { + free_map(exports); + return cgi_error_memory(); + } + + err = map(exports, "host", 1, html_encode(csp->http->host), 0); + if (!err) err = map(exports, "hostport", 1, html_encode(csp->http->hostport), 0); + if (!err) err = map(exports, "path", 1, html_encode(csp->http->path), 0); + if (!err) err = map(exports, "error", 1, html_encode_and_free_original(safe_strerror(sys_err)), 0); + if (!err) err = map(exports, "protocol", 1, csp->http->ssl ? "https://" : "http://", 1); + if (!err) + { + err = map(exports, "host-ip", 1, html_encode(csp->http->host_ip_addr_str), 0); + if (err) + { + /* Some failures, like "404 no such domain", don't have an IP address. */ + err = map(exports, "host-ip", 1, html_encode(csp->http->host), 0); + } + } + + + if (err) + { + free_map(exports); + free_http_response(rsp); + return cgi_error_memory(); + } + + if (!strcmp(templatename, "no-such-domain")) + { + rsp->status = strdup("404 No such domain"); + if (rsp->status == NULL) + { + free_map(exports); + free_http_response(rsp); + return cgi_error_memory(); + } + } + else if (!strcmp(templatename, "connect-failed")) + { + rsp->status = strdup("503 Connect failed"); + if (rsp->status == NULL) + { + free_map(exports); + free_http_response(rsp); + return cgi_error_memory(); + } + } + + err = template_fill_for_cgi(csp, templatename, exports, rsp); + if (err) + { + free_http_response(rsp); + return cgi_error_memory(); + } + + return finish_http_response(rsp); +} + + +/********************************************************************* + * + * Function : cgi_init_error_messages + * + * Description : Call at the start of the program to initialize + * the error message used by cgi_error_memory(). + * + * Parameters : N/A + * + * Returns : N/A + * + *********************************************************************/ +void cgi_init_error_messages(void) +{ + memset(cgi_error_memory_response, '\0', sizeof(*cgi_error_memory_response)); + cgi_error_memory_response->head = + "HTTP/1.0 500 Internal Privoxy Error\r\n" + "Content-Type: text/html\r\n" + "\r\n"; + cgi_error_memory_response->body = + "\r\n" + "500 Internal Privoxy Error\r\n" + "\r\n" + "

500 Internal Privoxy Error

\r\n" + "

Privoxy ran out of memory while processing your request.

\r\n" + "

Please contact your proxy administrator, or try again later

\r\n" + "\r\n" + "\r\n"; + + cgi_error_memory_response->head_length = + strlen(cgi_error_memory_response->head); + cgi_error_memory_response->content_length = + strlen(cgi_error_memory_response->body); +} + + +/********************************************************************* + * + * Function : cgi_error_memory + * + * Description : Called if a CGI function runs out of memory. + * Returns a statically-allocated error response. + * + * Parameters : N/A + * + * Returns : http_response data structure for output. This is + * statically allocated, for obvious reasons. + * + *********************************************************************/ +struct http_response *cgi_error_memory(void) +{ + /* assert that it's been initialized. */ + assert(cgi_error_memory_response->head); + + return cgi_error_memory_response; +} + + +/********************************************************************* + * + * Function : cgi_error_no_template + * + * Description : Almost-CGI function that is called if a template + * cannot be loaded. Note this is not a true CGI, + * it takes a template name rather than a map of + * parameters. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : rsp = http_response data structure for output + * 3 : template_name = Name of template that could not + * be loaded. + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err cgi_error_no_template(struct client_state *csp, + struct http_response *rsp, + const char *template_name) +{ + static const char status[] = + "500 Internal Privoxy Error"; + static const char body_prefix[] = + "\r\n" + "500 Internal Privoxy Error\r\n" + "\r\n" + "

500 Internal Privoxy Error

\r\n" + "

Privoxy encountered an error while processing your request:

\r\n" + "

Could not load template file "; + static const char body_suffix[] = + " or one of it's included components.

\r\n" + "

Please contact your proxy administrator.

\r\n" + "

If you are the proxy administrator, please put the required file(s)" + "in the (confdir)/templates directory. The " + "location of the (confdir) directory " + "is specified in the main Privoxy config " + "file. (It's typically the Privoxy install directory" +#ifndef _WIN32 + ", or /etc/privoxy/" +#endif /* ndef _WIN32 */ + ").

\r\n" + "\r\n" + "\r\n"; + + assert(csp); + assert(rsp); + assert(template_name); + + /* Reset rsp, if needed */ + freez(rsp->status); + freez(rsp->head); + freez(rsp->body); + rsp->content_length = 0; + rsp->head_length = 0; + rsp->is_static = 0; + + rsp->body = malloc(strlen(body_prefix) + strlen(template_name) + strlen(body_suffix) + 1); + if (rsp->body == NULL) + { + return JB_ERR_MEMORY; + } + strcpy(rsp->body, body_prefix); + strcat(rsp->body, template_name); + strcat(rsp->body, body_suffix); + + rsp->status = strdup(status); + if (rsp->body == NULL) + { + return JB_ERR_MEMORY; + } + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : cgi_error_unknown + * + * Description : Almost-CGI function that is called if an unexpected + * error occurs in the top-level CGI dispatcher. + * In this context, "unexpected" means "anything other + * than JB_ERR_MEMORY or JB_ERR_CGI_PARAMS" - CGIs are + * expected to handle all other errors internally, + * since they can give more relavent error messages + * that way. + * + * Note this is not a true CGI, it takes an error + * code rather than a map of parameters. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : rsp = http_response data structure for output + * 3 : error_to_report = Error code to report. + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err cgi_error_unknown(struct client_state *csp, + struct http_response *rsp, + jb_err error_to_report) +{ + static const char status[] = + "500 Internal Privoxy Error"; + static const char body_prefix[] = + "\r\n" + "500 Internal Privoxy Error\r\n" + "\r\n" + "

500 Internal Privoxy Error

\r\n" + "

Privoxy encountered an error while processing your request:

\r\n" + "

Unexpected internal error: "; + static const char body_suffix[] = + "

\r\n" + "

Please " + "" + "file a bug report.

\r\n" + "\r\n" + "\r\n"; + char errnumbuf[30]; + assert(csp); + assert(rsp); + + /* Reset rsp, if needed */ + freez(rsp->status); + freez(rsp->head); + freez(rsp->body); + rsp->content_length = 0; + rsp->head_length = 0; + rsp->is_static = 0; + + sprintf(errnumbuf, "%d", error_to_report); + + rsp->body = malloc(strlen(body_prefix) + strlen(errnumbuf) + strlen(body_suffix) + 1); + if (rsp->body == NULL) + { + return JB_ERR_MEMORY; + } + strcpy(rsp->body, body_prefix); + strcat(rsp->body, errnumbuf); + strcat(rsp->body, body_suffix); + + rsp->status = strdup(status); + if (rsp->body == NULL) + { + return JB_ERR_MEMORY; + } + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : cgi_error_bad_param + * + * Description : CGI function that is called if the parameters + * (query string) for a CGI were wrong. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : rsp = http_response data structure for output + * + * CGI Parameters : none + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err cgi_error_bad_param(struct client_state *csp, + struct http_response *rsp) +{ + struct map *exports; + + assert(csp); + assert(rsp); + + if (NULL == (exports = default_exports(csp, NULL))) + { + return JB_ERR_MEMORY; + } + + return template_fill_for_cgi(csp, "cgi-error-bad-param", exports, rsp); +} + + +/********************************************************************* + * + * Function : add_help_link + * + * Description : Produce a copy of the string given as item, + * embedded in an HTML link to its corresponding + * section (item name in uppercase) in the actions + * chapter of the user manual, (whose URL is given in + * the config and defaults to our web site). + * + * FIXME: I currently only work for actions, and would + * like to be generalized for other topics. + * + * Parameters : + * 1 : item = item (will NOT be free()d.) + * It is assumed to be HTML-safe. + * 2 : config = The current configuration. + * + * Returns : String with item embedded in link, or NULL on + * out-of-memory + * + *********************************************************************/ +char *add_help_link(const char *item, + struct configuration_spec *config) +{ + char *result; + + if (!item) return NULL; + + result = strdup("usermanual); + string_append(&result, ACTIONS_HELP_PREFIX); + string_join (&result, string_toupper(item)); + string_append(&result, "\">"); + string_append(&result, item); + string_append(&result, " "); + + return result; +} + + +/********************************************************************* + * + * Function : get_http_time + * + * Description : Get the time in a format suitable for use in a + * HTTP header - e.g.: + * "Sun, 06 Nov 1994 08:49:37 GMT" + * + * Parameters : + * 1 : time_offset = Time returned will be current time + * plus this number of seconds. + * 2 : buf = Destination for result. Must be long enough + * to hold 29 characters plus a trailing zero. + * + * Returns : N/A + * + *********************************************************************/ +void get_http_time(int time_offset, char *buf) +{ + static const char day_names[7][4] = + { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; + static const char month_names[12][4] = + { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + + struct tm *t; + time_t current_time; + + assert(buf); + + time(¤t_time); /* get current time */ + + current_time += time_offset; + + /* get and save the gmt */ + { +#ifdef HAVE_GMTIME_R + struct tm dummy; + t = gmtime_r(¤t_time, &dummy); +#else + t = gmtime(¤t_time); +#endif + } + + /* Format: "Sun, 06 Nov 1994 08:49:37 GMT" */ + snprintf(buf, 30, + "%s, %02d %s %4d %02d:%02d:%02d GMT", + day_names[t->tm_wday], + t->tm_mday, + month_names[t->tm_mon], + t->tm_year + 1900, + t->tm_hour, + t->tm_min, + t->tm_sec + ); + +} + + +/********************************************************************* + * + * Function : finish_http_response + * + * Description : Fill in the missing headers in an http response, + * and flatten the headers to an http head. + * + * Parameters : + * 1 : rsp = pointer to http_response to be processed + * + * Returns : A http_response, usually the rsp parameter. + * On error, free()s rsp and returns cgi_error_memory() + * + *********************************************************************/ +struct http_response *finish_http_response(struct http_response *rsp) +{ + char buf[BUFFER_SIZE]; + jb_err err; + + /* Special case - do NOT change this statically allocated response, + * which is ready for output anyway. + */ + if (rsp == cgi_error_memory_response) + { + return rsp; + } + + /* + * Fill in the HTTP Status + */ + sprintf(buf, "HTTP/1.0 %s", rsp->status ? rsp->status : "200 OK"); + err = enlist_first(rsp->headers, buf); + + /* + * Set the Content-Length + */ + if (rsp->content_length == 0) + { + rsp->content_length = rsp->body ? strlen(rsp->body) : 0; + } + if (!err) + { + sprintf(buf, "Content-Length: %d", (int)rsp->content_length); + err = enlist(rsp->headers, buf); + } + + /* + * Fill in the default headers: + * + * Content-Type: default to text/html if not already specified. + * Date: set to current date/time. + * Last-Modified: set to date/time the page was last changed. + * Expires: set to date/time page next needs reloading. + * Cache-Control: set to "no-cache" if applicable. + * + * See http://www.w3.org/Protocols/rfc2068/rfc2068 + */ + if (!err) err = enlist_unique(rsp->headers, "Content-Type: text/html", 13); + + if (rsp->is_static) + { + /* + * Set Expires to about 10 min into the future so it'll get reloaded + * occasionally, e.g. if Privoxy gets upgraded. + */ + + if (!err) + { + get_http_time(0, buf); + err = enlist_unique_header(rsp->headers, "Date", buf); + } + + /* Some date in the past. */ + if (!err) err = enlist_unique_header(rsp->headers, "Last-Modified", "Sat, 17 Jun 2000 12:00:00 GMT"); + + if (!err) + { + get_http_time(10 * 60, buf); /* 10 * 60sec = 10 minutes */ + err = enlist_unique_header(rsp->headers, "Expires", buf); + } + } + else + { + /* + * Compliant browsers should not cache this due to the "Cache-Control" + * setting. However, to be certain, we also set both "Last-Modified" + * and "Expires" to the current time. + */ + if (!err) err = enlist_unique_header(rsp->headers, "Cache-Control", "no-cache"); + + get_http_time(0, buf); + if (!err) err = enlist_unique_header(rsp->headers, "Date", buf); + if (!err) err = enlist_unique_header(rsp->headers, "Last-Modified", buf); + if (!err) err = enlist_unique_header(rsp->headers, "Expires", buf); + } + + + /* + * Write the head + */ + if (err || (NULL == (rsp->head = list_to_text(rsp->headers)))) + { + free_http_response(rsp); + return cgi_error_memory(); + } + rsp->head_length = strlen(rsp->head); + + return rsp; + +} + + +/********************************************************************* + * + * Function : alloc_http_response + * + * Description : Allocates a new http_response structure. + * + * Parameters : N/A + * + * Returns : pointer to a new http_response, or NULL. + * + *********************************************************************/ +struct http_response *alloc_http_response(void) +{ + return (struct http_response *) zalloc(sizeof(struct http_response)); + +} + + +/********************************************************************* + * + * Function : free_http_response + * + * Description : Free the memory occupied by an http_response + * and its depandant structures. + * + * Parameters : + * 1 : rsp = pointer to http_response to be freed + * + * Returns : N/A + * + *********************************************************************/ +void free_http_response(struct http_response *rsp) +{ + /* + * Must special case cgi_error_memory_response, which is never freed. + */ + if (rsp && (rsp != cgi_error_memory_response)) + { + freez(rsp->status); + freez(rsp->head); + freez(rsp->body); + destroy_list(rsp->headers); + free(rsp); + } + +} + + +/********************************************************************* + * + * Function : template_load + * + * Description : CGI support function that loads a given HTML + * template from the confdir, ignoring comment + * lines and following #include statements up to + * a depth of 1. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : template_ptr = Destination for pointer to loaded + * template text. + * 3 : templatename = name of the HTML template to be used + * 4 : recursive = Flag set if this function calls itself + * following an #include statament + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory error. + * JB_ERR_FILE if the template file cannot be read + * + *********************************************************************/ +jb_err template_load(struct client_state *csp, char **template_ptr, + const char *templatename, int recursive) +{ + jb_err err; + char *templates_dir_path; + char *full_path; + char *file_buffer; + char *included_module; + const char *p; + FILE *fp; + char buf[BUFFER_SIZE]; + + assert(csp); + assert(template_ptr); + assert(templatename); + + *template_ptr = NULL; + + /* Validate template name. Paranoia. */ + for (p = templatename; *p != 0; p++) + { + if ( ((*p < 'a') || (*p > 'z')) + && ((*p < 'A') || (*p > 'Z')) + && ((*p < '0') || (*p > '9')) + && (*p != '-') + && (*p != '.')) + { + /* Illegal character */ + return JB_ERR_FILE; + } + } + + /* Generate full path */ + + templates_dir_path = make_path(csp->config->confdir, "templates"); + if (templates_dir_path == NULL) + { + return JB_ERR_MEMORY; + } + + full_path = make_path(templates_dir_path, templatename); + free(templates_dir_path); + if (full_path == NULL) + { + return JB_ERR_MEMORY; + } + + /* Allocate buffer */ + + file_buffer = strdup(""); + if (file_buffer == NULL) + { + free(full_path); + return JB_ERR_MEMORY; + } + + /* Open template file */ + + if (NULL == (fp = fopen(full_path, "r"))) + { + log_error(LOG_LEVEL_ERROR, "Cannot open template file %s: %E", full_path); + free(full_path); + free(file_buffer); + return JB_ERR_FILE; + } + free(full_path); + + /* + * Read the file, ignoring comments, and honoring #include + * statements, unless we're already called recursively. + * + * FIXME: The comment handling could break with lines >BUFFER_SIZE long. + * This is unlikely in practise. + */ + while (fgets(buf, BUFFER_SIZE, fp)) + { + if (!recursive && !strncmp(buf, "#include ", 9)) + { + if (JB_ERR_OK != (err = template_load(csp, &included_module, chomp(buf + 9), 1))) + { + free(file_buffer); + fclose(fp); + return err; + } + + if (string_join(&file_buffer, included_module)) + { + fclose(fp); + return JB_ERR_MEMORY; + } + + continue; + } + + /* skip lines starting with '#' */ + if (*buf == '#') + { + continue; + } + + if (string_append(&file_buffer, buf)) + { + fclose(fp); + return JB_ERR_MEMORY; + } + } + fclose(fp); + + *template_ptr = file_buffer; + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : template_fill + * + * Description : CGI support function that fills in a pre-loaded + * HTML template by replacing @name@ with value using + * pcrs, for each item in the output map. + * + * Note that a leading '$' charachter in the export map's + * values will be stripped and toggle on backreference + * interpretation. + * + * Parameters : + * 1 : template_ptr = IN: Template to be filled out. + * Will be free()d. + * OUT: Filled out template. + * Caller must free(). + * 2 : exports = map with fill in symbol -> name pairs + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory error + * + *********************************************************************/ +jb_err template_fill(char **template_ptr, const struct map *exports) +{ + struct map_entry *m; + pcrs_job *job; + char buf[BUFFER_SIZE]; + char *tmp_out_buffer; + char *file_buffer; + size_t size; + int error; + const char *flags; + + assert(template_ptr); + assert(*template_ptr); + assert(exports); + + file_buffer = *template_ptr; + size = strlen(file_buffer) + 1; + + /* + * Assemble pcrs joblist from exports map + */ + for (m = exports->first; m != NULL; m = m->next) + { + if (*m->name == '$') + { + /* + * First character of name is '$', so remove this flag + * character and allow backreferences ($1 etc) in the + * "replace with" text. + */ + snprintf(buf, BUFFER_SIZE, "%s", m->name + 1); + flags = "sigU"; + } + else + { + /* + * Treat the "replace with" text as a literal string - + * no quoting needed, no backreferences allowed. + * ("Trivial" ['T'] flag). + */ + flags = "sigTU"; + + /* Enclose name in @@ */ + snprintf(buf, BUFFER_SIZE, "@%s@", m->name); + } + + + log_error(LOG_LEVEL_CGI, "Substituting: s/%s/%s/%s", buf, m->value, flags); + + /* Make and run job. */ + job = pcrs_compile(buf, m->value, flags, &error); + if (job == NULL) + { + if (error == PCRS_ERR_NOMEM) + { + free(file_buffer); + *template_ptr = NULL; + return JB_ERR_MEMORY; + } + else + { + log_error(LOG_LEVEL_ERROR, "Error compiling template fill job %s: %d", m->name, error); + /* Hope it wasn't important and silently ignore the invalid job */ + } + } + else + { + pcrs_execute(job, file_buffer, size, &tmp_out_buffer, &size); + free(file_buffer); + pcrs_free_job(job); + if (NULL == tmp_out_buffer) + { + *template_ptr = NULL; + return JB_ERR_MEMORY; + } + file_buffer = tmp_out_buffer; + } + } + + /* + * Return + */ + *template_ptr = file_buffer; + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : template_fill_for_cgi + * + * Description : CGI support function that loads a HTML template + * and fills it in. Handles file-not-found errors + * by sending a HTML error message. For convenience, + * this function also frees the passed "exports" map. + * + * Parameters : + * 1 : csp = Client state + * 2 : templatename = name of the HTML template to be used + * 3 : exports = map with fill in symbol -> name pairs. + * Will be freed by this function. + * 4 : rsp = Response structure to fill in. + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory error + * + *********************************************************************/ +jb_err template_fill_for_cgi(struct client_state *csp, + const char *templatename, + struct map *exports, + struct http_response *rsp) +{ + jb_err err; + + assert(csp); + assert(templatename); + assert(exports); + assert(rsp); + + err = template_load(csp, &rsp->body, templatename, 0); + if (err == JB_ERR_FILE) + { + free_map(exports); + return cgi_error_no_template(csp, rsp, templatename); + } + else if (err) + { + free_map(exports); + return err; /* JB_ERR_MEMORY */ + } + err = template_fill(&rsp->body, exports); + free_map(exports); + return err; +} + + +/********************************************************************* + * + * Function : default_exports + * + * Description : returns a struct map list that contains exports + * which are common to all CGI functions. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : caller = name of CGI who calls us and which should + * be excluded from the generated menu. May be + * NULL. + * Returns : NULL if no memory, else a new map. Caller frees. + * + *********************************************************************/ +struct map *default_exports(const struct client_state *csp, const char *caller) +{ + char buf[20]; + jb_err err; + struct map * exports; + int local_help_exists = 0; + + assert(csp); + + exports = new_map(); + if (exports == NULL) + { + return NULL; + } + + err = map(exports, "version", 1, html_encode(VERSION), 0); + if (!err) err = map(exports, "my-ip-address", 1, html_encode(csp->my_ip_addr_str ? csp->my_ip_addr_str : "unknown"), 0); + if (!err) err = map(exports, "my-hostname", 1, html_encode(csp->my_hostname ? csp->my_hostname : "unknown"), 0); + if (!err) err = map(exports, "homepage", 1, html_encode(HOME_PAGE_URL), 0); + if (!err) err = map(exports, "default-cgi", 1, html_encode(CGI_PREFIX), 0); + if (!err) err = map(exports, "menu", 1, make_menu(caller), 0); + if (!err) err = map(exports, "code-status", 1, CODE_STATUS, 1); + if (!err) err = map(exports, "user-manual", 1, csp->config->usermanual ,1); + if (!err) err = map(exports, "actions-help-prefix", 1, ACTIONS_HELP_PREFIX ,1); + if (!err) err = map_conditional(exports, "enabled-display", g_bToggleIJB); + + snprintf(buf, 20, "%d", csp->config->hport); + if (!err) err = map(exports, "my-port", 1, buf, 1); + + if(!strcmp(CODE_STATUS, "stable")) + { + if (!err) err = map_block_killer(exports, "unstable"); + } + + if (csp->config->admin_address != NULL) + { + if (!err) err = map(exports, "admin-address", 1, html_encode(csp->config->admin_address), 0); + local_help_exists = 1; + } + else + { + if (!err) err = map_block_killer(exports, "have-adminaddr-info"); + } + + if (csp->config->proxy_info_url != NULL) + { + if (!err) err = map(exports, "proxy-info-url", 1, html_encode(csp->config->proxy_info_url), 0); + local_help_exists = 1; + } + else + { + if (!err) err = map_block_killer(exports, "have-proxy-info"); + } + + if (local_help_exists == 0) + { + if (!err) err = map_block_killer(exports, "have-help-info"); + } + + if (err) + { + free_map(exports); + return NULL; + } + + return exports; +} + + +/********************************************************************* + * + * Function : map_block_killer + * + * Description : Convenience function. + * Adds a "killer" for the conditional HTML-template + * block , i.e. a substitution of the regex + * "if--start.*if--end" to the given + * export list. + * + * Parameters : + * 1 : exports = map to extend + * 2 : name = name of conditional block + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err map_block_killer(struct map *exports, const char *name) +{ + char buf[1000]; /* Will do, since the names are hardwired */ + + assert(exports); + assert(name); + assert(strlen(name) < 490); + + snprintf(buf, 1000, "if-%s-start.*if-%s-end", name, name); + return map(exports, buf, 1, "", 1); +} + + +/********************************************************************* + * + * Function : map_block_keep + * + * Description : Convenience function. Removes the markers used + * by map-block-killer, to save a few bytes. + * i.e. removes "@if--start@" and "@if--end@" + * + * Parameters : + * 1 : exports = map to extend + * 2 : name = name of conditional block + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err map_block_keep(struct map *exports, const char *name) +{ + jb_err err; + char buf[500]; /* Will do, since the names are hardwired */ + + assert(exports); + assert(name); + assert(strlen(name) < 490); + + snprintf(buf, 500, "if-%s-start", name); + err = map(exports, buf, 1, "", 1); + + if (err) + { + return err; + } + + snprintf(buf, 500, "if-%s-end", name); + return map(exports, buf, 1, "", 1); +} + + +/********************************************************************* + * + * Function : map_conditional + * + * Description : Convenience function. + * Adds an "if-then-else" for the conditional HTML-template + * block , i.e. a substitution of the form: + * @if--then@ + * True text + * @else-not-@ + * False text + * @endif-@ + * + * The control structure and one of the alternatives + * will be hidden. + * + * Parameters : + * 1 : exports = map to extend + * 2 : name = name of conditional block + * 3 : choose_first = nonzero for first, zero for second. + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err map_conditional(struct map *exports, const char *name, int choose_first) +{ + char buf[1000]; /* Will do, since the names are hardwired */ + jb_err err; + + assert(exports); + assert(name); + assert(strlen(name) < 480); + + snprintf(buf, 1000, (choose_first + ? "else-not-%s@.*@endif-%s" + : "if-%s-then@.*@else-not-%s"), + name, name); + + err = map(exports, buf, 1, "", 1); + if (err) + { + return err; + } + + snprintf(buf, 1000, (choose_first ? "if-%s-then" : "endif-%s"), name); + return map(exports, buf, 1, "", 1); +} + + +/********************************************************************* + * + * Function : make_menu + * + * Description : Returns an HTML-formatted menu of the available + * unhidden CGIs, excluding the one given in + * + * Parameters : self = name of CGI to leave out, can be NULL for + * complete listing. + * + * Returns : menu string, or NULL on out-of-memory error. + * + *********************************************************************/ +char *make_menu(const char *self) +{ + const struct cgi_dispatcher *d; + char *result = strdup(""); + + if (self == NULL) + { + self = "NO-SUCH-CGI!"; + } + + /* List available unhidden CGI's and export as "other-cgis" */ + for (d = cgi_dispatchers; d->name; d++) + { + if (d->description && strcmp(d->name, self)) + { + string_append(&result, "
  • name); + string_append(&result, "\">"); + string_append(&result, d->description); + string_append(&result, "
  • "); + } + } + + return result; +} + + +/********************************************************************* + * + * Function : dump_map + * + * Description : HTML-dump a map for debugging (as table) + * + * Parameters : + * 1 : the_map = map to dump + * + * Returns : string with HTML + * + *********************************************************************/ +char *dump_map(const struct map *the_map) +{ + struct map_entry *cur_entry; + char *ret = strdup(""); + + string_append(&ret, "\n"); + + for (cur_entry = the_map->first; + (cur_entry != NULL) && (ret != NULL); + cur_entry = cur_entry->next) + { + string_append(&ret, "\n"); + } + + string_append(&ret, "
    "); + string_join (&ret, html_encode(cur_entry->name)); + string_append(&ret, ""); + string_join (&ret, html_encode(cur_entry->value)); + string_append(&ret, "
    \n"); + return ret; +} + + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/cgi.h b/src/cgi.h new file mode 100644 index 00000000..813d1e8e --- /dev/null +++ b/src/cgi.h @@ -0,0 +1,257 @@ +#ifndef CGI_H_INCLUDED +#define CGI_H_INCLUDED +#define CGI_H_VERSION "$Id: cgi.h,v 1.29 2002/05/19 11:33:21 jongfoster Exp $" +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/cgi.h,v $ + * + * Purpose : Declares functions to intercept request, generate + * html or gif answers, and to compose HTTP resonses. + * + * Functions declared include: + * + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: cgi.h,v $ + * Revision 1.29 2002/05/19 11:33:21 jongfoster + * If a CGI error was not handled, and propogated back to + * dispatch_known_cgi(), then it was assumed to be "out of memory". + * This gave a very misleading error message. + * + * Now other errors will cause a simple message giving the error + * number and asking the user to report a bug. + * + * Bug report: + * http://sourceforge.net/tracker/index.php?func=detail + * &aid=557905&group_id=11118&atid=111118 + * + * Revision 1.28 2002/04/26 12:54:03 oes + * New function add_help_link + * + * Revision 1.27 2002/04/24 02:16:51 oes + * Moved get_char_param, get_string_param and get_number_param here from cgiedit.c + * + * Revision 1.26 2002/04/10 13:38:35 oes + * load_template signature changed + * + * Revision 1.25 2002/04/08 20:50:25 swa + * fixed JB spelling + * + * Revision 1.24 2002/03/26 22:29:54 swa + * we have a new homepage! + * + * Revision 1.23 2002/03/24 16:18:15 jongfoster + * Removing old logo + * + * Revision 1.22 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.21 2002/03/07 03:48:38 oes + * - Changed built-in images from GIF to PNG + * (with regard to Unisys patent issue) + * - Added a 4x4 pattern PNG which is less intrusive + * than the logo but also clearly marks the deleted banners + * + * Revision 1.20 2002/03/04 17:53:22 oes + * Fixed compiled warning + * + * Revision 1.19 2002/01/21 00:33:52 jongfoster + * Adding map_block_keep() to save a few bytes in the edit-actions-list HTML. + * + * Revision 1.18 2001/11/16 00:46:31 jongfoster + * Fixing compiler warnings + * + * Revision 1.17 2001/10/23 21:48:19 jongfoster + * Cleaning up error handling in CGI functions - they now send back + * a HTML error page and should never cause a FATAL error. (Fixes one + * potential source of "denial of service" attacks). + * + * CGI actions file editor that works and is actually useful. + * + * Ability to toggle Junkbuster remotely using a CGI call. + * + * You can turn off both the above features in the main configuration + * file, e.g. if you are running a multi-user proxy. + * + * Revision 1.16 2001/09/16 17:08:54 jongfoster + * Moving simple CGI functions from cgi.c to new file cgisimple.c + * + * Revision 1.15 2001/09/16 15:02:35 jongfoster + * Adding i.j.b/robots.txt. + * Inlining add_stats() since it's only ever called from one place. + * + * Revision 1.14 2001/09/16 11:38:02 jongfoster + * Splitting fill_template() into 2 functions: + * template_load() loads the file + * template_fill() performs the PCRS regexps. + * This is because the CGI edit interface has a "table row" + * template which is used many times in the page - this + * change means it's only loaded from disk once. + * + * Revision 1.13 2001/09/16 11:00:10 jongfoster + * New function alloc_http_response, for symmetry with free_http_response + * + * Revision 1.12 2001/09/13 23:31:25 jongfoster + * Moving image data to cgi.c rather than cgi.h. + * + * Revision 1.11 2001/08/05 16:06:20 jongfoster + * Modifiying "struct map" so that there are now separate header and + * "map_entry" structures. This means that functions which modify a + * map no longer need to return a pointer to the modified map. + * Also, it no longer reverses the order of the entries (which may be + * important with some advanced template substitutions). + * + * Revision 1.10 2001/08/01 21:19:22 jongfoster + * Moving file version information to a separate CGI page. + * + * Revision 1.9 2001/08/01 00:17:54 jongfoster + * Adding prototype for map_conditional + * + * Revision 1.8 2001/07/30 22:08:36 jongfoster + * Tidying up #defines: + * - All feature #defines are now of the form FEATURE_xxx + * - Permanently turned off WIN_GUI_EDIT + * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS + * + * Revision 1.7 2001/07/29 18:43:08 jongfoster + * Changing #ifdef _FILENAME_H to FILENAME_H_INCLUDED, to conform to + * ANSI C rules. + * + * Revision 1.6 2001/06/29 21:45:41 oes + * Indentation, CRLF->LF, Tab-> Space + * + * Revision 1.5 2001/06/29 13:22:44 oes + * - Cleaned up + * - Added new functions: default_exports(), make_menu(), + * error_response() etc, ranamed others and changed + * param and return types. + * - Removed HTTP/HTML snipplets + * - Removed logentry from cancelled commit + * + * Revision 1.4 2001/06/09 10:50:58 jongfoster + * Changing "show URL info" handler to new style. + * Adding "extern" to some function prototypes. + * + * Revision 1.3 2001/06/03 19:12:16 oes + * introduced new cgi handling + * + * No revisions before 1.3 + * + **********************************************************************/ + + +#include "project.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Main dispatch function + */ +extern struct http_response *dispatch_cgi(struct client_state *csp); + +/* Not exactly a CGI */ +extern struct http_response * error_response(struct client_state *csp, + const char *templatename, + int err); + +/* + * CGI support functions + */ +extern struct http_response * alloc_http_response(void); +extern void free_http_response(struct http_response *rsp); + +extern struct http_response *finish_http_response(struct http_response *rsp); + +extern struct map * default_exports(const struct client_state *csp, const char *caller); + +extern jb_err map_block_killer (struct map *exports, const char *name); +extern jb_err map_block_keep (struct map *exports, const char *name); +extern jb_err map_conditional (struct map *exports, const char *name, int choose_first); + +extern jb_err template_load(struct client_state *csp, char ** template_ptr, + const char *templatename, int recursive); +extern jb_err template_fill(char ** template_ptr, const struct map *exports); +extern jb_err template_fill_for_cgi(struct client_state *csp, + const char *templatename, + struct map *exports, + struct http_response *rsp); + +extern void cgi_init_error_messages(void); +extern struct http_response *cgi_error_memory(void); +extern jb_err cgi_error_no_template(struct client_state *csp, + struct http_response *rsp, + const char *template_name); +extern jb_err cgi_error_bad_param(struct client_state *csp, + struct http_response *rsp); +jb_err cgi_error_unknown(struct client_state *csp, + struct http_response *rsp, + jb_err error_to_report); + +extern jb_err get_number_param(struct client_state *csp, + const struct map *parameters, + char *name, + unsigned *pvalue); +extern jb_err get_string_param(const struct map *parameters, + const char *param_name, + const char **pparam); +extern char get_char_param(const struct map *parameters, + const char *param_name); + +/* + * Text generators + */ +extern void get_http_time(int time_offset, char * buf); +extern char *add_help_link(const char *item, struct configuration_spec *config); +extern char *make_menu(const char *self); +extern char *dump_map(const struct map *the_map); + +/* + * Ad replacement images + */ +extern const char image_pattern_data[]; +extern const size_t image_pattern_length; +extern const char image_blank_data[]; +extern const size_t image_blank_length; + +/* Revision control strings from this header and associated .c file */ +extern const char cgi_rcs[]; +extern const char cgi_h_rcs[]; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ndef CGI_H_INCLUDED */ + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/cgiedit.c b/src/cgiedit.c new file mode 100644 index 00000000..8630263a --- /dev/null +++ b/src/cgiedit.c @@ -0,0 +1,4715 @@ +const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.41 2002/05/21 19:09:45 oes Exp $"; +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/cgiedit.c,v $ + * + * Purpose : CGI-based actionsfile editor. + * + * Functions declared include: cgi_edit_* + * + * NOTE: The CGIs in this file use parameter names + * such as "f" and "s" which are really *BAD* choices. + * However, I'm trying to save bytes in the + * edit-actions-list HTML page - the standard actions + * file generated a 550kbyte page, which is ridiculous. + * + * Stick to the short names in this file for consistency. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: cgiedit.c,v $ + * Revision 1.41 2002/05/21 19:09:45 oes + * - Made Add/Edit/Remove URL Submit and Cancel + * buttons jump back to relevant section in eal + * - Bugfix: remove-url-form needs p export + * + * Revision 1.40 2002/05/19 11:34:35 jongfoster + * Handling read-only actions files better - report the actual + * error, not "Out of memory"! + * + * Bug report: + * http://sourceforge.net/tracker/index.php?func=detail + * &aid=557905&group_id=11118&atid=111118 + * + * Revision 1.39 2002/05/12 21:39:15 jongfoster + * - Adding Doxygen-style comments to structures and #defines. + * - Correcting function comments + * + * Revision 1.38 2002/05/03 23:00:38 jongfoster + * Support for templates for "standard actions" buttons. + * See bug #549871 + * + * Revision 1.37 2002/04/30 11:14:52 oes + * Made csp the first parameter in *action_to_html + * + * Revision 1.36 2002/04/26 21:53:30 jongfoster + * Fixing a memory leak. (Near, but not caused by, my earlier commit). + * + * Revision 1.35 2002/04/26 21:50:02 jongfoster + * Honouring default exports in edit-actions-for-url-filter template. + * + * Revision 1.34 2002/04/26 12:54:17 oes + * Adaptions to changes in actions.c + * + * Revision 1.33 2002/04/24 02:17:47 oes + * - Moved get_char_param, get_string_param and get_number_param to cgi.c + * - Comments + * - Activated Jon's code for editing multiple AFs + * - cgi_edit_list_actions now provides context-sensitive + * help, looks up all action sets from standard.action and + * makes buttons for them in the catchall section + * - cgi_edit_action_submit now honors a p parameter, looks up + * the corresponding action set, and sets the catchall pattern's + * actions accordingly. + * + * Revision 1.32 2002/04/19 16:55:31 jongfoster + * Fixing newline problems. If we do our own text file newline + * mangling, we don't want the library to do any, so we need to + * open the files in *binary* mode. + * + * Revision 1.31 2002/04/18 19:21:08 jongfoster + * Added code to detect "conventional" action files, that start + * with a set of actions for all URLs (the pattern "/"). + * These are special-cased in the "edit-actions-list" CGI, so + * that a special UI can be written for them. + * + * Revision 1.30 2002/04/10 13:38:35 oes + * load_template signature changed + * + * Revision 1.29 2002/04/08 16:59:08 oes + * Fixed comment + * + * Revision 1.28 2002/03/27 12:30:29 oes + * Deleted unsused variable + * + * Revision 1.27 2002/03/26 23:06:04 jongfoster + * Removing duplicate @ifs on the toggle page + * + * Revision 1.26 2002/03/26 22:59:17 jongfoster + * Fixing /toggle to display status consistently. + * + * Revision 1.25 2002/03/26 22:29:54 swa + * we have a new homepage! + * + * Revision 1.24 2002/03/24 15:23:33 jongfoster + * Name changes + * + * Revision 1.23 2002/03/24 13:32:41 swa + * name change related issues + * + * Revision 1.22 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.21 2002/03/22 18:02:48 jongfoster + * Fixing remote toggle + * + * Revision 1.20 2002/03/16 20:28:34 oes + * Added descriptions to the filters so users will know what they select in the cgi editor + * + * Revision 1.19 2002/03/16 18:38:14 jongfoster + * Stopping stupid or malicious users from breaking the actions + * file using the web-based editor. + * + * Revision 1.18 2002/03/16 14:57:44 jongfoster + * Full support for enabling/disabling modular filters. + * + * Revision 1.17 2002/03/16 14:26:42 jongfoster + * First version of modular filters support - READ ONLY! + * Fixing a double-free bug in the out-of-memory handling in map_radio(). + * + * Revision 1.16 2002/03/07 03:46:17 oes + * Fixed compiler warnings + * + * Revision 1.15 2002/03/06 22:54:35 jongfoster + * Automated function-comment nitpicking. + * + * Revision 1.14 2002/03/05 00:24:51 jongfoster + * Patch to always edit the current actions file. + * + * Revision 1.13 2002/03/04 02:07:59 david__schmidt + * Enable web editing of actions file on OS/2 (it had been broken all this time!) + * + * Revision 1.12 2002/03/03 09:18:03 joergs + * Made jumbjuster work on AmigaOS again. + * + * Revision 1.11 2002/01/23 01:03:31 jongfoster + * Fixing gcc [CygWin] compiler warnings + * + * Revision 1.10 2002/01/23 00:22:59 jongfoster + * Adding new function cgi_edit_actions_section_swap(), to reorder + * the actions file. + * + * Adding get_url_spec_param() to get a validated URL pattern. + * + * Moving edit_read_line() out of this file and into loaders.c. + * + * Adding missing html_encode() to many CGI functions. + * + * Moving the functions that #include actionlist.h to the end of the file, + * because the Visual C++ 97 debugger gets extremely confused if you try + * to debug any code that comes after them in the file. + * + * Major optimizations in cgi_edit_actions_list() to reduce the size of + * the generated HTML (down 40% from 550k to 304k), with major side-effects + * throughout the editor and templates. In particular, the length of the + * URLs throughout the editor has been drastically reduced, by cutting + * paramater names down to 1 character and CGI names down to 3-4 + * characters, by removing all non-essential CGI paramaters even at the + * expense of having to re-read the actions file for the most trivial + * page, and by using relative rather than absolute URLs. This means + * that this (typical example): + * + * + * + * is now this: + * + * + * + * Revision 1.9 2002/01/17 20:56:22 jongfoster + * Replacing hard references to the URL of the config interface + * with #defines from project.h + * + * Revision 1.8 2001/11/30 23:35:51 jongfoster + * Renaming actionsfile to ijb.action + * + * Revision 1.7 2001/11/13 00:28:24 jongfoster + * - Renaming parameters from edit-actions-for-url so that they only + * contain legal JavaScript characters. If we wanted to write + * JavaScript that worked with Netscape 4, this is nessacery. + * (Note that at the moment the JavaScript doesn't actually work + * with Netscape 4, but now this is purely a template issue, not + * one affecting code). + * - Adding new CGIs for use by non-JavaScript browsers: + * edit-actions-url-form + * edit-actions-add-url-form + * edit-actions-remove-url-form + * - Fixing || bug. + * + * Revision 1.6 2001/10/29 03:48:09 david__schmidt + * OS/2 native needed a snprintf() routine. Added one to miscutil, brackedted + * by and __OS2__ ifdef. + * + * Revision 1.5 2001/10/25 03:40:48 david__schmidt + * Change in porting tactics: OS/2's EMX porting layer doesn't allow multiple + * threads to call select() simultaneously. So, it's time to do a real, live, + * native OS/2 port. See defines for __EMX__ (the porting layer) vs. __OS2__ + * (native). Both versions will work, but using __OS2__ offers multi-threading. + * + * Revision 1.4 2001/10/23 21:48:19 jongfoster + * Cleaning up error handling in CGI functions - they now send back + * a HTML error page and should never cause a FATAL error. (Fixes one + * potential source of "denial of service" attacks). + * + * CGI actions file editor that works and is actually useful. + * + * Ability to toggle JunkBuster remotely using a CGI call. + * + * You can turn off both the above features in the main configuration + * file, e.g. if you are running a multi-user proxy. + * + * Revision 1.3 2001/10/14 22:12:49 jongfoster + * New version of CGI-based actionsfile editor. + * Major changes, including: + * - Completely new file parser and file output routines + * - edit-actions CGI renamed edit-actions-for-url + * - All CGIs now need a filename parameter, except for... + * - New CGI edit-actions which doesn't need a filename, + * to allow you to start the editor up. + * - edit-actions-submit now works, and now automatically + * redirects you back to the main edit-actions-list handler. + * + * Revision 1.2 2001/09/16 17:05:14 jongfoster + * Removing unused #include showarg.h + * + * Revision 1.1 2001/09/16 15:47:37 jongfoster + * First version of CGI-based edit interface. This is very much a + * work-in-progress, and you can't actually use it to edit anything + * yet. You must #define FEATURE_CGI_EDIT_ACTIONS for these changes + * to have any effect. + * + * + **********************************************************************/ + + +#include "config.h" + +/* + * FIXME: Following includes copied from cgi.c - which are actually needed? + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#define snprintf _snprintf +#endif /* def _WIN32 */ + +#include "project.h" +#include "cgi.h" +#include "cgiedit.h" +#include "cgisimple.h" +#include "list.h" +#include "encode.h" +#include "actions.h" +#include "miscutil.h" +#include "errlog.h" +#include "loaders.h" +#include "loadcfg.h" +/* loadcfg.h is for g_bToggleIJB only */ +#include "urlmatch.h" + +const char cgiedit_h_rcs[] = CGIEDIT_H_VERSION; + + +#ifdef FEATURE_CGI_EDIT_ACTIONS + +/** + * A line in an editable_file. + */ +struct file_line +{ + /** Next entry in the linked list */ + struct file_line * next; + + /** The raw data, to write out if this line is unmodified. */ + char * raw; + + /** Comments and/or whitespace to put before this line if it's modified + and then written out. */ + char * prefix; + + /** The actual data, as a string. Line continuation and comment removal + are performed on the data read from file before it's stored here, so + it will be a single line of data. */ + char * unprocessed; + + /** The type of data on this line. One of the FILE_LINE_xxx constants. */ + int type; + + /** The actual data, processed into some sensible data type. */ + union + { + + /** An action specification. */ + struct action_spec action[1]; + + /** A name=value pair. */ + struct + { + + /** The name in the name=value pair. */ + char * name; + + /** The value in the name=value pair, as a string. */ + char * svalue; + + /** The value in the name=value pair, as an integer. */ + int ivalue; + + } setting; + + /* Add more data types here... e.g. + + + struct url_spec url[1]; + + struct + { + struct action_spec action[1]; + const char * name; + } alias; + + */ + + } data; + +}; + +/** This file_line has not been processed yet. */ +#define FILE_LINE_UNPROCESSED 1 + +/** This file_line is blank. Can only appear at the end of a file, due to + the way the parser works. */ +#define FILE_LINE_BLANK 2 + +/** This file_line says {{alias}}. */ +#define FILE_LINE_ALIAS_HEADER 3 + +/** This file_line defines an alias. */ +#define FILE_LINE_ALIAS_ENTRY 4 + +/** This file_line defines an {action}. */ +#define FILE_LINE_ACTION 5 + +/** This file_line specifies a URL pattern. */ +#define FILE_LINE_URL 6 + +/** This file_line says {{settings}}. */ +#define FILE_LINE_SETTINGS_HEADER 7 + +/** This file_line is in a {{settings}} block. */ +#define FILE_LINE_SETTINGS_ENTRY 8 + +/** This file_line says {{description}}. */ +#define FILE_LINE_DESCRIPTION_HEADER 9 + +/** This file_line is in a {{description}} block. */ +#define FILE_LINE_DESCRIPTION_ENTRY 10 + + +/** + * A configuration file, in a format that can be edited and written back to + * disk. + */ +struct editable_file +{ + struct file_line * lines; /**< The contents of the file. A linked list of lines. */ + const char * filename; /**< Full pathname - e.g. "/etc/privoxy/wibble.action". */ + const char * identifier; /**< Filename stub - e.g. "wibble". Use for CGI param. */ + /**< Pre-encoded with url_encode() for ease of use. */ + const char * version_str; /**< Last modification time, as a string. For CGI param. */ + /**< Can be used in URL without using url_param(). */ + unsigned version; /**< Last modification time - prevents chaos with + the browser's "back" button. Note that this is a + time_t cast to an unsigned. When comparing, always + cast the time_t to an unsigned, and *NOT* vice-versa. + This may lose the top few bits, but they're not + significant anyway. */ + int newline; /**< Newline convention - one of the NEWLINE_xxx constants. + Note that changing this after the file has been + read in will cause a mess. */ + struct file_line * parse_error; /**< On parse error, this is the offending line. */ + const char * parse_error_text; /**< On parse error, this is the problem. + (Statically allocated) */ +}; + +/* FIXME: Following non-static functions should be prototyped in .h or made static */ + +/* Functions to read and write arbitrary config files */ +jb_err edit_read_file(struct client_state *csp, + const struct map *parameters, + int require_version, + const char *suffix, + struct editable_file **pfile); +jb_err edit_write_file(struct editable_file * file); +void edit_free_file(struct editable_file * file); + +/* Functions to read and write actions files */ +jb_err edit_parse_actions_file(struct editable_file * file); +jb_err edit_read_actions_file(struct client_state *csp, + struct http_response *rsp, + const struct map *parameters, + int require_version, + struct editable_file **pfile); + +/* Error handlers */ +jb_err cgi_error_modified(struct client_state *csp, + struct http_response *rsp, + const char *filename); +jb_err cgi_error_parse(struct client_state *csp, + struct http_response *rsp, + struct editable_file *file); +jb_err cgi_error_file(struct client_state *csp, + struct http_response *rsp, + const char *filename); +jb_err cgi_error_file_read_only(struct client_state *csp, + struct http_response *rsp, + const char *filename); +jb_err cgi_error_disabled(struct client_state *csp, + struct http_response *rsp); + +/* Internal arbitrary config file support functions */ +static jb_err edit_read_file_lines(FILE *fp, struct file_line ** pfile, int *newline); +static void edit_free_file_lines(struct file_line * first_line); + +/* Internal actions file support functions */ +static int match_actions_file_header_line(const char * line, const char * name); +static jb_err split_line_on_equals(const char * line, char ** pname, char ** pvalue); + +/* Internal parameter parsing functions */ +static jb_err get_file_name_param(struct client_state *csp, + const struct map *parameters, + const char *param_name, + const char *suffix, + char **pfilename, + const char **pparam); + +static jb_err get_url_spec_param(struct client_state *csp, + const struct map *parameters, + const char *name, + char **pvalue); + + +/* Internal actionsfile <==> HTML conversion functions */ +static jb_err map_radio(struct map * exports, + const char * optionname, + const char * values, + int value); +static jb_err actions_to_radio(struct map * exports, + const struct action_spec *action); +static jb_err actions_from_radio(const struct map * parameters, + struct action_spec *action); + + +static jb_err map_copy_parameter_html(struct map *out, + const struct map *in, + const char *name); +#if 0 /* unused function */ +static jb_err map_copy_parameter_url(struct map *out, + const struct map *in, + const char *name); +#endif /* unused function */ + +/* Internal convenience functions */ +static char *section_target(const unsigned sectionid); + +/********************************************************************* + * + * Function : section_target + * + * Description : Given an unsigned (section id) n, produce a dynamically + * allocated string of the form #l, for use in link + * targets. + * + * Parameters : + * 1 : sectionid = start line number of section + * + * Returns : String with link target, or NULL if out of + * memory + * + *********************************************************************/ +static char *section_target(const unsigned sectionid) +{ + char buf[30]; + + snprintf(buf, 30, "#l%d", sectionid); + return(strdup(buf)); + +} + + +/********************************************************************* + * + * Function : map_copy_parameter_html + * + * Description : Copy a CGI parameter from one map to another, HTML + * encoding it. + * + * Parameters : + * 1 : out = target map + * 2 : in = source map + * 3 : name = name of cgi parameter to copy + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory + * JB_ERR_CGI_PARAMS if the parameter doesn't exist + * in the source map + * + *********************************************************************/ +static jb_err map_copy_parameter_html(struct map *out, + const struct map *in, + const char *name) +{ + const char * value; + jb_err err; + + assert(out); + assert(in); + assert(name); + + value = lookup(in, name); + err = map(out, name, 1, html_encode(value), 0); + + if (err) + { + /* Out of memory */ + return err; + } + else if (*value == '\0') + { + return JB_ERR_CGI_PARAMS; + } + else + { + return JB_ERR_OK; + } +} + + +#if 0 /* unused function */ +/********************************************************************* + * + * Function : map_copy_parameter_url + * + * Description : Copy a CGI parameter from one map to another, URL + * encoding it. + * + * Parameters : + * 1 : out = target map + * 2 : in = source map + * 3 : name = name of cgi parameter to copy + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory + * JB_ERR_CGI_PARAMS if the parameter doesn't exist + * in the source map + * + *********************************************************************/ +static jb_err map_copy_parameter_url(struct map *out, + const struct map *in, + const char *name) +{ + const char * value; + jb_err err; + + assert(out); + assert(in); + assert(name); + + value = lookup(in, name); + err = map(out, name, 1, url_encode(value), 0); + + if (err) + { + /* Out of memory */ + return err; + } + else if (*value == '\0') + { + return JB_ERR_CGI_PARAMS; + } + else + { + return JB_ERR_OK; + } +} +#endif /* 0 - unused function */ + +/********************************************************************* + * + * Function : cgi_edit_actions_url_form + * + * Description : CGI function that displays a form for + * edit-actions-url + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : rsp = http_response data structure for output + * 3 : parameters = map of cgi parameters + * + * CGI Parameters + * f : (filename) Identifies the file to edit + * v : (version) File's last-modified time + * p : (pattern) Line number of pattern to edit + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory + * JB_ERR_CGI_PARAMS if the CGI parameters are not + * specified or not valid. + * + *********************************************************************/ +jb_err cgi_edit_actions_url_form(struct client_state *csp, + struct http_response *rsp, + const struct map *parameters) +{ + struct map * exports; + unsigned patternid; + struct editable_file * file; + struct file_line * cur_line; + unsigned line_number; + unsigned section_start_line_number = 0; + jb_err err; + + assert(csp); + assert(rsp); + assert(parameters); + + if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS)) + { + return cgi_error_disabled(csp, rsp); + } + + err = get_number_param(csp, parameters, "p", &patternid); + if (err) + { + return err; + } + + err = edit_read_actions_file(csp, rsp, parameters, 1, &file); + if (err) + { + /* No filename specified, can't read file, modified, or out of memory. */ + return (err == JB_ERR_FILE ? JB_ERR_OK : err); + } + + cur_line = file->lines; + + for (line_number = 1; (cur_line != NULL) && (line_number < patternid); line_number++) + { + if (cur_line->type == FILE_LINE_ACTION) + { + section_start_line_number = line_number; + } + cur_line = cur_line->next; + } + + if ( (cur_line == NULL) + || (line_number != patternid) + || (patternid < 1) + || (cur_line->type != FILE_LINE_URL)) + { + /* Invalid "patternid" parameter */ + edit_free_file(file); + return JB_ERR_CGI_PARAMS; + } + + if (NULL == (exports = default_exports(csp, NULL))) + { + edit_free_file(file); + return JB_ERR_MEMORY; + } + + err = map(exports, "f", 1, file->identifier, 1); + if (!err) err = map(exports, "v", 1, file->version_str, 1); + if (!err) err = map(exports, "p", 1, url_encode(lookup(parameters, "p")), 0); + if (!err) err = map(exports, "u", 1, html_encode(cur_line->unprocessed), 0); + if (!err) err = map(exports, "jumptarget", 1, section_target(section_start_line_number), 0); + + edit_free_file(file); + + if (err) + { + free_map(exports); + return err; + } + + return template_fill_for_cgi(csp, "edit-actions-url-form", exports, rsp); +} + + +/********************************************************************* + * + * Function : cgi_edit_actions_add_url_form + * + * Description : CGI function that displays a form for + * edit-actions-url + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : rsp = http_response data structure for output + * 3 : parameters = map of cgi parameters + * + * CGI Parameters : + * f : (filename) Identifies the file to edit + * v : (version) File's last-modified time + * s : (section) Line number of section to edit + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory + * JB_ERR_CGI_PARAMS if the CGI parameters are not + * specified or not valid. + * + *********************************************************************/ +jb_err cgi_edit_actions_add_url_form(struct client_state *csp, + struct http_response *rsp, + const struct map *parameters) +{ + struct map *exports; + jb_err err; + + assert(csp); + assert(rsp); + assert(parameters); + + if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS)) + { + return cgi_error_disabled(csp, rsp); + } + + if (NULL == (exports = default_exports(csp, NULL))) + { + return JB_ERR_MEMORY; + } + + err = map_copy_parameter_html(exports, parameters, "f"); + if (!err) err = map_copy_parameter_html(exports, parameters, "v"); + if (!err) err = map_copy_parameter_html(exports, parameters, "s"); + + if (err) + { + free_map(exports); + return err; + } + + return template_fill_for_cgi(csp, "edit-actions-add-url-form", exports, rsp); +} + + +/********************************************************************* + * + * Function : cgi_edit_actions_remove_url_form + * + * Description : CGI function that displays a form for + * edit-actions-url + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : rsp = http_response data structure for output + * 3 : parameters = map of cgi parameters + * + * CGI Parameters : + * f : (filename) Identifies the file to edit + * v : (version) File's last-modified time + * p : (pattern) Line number of pattern to edit + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory + * JB_ERR_CGI_PARAMS if the CGI parameters are not + * specified or not valid. + * + *********************************************************************/ +jb_err cgi_edit_actions_remove_url_form(struct client_state *csp, + struct http_response *rsp, + const struct map *parameters) +{ + struct map * exports; + unsigned patternid; + struct editable_file * file; + struct file_line * cur_line; + unsigned line_number; + unsigned section_start_line_number = 0; + jb_err err; + + assert(csp); + assert(rsp); + assert(parameters); + + if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS)) + { + return cgi_error_disabled(csp, rsp); + } + + err = get_number_param(csp, parameters, "p", &patternid); + if (err) + { + return err; + } + + err = edit_read_actions_file(csp, rsp, parameters, 1, &file); + if (err) + { + /* No filename specified, can't read file, modified, or out of memory. */ + return (err == JB_ERR_FILE ? JB_ERR_OK : err); + } + + cur_line = file->lines; + + for (line_number = 1; (cur_line != NULL) && (line_number < patternid); line_number++) + { + if (cur_line->type == FILE_LINE_ACTION) + { + section_start_line_number = line_number; + } + cur_line = cur_line->next; + } + + if ( (cur_line == NULL) + || (line_number != patternid) + || (patternid < 1) + || (cur_line->type != FILE_LINE_URL)) + { + /* Invalid "patternid" parameter */ + edit_free_file(file); + return JB_ERR_CGI_PARAMS; + } + + if (NULL == (exports = default_exports(csp, NULL))) + { + edit_free_file(file); + return JB_ERR_MEMORY; + } + + err = map(exports, "f", 1, file->identifier, 1); + if (!err) err = map(exports, "v", 1, file->version_str, 1); + if (!err) err = map(exports, "p", 1, url_encode(lookup(parameters, "p")), 0); + if (!err) err = map(exports, "u", 1, html_encode(cur_line->unprocessed), 0); + if (!err) err = map(exports, "jumptarget", 1, section_target(section_start_line_number), 0); + + edit_free_file(file); + + if (err) + { + free_map(exports); + return err; + } + + return template_fill_for_cgi(csp, "edit-actions-remove-url-form", exports, rsp); +} + + +/********************************************************************* + * + * Function : edit_write_file + * + * Description : Write a complete file to disk. + * + * Parameters : + * 1 : file = File to write. + * + * Returns : JB_ERR_OK on success + * JB_ERR_FILE on error writing to file. + * JB_ERR_MEMORY on out of memory + * + *********************************************************************/ +jb_err edit_write_file(struct editable_file * file) +{ + FILE * fp; + struct file_line * cur_line; + struct stat statbuf[1]; + char version_buf[22]; /* 22 = ceil(log10(2^64)) + 2 = max number of + digits in time_t, assuming this is a 64-bit + machine, plus null terminator, plus one + for paranoia */ + + assert(file); + assert(file->filename); + + if (NULL == (fp = fopen(file->filename, "wb"))) + { + return JB_ERR_FILE; + } + + cur_line = file->lines; + while (cur_line != NULL) + { + if (cur_line->raw) + { + if (fputs(cur_line->raw, fp) < 0) + { + fclose(fp); + return JB_ERR_FILE; + } + } + else + { + if (cur_line->prefix) + { + if (fputs(cur_line->prefix, fp) < 0) + { + fclose(fp); + return JB_ERR_FILE; + } + } + if (cur_line->unprocessed) + { + + if (NULL != strchr(cur_line->unprocessed, '#')) + { + /* Must quote '#' characters */ + int numhash = 0; + int len; + char * src; + char * dest; + char * str; + + /* Count number of # characters, so we know length of output string */ + src = cur_line->unprocessed; + while (NULL != (src = strchr(src, '#'))) + { + numhash++; + src++; + } + assert(numhash > 0); + + /* Allocate new memory for string */ + len = strlen(cur_line->unprocessed); + if (NULL == (str = malloc((size_t) len + 1 + numhash))) + { + /* Uh oh, just trashed file! */ + fclose(fp); + return JB_ERR_MEMORY; + } + + /* Loop through string from end */ + src = cur_line->unprocessed + len; + dest = str + len + numhash; + for ( ; len >= 0; len--) + { + if ((*dest-- = *src--) == '#') + { + *dest-- = '\\'; + numhash--; + assert(numhash >= 0); + } + } + assert(numhash == 0); + assert(src + 1 == cur_line->unprocessed); + assert(dest + 1 == str); + + if (fputs(str, fp) < 0) + { + free(str); + fclose(fp); + return JB_ERR_FILE; + } + + free(str); + } + else + { + /* Can write without quoting '#' characters. */ + if (fputs(cur_line->unprocessed, fp) < 0) + { + fclose(fp); + return JB_ERR_FILE; + } + } + if (fputs(NEWLINE(file->newline), fp) < 0) + { + fclose(fp); + return JB_ERR_FILE; + } + } + else + { + /* FIXME: Write data from file->data->whatever */ + assert(0); + } + } + cur_line = cur_line->next; + } + + fclose(fp); + + + /* Update the version stamp in the file structure, since we just + * wrote to the file & changed it's date. + */ + if (stat(file->filename, statbuf) < 0) + { + /* Error, probably file not found. */ + return JB_ERR_FILE; + } + file->version = (unsigned)statbuf->st_mtime; + + /* Correct file->version_str */ + freez(file->version_str); + snprintf(version_buf, 22, "%u", file->version); + version_buf[21] = '\0'; + file->version_str = strdup(version_buf); + if (version_buf == NULL) + { + return JB_ERR_MEMORY; + } + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : edit_free_file + * + * Description : Free a complete file in memory. + * + * Parameters : + * 1 : file = Data structure to free. + * + * Returns : N/A + * + *********************************************************************/ +void edit_free_file(struct editable_file * file) +{ + if (!file) + { + /* Silently ignore NULL pointer */ + return; + } + + edit_free_file_lines(file->lines); + freez(file->filename); + freez(file->identifier); + freez(file->version_str); + file->version = 0; + file->parse_error_text = NULL; /* Statically allocated */ + file->parse_error = NULL; + + free(file); +} + + +/********************************************************************* + * + * Function : edit_free_file_lines + * + * Description : Free an entire linked list of file lines. + * + * Parameters : + * 1 : first_line = Data structure to free. + * + * Returns : N/A + * + *********************************************************************/ +static void edit_free_file_lines(struct file_line * first_line) +{ + struct file_line * next_line; + + while (first_line != NULL) + { + next_line = first_line->next; + first_line->next = NULL; + freez(first_line->raw); + freez(first_line->prefix); + freez(first_line->unprocessed); + switch(first_line->type) + { + case 0: /* special case if memory zeroed */ + case FILE_LINE_UNPROCESSED: + case FILE_LINE_BLANK: + case FILE_LINE_ALIAS_HEADER: + case FILE_LINE_SETTINGS_HEADER: + case FILE_LINE_DESCRIPTION_HEADER: + case FILE_LINE_DESCRIPTION_ENTRY: + case FILE_LINE_ALIAS_ENTRY: + case FILE_LINE_URL: + /* No data is stored for these */ + break; + + case FILE_LINE_ACTION: + free_action(first_line->data.action); + break; + + case FILE_LINE_SETTINGS_ENTRY: + freez(first_line->data.setting.name); + freez(first_line->data.setting.svalue); + break; + default: + /* Should never happen */ + assert(0); + break; + } + first_line->type = 0; /* paranoia */ + free(first_line); + first_line = next_line; + } +} + + +/********************************************************************* + * + * Function : match_actions_file_header_line + * + * Description : Match an actions file {{header}} line + * + * Parameters : + * 1 : line = String from file + * 2 : name = Header to match against + * + * Returns : 0 iff they match. + * + *********************************************************************/ +static int match_actions_file_header_line(const char * line, const char * name) +{ + size_t len; + + assert(line); + assert(name); + + /* Look for "{{" */ + if ((line[0] != '{') || (line[1] != '{')) + { + return 1; + } + line += 2; + + /* Look for optional whitespace */ + while ( (*line == ' ') || (*line == '\t') ) + { + line++; + } + + /* Look for the specified name (case-insensitive) */ + len = strlen(name); + if (0 != strncmpic(line, name, len)) + { + return 1; + } + line += len; + + /* Look for optional whitespace */ + while ( (*line == ' ') || (*line == '\t') ) + { + line++; + } + + /* Look for "}}" and end of string*/ + if ((line[0] != '}') || (line[1] != '}') || (line[2] != '\0')) + { + return 1; + } + + /* It matched!! */ + return 0; +} + + +/********************************************************************* + * + * Function : match_actions_file_header_line + * + * Description : Match an actions file {{header}} line + * + * Parameters : + * 1 : line = String from file. Must not start with + * whitespace (else infinite loop!) + * 2 : pname = Destination for name + * 2 : pvalue = Destination for value + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory + * JB_ERR_PARSE if there's no "=" sign, or if there's + * nothing before the "=" sign (but empty + * values *after* the "=" sign are legal). + * + *********************************************************************/ +static jb_err split_line_on_equals(const char * line, char ** pname, char ** pvalue) +{ + const char * name_end; + const char * value_start; + size_t name_len; + + assert(line); + assert(pname); + assert(pvalue); + assert(*line != ' '); + assert(*line != '\t'); + + *pname = NULL; + *pvalue = NULL; + + value_start = strchr(line, '='); + if ((value_start == NULL) || (value_start == line)) + { + return JB_ERR_PARSE; + } + + name_end = value_start - 1; + + /* Eat any whitespace before the '=' */ + while ((*name_end == ' ') || (*name_end == '\t')) + { + /* + * we already know we must have at least 1 non-ws char + * at start of buf - no need to check + */ + name_end--; + } + + name_len = name_end - line + 1; /* Length excluding \0 */ + if (NULL == (*pname = (char *) malloc(name_len + 1))) + { + return JB_ERR_MEMORY; + } + strncpy(*pname, line, name_len); + (*pname)[name_len] = '\0'; + + /* Eat any the whitespace after the '=' */ + value_start++; + while ((*value_start == ' ') || (*value_start == '\t')) + { + value_start++; + } + + if (NULL == (*pvalue = strdup(value_start))) + { + free(*pname); + *pname = NULL; + return JB_ERR_MEMORY; + } + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : edit_parse_actions_file + * + * Description : Parse an actions file in memory. + * + * Passed linked list must have the "data" member + * zeroed, and must contain valid "next" and + * "unprocessed" fields. The "raw" and "prefix" + * fields are ignored, and "type" is just overwritten. + * + * Note that on error the file may have been + * partially parsed. + * + * Parameters : + * 1 : file = Actions file to be parsed in-place. + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory + * JB_ERR_PARSE on error + * + *********************************************************************/ +jb_err edit_parse_actions_file(struct editable_file * file) +{ + struct file_line * cur_line; + size_t len; + const char * text; /* Text from a line */ + char * name; /* For lines of the form name=value */ + char * value; /* For lines of the form name=value */ + struct action_alias * alias_list = NULL; + jb_err err = JB_ERR_OK; + + /* alias_list contains the aliases defined in this file. + * It might be better to use the "file_line.data" fields + * in the relavent places instead. + */ + + cur_line = file->lines; + + /* A note about blank line support: Blank lines should only + * ever occur as the last line in the file. This function + * is more forgiving than that - FILE_LINE_BLANK can occur + * anywhere. + */ + + /* Skip leading blanks. Should only happen if file is + * empty (which is valid, but pointless). + */ + while ( (cur_line != NULL) + && (cur_line->unprocessed[0] == '\0') ) + { + /* Blank line */ + cur_line->type = FILE_LINE_BLANK; + cur_line = cur_line->next; + } + + if ( (cur_line != NULL) + && (cur_line->unprocessed[0] != '{') ) + { + /* File doesn't start with a header */ + file->parse_error = cur_line; + file->parse_error_text = "First (non-comment) line of the file must contain a header."; + return JB_ERR_PARSE; + } + + if ( (cur_line != NULL) && (0 == + match_actions_file_header_line(cur_line->unprocessed, "settings") ) ) + { + cur_line->type = FILE_LINE_SETTINGS_HEADER; + + cur_line = cur_line->next; + while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{')) + { + if (cur_line->unprocessed[0]) + { + cur_line->type = FILE_LINE_SETTINGS_ENTRY; + + err = split_line_on_equals(cur_line->unprocessed, + &cur_line->data.setting.name, + &cur_line->data.setting.svalue); + if (err == JB_ERR_MEMORY) + { + return err; + } + else if (err != JB_ERR_OK) + { + /* Line does not contain a name=value pair */ + file->parse_error = cur_line; + file->parse_error_text = "Expected a name=value pair on this {{description}} line, but couldn't find one."; + return JB_ERR_PARSE; + } + } + else + { + cur_line->type = FILE_LINE_BLANK; + } + cur_line = cur_line->next; + } + } + + if ( (cur_line != NULL) && (0 == + match_actions_file_header_line(cur_line->unprocessed, "description") ) ) + { + cur_line->type = FILE_LINE_DESCRIPTION_HEADER; + + cur_line = cur_line->next; + while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{')) + { + if (cur_line->unprocessed[0]) + { + cur_line->type = FILE_LINE_DESCRIPTION_ENTRY; + } + else + { + cur_line->type = FILE_LINE_BLANK; + } + cur_line = cur_line->next; + } + } + + if ( (cur_line != NULL) && (0 == + match_actions_file_header_line(cur_line->unprocessed, "alias") ) ) + { + cur_line->type = FILE_LINE_ALIAS_HEADER; + + cur_line = cur_line->next; + while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{')) + { + if (cur_line->unprocessed[0]) + { + /* define an alias */ + struct action_alias * new_alias; + + cur_line->type = FILE_LINE_ALIAS_ENTRY; + + err = split_line_on_equals(cur_line->unprocessed, &name, &value); + if (err == JB_ERR_MEMORY) + { + return err; + } + else if (err != JB_ERR_OK) + { + /* Line does not contain a name=value pair */ + file->parse_error = cur_line; + file->parse_error_text = "Expected a name=value pair on this {{alias}} line, but couldn't find one."; + return JB_ERR_PARSE; + } + + if ((new_alias = zalloc(sizeof(*new_alias))) == NULL) + { + /* Out of memory */ + free(name); + free(value); + free_alias_list(alias_list); + return JB_ERR_MEMORY; + } + + err = get_actions(value, alias_list, new_alias->action); + if (err) + { + /* Invalid action or out of memory */ + free(name); + free(value); + free(new_alias); + free_alias_list(alias_list); + if (err == JB_ERR_MEMORY) + { + return err; + } + else + { + /* Line does not contain a name=value pair */ + file->parse_error = cur_line; + file->parse_error_text = "This alias does not specify a valid set of actions."; + return JB_ERR_PARSE; + } + } + + free(value); + + new_alias->name = name; + + /* add to list */ + new_alias->next = alias_list; + alias_list = new_alias; + } + else + { + cur_line->type = FILE_LINE_BLANK; + } + cur_line = cur_line->next; + } + } + + /* Header done, process the main part of the file */ + while (cur_line != NULL) + { + /* At this point, (cur_line->unprocessed[0] == '{') */ + assert(cur_line->unprocessed[0] == '{'); + text = cur_line->unprocessed + 1; + len = strlen(text) - 1; + if (text[len] != '}') + { + /* No closing } on header */ + free_alias_list(alias_list); + file->parse_error = cur_line; + file->parse_error_text = "Headers starting with '{' must have a " + "closing bracket ('}'). Headers starting with two brackets ('{{') " + "must close with two brackets ('}}')."; + return JB_ERR_PARSE; + } + + if (text[0] == '{') + { + /* An invalid {{ header. */ + free_alias_list(alias_list); + file->parse_error = cur_line; + file->parse_error_text = "Unknown or unexpected two-bracket header. " + "Please remember that the system (two-bracket) headers must " + "appear in the order {{settings}}, {{description}}, {{alias}}, " + "and must appear before any actions (one-bracket) headers. " + "Also note that system headers may not be repeated."; + return JB_ERR_PARSE; + } + + while ( (*text == ' ') || (*text == '\t') ) + { + text++; + len--; + } + while ( (len > 0) + && ( (text[len - 1] == ' ') + || (text[len - 1] == '\t') ) ) + { + len--; + } + + cur_line->type = FILE_LINE_ACTION; + + /* Remove {} and make copy */ + if (NULL == (value = (char *) malloc(len + 1))) + { + /* Out of memory */ + free_alias_list(alias_list); + return JB_ERR_MEMORY; + } + strncpy(value, text, len); + value[len] = '\0'; + + /* Get actions */ + err = get_actions(value, alias_list, cur_line->data.action); + if (err) + { + /* Invalid action or out of memory */ + free(value); + free_alias_list(alias_list); + if (err == JB_ERR_MEMORY) + { + return err; + } + else + { + /* Line does not contain a name=value pair */ + file->parse_error = cur_line; + file->parse_error_text = "This header does not specify a valid set of actions."; + return JB_ERR_PARSE; + } + } + + /* Done with string - it was clobbered anyway */ + free(value); + + /* Process next line */ + cur_line = cur_line->next; + + /* Loop processing URL patterns */ + while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{')) + { + if (cur_line->unprocessed[0]) + { + /* Could parse URL here, but this isn't currently needed */ + + cur_line->type = FILE_LINE_URL; + } + else + { + cur_line->type = FILE_LINE_BLANK; + } + cur_line = cur_line->next; + } + } /* End main while(cur_line != NULL) loop */ + + free_alias_list(alias_list); + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : edit_read_file_lines + * + * Description : Read all the lines of a file into memory. + * Handles whitespace, comments and line continuation. + * + * Parameters : + * 1 : fp = File to read from. On return, this will be + * at EOF but it will not have been closed. + * 2 : pfile = Destination for a linked list of file_lines. + * Will be set to NULL on error. + * 3 : newline = How to handle newlines. + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory + * + *********************************************************************/ +jb_err edit_read_file_lines(FILE *fp, struct file_line ** pfile, int *newline) +{ + struct file_line * first_line; /* Keep for return value or to free */ + struct file_line * cur_line; /* Current line */ + struct file_line * prev_line; /* Entry with prev_line->next = cur_line */ + jb_err rval; + + assert(fp); + assert(pfile); + + *pfile = NULL; + + cur_line = first_line = zalloc(sizeof(struct file_line)); + if (cur_line == NULL) + { + return JB_ERR_MEMORY; + } + + cur_line->type = FILE_LINE_UNPROCESSED; + + rval = edit_read_line(fp, &cur_line->raw, &cur_line->prefix, &cur_line->unprocessed, newline, NULL); + if (rval) + { + /* Out of memory or empty file. */ + /* Note that empty file is not an error we propogate up */ + free(cur_line); + return ((rval == JB_ERR_FILE) ? JB_ERR_OK : rval); + } + + do + { + prev_line = cur_line; + cur_line = prev_line->next = zalloc(sizeof(struct file_line)); + if (cur_line == NULL) + { + /* Out of memory */ + edit_free_file_lines(first_line); + return JB_ERR_MEMORY; + } + + cur_line->type = FILE_LINE_UNPROCESSED; + + rval = edit_read_line(fp, &cur_line->raw, &cur_line->prefix, &cur_line->unprocessed, newline, NULL); + if ((rval != JB_ERR_OK) && (rval != JB_ERR_FILE)) + { + /* Out of memory */ + edit_free_file_lines(first_line); + return JB_ERR_MEMORY; + } + + } + while (rval != JB_ERR_FILE); + + /* EOF */ + + /* We allocated one too many - free it */ + prev_line->next = NULL; + free(cur_line); + + *pfile = first_line; + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : edit_read_file + * + * Description : Read a complete file into memory. + * Handles CGI parameter parsing. If requested, also + * checks the file's modification timestamp. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : parameters = map of cgi parameters. + * 3 : require_version = true to check "ver" parameter. + * 4 : suffix = File extension, e.g. ".action". + * 5 : pfile = Destination for the file. Will be set + * to NULL on error. + * + * CGI Parameters : + * filename : The name of the file to read, without the + * path or ".action" extension. + * ver : (Only if require_version is nonzero) + * Timestamp of the actions file. If wrong, this + * function fails with JB_ERR_MODIFIED. + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory + * JB_ERR_CGI_PARAMS if "filename" was not specified + * or is not valid. + * JB_ERR_FILE if the file cannot be opened or + * contains no data + * JB_ERR_MODIFIED if version checking was requested and + * failed - the file was modified outside + * of this CGI editor instance. + * + *********************************************************************/ +jb_err edit_read_file(struct client_state *csp, + const struct map *parameters, + int require_version, + const char *suffix, + struct editable_file **pfile) +{ + struct file_line * lines; + FILE * fp; + jb_err err; + char * filename; + const char * identifier; + struct editable_file * file; + unsigned version = 0; + struct stat statbuf[1]; + char version_buf[22]; + int newline = NEWLINE_UNKNOWN; + + assert(csp); + assert(parameters); + assert(pfile); + + *pfile = NULL; + + err = get_file_name_param(csp, parameters, "f", suffix, + &filename, &identifier); + if (err) + { + return err; + } + + if (stat(filename, statbuf) < 0) + { + /* Error, probably file not found. */ + free(filename); + return JB_ERR_FILE; + } + version = (unsigned) statbuf->st_mtime; + + if (require_version) + { + unsigned specified_version; + err = get_number_param(csp, parameters, "v", &specified_version); + if (err) + { + free(filename); + return err; + } + + if (version != specified_version) + { + return JB_ERR_MODIFIED; + } + } + + if (NULL == (fp = fopen(filename,"rb"))) + { + free(filename); + return JB_ERR_FILE; + } + + err = edit_read_file_lines(fp, &lines, &newline); + + fclose(fp); + + if (err) + { + free(filename); + return err; + } + + file = (struct editable_file *) zalloc(sizeof(*file)); + if (err) + { + free(filename); + edit_free_file_lines(lines); + return err; + } + + file->lines = lines; + file->newline = newline; + file->filename = filename; + file->version = version; + file->identifier = url_encode(identifier); + + if (file->identifier == NULL) + { + edit_free_file(file); + return JB_ERR_MEMORY; + } + + /* Correct file->version_str */ + freez(file->version_str); + snprintf(version_buf, 22, "%u", file->version); + version_buf[21] = '\0'; + file->version_str = strdup(version_buf); + if (version_buf == NULL) + { + edit_free_file(file); + return JB_ERR_MEMORY; + } + + *pfile = file; + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : edit_read_actions_file + * + * Description : Read a complete actions file into memory. + * Handles CGI parameter parsing. If requested, also + * checks the file's modification timestamp. + * + * If this function detects an error in the categories + * JB_ERR_FILE, JB_ERR_MODIFIED, or JB_ERR_PARSE, + * then it handles it by filling in the specified + * response structure and returning JB_ERR_FILE. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : rsp = HTTP response. Only filled in on error. + * 2 : parameters = map of cgi parameters. + * 3 : require_version = true to check "ver" parameter. + * 4 : pfile = Destination for the file. Will be set + * to NULL on error. + * + * CGI Parameters : + * filename : The name of the actions file to read, without the + * path or ".action" extension. + * ver : (Only if require_version is nonzero) + * Timestamp of the actions file. If wrong, this + * function fails with JB_ERR_MODIFIED. + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory + * JB_ERR_CGI_PARAMS if "filename" was not specified + * or is not valid. + * JB_ERR_FILE if the file does not contain valid data, + * or if file cannot be opened or + * contains no data, or if version + * checking was requested and failed. + * + *********************************************************************/ +jb_err edit_read_actions_file(struct client_state *csp, + struct http_response *rsp, + const struct map *parameters, + int require_version, + struct editable_file **pfile) +{ + jb_err err; + struct editable_file *file; + + assert(csp); + assert(parameters); + assert(pfile); + + *pfile = NULL; + + err = edit_read_file(csp, parameters, require_version, ".action", &file); + if (err) + { + /* Try to handle if possible */ + if (err == JB_ERR_FILE) + { + err = cgi_error_file(csp, rsp, lookup(parameters, "f")); + } + else if (err == JB_ERR_MODIFIED) + { + err = cgi_error_modified(csp, rsp, lookup(parameters, "f")); + } + if (err == JB_ERR_OK) + { + /* + * Signal to higher-level CGI code that there was a problem but we + * handled it, they should just return JB_ERR_OK. + */ + err = JB_ERR_FILE; + } + return err; + } + + err = edit_parse_actions_file(file); + if (err) + { + if (err == JB_ERR_PARSE) + { + err = cgi_error_parse(csp, rsp, file); + if (err == JB_ERR_OK) + { + /* + * Signal to higher-level CGI code that there was a problem but we + * handled it, they should just return JB_ERR_OK. + */ + err = JB_ERR_FILE; + } + } + edit_free_file(file); + return err; + } + + *pfile = file; + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : get_file_name_param + * + * Description : Get the name of the file to edit from the parameters + * passed to a CGI function. This function handles + * security checks such as blocking urls containing + * "/" or ".", prepending the config file directory, + * and adding the specified suffix. + * + * (This is an essential security check, otherwise + * users may be able to pass "../../../etc/passwd" + * and overwrite the password file [linux], "prn:" + * and print random data [Windows], etc...) + * + * This function only allows filenames contining the + * characters '-', '_', 'A'-'Z', 'a'-'z', and '0'-'9'. + * That's probably too restrictive but at least it's + * secure. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : parameters = map of cgi parameters + * 3 : param_name = The name of the parameter to read + * 4 : suffix = File extension, e.g. ".actions" + * 5 : pfilename = destination for full filename. Caller + * free()s. Set to NULL on error. + * 6 : pparam = destination for partial filename, + * suitable for use in another URL. Allocated as part + * of the map "parameters", so don't free it. + * Set to NULL if not specified. + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory + * JB_ERR_CGI_PARAMS if "filename" was not specified + * or is not valid. + * + *********************************************************************/ +static jb_err get_file_name_param(struct client_state *csp, + const struct map *parameters, + const char *param_name, + const char *suffix, + char **pfilename, + const char **pparam) +{ + const char *param; + const char *s; + char *name; + char *fullpath; + char ch; + int len; + + assert(csp); + assert(parameters); + assert(suffix); + assert(pfilename); + assert(pparam); + + *pfilename = NULL; + *pparam = NULL; + + param = lookup(parameters, param_name); + if (!*param) + { + return JB_ERR_CGI_PARAMS; + } + + *pparam = param; + + len = strlen(param); + if (len >= FILENAME_MAX) + { + /* Too long. */ + return JB_ERR_CGI_PARAMS; + } + + /* Check every character to see if it's legal */ + s = param; + while ((ch = *s++) != '\0') + { + if ( ((ch < 'A') || (ch > 'Z')) + && ((ch < 'a') || (ch > 'z')) + && ((ch < '0') || (ch > '9')) + && (ch != '-') + && (ch != '_') ) + { + /* Probable hack attempt. */ + return JB_ERR_CGI_PARAMS; + } + } + + /* Append extension */ + name = malloc(len + strlen(suffix) + 1); + if (name == NULL) + { + return JB_ERR_MEMORY; + } + strcpy(name, param); + strcpy(name + len, suffix); + + /* Prepend path */ + fullpath = make_path(csp->config->confdir, name); + free(name); + + if (fullpath == NULL) + { + return JB_ERR_MEMORY; + } + + /* Success */ + *pfilename = fullpath; + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : get_url_spec_param + * + * Description : Get a URL pattern from the parameters + * passed to a CGI function. Removes leading/trailing + * spaces and validates it. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : parameters = map of cgi parameters + * 3 : name = Name of CGI parameter to read + * 4 : pvalue = destination for value. Will be malloc()'d. + * Set to NULL on error. + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory + * JB_ERR_CGI_PARAMS if the parameter was not specified + * or is not valid. + * + *********************************************************************/ +static jb_err get_url_spec_param(struct client_state *csp, + const struct map *parameters, + const char *name, + char **pvalue) +{ + const char *orig_param; + char *param; + char *s; + struct url_spec compiled[1]; + jb_err err; + + assert(csp); + assert(parameters); + assert(name); + assert(pvalue); + + *pvalue = NULL; + + orig_param = lookup(parameters, name); + if (!*orig_param) + { + return JB_ERR_CGI_PARAMS; + } + + /* Copy and trim whitespace */ + param = strdup(orig_param); + if (param == NULL) + { + return JB_ERR_MEMORY; + } + chomp(param); + + /* Must be non-empty, and can't allow 1st character to be '{' */ + if (param[0] == '\0' || param[0] == '{') + { + free(param); + return JB_ERR_CGI_PARAMS; + } + + /* Check for embedded newlines */ + for (s = param; *s != '\0'; s++) + { + if ((*s == '\r') || (*s == '\n')) + { + free(param); + return JB_ERR_CGI_PARAMS; + } + } + + /* Check that regex is valid */ + s = strdup(param); + if (s == NULL) + { + free(param); + return JB_ERR_MEMORY; + } + err = create_url_spec(compiled, s); + free(s); + if (err) + { + free(param); + return (err == JB_ERR_MEMORY) ? JB_ERR_MEMORY : JB_ERR_CGI_PARAMS; + } + free_url_spec(compiled); + + if (param[strlen(param) - 1] == '\\') + { + /* + * Must protect trailing '\\' from becoming line continuation character. + * Two methods: 1) If it's a domain only, add a trailing '/'. + * 2) For path, add the do-nothing PCRE expression (?:) to the end + */ + if (strchr(param, '/') == NULL) + { + err = string_append(¶m, "/"); + } + else + { + err = string_append(¶m, "(?:)"); + } + if (err) + { + return err; + } + + /* Check that the modified regex is valid */ + s = strdup(param); + if (s == NULL) + { + free(param); + return JB_ERR_MEMORY; + } + err = create_url_spec(compiled, s); + free(s); + if (err) + { + free(param); + return (err == JB_ERR_MEMORY) ? JB_ERR_MEMORY : JB_ERR_CGI_PARAMS; + } + free_url_spec(compiled); + } + + *pvalue = param; + return JB_ERR_OK; +} + +/********************************************************************* + * + * Function : map_radio + * + * Description : Map a set of radio button values. E.g. if you have + * 3 radio buttons, declare them as: + * View ", i); + if (!err) err = string_append(&s, buf); + + if (NULL == strstr(csp->actions_list[i]->filename, "standard.action") && NULL != csp->config->actions_file_short[i]) + { + snprintf(buf, 100, "Edit", csp->config->actions_file_short[i]); + if (!err) err = string_append(&s, buf); + } + + if (!err) err = string_append(&s, "\n"); + } + } + if (*s != '\0') + { + if (!err) err = map(exports, "actions-filenames", 1, s, 0); + } + else + { + if (!err) err = map(exports, "actions-filenames", 1, "None specified", 1); + } + + if (csp->rlist) + { + if (!err) err = map(exports, "re-filter-filename", 1, html_encode(csp->rlist->filename), 0); + } + else + { + if (!err) err = map(exports, "re-filter-filename", 1, "None specified", 1); + if (!err) err = map_block_killer(exports, "have-filterfile"); + } + +#ifdef FEATURE_TRUST + if (csp->tlist) + { + if (!err) err = map(exports, "trust-filename", 1, html_encode(csp->tlist->filename), 0); + } + else + { + if (!err) err = map(exports, "trust-filename", 1, "None specified", 1); + if (!err) err = map_block_killer(exports, "have-trustfile"); + } +#else + if (!err) err = map_block_killer(exports, "trust-support"); +#endif /* ndef FEATURE_TRUST */ + + if (err) + { + free_map(exports); + return JB_ERR_MEMORY; + } + + return template_fill_for_cgi(csp, "show-status", exports, rsp); +} + + +/********************************************************************* + * + * Function : cgi_show_url_info + * + * Description : CGI function that determines and shows which actions + * Privoxy will perform for a given url, and which + * matches starting from the defaults have lead to that. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : rsp = http_response data structure for output + * 3 : parameters = map of cgi parameters + * + * CGI Parameters : + * url : The url whose actions are to be determined. + * If url is unset, the url-given conditional will be + * set, so that all but the form can be suppressed in + * the template. + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err cgi_show_url_info(struct client_state *csp, + struct http_response *rsp, + const struct map *parameters) +{ + char *url_param; + struct map *exports; + char buf[150]; + + assert(csp); + assert(rsp); + assert(parameters); + + if (NULL == (exports = default_exports(csp, "show-url-info"))) + { + return JB_ERR_MEMORY; + } + + /* + * Get the url= parameter (if present) and remove any leading/trailing spaces. + */ + url_param = strdup(lookup(parameters, "url")); + if (url_param == NULL) + { + free_map(exports); + return JB_ERR_MEMORY; + } + chomp(url_param); + + /* + * Handle prefixes. 4 possibilities: + * 1) "http://" or "https://" prefix present and followed by URL - OK + * 2) Only the "http://" or "https://" part is present, no URL - change + * to empty string so it will be detected later as "no URL". + * 3) Parameter specified but doesn't contain "http(s?)://" - add a + * "http://" prefix. + * 4) Parameter not specified or is empty string - let this fall through + * for now, next block of code will handle it. + */ + if (0 == strncmp(url_param, "http://", 7)) + { + if (url_param[7] == '\0') + { + /* + * Empty URL (just prefix). + * Make it totally empty so it's caught by the next if() + */ + url_param[0] = '\0'; + } + } + else if (0 == strncmp(url_param, "https://", 8)) + { + if (url_param[8] == '\0') + { + /* + * Empty URL (just prefix). + * Make it totally empty so it's caught by the next if() + */ + url_param[0] = '\0'; + } + } + else if (url_param[0] != '\0') + { + /* + * Unknown prefix - assume http:// + */ + char * url_param_prefixed = malloc(7 + 1 + strlen(url_param)); + if (NULL == url_param_prefixed) + { + free(url_param); + free_map(exports); + return JB_ERR_MEMORY; + } + strcpy(url_param_prefixed, "http://"); + strcpy(url_param_prefixed + 7, url_param); + free(url_param); + url_param = url_param_prefixed; + } + + + if (url_param[0] == '\0') + { + /* URL paramater not specified, display query form only. */ + free(url_param); + if (map_block_killer(exports, "url-given") + || map(exports, "url", 1, "", 1)) + { + free_map(exports); + return JB_ERR_MEMORY; + } + } + else + { + /* Given a URL, so query it. */ + jb_err err; + char *matches; + char *s; + int hits = 0; + struct file_list *fl; + struct url_actions *b; + struct http_request url_to_query[1]; + struct current_action_spec action[1]; + int i; + + if (map(exports, "url", 1, html_encode(url_param), 0)) + { + free(url_param); + free_map(exports); + return JB_ERR_MEMORY; + } + + init_current_action(action); + + if (map(exports, "default", 1, current_action_to_html(csp, action), 0)) + { + free_current_action(action); + free(url_param); + free_map(exports); + return JB_ERR_MEMORY; + } + + err = parse_http_url(url_param, url_to_query, csp); + + free(url_param); + + if (err == JB_ERR_MEMORY) + { + free_current_action(action); + free_map(exports); + return JB_ERR_MEMORY; + } + else if (err) + { + /* Invalid URL */ + + err = map(exports, "matches", 1, "[Invalid URL specified!]" , 1); + if (!err) err = map(exports, "final", 1, lookup(exports, "default"), 1); + + free_current_action(action); + + if (err) + { + free_map(exports); + return JB_ERR_MEMORY; + } + + return template_fill_for_cgi(csp, "show-url-info", exports, rsp); + } + + /* + * We have a warning about SSL paths. Hide it for insecure sites. + */ + if (!url_to_query->ssl) + { + if (map_block_killer(exports, "https")) + { + free_current_action(action); + free_map(exports); + return JB_ERR_MEMORY; + } + } + + matches = strdup(""); + + for (i = 0; i < MAX_ACTION_FILES; i++) + { + if (NULL == csp->config->actions_file_short[i] + || !strcmp(csp->config->actions_file_short[i], "standard")) continue; + + b = NULL; + hits = 1; + if ((fl = csp->actions_list[i]) != NULL) + { + if ((b = fl->f) != NULL) + { + /* FIXME: Hardcoded HTML! */ + string_append(&matches, "\n"); + + hits = 0; + b = b->next; + } + } + + for (; (b != NULL) && (matches != NULL); b = b->next) + { + if (url_match(b->url, url_to_query)) + { + string_append(&matches, "\n"); + + if (merge_current_action(action, b->action)) + { + freez(matches); + free_http_request(url_to_query); + free_current_action(action); + free_map(exports); + return JB_ERR_MEMORY; + } + hits++; + } + } + + if (!hits) + { + string_append(&matches, "\n"); + } + } + string_append(&matches, "
    In file: "); + string_join (&matches, html_encode(csp->config->actions_file_short[i])); + snprintf(buf, 150, ".action ", i); + string_append(&matches, buf); + string_append(&matches, "View config->actions_file_short[i])); + string_append(&matches, "\">Edit
    {"); + string_join (&matches, actions_to_html(csp, b->action)); + string_append(&matches, " }
    \n"); + string_join (&matches, html_encode(b->url->spec)); + string_append(&matches, "
    (no matches in this file)
    \n"); + + free_http_request(url_to_query); + + if (matches == NULL) + { + free_current_action(action); + free_map(exports); + return JB_ERR_MEMORY; + } + + if (map(exports, "matches", 1, matches , 0)) + { + free_current_action(action); + free_map(exports); + return JB_ERR_MEMORY; + } + + s = current_action_to_html(csp, action); + + free_current_action(action); + + if (map(exports, "final", 1, s, 0)) + { + free_map(exports); + return JB_ERR_MEMORY; + } + } + + return template_fill_for_cgi(csp, "show-url-info", exports, rsp); +} + + +/********************************************************************* + * + * Function : cgi_robots_txt + * + * Description : CGI function to return "/robots.txt". + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : rsp = http_response data structure for output + * 3 : parameters = map of cgi parameters + * + * CGI Parameters : None + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err cgi_robots_txt(struct client_state *csp, + struct http_response *rsp, + const struct map *parameters) +{ + char buf[100]; + jb_err err; + + rsp->body = strdup( + "# This is the Privoxy control interface.\n" + "# It isn't very useful to index it, and you're likely to break stuff.\n" + "# So go away!\n" + "\n" + "User-agent: *\n" + "Disallow: /\n" + "\n"); + if (rsp->body == NULL) + { + return JB_ERR_MEMORY; + } + + err = enlist_unique(rsp->headers, "Content-Type: text/plain", 13); + + rsp->is_static = 1; + + get_http_time(7 * 24 * 60 * 60, buf); /* 7 days into future */ + if (!err) err = enlist_unique_header(rsp->headers, "Expires", buf); + + return (err ? JB_ERR_MEMORY : JB_ERR_OK); +} + + +/********************************************************************* + * + * Function : show_defines + * + * Description : Add to a map the state od all conditional #defines + * used when building + * + * Parameters : + * 1 : exports = map to extend + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +static jb_err show_defines(struct map *exports) +{ + jb_err err = JB_ERR_OK; + +#ifdef FEATURE_ACL + if (!err) err = map_conditional(exports, "FEATURE_ACL", 1); +#else /* ifndef FEATURE_ACL */ + if (!err) err = map_conditional(exports, "FEATURE_ACL", 0); +#endif /* ndef FEATURE_ACL */ + +#ifdef FEATURE_CGI_EDIT_ACTIONS + if (!err) err = map_conditional(exports, "FEATURE_CGI_EDIT_ACTIONS", 1); +#else /* ifndef FEATURE_COOKIE_JAR */ + if (!err) err = map_conditional(exports, "FEATURE_CGI_EDIT_ACTIONS", 0); +#endif /* ndef FEATURE_COOKIE_JAR */ + +#ifdef FEATURE_COOKIE_JAR + if (!err) err = map_conditional(exports, "FEATURE_COOKIE_JAR", 1); +#else /* ifndef FEATURE_COOKIE_JAR */ + if (!err) err = map_conditional(exports, "FEATURE_COOKIE_JAR", 0); +#endif /* ndef FEATURE_COOKIE_JAR */ + +#ifdef FEATURE_FAST_REDIRECTS + if (!err) err = map_conditional(exports, "FEATURE_FAST_REDIRECTS", 1); +#else /* ifndef FEATURE_FAST_REDIRECTS */ + if (!err) err = map_conditional(exports, "FEATURE_FAST_REDIRECTS", 0); +#endif /* ndef FEATURE_FAST_REDIRECTS */ + +#ifdef FEATURE_FORCE_LOAD + if (!err) err = map_conditional(exports, "FEATURE_FORCE_LOAD", 1); + if (!err) err = map(exports, "FORCE_PREFIX", 1, FORCE_PREFIX, 1); +#else /* ifndef FEATURE_FORCE_LOAD */ + if (!err) err = map_conditional(exports, "FEATURE_FORCE_LOAD", 0); + if (!err) err = map(exports, "FORCE_PREFIX", 1, "(none - disabled)", 1); +#endif /* ndef FEATURE_FORCE_LOAD */ + +#ifdef FEATURE_IMAGE_BLOCKING + if (!err) err = map_conditional(exports, "FEATURE_IMAGE_BLOCKING", 1); +#else /* ifndef FEATURE_IMAGE_BLOCKING */ + if (!err) err = map_conditional(exports, "FEATURE_IMAGE_BLOCKING", 0); +#endif /* ndef FEATURE_IMAGE_BLOCKING */ + +#ifdef FEATURE_IMAGE_DETECT_MSIE + if (!err) err = map_conditional(exports, "FEATURE_IMAGE_DETECT_MSIE", 1); +#else /* ifndef FEATURE_IMAGE_DETECT_MSIE */ + if (!err) err = map_conditional(exports, "FEATURE_IMAGE_DETECT_MSIE", 0); +#endif /* ndef FEATURE_IMAGE_DETECT_MSIE */ + +#ifdef FEATURE_KILL_POPUPS + if (!err) err = map_conditional(exports, "FEATURE_KILL_POPUPS", 1); +#else /* ifndef FEATURE_KILL_POPUPS */ + if (!err) err = map_conditional(exports, "FEATURE_KILL_POPUPS", 0); +#endif /* ndef FEATURE_KILL_POPUPS */ + +#ifdef FEATURE_NO_GIFS + if (!err) err = map_conditional(exports, "FEATURE_NO_GIFS", 1); +#else /* ifndef FEATURE_NO_GIFS */ + if (!err) err = map_conditional(exports, "FEATURE_NO_GIFS", 0); +#endif /* ndef FEATURE_NO_GIFS */ + +#ifdef FEATURE_PTHREAD + if (!err) err = map_conditional(exports, "FEATURE_PTHREAD", 1); +#else /* ifndef FEATURE_PTHREAD */ + if (!err) err = map_conditional(exports, "FEATURE_PTHREAD", 0); +#endif /* ndef FEATURE_PTHREAD */ + +#ifdef FEATURE_STATISTICS + if (!err) err = map_conditional(exports, "FEATURE_STATISTICS", 1); +#else /* ifndef FEATURE_STATISTICS */ + if (!err) err = map_conditional(exports, "FEATURE_STATISTICS", 0); +#endif /* ndef FEATURE_STATISTICS */ + +#ifdef FEATURE_TOGGLE + if (!err) err = map_conditional(exports, "FEATURE_TOGGLE", 1); +#else /* ifndef FEATURE_TOGGLE */ + if (!err) err = map_conditional(exports, "FEATURE_TOGGLE", 0); +#endif /* ndef FEATURE_TOGGLE */ + +#ifdef FEATURE_TRUST + if (!err) err = map_conditional(exports, "FEATURE_TRUST", 1); +#else /* ifndef FEATURE_TRUST */ + if (!err) err = map_conditional(exports, "FEATURE_TRUST", 0); +#endif /* ndef FEATURE_TRUST */ + +#ifdef STATIC_PCRE + if (!err) err = map_conditional(exports, "STATIC_PCRE", 1); +#else /* ifndef STATIC_PCRE */ + if (!err) err = map_conditional(exports, "STATIC_PCRE", 0); +#endif /* ndef STATIC_PCRE */ + +#ifdef STATIC_PCRS + if (!err) err = map_conditional(exports, "STATIC_PCRS", 1); +#else /* ifndef STATIC_PCRS */ + if (!err) err = map_conditional(exports, "STATIC_PCRS", 0); +#endif /* ndef STATIC_PCRS */ + + return err; +} + + +/********************************************************************* + * + * Function : show_rcs + * + * Description : Create a string with the rcs info for all sourcefiles + * + * Parameters : None + * + * Returns : A string, or NULL on out-of-memory. + * + *********************************************************************/ +static char *show_rcs(void) +{ + char *result = strdup(""); + char buf[BUFFER_SIZE]; + + /* Instead of including *all* dot h's in the project (thus creating a + * tremendous amount of dependencies), I will concede to declaring them + * as extern's. This forces the developer to add to this list, but oh well. + */ + +#define SHOW_RCS(__x) \ + { \ + extern const char __x[]; \ + sprintf(buf, "%s\n", __x); \ + string_append(&result, buf); \ + } + + /* In alphabetical order */ + SHOW_RCS(actions_h_rcs) + SHOW_RCS(actions_rcs) +#ifdef AMIGA + SHOW_RCS(amiga_h_rcs) + SHOW_RCS(amiga_rcs) +#endif /* def AMIGA */ + SHOW_RCS(cgi_h_rcs) + SHOW_RCS(cgi_rcs) +#ifdef FEATURE_CGI_EDIT_ACTIONS + SHOW_RCS(cgiedit_h_rcs) + SHOW_RCS(cgiedit_rcs) +#endif /* def FEATURE_CGI_EDIT_ACTIONS */ + SHOW_RCS(cgisimple_h_rcs) + SHOW_RCS(cgisimple_rcs) +#ifdef __MINGW32__ + SHOW_RCS(cygwin_h_rcs) +#endif + SHOW_RCS(deanimate_h_rcs) + SHOW_RCS(deanimate_rcs) + SHOW_RCS(encode_h_rcs) + SHOW_RCS(encode_rcs) + SHOW_RCS(errlog_h_rcs) + SHOW_RCS(errlog_rcs) + SHOW_RCS(filters_h_rcs) + SHOW_RCS(filters_rcs) + SHOW_RCS(gateway_h_rcs) + SHOW_RCS(gateway_rcs) + SHOW_RCS(jbsockets_h_rcs) + SHOW_RCS(jbsockets_rcs) + SHOW_RCS(jcc_h_rcs) + SHOW_RCS(jcc_rcs) +#ifdef FEATURE_KILL_POPUPS + SHOW_RCS(killpopup_h_rcs) + SHOW_RCS(killpopup_rcs) +#endif /* def FEATURE_KILL_POPUPS */ + SHOW_RCS(list_h_rcs) + SHOW_RCS(list_rcs) + SHOW_RCS(loadcfg_h_rcs) + SHOW_RCS(loadcfg_rcs) + SHOW_RCS(loaders_h_rcs) + SHOW_RCS(loaders_rcs) + SHOW_RCS(miscutil_h_rcs) + SHOW_RCS(miscutil_rcs) + SHOW_RCS(parsers_h_rcs) + SHOW_RCS(parsers_rcs) + SHOW_RCS(pcrs_rcs) + SHOW_RCS(pcrs_h_rcs) + SHOW_RCS(project_h_rcs) + SHOW_RCS(ssplit_h_rcs) + SHOW_RCS(ssplit_rcs) + SHOW_RCS(urlmatch_h_rcs) + SHOW_RCS(urlmatch_rcs) +#ifdef _WIN32 +#ifndef _WIN_CONSOLE + SHOW_RCS(w32log_h_rcs) + SHOW_RCS(w32log_rcs) + SHOW_RCS(w32res_h_rcs) + SHOW_RCS(w32taskbar_h_rcs) + SHOW_RCS(w32taskbar_rcs) +#endif /* ndef _WIN_CONSOLE */ + SHOW_RCS(win32_h_rcs) + SHOW_RCS(win32_rcs) +#endif /* def _WIN32 */ + +#undef SHOW_RCS + + return result; + +} + + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/cgisimple.h b/src/cgisimple.h new file mode 100644 index 00000000..8f367499 --- /dev/null +++ b/src/cgisimple.h @@ -0,0 +1,152 @@ +#ifndef CGISIMPLE_H_INCLUDED +#define CGISIMPLE_H_INCLUDED +#define CGISIMPLE_H_VERSION "$Id: cgisimple.h,v 1.11 2002/04/05 15:50:53 oes Exp $" +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/cgisimple.h,v $ + * + * Purpose : Declares functions to intercept request, generate + * html or gif answers, and to compose HTTP resonses. + * + * Functions declared include: + * + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: cgisimple.h,v $ + * Revision 1.11 2002/04/05 15:50:53 oes + * added send-stylesheet CGI + * + * Revision 1.10 2002/03/26 22:29:54 swa + * we have a new homepage! + * + * Revision 1.9 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.8 2002/03/16 23:54:06 jongfoster + * Adding graceful termination feature, to help look for memory leaks. + * If you enable this (which, by design, has to be done by hand + * editing config.h) and then go to http://i.j.b/die, then the program + * will exit cleanly after the *next* request. It should free all the + * memory that was used. + * + * Revision 1.7 2002/03/08 16:43:59 oes + * Renamed cgi_transparent_png to cgi_transparent_image + * + * Revision 1.6 2002/03/07 03:48:59 oes + * - Changed built-in images from GIF to PNG + * (with regard to Unisys patent issue) + * + * Revision 1.5 2002/01/22 23:26:03 jongfoster + * Adding cgi_transparent_gif() for http://i.j.b/t + * + * Revision 1.4 2001/10/23 21:48:19 jongfoster + * Cleaning up error handling in CGI functions - they now send back + * a HTML error page and should never cause a FATAL error. (Fixes one + * potential source of "denial of service" attacks). + * + * CGI actions file editor that works and is actually useful. + * + * Ability to toggle JunkBuster remotely using a CGI call. + * + * You can turn off both the above features in the main configuration + * file, e.g. if you are running a multi-user proxy. + * + * Revision 1.3 2001/10/14 22:00:32 jongfoster + * Adding support for a 404 error when an invalid CGI page is requested. + * + * Revision 1.2 2001/10/02 15:31:20 oes + * Introduced show-request cgi + * + * Revision 1.1 2001/09/16 17:08:54 jongfoster + * Moving simple CGI functions from cgi.c to new file cgisimple.c + * + * + **********************************************************************/ + + +#include "project.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * CGI functions + */ +extern jb_err cgi_default (struct client_state *csp, + struct http_response *rsp, + const struct map *parameters); +extern jb_err cgi_error_404 (struct client_state *csp, + struct http_response *rsp, + const struct map *parameters); +extern jb_err cgi_robots_txt (struct client_state *csp, + struct http_response *rsp, + const struct map *parameters); +extern jb_err cgi_send_banner (struct client_state *csp, + struct http_response *rsp, + const struct map *parameters); +extern jb_err cgi_show_status (struct client_state *csp, + struct http_response *rsp, + const struct map *parameters); +extern jb_err cgi_show_url_info(struct client_state *csp, + struct http_response *rsp, + const struct map *parameters); +extern jb_err cgi_show_version (struct client_state *csp, + struct http_response *rsp, + const struct map *parameters); +extern jb_err cgi_show_request (struct client_state *csp, + struct http_response *rsp, + const struct map *parameters); +extern jb_err cgi_transparent_image (struct client_state *csp, + struct http_response *rsp, + const struct map *parameters); +extern jb_err cgi_send_stylesheet(struct client_state *csp, + struct http_response *rsp, + const struct map *parameters); + +#ifdef FEATURE_GRACEFUL_TERMINATION +extern jb_err cgi_die (struct client_state *csp, + struct http_response *rsp, + const struct map *parameters); +#endif + +/* Revision control strings from this header and associated .c file */ +extern const char cgisimple_rcs[]; +extern const char cgisimple_h_rcs[]; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ndef CGISIMPLE_H_INCLUDED */ + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/cygwin.h b/src/cygwin.h new file mode 100644 index 00000000..1ed69f62 --- /dev/null +++ b/src/cygwin.h @@ -0,0 +1,76 @@ +#ifndef CYGWIN_H_INCLUDED +#define CYGWIN_H_INCLUDED +#define CYGWIN_H_VERSION "$Id: cygwin.h,v 1.4 2002/03/26 22:29:54 swa Exp $" +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/cygwin.h,v $ + * + * Purpose : The windows.h file seems to be a *tad* different, so I + * will bridge the gaps here. Perhaps I should convert the + * latest SDK too? Shudder, I think not. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: cygwin.h,v $ + * Revision 1.4 2002/03/26 22:29:54 swa + * we have a new homepage! + * + * Revision 1.3 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.2 2001/07/29 18:43:08 jongfoster + * Changing #ifdef _FILENAME_H to FILENAME_H_INCLUDED, to conform to + * ANSI C rules. + * + * Revision 1.1.1.1 2001/05/15 13:58:51 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + +/* Conditionally include this whole file. */ +#ifdef __MINGW32__ + +/* Hmmm, seems to be overlooked. */ +#define _RICHEDIT_VER 0x0300 + +/* + * Named slightly different ... but not in Cygwin v1.3.1 ... + * + * #define LVITEM LV_ITEM + * #define LVCOLUMN LV_COLUMN + */ + +#endif /* def __MINGW32__ */ +#endif /* ndef CYGWIN_H_INCLUDED */ + + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/deanimate.c b/src/deanimate.c new file mode 100644 index 00000000..8c33bc53 --- /dev/null +++ b/src/deanimate.c @@ -0,0 +1,507 @@ +const char deanimate_rcs[] = "$Id: deanimate.c,v 1.12 2002/05/12 21:36:29 jongfoster Exp $"; +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/deanimate.c,v $ + * + * Purpose : Declares functions to deanimate GIF images on the fly. + * + * Functions declared include: gif_deanimate, buf_free, + * buf_copy, buf_getbyte, gif_skip_data_block, and + * gif_extract_image + * + * Copyright : Written by and Copyright (C) 2001 by the the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the GIF file format specification (see + * http://tronche.com/computer-graphics/gif/gif89a.html) + * and ideas from the Image::DeAnim Perl module by + * Ken MacFarlane, + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: deanimate.c,v $ + * Revision 1.12 2002/05/12 21:36:29 jongfoster + * Correcting function comments + * + * Revision 1.11 2002/03/26 22:29:54 swa + * we have a new homepage! + * + * Revision 1.10 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.9 2002/03/13 00:27:04 jongfoster + * Killing warnings + * + * Revision 1.8 2002/03/09 19:42:47 jongfoster + * Fixing more warnings + * + * Revision 1.7 2002/03/08 17:46:04 jongfoster + * Fixing int/size_t warnings + * + * Revision 1.6 2002/03/07 03:46:17 oes + * Fixed compiler warnings + * + * Revision 1.5 2001/09/10 10:16:06 oes + * Silenced compiler warnings + * + * Revision 1.4 2001/07/18 12:28:49 oes + * - Added feature for extracting the first frame + * to gif_deanimate + * - Separated image buffer extension into buf_extend + * - Extended gif deanimation to GIF87a (untested!) + * - Cosmetics + * + * Revision 1.3 2001/07/15 13:57:50 jongfoster + * Adding #includes string.h and miscutil.h + * + * Revision 1.2 2001/07/13 13:46:20 oes + * Introduced GIF deanimation feature + * + * + **********************************************************************/ + + +#include "config.h" + +#include +#include + +#include "project.h" +#include "deanimate.h" +#include "miscutil.h" + +const char deanimate_h_rcs[] = DEANIMATE_H_VERSION; + +/********************************************************************* + * + * Function : buf_free + * + * Description : Safely frees a struct binbuffer + * + * Parameters : + * 1 : buf = Pointer to the binbuffer to be freed + * + * Returns : N/A + * + *********************************************************************/ +void buf_free(struct binbuffer *buf) +{ + if (buf == NULL) return; + + if (buf->buffer != NULL) + { + free(buf->buffer); + } + + free(buf); + +} + + +/********************************************************************* + * + * Function : buf_extend + * + * Description : Ensure that a given binbuffer can hold a given amount + * of bytes, by reallocating its buffer if necessary. + * Allocate new mem in chunks of 1024 bytes, so we don't + * have to realloc() too often. + * + * Parameters : + * 1 : buf = Pointer to the binbuffer + * 2 : length = Desired minimum size + * + * + * Returns : 0 on success, 1 on failiure. + * + *********************************************************************/ +int buf_extend(struct binbuffer *buf, size_t length) +{ + char *newbuf; + + if (buf->offset + length > buf->size) + { + buf->size = ((buf->size + length + (size_t)1023) & ~(size_t)1023); + newbuf = (char *)realloc(buf->buffer, buf->size); + + if (newbuf == NULL) + { + freez(buf->buffer); + return 1; + } + else + { + buf->buffer = newbuf; + return 0; + } + } + return 0; + +} + + +/********************************************************************* + * + * Function : buf_copy + * + * Description : Safely copies a given amount of bytes from one + * struct binbuffer to another, advancing the + * offsets appropriately. + * + * Parameters : + * 1 : src = Pointer to the source binbuffer + * 2 : dst = Pointer to the destination binbuffer + * 3 : length = Number of bytes to be copied + * + * Returns : 0 on success, 1 on failiure. + * + *********************************************************************/ +int buf_copy(struct binbuffer *src, struct binbuffer *dst, size_t length) +{ + + /* + * Sanity check: Can't copy more data than we have + */ + if (src->offset + length > src->size) + { + return 1; + } + + /* + * Ensure that dst can hold the new data + */ + if (buf_extend(dst, length)) + { + return 1; + } + + /* + * Now that it's safe, memcpy() the desired amount of + * data from src to dst and adjust the offsets + */ + memcpy(dst->buffer + dst->offset, src->buffer + src->offset, length); + src->offset += length; + dst->offset += length; + + return 0; + +} + + +/********************************************************************* + * + * Function : buf_getbyte + * + * Description : Safely gets a byte from a given binbuffer at a + * given offset + * + * Parameters : + * 1 : src = Pointer to the source binbuffer + * 2 : offset = Offset to the desired byte + * + * Returns : The byte on success, or 0 on failiure + * + *********************************************************************/ +unsigned char buf_getbyte(struct binbuffer *src, size_t offset) +{ + if (src->offset + offset < src->size) + { + return (unsigned char)*(src->buffer + src->offset + offset); + } + else + { + return '\0'; + } + +} + + +/********************************************************************* + * + * Function : gif_skip_data_block + * + * Description : Safely advances the offset of a given struct binbuffer + * that contains a GIF image and whose offset is + * positioned at the start of a data block, behind + * that block. + * + * Parameters : + * 1 : buf = Pointer to the binbuffer + * + * Returns : 0 on success, or 1 on failiure + * + *********************************************************************/ +int gif_skip_data_block(struct binbuffer *buf) +{ + unsigned char c; + + /* + * Data blocks are sequences of chunks, which are headed + * by a one-byte length field, with the last chunk having + * zero length. + */ + while((c = buf_getbyte(buf, 0)) != '\0') + { + if ((buf->offset += c + 1) >= buf->size - 1) + { + return 1; + } + } + buf->offset++; + + return 0; + +} + + +/********************************************************************* + * + * Function : gif_extract_image + * + * Description : Safely extracts an image data block from a given + * struct binbuffer that contains a GIF image and whose + * offset is positioned at the start of a data block + * into a given destination binbuffer. + * + * Parameters : + * 1 : src = Pointer to the source binbuffer + * 2 : dst = Pointer to the destination binbuffer + * + * Returns : 0 on success, or 1 on failiure + * + *********************************************************************/ +int gif_extract_image(struct binbuffer *src, struct binbuffer *dst) +{ + unsigned char c; + + /* + * Remember the colormap flag and copy the image head + */ + c = buf_getbyte(src, 9); + if (buf_copy(src, dst, 10)) + { + return 1; + } + + /* + * If the image has a local colormap, copy it. + */ + if (c & 0x80) + { + if (buf_copy(src, dst, (size_t) 3 * (1 << ((c & 0x07) + 1)))) + { + return 1; + } + } + if (buf_copy(src, dst, 1)) return 1; + + /* + * Copy the image chunk by chunk. + */ + while((c = buf_getbyte(src, 0)) != '\0') + { + if (buf_copy(src, dst, 1 + (size_t) c)) return 1; + } + if (buf_copy(src, dst, 1)) return 1; + + /* + * Trim and rewind the dst buffer + */ + if (NULL == (dst->buffer = (char *)realloc(dst->buffer, dst->offset))) return 1; + dst->size = dst->offset; + dst->offset = 0; + + return(0); + +} + +/********************************************************************* + * + * Function : gif_deanimate + * + * Description : Deanimate a given GIF image, i.e. given a GIF with + * an (optional) image block and an arbitrary number + * of image extension blocks, produce an output GIF with + * only one image block that contains the last image + * (extenstion) block of the original. + * Also strip Comments, Application extenstions, etc. + * + * Parameters : + * 1 : src = Pointer to the source binbuffer + * 2 : dst = Pointer to the destination binbuffer + * 3 : get_first_image = Flag: If set, get the first image + * If unset (default), get the last + * + * Returns : 0 on success, or 1 on failiure + * + *********************************************************************/ +int gif_deanimate(struct binbuffer *src, struct binbuffer *dst, int get_first_image) +{ + unsigned char c; + struct binbuffer *image; + + if (NULL == src || NULL == dst) + { + return 1; + } + + c = buf_getbyte(src, 10); + + /* + * Check & copy GIF header + */ + if (strncmp(src->buffer, "GIF89a", 6) && strncmp(src->buffer, "GIF87a", 6)) + { + return 1; + } + else + { + if (buf_copy(src, dst, 13)) + { + return 1; + } + } + + /* + * Look for global colormap and copy if found. + */ + if(c & 0x80) + { + if (buf_copy(src, dst, (size_t) 3 * (1 << ((c & 0x07) + 1)))) + { + return 1; + } + } + + /* + * Reserve a buffer for the current image block + */ + if (NULL == (image = (struct binbuffer *)zalloc(sizeof(*image)))) + { + return 1; + } + + /* + * Parse the GIF block by block and copy the relevant + * parts to dst + */ + while(src->offset < src->size) + { + switch(buf_getbyte(src, 0)) + { + /* + * End-of-GIF Marker: Append current image and return + */ + case 0x3b: + goto write; + + /* + * Image block: Extract to current image buffer. + */ + case 0x2c: + image->offset = 0; + if (gif_extract_image(src, image)) goto failed; + if (get_first_image) goto write; + continue; + + /* + * Extension block: Look at next byte and decide + */ + case 0x21: + switch (buf_getbyte(src, 1)) + { + /* + * Image extension: Copy extension header and image + * to the current image buffer + */ + case 0xf9: + image->offset = 0; + if (buf_copy(src, image, 8) || buf_getbyte(src, 0) != 0x2c) goto failed; + if (gif_extract_image(src, image)) goto failed; + if (get_first_image) goto write; + continue; + + /* + * Application extension: Skip + */ + case 0xff: + if ((src->offset += 14) >= src->size || gif_skip_data_block(src)) goto failed; + continue; + + /* + * Comment extension: Skip + */ + case 0xfe: + if ((src->offset += 2) >= src->size || gif_skip_data_block(src)) goto failed; + continue; + + /* + * Plain text extension: Skip + */ + case 0x01: + if ((src->offset += 15) >= src->size || gif_skip_data_block(src)) goto failed; + continue; + + /* + * Ooops, what type of extension is that? + */ + default: + goto failed; + + } + + /* + * Ooops, what type of block is that? + */ + default: + goto failed; + + } + } /* -END- while src */ + + /* + * Either we got here by goto, or because the GIF is + * bogus and EOF was reached before an end-of-gif marker + * was found. + */ + +failed: + buf_free(image); + return 1; + + /* + * Append the current image to dst and return + */ + +write: + if (buf_copy(image, dst, image->size)) goto failed; + if (buf_extend(dst, 1)) goto failed; + *(dst->buffer + dst->offset++) = 0x3b; + buf_free(image); + return 0; + +} + + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/deanimate.h b/src/deanimate.h new file mode 100644 index 00000000..1e8d60f5 --- /dev/null +++ b/src/deanimate.h @@ -0,0 +1,105 @@ +#ifndef DEANIMATE_H_INCLUDED +#define DEANIMATE_H_INCLUDED +#define DEANIMATE_H_VERSION "$Id: deanimate.h,v 1.8 2002/03/26 22:29:54 swa Exp $" +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/deanimate.h,v $ + * + * Purpose : Declares functions to deanimate GIF images on the fly. + * + * Functions declared include: gif_deanimate, buf_free + * + * + * Copyright : Written by and Copyright (C) 2001 Andreas S. Oesterhelt + * for the Privoxy team. http://www.privoxy.org/ + * + * Based on ideas from the Image::DeAnim Perl module by + * Ken MacFarlane, + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: deanimate.h,v $ + * Revision 1.8 2002/03/26 22:29:54 swa + * we have a new homepage! + * + * Revision 1.7 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.6 2002/03/08 17:46:04 jongfoster + * Fixing int/size_t warnings + * + * Revision 1.5 2002/03/07 03:46:17 oes + * Fixed compiler warnings + * + * Revision 1.4 2001/07/29 18:50:04 jongfoster + * Fixing "extern C" block, and renaming #define _DEANIMATE_H + * + * Revision 1.3 2001/07/18 12:29:05 oes + * Updated prototype for gif_deanimate + * + * Revision 1.2 2001/07/13 13:46:20 oes + * Introduced GIF deanimation feature + * + * + *********************************************************************/ + + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * A struct that holds a buffer, a read/write offset, + * and the buffer's capacity. + */ +struct binbuffer +{ + char *buffer; + size_t offset; + size_t size; +}; + +/* + * Function prototypes + */ +extern int gif_deanimate(struct binbuffer *src, struct binbuffer *dst, int get_first_image); +extern void buf_free(struct binbuffer *buf); + +/* + * Revision control strings from this header and associated .c file + */ +extern const char deanimate_rcs[]; +extern const char deanimate_h_rcs[]; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ndef DEANIMATE_H_INCLUDED */ + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/encode.c b/src/encode.c new file mode 100644 index 00000000..c1e38d85 --- /dev/null +++ b/src/encode.c @@ -0,0 +1,486 @@ +const char encode_rcs[] = "$Id: encode.c,v 1.8 2002/03/26 22:29:54 swa Exp $"; +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/encode.c,v $ + * + * Purpose : Functions to encode and decode URLs, and also to + * encode cookies and HTML text. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: encode.c,v $ + * Revision 1.8 2002/03/26 22:29:54 swa + * we have a new homepage! + * + * Revision 1.7 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.6 2002/03/13 00:27:04 jongfoster + * Killing warnings + * + * Revision 1.5 2002/03/07 03:46:53 oes + * Fixed compiler warnings etc + * + * Revision 1.4 2002/01/22 23:28:07 jongfoster + * Adding convenience function html_encode_and_free_original() + * Making all functions accept NULL paramaters - in this case, they + * simply return NULL. This allows error-checking to be deferred. + * + * Revision 1.3 2001/11/13 00:16:40 jongfoster + * Replacing references to malloc.h with the standard stdlib.h + * (See ANSI or K&R 2nd Ed) + * + * Revision 1.2 2001/05/17 22:52:35 oes + * - Cleaned CRLF's from the sources and related files + * + * Revision 1.1.1.1 2001/05/15 13:58:51 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#include "config.h" + +#include +#include +#include + +#include "encode.h" + +const char encode_h_rcs[] = ENCODE_H_VERSION; + +/* Maps special characters in a URL to their equivalent % codes. */ +static const char * const url_code_map[256] = { + NULL, "%01", "%02", "%03", "%04", "%05", "%06", "%07", "%08", "%09", + "%0A", "%0B", "%0C", "%0D", "%0E", "%0F", "%10", "%11", "%12", "%13", + "%14", "%15", "%16", "%17", "%18", "%19", "%1A", "%1B", "%1C", "%1D", + "%1E", "%1F", "+", "%21", "%22", "%23", "%24", "%25", "%26", "%27", + "%28", "%29", NULL, "%2B", "%2C", NULL, NULL, "%2F", NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "%3A", "%3B", + "%3C", "%3D", "%3E", "%3F", NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, "%5B", "%5C", "%5D", "%5E", NULL, "%60", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, "%7B", "%7C", "%7D", "%7E", "%7F", "%80", "%81", + "%82", "%83", "%84", "%85", "%86", "%87", "%88", "%89", "%8A", "%8B", + "%8C", "%8D", "%8E", "%8F", "%90", "%91", "%92", "%93", "%94", "%95", + "%96", "%97", "%98", "%99", "%9A", "%9B", "%9C", "%9D", "%9E", "%9F", + "%A0", "%A1", "%A2", "%A3", "%A4", "%A5", "%A6", "%A7", "%A8", "%A9", + "%AA", "%AB", "%AC", "%AD", "%AE", "%AF", "%B0", "%B1", "%B2", "%B3", + "%B4", "%B5", "%B6", "%B7", "%B8", "%B9", "%BA", "%BB", "%BC", "%BD", + "%BE", "%BF", "%C0", "%C1", "%C2", "%C3", "%C4", "%C5", "%C6", "%C7", + "%C8", "%C9", "%CA", "%CB", "%CC", "%CD", "%CE", "%CF", "%D0", "%D1", + "%D2", "%D3", "%D4", "%D5", "%D6", "%D7", "%D8", "%D9", "%DA", "%DB", + "%DC", "%DD", "%DE", "%DF", "%E0", "%E1", "%E2", "%E3", "%E4", "%E5", + "%E6", "%E7", "%E8", "%E9", "%EA", "%EB", "%EC", "%ED", "%EE", "%EF", + "%F0", "%F1", "%F2", "%F3", "%F4", "%F5", "%F6", "%F7", "%F8", "%F9", + "%FA", "%FB", "%FC", "%FD", "%FE", "%FF" +}; + +/* Maps special characters in HTML to their equivalent entites. */ +static const char * const html_code_map[256] = { + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL,""",NULL,NULL,NULL,"&",NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "<",NULL,">",NULL,NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL +}; + +/* Maps special characters in a cookie to their equivalent % codes. */ +static const char * const cookie_code_map[256] = { + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, "+", NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, "%2C",NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "%3B", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL +}; + + +/********************************************************************* + * + * Function : html_encode + * + * Description : Encodes a string so it's not interpreted as + * containing HTML tags or entities. + * Replaces <, >, &, and " with the appropriate HTML + * entities. + * + * Parameters : + * 1 : s = String to encode. Null-terminated. + * + * Returns : Encoded string, newly allocated on the heap. + * Caller is responsible for freeing it with free(). + * If s is NULL, or on out-of memory, returns NULL. + * + *********************************************************************/ +char * html_encode(const char *s) +{ + char * buf; + + if (s == NULL) + { + return NULL; + } + + /* each input char can expand to at most 6 chars */ + buf = (char *) malloc((strlen(s) * 6) + 1); + + if (buf) + { + char c; + char * p = buf; + while ( (c = *s++) != '\0') + { + const char * replace_with = html_code_map[(unsigned char) c]; + if(replace_with != NULL) + { + strcpy(p, replace_with); + p += strlen(replace_with); + } + else + { + *p++ = c; + } + } + + *p = '\0'; + } + + return(buf); +} + + +/********************************************************************* + * + * Function : html_encode_and_free_original + * + * Description : Encodes a string so it's not interpreted as + * containing HTML tags or entities. + * Replaces <, >, &, and " with the appropriate HTML + * entities. Free()s original string. + * If original string is NULL, simply returns NULL. + * + * Parameters : + * 1 : s = String to encode. Null-terminated. + * + * Returns : Encoded string, newly allocated on the heap. + * Caller is responsible for freeing it with free(). + * If s is NULL, or on out-of memory, returns NULL. + * + *********************************************************************/ +char * html_encode_and_free_original(char *s) +{ + char * result; + + if (s == NULL) + { + return NULL; + } + + result = html_encode(s); + free(s); + + return result; +} + + +/********************************************************************* + * + * Function : cookie_encode + * + * Description : Encodes a string so it can be used in a cookie. + * Replaces " ", ",", and ";" with the appropriate + * codes. + * + * Parameters : + * 1 : s = String to encode. Null-terminated. + * + * Returns : Encoded string, newly allocated on the heap. + * Caller is responsible for freeing it with free(). + * If s is NULL, or on out-of memory, returns NULL. + * + *********************************************************************/ +char * cookie_encode(const char *s) +{ + char * buf; + + if (s == NULL) + { + return NULL; + } + + /* each input char can expand to at most 3 chars */ + buf = (char *) malloc((strlen(s) * 3) + 1); + + if (buf) + { + char c; + char * p = buf; + while ( (c = *s++) != '\0') + { + const char * replace_with = cookie_code_map[(unsigned char) c]; + if (replace_with != NULL) + { + strcpy(p, replace_with); + p += strlen(replace_with); + } + else + { + *p++ = c; + } + } + + *p = '\0'; + } + + return(buf); +} + +/********************************************************************* + * + * Function : url_encode + * + * Description : Encodes a string so it can be used in a URL + * query string. Replaces special characters with + * the appropriate %xx codes. + * + * Parameters : + * 1 : s = String to encode. Null-terminated. + * + * Returns : Encoded string, newly allocated on the heap. + * Caller is responsible for freeing it with free(). + * If s is NULL, or on out-of memory, returns NULL. + * + *********************************************************************/ +char * url_encode(const char *s) +{ + char * buf; + + if (s == NULL) + { + return NULL; + } + + /* each input char can expand to at most 3 chars */ + buf = (char *) malloc((strlen(s) * 3) + 1); + + if (buf) + { + char c; + char * p = buf; + while( (c = *s++) != '\0') + { + const char * replace_with = url_code_map[(unsigned char) c]; + if (replace_with != NULL) + { + strcpy(p, replace_with); + p += strlen(replace_with); + } + else + { + *p++ = c; + } + } + + *p = '\0'; + + } + + return(buf); +} + + +/********************************************************************* + * + * Function : xdtoi + * + * Description : Converts a single hex digit to an integer. + * + * Parameters : + * 1 : d = in the range of ['0'..'9', 'A'..'F', 'a'..'f'] + * + * Returns : The integer value, or -1 for non-hex characters. + * + *********************************************************************/ +static int xdtoi(const int d) +{ + if ((d >= '0') && (d <= '9')) + { + return(d - '0'); + } + else if ((d >= 'a') && (d <= 'f')) + { + return(d - 'a' + 10); + } + else if ((d >= 'A') && (d <= 'F')) + { + return(d - 'A' + 10); + } + else + { + return(-1); + } +} + + +/********************************************************************* + * + * Function : xtoi + * + * Description : Hex string to integer conversion. + * + * Parameters : + * 1 : s = a 2 digit hex string (e.g. "1f"). Only the + * first two characters will be looked at. + * + * Returns : The integer value, or 0 for non-hex strings. + * + *********************************************************************/ +static int xtoi(const char *s) +{ + int d1, d2; + + d1 = xdtoi(*s); + if(d1 >= 0) + { + d2 = xdtoi(*(s+1)); + if(d2 >= 0) + { + return (d1 << 4) + d2; + } + } + + return 0; +} + + +/********************************************************************* + * + * Function : url_decode + * + * Description : Decodes a URL query string, replacing %xx codes + * with their decoded form. + * + * Parameters : + * 1 : s = String to decode. Null-terminated. + * + * Returns : Decoded string, newly allocated on the heap. + * Caller is responsible for freeing it with free(). + * + *********************************************************************/ +char *url_decode(const char * s) +{ + char *buf = malloc(strlen(s) + 1); + char *q = buf; + + if (buf) + { + while (*s) + { + switch (*s) + { + case '+': + s++; + *q++ = ' '; + break; + + case '%': + if ((*q = xtoi(s + 1)) != '\0') + { + s += 3; + q++; + } + else + { + /* malformed, just use it */ + *q++ = *s++; + } + break; + + default: + *q++ = *s++; + break; + } + } + *q = '\0'; + } + + return(buf); + +} + + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/encode.h b/src/encode.h new file mode 100644 index 00000000..b810e427 --- /dev/null +++ b/src/encode.h @@ -0,0 +1,85 @@ +#ifndef ENCODE_H_INCLUDED +#define ENCODE_H_INCLUDED +#define ENCODE_H_VERSION "$Id: encode.h,v 1.5 2002/03/26 22:29:54 swa Exp $" +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/encode.h,v $ + * + * Purpose : Functions to encode and decode URLs, and also to + * encode cookies and HTML text. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: encode.h,v $ + * Revision 1.5 2002/03/26 22:29:54 swa + * we have a new homepage! + * + * Revision 1.4 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.3 2002/01/22 23:28:07 jongfoster + * Adding convenience function html_encode_and_free_original() + * Making all functions accept NULL paramaters - in this case, they + * simply return NULL. This allows error-checking to be deferred. + * + * Revision 1.2 2001/07/29 18:43:08 jongfoster + * Changing #ifdef _FILENAME_H to FILENAME_H_INCLUDED, to conform to + * ANSI C rules. + * + * Revision 1.1.1.1 2001/05/15 13:58:51 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#ifdef __cplusplus +extern "C" { +#endif + +extern char * html_encode(const char *s); +extern char * cookie_encode(const char *s); +extern char * url_encode(const char *s); +extern char * url_decode(const char *str); + +extern char * html_encode_and_free_original(char *s); + +/* Revision control strings from this header and associated .c file */ +extern const char encode_rcs[]; +extern const char encode_h_rcs[]; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ndef ENCODE_H_INCLUDED */ + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/errlog.c b/src/errlog.c new file mode 100644 index 00000000..c65aec99 --- /dev/null +++ b/src/errlog.c @@ -0,0 +1,973 @@ +const char errlog_rcs[] = "$Id: errlog.c,v 1.40 2002/05/22 01:27:27 david__schmidt Exp $"; +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/errlog.c,v $ + * + * Purpose : Log errors to a designated destination in an elegant, + * printf-like fashion. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: errlog.c,v $ + * Revision 1.40 2002/05/22 01:27:27 david__schmidt + * + * Add os2_socket_strerr mirroring w32_socket_strerr. + * + * Revision 1.39 2002/04/03 17:15:27 gliptak + * zero padding thread ids in log + * + * Revision 1.38 2002/03/31 17:18:59 jongfoster + * Win32 only: Enabling STRICT to fix a VC++ compile warning. + * + * Revision 1.37 2002/03/27 14:32:43 david__schmidt + * More compiler warning message maintenance + * + * Revision 1.36 2002/03/26 22:29:54 swa + * we have a new homepage! + * + * Revision 1.35 2002/03/24 15:23:33 jongfoster + * Name changes + * + * Revision 1.34 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.33 2002/03/13 00:27:04 jongfoster + * Killing warnings + * + * Revision 1.32 2002/03/07 03:46:17 oes + * Fixed compiler warnings + * + * Revision 1.31 2002/03/06 23:02:57 jongfoster + * Removing tabs + * + * Revision 1.30 2002/03/05 22:43:45 david__schmidt + * - Better error reporting on OS/2 + * - Fix double-slash comment (oops) + * + * Revision 1.29 2002/03/04 23:45:13 jongfoster + * Printing thread ID if using Win32 native threads + * + * Revision 1.28 2002/03/04 17:59:59 oes + * Deleted deletePidFile(), cosmetics + * + * Revision 1.27 2002/03/04 02:08:01 david__schmidt + * Enable web editing of actions file on OS/2 (it had been broken all this time!) + * + * Revision 1.26 2002/01/09 19:05:45 steudten + * Fix big memory leak. + * + * Revision 1.25 2002/01/09 14:32:08 oes + * Added support for gmtime_r and localtime_r. + * + * Revision 1.24 2001/12/30 14:07:32 steudten + * - Add signal handling (unix) + * - Add SIGHUP handler (unix) + * - Add creation of pidfile (unix) + * - Add action 'top' in rc file (RH) + * - Add entry 'SIGNALS' to manpage + * - Add exit message to logfile (unix) + * + * Revision 1.23 2001/11/07 00:02:13 steudten + * Add line number in error output for lineparsing for + * actionsfile and configfile. + * Special handling for CLF added. + * + * Revision 1.22 2001/11/05 23:43:05 steudten + * Add time+date to log files. + * + * Revision 1.21 2001/10/25 03:40:47 david__schmidt + * Change in porting tactics: OS/2's EMX porting layer doesn't allow multiple + * threads to call select() simultaneously. So, it's time to do a real, live, + * native OS/2 port. See defines for __EMX__ (the porting layer) vs. __OS2__ + * (native). Both versions will work, but using __OS2__ offers multi-threading. + * + * Revision 1.20 2001/09/16 23:04:34 jongfoster + * Fixing a warning + * + * Revision 1.19 2001/09/13 20:08:06 jongfoster + * Adding support for LOG_LEVEL_CGI + * + * Revision 1.18 2001/09/10 11:27:24 oes + * Declaration of w32_socket_strerr now conditional + * + * Revision 1.17 2001/09/10 10:17:13 oes + * Removed unused variable; Fixed sprintf format + * + * Revision 1.16 2001/07/30 22:08:36 jongfoster + * Tidying up #defines: + * - All feature #defines are now of the form FEATURE_xxx + * - Permanently turned off WIN_GUI_EDIT + * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS + * + * Revision 1.15 2001/07/29 17:41:10 jongfoster + * Now prints thread ID for each message (pthreads only) + * + * Revision 1.14 2001/07/19 19:03:48 haroon + * - Added case for LOG_LEVEL_POPUPS + * + * Revision 1.13 2001/07/13 13:58:58 oes + * - Added case for LOG_LEVEL_DEANIMATE + * - Removed all #ifdef PCRS + * + * Revision 1.12 2001/06/09 10:55:28 jongfoster + * Changing BUFSIZ ==> BUFFER_SIZE + * + * Revision 1.11 2001/06/01 18:14:49 jongfoster + * Changing the calls to strerr() to check HAVE_STRERR (which is defined + * in config.h if appropriate) rather than the NO_STRERR macro. + * + * Revision 1.10 2001/05/29 11:52:21 oes + * Conditional compilation of w32_socket_error + * + * Revision 1.9 2001/05/28 16:15:17 jongfoster + * Improved reporting of errors under Win32. + * + * Revision 1.8 2001/05/26 17:25:14 jongfoster + * Added support for CLF (Common Log Format) and fixed LOG_LEVEL_LOG + * + * Revision 1.7 2001/05/26 15:21:28 jongfoster + * Activity animation in Win32 GUI now works even if debug==0 + * + * Revision 1.6 2001/05/25 21:55:08 jongfoster + * Now cleans up properly on FATAL (removes taskbar icon etc) + * + * Revision 1.5 2001/05/22 18:46:04 oes + * + * - Enabled filtering banners by size rather than URL + * by adding patterns that replace all standard banner + * sizes with the "Junkbuster" gif to the re_filterfile + * + * - Enabled filtering WebBugs by providing a pattern + * which kills all 1x1 images + * + * - Added support for PCRE_UNGREEDY behaviour to pcrs, + * which is selected by the (nonstandard and therefore + * capital) letter 'U' in the option string. + * It causes the quantifiers to be ungreedy by default. + * Appending a ? turns back to greedy (!). + * + * - Added a new interceptor ijb-send-banner, which + * sends back the "Junkbuster" gif. Without imagelist or + * MSIE detection support, or if tinygif = 1, or the + * URL isn't recognized as an imageurl, a lame HTML + * explanation is sent instead. + * + * - Added new feature, which permits blocking remote + * script redirects and firing back a local redirect + * to the browser. + * The feature is conditionally compiled, i.e. it + * can be disabled with --disable-fast-redirects, + * plus it must be activated by a "fast-redirects" + * line in the config file, has its own log level + * and of course wants to be displayed by show-proxy-args + * Note: Boy, all the #ifdefs in 1001 locations and + * all the fumbling with configure.in and acconfig.h + * were *way* more work than the feature itself :-( + * + * - Because a generic redirect template was needed for + * this, tinygif = 3 now uses the same. + * + * - Moved GIFs, and other static HTTP response templates + * to project.h + * + * - Some minor fixes + * + * - Removed some >400 CRs again (Jon, you really worked + * a lot! ;-) + * + * Revision 1.4 2001/05/21 19:32:54 jongfoster + * Added another #ifdef _WIN_CONSOLE + * + * Revision 1.3 2001/05/20 01:11:40 jongfoster + * Added support for LOG_LEVEL_FATAL + * Renamed LOG_LEVEL_FRC to LOG_LEVEL_FORCE, + * and LOG_LEVEL_REF to LOG_LEVEL_RE_FILTER + * + * Revision 1.2 2001/05/17 22:42:01 oes + * - Cleaned CRLF's from the sources and related files + * - Repaired logging for REF and FRC + * + * Revision 1.1.1.1 2001/05/15 13:58:51 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#include "config.h" +#include "miscutil.h" + +#include +#include +#include +#include + +#if !defined(_WIN32) && !defined(__OS2__) +#include +#endif /* !defined(_WIN32) && !defined(__OS2__) */ + +#include +#include +#ifdef FEATURE_PTHREAD +#include +#endif /* def FEATURE_PTHREAD */ + +#ifdef _WIN32 +#ifndef STRICT +#define STRICT +#endif +#include +#ifndef _WIN_CONSOLE +#include "w32log.h" +#endif /* ndef _WIN_CONSOLE */ +#endif /* def _WIN32 */ + +#ifdef __OS2__ +#include /* For sock_errno */ +#define INCL_DOS +#include +#endif + +#include "errlog.h" +#include "project.h" +#include "jcc.h" + +const char errlog_h_rcs[] = ERRLOG_H_VERSION; + + +/* + * LOG_LEVEL_FATAL cannot be turned off. (There are + * some exceptional situations where we need to get a + * message to the user). + */ +#define LOG_LEVEL_MINIMUM LOG_LEVEL_FATAL + +/* where to log (default: stderr) */ +static FILE *logfp = NULL; + +/* logging detail level. */ +static int debug = (LOG_LEVEL_FATAL | LOG_LEVEL_ERROR | LOG_LEVEL_INFO); + +/* static functions */ +static void fatal_error(const char * error_message); +#ifdef _WIN32 +static char *w32_socket_strerr(int errcode, char *tmp_buf); +#endif +#ifdef __OS2__ +static char *os2_socket_strerr(int errcode, char *tmp_buf); +#endif + +/********************************************************************* + * + * Function : fatal_error + * + * Description : Displays a fatal error to standard error (or, on + * a WIN32 GUI, to a dialog box), and exits + * JunkBuster with status code 1. + * + * Parameters : + * 1 : error_message = The error message to display. + * + * Returns : Does not return. + * + *********************************************************************/ +static void fatal_error(const char * error_message) +{ +#if defined(_WIN32) && !defined(_WIN_CONSOLE) + MessageBox(g_hwndLogFrame, error_message, "Privoxy Error", + MB_OK | MB_ICONERROR | MB_TASKMODAL | MB_SETFOREGROUND | MB_TOPMOST); + + /* Cleanup - remove taskbar icon etc. */ + TermLogWindow(); + +#else /* if !defined(_WIN32) || defined(_WIN_CONSOLE) */ + fputs(error_message, stderr); +#endif /* defined(_WIN32) && !defined(_WIN_CONSOLE) */ + +#if defined(unix) + unlink(pidfile); +#endif /* unix */ + + exit(1); +} + + +/********************************************************************* + * + * Function : init_error_log + * + * Description : Initializes the logging module. Must call before + * calling log_error. + * + * Parameters : + * 1 : prog_name = The program name. + * 2 : logfname = The logfile name, or NULL for stderr. + * 3 : debuglevel = The debugging level. + * + * Returns : N/A + * + *********************************************************************/ +void init_error_log(const char *prog_name, const char *logfname, int debuglevel) +{ + FILE *fp; + + /* FIXME RACE HAZARD: should start critical section error_log_use here */ + + /* set the logging detail level */ + debug = debuglevel | LOG_LEVEL_MINIMUM; + + if ((logfp != NULL) && (logfp != stderr)) + { + log_error(LOG_LEVEL_INFO, "(Re-)Open logfile %s", logfname ? logfname : "none"); + fclose(logfp); + } + logfp = stderr; + + /* set the designated log file */ + if( logfname ) + { + if( NULL == (fp = fopen(logfname, "a")) ) + { + log_error(LOG_LEVEL_FATAL, "init_error_log(): can't open logfile: %s", logfname); + } + + /* set logging to be completely unbuffered */ + setbuf(fp, NULL); + + logfp = fp; + } + + log_error(LOG_LEVEL_INFO, "Privoxy version " VERSION); + if (prog_name != NULL) + { + log_error(LOG_LEVEL_INFO, "Program name: %s", prog_name); + } + + /* FIXME RACE HAZARD: should end critical section error_log_use here */ + +} /* init_error_log */ + + +/********************************************************************* + * + * Function : log_error + * + * Description : This is the error-reporting and logging function. + * + * Parameters : + * 1 : loglevel = the type of message to be logged + * 2 : fmt = the main string we want logged, printf-like + * 3 : ... = arguments to be inserted in fmt (printf-like). + * + * Returns : N/A + * + *********************************************************************/ +void log_error(int loglevel, char *fmt, ...) +{ + va_list ap; + char *outbuf= NULL; + static char *outbuf_save = NULL; + char * src = fmt; + int outc = 0; + long this_thread = 1; /* was: pthread_t this_thread;*/ +#ifdef __OS2__ + PTIB ptib; + APIRET ulrc; +#endif /* __OS2__ */ + +#if defined(_WIN32) && !defined(_WIN_CONSOLE) + /* + * Irrespective of debug setting, a GET/POST/CONNECT makes + * the taskbar icon animate. (There is an option to disable + * this but checking that is handled inside LogShowActivity()). + */ + if (loglevel == LOG_LEVEL_GPC) + { + LogShowActivity(); + } +#endif /* defined(_WIN32) && !defined(_WIN_CONSOLE) */ + + /* verify if loglevel applies to current settings and bail out if negative */ + if ((loglevel & debug) == 0) + { + return; + } + + /* FIXME get current thread id */ +#ifdef FEATURE_PTHREAD + this_thread = (long)pthread_self(); +#elif defined(_WIN32) + this_thread = GetCurrentThreadId(); +#elif defined(__OS2__) + ulrc = DosGetInfoBlocks(&ptib, NULL); + if (ulrc == 0) + this_thread = ptib -> tib_ptib2 -> tib2_ultid; +#endif /* def FEATURE_PTHREAD */ + + if ( !outbuf_save ) + { + outbuf_save = outbuf = (char*)malloc(BUFFER_SIZE); + assert(outbuf); + } + outbuf = outbuf_save; + + { + /* + * Write timestamp into tempbuf. + * + * Complex because not all OSs have tm_gmtoff or + * the %z field in strftime() + */ + time_t now; + struct tm tm_now; + time (&now); +#ifdef HAVE_LOCALTIME_R + tm_now = *localtime_r(&now, &tm_now); +#else + tm_now = *localtime (&now); +#endif + strftime(outbuf, BUFFER_SIZE-6, "%b %d %H:%M:%S ", &tm_now); + outbuf += strlen( outbuf ); + } + switch (loglevel) + { + case LOG_LEVEL_ERROR: + outc = sprintf(outbuf, "Privoxy(%05ld) Error: ", this_thread); + break; + case LOG_LEVEL_FATAL: + outc = sprintf(outbuf, "Privoxy(%05ld) Fatal error: ", this_thread); + break; + case LOG_LEVEL_GPC: + outc = sprintf(outbuf, "Privoxy(%05ld) Request: ", this_thread); + break; + case LOG_LEVEL_CONNECT: + outc = sprintf(outbuf, "Privoxy(%05ld) Connect: ", this_thread); + break; + case LOG_LEVEL_LOG: + outc = sprintf(outbuf, "Privoxy(%05ld) Writing: ", this_thread); + break; + case LOG_LEVEL_HEADER: + outc = sprintf(outbuf, "Privoxy(%05ld) Header: ", this_thread); + break; + case LOG_LEVEL_INFO: + outc = sprintf(outbuf, "Privoxy(%05ld) Info: ", this_thread); + break; + case LOG_LEVEL_RE_FILTER: + outc = sprintf(outbuf, "Privoxy(%05ld) Re-Filter: ", this_thread); + break; +#ifdef FEATURE_FORCE_LOAD + case LOG_LEVEL_FORCE: + outc = sprintf(outbuf, "Privoxy(%05ld) Force: ", this_thread); + break; +#endif /* def FEATURE_FORCE_LOAD */ +#ifdef FEATURE_FAST_REDIRECTS + case LOG_LEVEL_REDIRECTS: + outc = sprintf(outbuf, "Privoxy(%05ld) Redirect: ", this_thread); + break; +#endif /* def FEATURE_FAST_REDIRECTS */ + case LOG_LEVEL_DEANIMATE: + outc = sprintf(outbuf, "Privoxy(%05ld) Gif-Deanimate: ", this_thread); + break; + case LOG_LEVEL_CLF: + outbuf = outbuf_save; + outc = 0; + outbuf[0] = '\0'; + break; +#ifdef FEATURE_KILL_POPUPS + case LOG_LEVEL_POPUPS: + outc = sprintf(outbuf, "Privoxy(%05ld) Kill-Popups: ", this_thread); + break; +#endif /* def FEATURE_KILL_POPUPS */ + case LOG_LEVEL_CGI: + outc = sprintf(outbuf, "Privoxy(%05ld) CGI: ", this_thread); + break; + default: + outc = sprintf(outbuf, "Privoxy(%05ld) UNKNOWN LOG TYPE(%d): ", this_thread, loglevel); + break; + } + + /* get ready to scan var. args. */ + va_start( ap, fmt ); + + /* build formatted message from fmt and var-args */ + while ((*src) && (outc < BUFFER_SIZE-2)) + { + char tempbuf[BUFFER_SIZE]; + char *sval = NULL; + int ival; + unsigned uval; + long lval; + unsigned long ulval; + int oldoutc; + char ch; + + ch = *src++; + if( ch != '%' ) + { + outbuf[outc++] = ch; + continue; + } + + ch = *src++; + switch (ch) { + case '%': + outbuf[outc++] = '%'; + break; + case 'd': + ival = va_arg( ap, int ); + oldoutc = outc; + outc += sprintf(tempbuf, "%d", ival); + if (outc < BUFFER_SIZE-1) + { + strcpy(outbuf + oldoutc, tempbuf); + } + else + { + outbuf[oldoutc] = '\0'; + } + break; + case 'u': + uval = va_arg( ap, unsigned ); + oldoutc = outc; + outc += sprintf(tempbuf, "%u", uval); + if (outc < BUFFER_SIZE-1) + { + strcpy(outbuf + oldoutc, tempbuf); + } + else + { + outbuf[oldoutc] = '\0'; + } + break; + case 'l': + /* this is a modifier that must be followed by u or d */ + ch = *src++; + if (ch == 'd') + { + lval = va_arg( ap, long ); + oldoutc = outc; + outc += sprintf(tempbuf, "%ld", lval); + } + else if (ch == 'u') + { + ulval = va_arg( ap, unsigned long ); + oldoutc = outc; + outc += sprintf(tempbuf, "%lu", ulval); + } + else + { + /* Error */ + sprintf(outbuf, "Privoxy(%ld) Error: log_error(): Bad format string:\n" + "Format = \"%s\"\n" + "Exiting.", this_thread, fmt); + /* FIXME RACE HAZARD: should start critical section error_log_use here */ + if( !logfp ) + { + logfp = stderr; + } + fputs(outbuf, logfp); + /* FIXME RACE HAZARD: should end critical section error_log_use here */ + fatal_error(outbuf); + /* Never get here */ + break; + } + if (outc < BUFFER_SIZE-1) + { + strcpy(outbuf + oldoutc, tempbuf); + } + else + { + outbuf[oldoutc] = '\0'; + } + break; + case 'c': + /* + * Note that char paramaters are converted to int, so we need to + * pass "int" to va_arg. (See K&R, 2nd ed, section A7.3.2, page 202) + */ + outbuf[outc++] = (char) va_arg( ap, int ); + break; + case 's': + sval = va_arg( ap, char * ); + oldoutc = outc; + outc += strlen(sval); + if (outc < BUFFER_SIZE-1) + { + strcpy(outbuf + oldoutc, sval); + } + else + { + outbuf[oldoutc] = '\0'; + } + break; + case 'N': + /* Non-standard: Print a counted string. Takes 2 parameters: + * int length, const char * string + */ + ival = va_arg( ap, int ); + sval = va_arg( ap, char * ); + if (ival < 0) + { + ival = 0; + } + oldoutc = outc; + outc += ival; + if (outc < BUFFER_SIZE-1) + { + memcpy(outbuf + oldoutc, sval, (size_t) ival); + } + else + { + outbuf[oldoutc] = '\0'; + } + break; + case 'E': + /* Non-standard: Print error code from errno */ +#ifdef _WIN32 + ival = WSAGetLastError(); + sval = w32_socket_strerr(ival, tempbuf); +#elif __OS2__ + ival = sock_errno(); + if (ival != 0) + sval = os2_socket_strerr(ival, tempbuf); + else + { + ival = errno; + sval = strerror(ival); + } +#else /* ifndef _WIN32 */ + ival = errno; +#ifdef HAVE_STRERROR + sval = strerror(ival); +#else /* ifndef HAVE_STRERROR */ + sval = NULL; +#endif /* ndef HAVE_STRERROR */ + if (sval == NULL) + { + sprintf(tempbuf, "(errno = %d)", ival); + sval = tempbuf; + } +#endif /* ndef _WIN32 */ + oldoutc = outc; + outc += strlen(sval); + if (outc < BUFFER_SIZE-1) + { + strcpy(outbuf + oldoutc, sval); + } + else + { + outbuf[oldoutc] = '\0'; + } + break; + case 'T': + /* Non-standard: Print a Common Log File timestamp */ + { + /* + * Write timestamp into tempbuf. + * + * Complex because not all OSs have tm_gmtoff or + * the %z field in strftime() + */ + time_t now; + struct tm *tm_now; + struct tm gmt; +#ifdef HAVE_LOCALTIME_R + struct tm dummy; +#endif + int days, hrs, mins; + time (&now); +#ifdef HAVE_GMTIME_R + gmt = *gmtime_r(&now, &gmt); +#else + gmt = *gmtime(&now); +#endif +#ifdef HAVE_LOCALTIME_R + tm_now = localtime_r(&now, &dummy); +#else + tm_now = localtime (&now); +#endif + days = tm_now->tm_yday - gmt.tm_yday; + hrs = ((days < -1 ? 24 : 1 < days ? -24 : days * 24) + tm_now->tm_hour - gmt.tm_hour); + mins = hrs * 60 + tm_now->tm_min - gmt.tm_min; + strftime (tempbuf, BUFFER_SIZE-6, "%d/%b/%Y:%H:%M:%S ", tm_now); + sprintf (tempbuf + strlen(tempbuf), "%+03d%02d", mins / 60, abs(mins) % 60); + } + oldoutc = outc; + outc += strlen(tempbuf); + if (outc < BUFFER_SIZE-1) + { + strcpy(outbuf + oldoutc, tempbuf); + } + else + { + outbuf[oldoutc] = '\0'; + } + break; + default: + sprintf(outbuf, "Privoxy(%ld) Error: log_error(): Bad format string:\n" + "Format = \"%s\"\n" + "Exiting.", this_thread, fmt); + /* FIXME RACE HAZARD: should start critical section error_log_use here */ + if( !logfp ) + { + logfp = stderr; + } + fputs(outbuf_save, logfp); + /* FIXME RACE HAZARD: should end critical section error_log_use here */ + fatal_error(outbuf_save); + /* Never get here */ + break; + + } /* switch( p ) */ + + } /* for( p ... ) */ + + /* done with var. args */ + va_end( ap ); + + if (outc >= BUFFER_SIZE-2) + { + /* insufficient room for newline and trailing null. */ + + static const char warning[] = "... [too long, truncated]\n"; + + if (outc < BUFFER_SIZE) + { + /* Need to add terminating null in this case. */ + outbuf[outc] = '\0'; + } + + /* Truncate output */ + outbuf[BUFFER_SIZE - sizeof(warning)] = '\0'; + + /* Append warning */ + strcat(outbuf, warning); + } + else + { + /* Add terminating newline and null */ + outbuf[outc++] = '\n'; + outbuf[outc] = '\0'; + } + + /* FIXME RACE HAZARD: should start critical section error_log_use here */ + + /* deal with glibc stupidity - it won't let you initialize logfp */ + if( !logfp ) + { + logfp = stderr; + } + + fputs(outbuf_save, logfp); + + if (loglevel == LOG_LEVEL_FATAL) + { + fatal_error(outbuf_save); + /* Never get here */ + } + + /* FIXME RACE HAZARD: should end critical section error_log_use here */ + +#if defined(_WIN32) && !defined(_WIN_CONSOLE) + /* Write to display */ + LogPutString(outbuf_save); +#endif /* defined(_WIN32) && !defined(_WIN_CONSOLE) */ + +} + + +#ifdef _WIN32 +/********************************************************************* + * + * Function : w32_socket_strerr + * + * Description : Translate the return value from WSAGetLastError() + * into a string. + * + * Parameters : + * 1 : errcode = The return value from WSAGetLastError(). + * 2 : tmp_buf = A temporary buffer that might be used to + * store the string. + * + * Returns : String representing the error code. This may be + * a global string constant or a string stored in + * tmp_buf. + * + *********************************************************************/ +static char *w32_socket_strerr(int errcode, char *tmp_buf) +{ +#define TEXT_FOR_ERROR(code,text) \ + if (errcode == code) \ + { \ + return #code " - " text; \ + } + + TEXT_FOR_ERROR(WSAEACCES, "Permission denied") + TEXT_FOR_ERROR(WSAEADDRINUSE, "Address already in use.") + TEXT_FOR_ERROR(WSAEADDRNOTAVAIL, "Cannot assign requested address."); + TEXT_FOR_ERROR(WSAEAFNOSUPPORT, "Address family not supported by protocol family."); + TEXT_FOR_ERROR(WSAEALREADY, "Operation already in progress."); + TEXT_FOR_ERROR(WSAECONNABORTED, "Software caused connection abort."); + TEXT_FOR_ERROR(WSAECONNREFUSED, "Connection refused."); + TEXT_FOR_ERROR(WSAECONNRESET, "Connection reset by peer."); + TEXT_FOR_ERROR(WSAEDESTADDRREQ, "Destination address required."); + TEXT_FOR_ERROR(WSAEFAULT, "Bad address."); + TEXT_FOR_ERROR(WSAEHOSTDOWN, "Host is down."); + TEXT_FOR_ERROR(WSAEHOSTUNREACH, "No route to host."); + TEXT_FOR_ERROR(WSAEINPROGRESS, "Operation now in progress."); + TEXT_FOR_ERROR(WSAEINTR, "Interrupted function call."); + TEXT_FOR_ERROR(WSAEINVAL, "Invalid argument."); + TEXT_FOR_ERROR(WSAEISCONN, "Socket is already connected."); + TEXT_FOR_ERROR(WSAEMFILE, "Too many open sockets."); + TEXT_FOR_ERROR(WSAEMSGSIZE, "Message too long."); + TEXT_FOR_ERROR(WSAENETDOWN, "Network is down."); + TEXT_FOR_ERROR(WSAENETRESET, "Network dropped connection on reset."); + TEXT_FOR_ERROR(WSAENETUNREACH, "Network is unreachable."); + TEXT_FOR_ERROR(WSAENOBUFS, "No buffer space available."); + TEXT_FOR_ERROR(WSAENOPROTOOPT, "Bad protocol option."); + TEXT_FOR_ERROR(WSAENOTCONN, "Socket is not connected."); + TEXT_FOR_ERROR(WSAENOTSOCK, "Socket operation on non-socket."); + TEXT_FOR_ERROR(WSAEOPNOTSUPP, "Operation not supported."); + TEXT_FOR_ERROR(WSAEPFNOSUPPORT, "Protocol family not supported."); + TEXT_FOR_ERROR(WSAEPROCLIM, "Too many processes."); + TEXT_FOR_ERROR(WSAEPROTONOSUPPORT, "Protocol not supported."); + TEXT_FOR_ERROR(WSAEPROTOTYPE, "Protocol wrong type for socket."); + TEXT_FOR_ERROR(WSAESHUTDOWN, "Cannot send after socket shutdown."); + TEXT_FOR_ERROR(WSAESOCKTNOSUPPORT, "Socket type not supported."); + TEXT_FOR_ERROR(WSAETIMEDOUT, "Connection timed out."); + TEXT_FOR_ERROR(WSAEWOULDBLOCK, "Resource temporarily unavailable."); + TEXT_FOR_ERROR(WSAHOST_NOT_FOUND, "Host not found."); + TEXT_FOR_ERROR(WSANOTINITIALISED, "Successful WSAStartup not yet performed."); + TEXT_FOR_ERROR(WSANO_DATA, "Valid name, no data record of requested type."); + TEXT_FOR_ERROR(WSANO_RECOVERY, "This is a non-recoverable error."); + TEXT_FOR_ERROR(WSASYSNOTREADY, "Network subsystem is unavailable."); + TEXT_FOR_ERROR(WSATRY_AGAIN, "Non-authoritative host not found."); + TEXT_FOR_ERROR(WSAVERNOTSUPPORTED, "WINSOCK.DLL version out of range."); + TEXT_FOR_ERROR(WSAEDISCON, "Graceful shutdown in progress."); + /* + * The following error codes are documented in the Microsoft WinSock + * reference guide, but don't actually exist. + * + * TEXT_FOR_ERROR(WSA_INVALID_HANDLE, "Specified event object handle is invalid."); + * TEXT_FOR_ERROR(WSA_INVALID_PARAMETER, "One or more parameters are invalid."); + * TEXT_FOR_ERROR(WSAINVALIDPROCTABLE, "Invalid procedure table from service provider."); + * TEXT_FOR_ERROR(WSAINVALIDPROVIDER, "Invalid service provider version number."); + * TEXT_FOR_ERROR(WSA_IO_PENDING, "Overlapped operations will complete later."); + * TEXT_FOR_ERROR(WSA_IO_INCOMPLETE, "Overlapped I/O event object not in signaled state."); + * TEXT_FOR_ERROR(WSA_NOT_ENOUGH_MEMORY, "Insufficient memory available."); + * TEXT_FOR_ERROR(WSAPROVIDERFAILEDINIT, "Unable to initialize a service provider."); + * TEXT_FOR_ERROR(WSASYSCALLFAILURE, "System call failure."); + * TEXT_FOR_ERROR(WSA_OPERATION_ABORTED, "Overlapped operation aborted."); + */ + + sprintf(tmp_buf, "(error number %d)", errcode); + return tmp_buf; +} +#endif /* def _WIN32 */ + + +#ifdef __OS2__ +/********************************************************************* + * + * Function : os2_socket_strerr + * + * Description : Translate the return value from sock_errno() + * into a string. + * + * Parameters : + * 1 : errcode = The return value from sock_errno(). + * 2 : tmp_buf = A temporary buffer that might be used to + * store the string. + * + * Returns : String representing the error code. This may be + * a global string constant or a string stored in + * tmp_buf. + * + *********************************************************************/ +static char *os2_socket_strerr(int errcode, char *tmp_buf) +{ +#define TEXT_FOR_ERROR(code,text) \ + if (errcode == code) \ + { \ + return #code " - " text; \ + } + + TEXT_FOR_ERROR(SOCEPERM , "Not owner.") + TEXT_FOR_ERROR(SOCESRCH , "No such process.") + TEXT_FOR_ERROR(SOCEINTR , "Interrupted system call.") + TEXT_FOR_ERROR(SOCENXIO , "No such device or address.") + TEXT_FOR_ERROR(SOCEBADF , "Bad file number.") + TEXT_FOR_ERROR(SOCEACCES , "Permission denied.") + TEXT_FOR_ERROR(SOCEFAULT , "Bad address.") + TEXT_FOR_ERROR(SOCEINVAL , "Invalid argument.") + TEXT_FOR_ERROR(SOCEMFILE , "Too many open files.") + TEXT_FOR_ERROR(SOCEPIPE , "Broken pipe.") + TEXT_FOR_ERROR(SOCEWOULDBLOCK , "Operation would block.") + TEXT_FOR_ERROR(SOCEINPROGRESS , "Operation now in progress.") + TEXT_FOR_ERROR(SOCEALREADY , "Operation already in progress.") + TEXT_FOR_ERROR(SOCENOTSOCK , "Socket operation on non-socket.") + TEXT_FOR_ERROR(SOCEDESTADDRREQ , "Destination address required.") + TEXT_FOR_ERROR(SOCEMSGSIZE , "Message too long.") + TEXT_FOR_ERROR(SOCEPROTOTYPE , "Protocol wrong type for socket.") + TEXT_FOR_ERROR(SOCENOPROTOOPT , "Protocol not available.") + TEXT_FOR_ERROR(SOCEPROTONOSUPPORT, "Protocol not supported.") + TEXT_FOR_ERROR(SOCESOCKTNOSUPPORT, "Socket type not supported.") + TEXT_FOR_ERROR(SOCEOPNOTSUPP , "Operation not supported.") + TEXT_FOR_ERROR(SOCEPFNOSUPPORT , "Protocol family not supported.") + TEXT_FOR_ERROR(SOCEAFNOSUPPORT , "Address family not supported by protocol family.") + TEXT_FOR_ERROR(SOCEADDRINUSE , "Address already in use.") + TEXT_FOR_ERROR(SOCEADDRNOTAVAIL , "Can't assign requested address.") + TEXT_FOR_ERROR(SOCENETDOWN , "Network is down.") + TEXT_FOR_ERROR(SOCENETUNREACH , "Network is unreachable.") + TEXT_FOR_ERROR(SOCENETRESET , "Network dropped connection on reset.") + TEXT_FOR_ERROR(SOCECONNABORTED , "Software caused connection abort.") + TEXT_FOR_ERROR(SOCECONNRESET , "Connection reset by peer.") + TEXT_FOR_ERROR(SOCENOBUFS , "No buffer space available.") + TEXT_FOR_ERROR(SOCEISCONN , "Socket is already connected.") + TEXT_FOR_ERROR(SOCENOTCONN , "Socket is not connected.") + TEXT_FOR_ERROR(SOCESHUTDOWN , "Can't send after socket shutdown.") + TEXT_FOR_ERROR(SOCETOOMANYREFS , "Too many references: can't splice.") + TEXT_FOR_ERROR(SOCETIMEDOUT , "Operation timed out.") + TEXT_FOR_ERROR(SOCECONNREFUSED , "Connection refused.") + TEXT_FOR_ERROR(SOCELOOP , "Too many levels of symbolic links.") + TEXT_FOR_ERROR(SOCENAMETOOLONG , "File name too long.") + TEXT_FOR_ERROR(SOCEHOSTDOWN , "Host is down.") + TEXT_FOR_ERROR(SOCEHOSTUNREACH , "No route to host.") + TEXT_FOR_ERROR(SOCENOTEMPTY , "Directory not empty.") + TEXT_FOR_ERROR(SOCEOS2ERR , "OS/2 Error.") + + sprintf(tmp_buf, "(error number %d)", errcode); + return tmp_buf; +} +#endif /* def __OS2__ */ + + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/errlog.h b/src/errlog.h new file mode 100644 index 00000000..2f024933 --- /dev/null +++ b/src/errlog.h @@ -0,0 +1,180 @@ +#ifndef ERRLOG_H_INCLUDED +#define ERRLOG_H_INCLUDED +#define ERRLOG_H_VERSION "$Id: errlog.h,v 1.13 2002/03/26 22:29:54 swa Exp $" +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/errlog.h,v $ + * + * Purpose : Log errors to a designated destination in an elegant, + * printf-like fashion. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: errlog.h,v $ + * Revision 1.13 2002/03/26 22:29:54 swa + * we have a new homepage! + * + * Revision 1.12 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.11 2002/03/06 23:02:57 jongfoster + * Removing tabs + * + * Revision 1.10 2001/09/13 20:08:06 jongfoster + * Adding support for LOG_LEVEL_CGI + * + * Revision 1.9 2001/07/30 22:08:36 jongfoster + * Tidying up #defines: + * - All feature #defines are now of the form FEATURE_xxx + * - Permanently turned off WIN_GUI_EDIT + * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS + * + * Revision 1.8 2001/07/29 18:43:08 jongfoster + * Changing #ifdef _FILENAME_H to FILENAME_H_INCLUDED, to conform to + * ANSI C rules. + * + * Revision 1.7 2001/07/19 19:02:53 haroon + * Added define for LOG_LEVEL_POPUPS + * + * Revision 1.6 2001/07/13 13:59:22 oes + * - Added LOG_LEVEL_DEANIMATE + * - Changed LOG_LEVEL_CLF + * - Removed all #ifdef PCRS + * + * Revision 1.5 2001/05/26 17:25:14 jongfoster + * Added support for CLF (Common Log Format) and fixed LOG_LEVEL_LOG + * + * Revision 1.4 2001/05/25 21:56:06 jongfoster + * Added FIXME comment to (broken) LOG_LEVEL_LOG + * + * Revision 1.3 2001/05/22 18:46:04 oes + * + * - Enabled filtering banners by size rather than URL + * by adding patterns that replace all standard banner + * sizes with the "Junkbuster" gif to the re_filterfile + * + * - Enabled filtering WebBugs by providing a pattern + * which kills all 1x1 images + * + * - Added support for PCRE_UNGREEDY behaviour to pcrs, + * which is selected by the (nonstandard and therefore + * capital) letter 'U' in the option string. + * It causes the quantifiers to be ungreedy by default. + * Appending a ? turns back to greedy (!). + * + * - Added a new interceptor ijb-send-banner, which + * sends back the "Junkbuster" gif. Without imagelist or + * MSIE detection support, or if tinygif = 1, or the + * URL isn't recognized as an imageurl, a lame HTML + * explanation is sent instead. + * + * - Added new feature, which permits blocking remote + * script redirects and firing back a local redirect + * to the browser. + * The feature is conditionally compiled, i.e. it + * can be disabled with --disable-fast-redirects, + * plus it must be activated by a "fast-redirects" + * line in the config file, has its own log level + * and of course wants to be displayed by show-proxy-args + * Note: Boy, all the #ifdefs in 1001 locations and + * all the fumbling with configure.in and acconfig.h + * were *way* more work than the feature itself :-( + * + * - Because a generic redirect template was needed for + * this, tinygif = 3 now uses the same. + * + * - Moved GIFs, and other static HTTP response templates + * to project.h + * + * - Some minor fixes + * + * - Removed some >400 CRs again (Jon, you really worked + * a lot! ;-) + * + * Revision 1.2 2001/05/20 01:11:40 jongfoster + * Added support for LOG_LEVEL_FATAL + * Renamed LOG_LEVEL_FRC to LOG_LEVEL_FORCE, + * and LOG_LEVEL_REF to LOG_LEVEL_RE_FILTER + * + * Revision 1.1.1.1 2001/05/15 13:58:51 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Debug level for errors */ + +#define LOG_LEVEL_GPC 0x0001 +#define LOG_LEVEL_CONNECT 0x0002 +#define LOG_LEVEL_IO 0x0004 +#define LOG_LEVEL_HEADER 0x0008 +#define LOG_LEVEL_LOG 0x0010 +#ifdef FEATURE_FORCE_LOAD +#define LOG_LEVEL_FORCE 0x0020 +#endif /* def FEATURE_FORCE_LOAD */ +#define LOG_LEVEL_RE_FILTER 0x0040 +#ifdef FEATURE_FAST_REDIRECTS +#define LOG_LEVEL_REDIRECTS 0x0080 +#endif /* def FEATURE_FAST_REDIRECTS */ +#define LOG_LEVEL_DEANIMATE 0x0100 + +#define LOG_LEVEL_CLF 0x0200 /* Common Log File format */ +#ifdef FEATURE_KILL_POPUPS +#define LOG_LEVEL_POPUPS 0x0400 /* Kill Popups */ +#endif /* def FEATURE_KILL_POPUPS */ + +#define LOG_LEVEL_CGI 0x0800 /* CGI / templates */ + +/* Following are always on: */ +#define LOG_LEVEL_INFO 0x1000 +#define LOG_LEVEL_ERROR 0x2000 +#define LOG_LEVEL_FATAL 0x4000 /* Exits after writing log */ + +extern void init_error_log(const char *prog_name, const char *logfname, int debuglevel); +extern void log_error(int loglevel, char *fmt, ...); + +/* Revision control strings from this header and associated .c file */ +extern const char errlog_rcs[]; +extern const char errlog_h_rcs[]; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ndef ERRLOG_H_INCLUDED */ + +/* + Local Variables: + tab-width: 3 + end: +*/ + diff --git a/src/filters.c b/src/filters.c new file mode 100644 index 00000000..60f47e97 --- /dev/null +++ b/src/filters.c @@ -0,0 +1,1601 @@ +const char filters_rcs[] = "$Id: filters.c,v 1.58 2002/04/24 02:11:17 oes Exp $"; +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/filters.c,v $ + * + * Purpose : Declares functions to parse/crunch headers and pages. + * Functions declared include: + * `acl_addr', `add_stats', `block_acl', `block_imageurl', + * `block_url', `url_actions', `domain_split', + * `filter_popups', `forward_url', 'redirect_url', + * `ij_untrusted_url', `intercept_url', `pcrs_filter_respose', + * 'ijb_send_banner', and `trust_url' + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: filters.c,v $ + * Revision 1.58 2002/04/24 02:11:17 oes + * Jon's multiple AF patch: url_actions now evaluates rules + * from all AFs. + * + * Revision 1.57 2002/04/08 20:38:34 swa + * fixed JB spelling + * + * Revision 1.56 2002/04/05 15:51:24 oes + * - bugfix: error-pages now get correct request protocol + * - fix for invalid HTML in trust info + * + * Revision 1.55 2002/04/02 16:13:51 oes + * Fix: No "Go there anyway" for SSL + * + * Revision 1.54 2002/04/02 14:55:56 oes + * Bugfix: is_untrusted_url() now depends on FEATURE_TRUST, not FEATURE_COOKIE_JAR + * + * Revision 1.53 2002/03/26 22:29:54 swa + * we have a new homepage! + * + * Revision 1.52 2002/03/24 16:35:57 jongfoster + * Removing logo + * + * Revision 1.51 2002/03/24 15:23:33 jongfoster + * Name changes + * + * Revision 1.50 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.49 2002/03/16 20:29:14 oes + * Cosmetics + * + * Revision 1.48 2002/03/13 20:25:34 oes + * Better logging for content filters + * + * Revision 1.47 2002/03/13 00:30:52 jongfoster + * Killing warnings + * Added option of always sending redirect for imageblock, + * currently disabled with #if 0. + * + * Revision 1.46 2002/03/12 01:42:49 oes + * Introduced modular filters + * + * Revision 1.45 2002/03/08 16:47:50 oes + * Added choice beween GIF and PNG built-in images + * + * Revision 1.44 2002/03/07 03:49:31 oes + * - Fixed compiler warnings etc + * - Changed built-in images from GIF to PNG + * (with regard to Unisys patent issue) + * - Added a 4x4 pattern PNG which is less intrusive + * than the logo but also clearly marks the deleted banners + * + * Revision 1.43 2002/01/22 23:51:59 jongfoster + * Replacing strsav() with the safer string_append(). + * + * Adding missing html_encode() to error message generators. Where encoded + * and unencoded versions of a string were provided, removing the unencoded + * one. + * + * Revision 1.42 2002/01/17 21:00:32 jongfoster + * Moving all our URL and URL pattern parsing code to urlmatch.c. + * + * Using a single, simple url_match(pattern,url) function - rather than + * the 3-line match routine which was repeated all over the place. + * + * Renaming free_url to free_url_spec, since it frees a struct url_spec. + * + * Using parse_http_url() to parse URLs without faking a HTTP + * request line for parse_http_request(). + * + * Revision 1.41 2001/11/13 00:14:07 jongfoster + * Fixing stupid bug now I've figured out what || means. + * (It always returns 0 or 1, not one of it's paramaters.) + * + * Revision 1.40 2001/10/26 17:37:55 oes + * - Re-enabled Netscape 200/404 bug workaround in block_url(): + * - Removed OS/2 special case + * - Made block_url() independant from sed() having been run + * - Made trust_url independant from sed() having been run + * - Made is_imageurl independant from sed() having been run. + * It now checks User-Agent: and Accept: by itself. + * + * + * Revision 1.39 2001/10/25 03:40:48 david__schmidt + * Change in porting tactics: OS/2's EMX porting layer doesn't allow multiple + * threads to call select() simultaneously. So, it's time to do a real, live, + * native OS/2 port. See defines for __EMX__ (the porting layer) vs. __OS2__ + * (native). Both versions will work, but using __OS2__ offers multi-threading. + * + * Revision 1.38 2001/10/23 21:32:33 jongfoster + * Adding error-checking to selected functions + * + * Revision 1.37 2001/10/22 15:33:56 david__schmidt + * Special-cased OS/2 out of the Netscape-abort-on-404-in-js problem in + * filters.c. Added a FIXME in front of the offending code. I'll gladly + * put in a better/more robust fix for all parties if one is presented... + * It seems that just returning 200 instead of 404 would pretty much fix + * it for everyone, but I don't know all the history of the problem. + * + * Revision 1.36 2001/10/10 16:44:16 oes + * Added match_portlist function + * + * Revision 1.35 2001/10/07 15:41:23 oes + * Replaced 6 boolean members of csp with one bitmap (csp->flags) + * + * New function remove_chunked_transfer_coding that strips chunked + * transfer coding to plain and is called by pcrs_filter_response + * and gif_deanimate_response if neccessary + * + * Improved handling of zero-change re_filter runs + * + * pcrs_filter_response and gif_deanimate_response now remove + * chunked transfer codeing before processing the body. + * + * Revision 1.34 2001/09/20 15:49:36 steudten + * + * Fix BUG: Change int size to size_t size in pcrs_filter_response(). + * See cgi.c fill_template(). + * + * Revision 1.33 2001/09/16 17:05:14 jongfoster + * Removing unused #include showarg.h + * + * Revision 1.32 2001/09/16 13:21:27 jongfoster + * Changes to use new list functions. + * + * Revision 1.31 2001/09/16 11:38:02 jongfoster + * Splitting fill_template() into 2 functions: + * template_load() loads the file + * template_fill() performs the PCRS regexps. + * This is because the CGI edit interface has a "table row" + * template which is used many times in the page - this + * change means it's only loaded from disk once. + * + * Revision 1.30 2001/09/16 11:00:10 jongfoster + * New function alloc_http_response, for symmetry with free_http_response + * + * Revision 1.29 2001/09/13 23:32:40 jongfoster + * Moving image data to cgi.c rather than cgi.h + * Fixing a GPF under Win32 (and any other OS that protects global + * constants from being written to). + * + * Revision 1.28 2001/09/10 10:18:51 oes + * Silenced compiler warnings + * + * Revision 1.27 2001/08/05 16:06:20 jongfoster + * Modifiying "struct map" so that there are now separate header and + * "map_entry" structures. This means that functions which modify a + * map no longer need to return a pointer to the modified map. + * Also, it no longer reverses the order of the entries (which may be + * important with some advanced template substitutions). + * + * Revision 1.26 2001/07/30 22:08:36 jongfoster + * Tidying up #defines: + * - All feature #defines are now of the form FEATURE_xxx + * - Permanently turned off WIN_GUI_EDIT + * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS + * + * Revision 1.25 2001/07/26 10:09:46 oes + * Made browser detection a little less naive + * + * Revision 1.24 2001/07/25 17:22:51 oes + * Added workaround for Netscape bug that prevents display of page when loading a component fails. + * + * Revision 1.23 2001/07/23 13:40:12 oes + * Fixed bug that caused document body to be dropped when pcrs joblist was empty. + * + * Revision 1.22 2001/07/18 12:29:34 oes + * - Made gif_deanimate_response respect + * csp->action->string[ACTION_STRING_DEANIMATE] + * - Logging cosmetics + * + * Revision 1.21 2001/07/13 13:59:53 oes + * - Introduced gif_deanimate_response which shares the + * generic content modification interface of pcrs_filter_response + * and acts as a wrapper to deanimate.c:gif_deanimate() + * - Renamed re_process_buffer to pcrs_filter_response + * - pcrs_filter_response now returns NULL on failiure + * - Removed all #ifdef PCRS + * + * Revision 1.20 2001/07/01 17:01:04 oes + * Added comments and missing return statement in is_untrusted_url() + * + * Revision 1.19 2001/06/29 21:45:41 oes + * Indentation, CRLF->LF, Tab-> Space + * + * Revision 1.18 2001/06/29 13:27:38 oes + * - Cleaned up, renamed and reorderd functions + * and improved comments + * + * - block_url: + * - Ported to CGI platform. Now delivers + * http_response or NULL + * - Unified HTML and GIF generation (moved image detection + * and GIF generation here from jcc.c:chat()) + * - Fixed HTTP status to: + * - 403 (Forbidden) for the "blocked" HTML message + * - 200 (OK) for GIF answers + * - 302 (Redirect) for redirect to GIF + * + * - trust_url: + * - Ported to CGI platform. Now delivers + * http_response or NULL + * - Separated detection of untrusted URL into + * (bool)is_untrusted_url + * - Added enforcement of untrusted requests + * + * - Moved redirect_url() from cgi.c to here + * and ported it to the CGI platform + * + * - Removed logentry from cancelled commit + * + * Revision 1.17 2001/06/09 10:55:28 jongfoster + * Changing BUFSIZ ==> BUFFER_SIZE + * + * Revision 1.16 2001/06/07 23:10:26 jongfoster + * Allowing unanchored domain patterns to back off and retry + * if they partially match. Optimized right-anchored patterns. + * Moving ACL and forward files into config file. + * Replacing struct gateway with struct forward_spec + * + * Revision 1.15 2001/06/03 19:12:00 oes + * extracted-CGI relevant stuff + * + * Revision 1.14 2001/06/01 10:30:55 oes + * Added optional left-anchoring to domaincmp + * + * Revision 1.13 2001/05/31 21:21:30 jongfoster + * Permissionsfile / actions file changes: + * - Changed "permission" to "action" throughout + * - changes to file format to allow string parameters + * - Moved helper functions to actions.c + * + * Revision 1.12 2001/05/31 17:35:20 oes + * + * - Enhanced domain part globbing with infix and prefix asterisk + * matching and optional unanchored operation + * + * Revision 1.11 2001/05/29 11:53:23 oes + * "See why" link added to "blocked" page + * + * Revision 1.10 2001/05/29 09:50:24 jongfoster + * Unified blocklist/imagelist/permissionslist. + * File format is still under discussion, but the internal changes + * are (mostly) done. + * + * Also modified interceptor behaviour: + * - We now intercept all URLs beginning with one of the following + * prefixes (and *only* these prefixes): + * * http://i.j.b/ + * * http://ijbswa.sf.net/config/ + * * http://ijbswa.sourceforge.net/config/ + * - New interceptors "home page" - go to http://i.j.b/ to see it. + * - Internal changes so that intercepted and fast redirect pages + * are not replaced with an image. + * - Interceptors now have the option to send a binary page direct + * to the client. (i.e. ijb-send-banner uses this) + * - Implemented show-url-info interceptor. (Which is why I needed + * the above interceptors changes - a typical URL is + * "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif". + * The previous mechanism would not have intercepted that, and + * if it had been intercepted then it then it would have replaced + * it with an image.) + * + * Revision 1.9 2001/05/27 22:17:04 oes + * + * - re_process_buffer no longer writes the modified buffer + * to the client, which was very ugly. It now returns the + * buffer, which it is then written by chat. + * + * - content_length now adjusts the Content-Length: header + * for modified documents rather than crunch()ing it. + * (Length info in csp->content_length, which is 0 for + * unmodified documents) + * + * - For this to work, sed() is called twice when filtering. + * + * Revision 1.8 2001/05/26 17:13:28 jongfoster + * Filled in a function comment. + * + * Revision 1.7 2001/05/26 15:26:15 jongfoster + * ACL feature now provides more security by immediately dropping + * connections from untrusted hosts. + * + * Revision 1.6 2001/05/26 00:28:36 jongfoster + * Automatic reloading of config file. + * Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32). + * Most of the global variables have been moved to a new + * struct configuration_spec, accessed through csp->config->globalname + * Most of the globals remaining are used by the Win32 GUI. + * + * Revision 1.5 2001/05/25 22:34:30 jongfoster + * Hard tabs->Spaces + * + * Revision 1.4 2001/05/22 18:46:04 oes + * + * - Enabled filtering banners by size rather than URL + * by adding patterns that replace all standard banner + * sizes with the "Junkbuster" gif to the re_filterfile + * + * - Enabled filtering WebBugs by providing a pattern + * which kills all 1x1 images + * + * - Added support for PCRE_UNGREEDY behaviour to pcrs, + * which is selected by the (nonstandard and therefore + * capital) letter 'U' in the option string. + * It causes the quantifiers to be ungreedy by default. + * Appending a ? turns back to greedy (!). + * + * - Added a new interceptor ijb-send-banner, which + * sends back the "Junkbuster" gif. Without imagelist or + * MSIE detection support, or if tinygif = 1, or the + * URL isn't recognized as an imageurl, a lame HTML + * explanation is sent instead. + * + * - Added new feature, which permits blocking remote + * script redirects and firing back a local redirect + * to the browser. + * The feature is conditionally compiled, i.e. it + * can be disabled with --disable-fast-redirects, + * plus it must be activated by a "fast-redirects" + * line in the config file, has its own log level + * and of course wants to be displayed by show-proxy-args + * Note: Boy, all the #ifdefs in 1001 locations and + * all the fumbling with configure.in and acconfig.h + * were *way* more work than the feature itself :-( + * + * - Because a generic redirect template was needed for + * this, tinygif = 3 now uses the same. + * + * - Moved GIFs, and other static HTTP response templates + * to project.h + * + * - Some minor fixes + * + * - Removed some >400 CRs again (Jon, you really worked + * a lot! ;-) + * + * Revision 1.3 2001/05/20 16:44:47 jongfoster + * Removing last hardcoded Junkbusters.com URLs. + * + * Revision 1.2 2001/05/20 01:21:20 jongfoster + * Version 2.9.4 checkin. + * - Merged popupfile and cookiefile, and added control over PCRS + * filtering, in new "permissionsfile". + * - Implemented LOG_LEVEL_FATAL, so that if there is a configuration + * file error you now get a message box (in the Win32 GUI) rather + * than the program exiting with no explanation. + * - Made killpopup use the PCRS MIME-type checking and HTTP-header + * skipping. + * - Removed tabs from "config" + * - Moved duplicated url parsing code in "loaders.c" to a new funcition. + * - Bumped up version number. + * + * Revision 1.1.1.1 2001/05/15 13:58:52 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#ifndef __OS2__ +#include +#endif /* ndef __OS2__ */ +#include +#else +#include +#endif /* ndef _WIN32 */ + +#ifdef __OS2__ +#include +#endif /* def __OS2__ */ + +#include "project.h" +#include "filters.h" +#include "encode.h" +#include "parsers.h" +#include "ssplit.h" +#include "errlog.h" +#include "jbsockets.h" +#include "miscutil.h" +#include "actions.h" +#include "cgi.h" +#include "list.h" +#include "deanimate.h" +#include "urlmatch.h" + +#ifdef _WIN32 +#include "win32.h" +#endif + +const char filters_h_rcs[] = FILTERS_H_VERSION; + +/* Fix a problem with Solaris. There should be no effect on other + * platforms. + * Solaris's isspace() is a macro which uses it's argument directly + * as an array index. Therefore we need to make sure that high-bit + * characters generate +ve values, and ideally we also want to make + * the argument match the declared parameter type of "int". + */ +#define ijb_isdigit(__X) isdigit((int)(unsigned char)(__X)) + + +#ifdef FEATURE_ACL +/********************************************************************* + * + * Function : block_acl + * + * Description : Block this request? + * Decide yes or no based on ACL file. + * + * Parameters : + * 1 : dst = The proxy or gateway address this is going to. + * Or NULL to check all possible targets. + * 2 : csp = Current client state (buffers, headers, etc...) + * Also includes the client IP address. + * + * Returns : 0 = FALSE (don't block) and 1 = TRUE (do block) + * + *********************************************************************/ +int block_acl(struct access_control_addr *dst, struct client_state *csp) +{ + struct access_control_list *acl = csp->config->acl; + + /* if not using an access control list, then permit the connection */ + if (acl == NULL) + { + return(0); + } + + /* search the list */ + while (acl != NULL) + { + if ((csp->ip_addr_long & acl->src->mask) == acl->src->addr) + { + if (dst == NULL) + { + /* Just want to check if they have any access */ + if (acl->action == ACL_PERMIT) + { + return(0); + } + } + else if ( ((dst->addr & acl->dst->mask) == acl->dst->addr) + && ((dst->port == acl->dst->port) || (acl->dst->port == 0))) + { + if (acl->action == ACL_PERMIT) + { + return(0); + } + else + { + return(1); + } + } + } + acl = acl->next; + } + + return(1); + +} + + +/********************************************************************* + * + * Function : acl_addr + * + * Description : Called from `load_config' to parse an ACL address. + * + * Parameters : + * 1 : aspec = String specifying ACL address. + * 2 : aca = struct access_control_addr to fill in. + * + * Returns : 0 => Ok, everything else is an error. + * + *********************************************************************/ +int acl_addr(char *aspec, struct access_control_addr *aca) +{ + int i, masklength, port; + char *p; + + masklength = 32; + port = 0; + + if ((p = strchr(aspec, '/')) != NULL) + { + *p++ = '\0'; + + if (ijb_isdigit(*p) == 0) + { + return(-1); + } + masklength = atoi(p); + } + + if ((masklength < 0) || (masklength > 32)) + { + return(-1); + } + + if ((p = strchr(aspec, ':')) != NULL) + { + *p++ = '\0'; + + if (ijb_isdigit(*p) == 0) + { + return(-1); + } + port = atoi(p); + } + + aca->port = port; + + aca->addr = ntohl(resolve_hostname_to_ip(aspec)); + + if (aca->addr == INADDR_NONE) + { + return(-1); + } + + /* build the netmask */ + aca->mask = 0; + for (i=1; i <= masklength ; i++) + { + aca->mask |= (1 << (32 - i)); + } + + /* now mask off the host portion of the ip address + * (i.e. save on the network portion of the address). + */ + aca->addr = aca->addr & aca->mask; + + return(0); + +} +#endif /* def FEATURE_ACL */ + + +/********************************************************************* + * + * Function : match_portlist + * + * Description : Check if a given number is covered by a comma + * separated list of numbers and ranges (a,b-c,d,..) + * + * Parameters : + * 1 : portlist = String with list + * 2 : port = port to check + * + * Returns : 0 => no match + * 1 => match + * + *********************************************************************/ +int match_portlist(const char *portlist, int port) +{ + char *min, *max, *next, *portlist_copy; + + min = next = portlist_copy = strdup(portlist); + + /* + * Zero-terminate first item and remember offset for next + */ + if (NULL != (next = strchr(portlist_copy, (int) ','))) + { + *next++ = '\0'; + } + + /* + * Loop through all items, checking for match + */ + while(min) + { + if (NULL == (max = strchr(min, (int) '-'))) + { + /* + * No dash, check for equality + */ + if (port == atoi(min)) + { + free(portlist_copy); + return(1); + } + } + else + { + /* + * This is a range, so check if between min and max, + * or, if max was omitted, between min and 65K + */ + *max++ = '\0'; + if(port >= atoi(min) && port <= (atoi(max) ? atoi(max) : 65535)) + { + free(portlist_copy); + return(1); + } + + } + + /* + * Jump to next item + */ + min = next; + + /* + * Zero-terminate next item and remember offset for n+1 + */ + if ((NULL != next) && (NULL != (next = strchr(next, (int) ',')))) + { + *next++ = '\0'; + } + } + + free(portlist_copy); + return 0; + +} + + +/********************************************************************* + * + * Function : block_url + * + * Description : Called from `chat'. Check to see if we need to block this. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : NULL => unblocked, else HTTP block response + * + *********************************************************************/ +struct http_response *block_url(struct client_state *csp) +{ +#ifdef FEATURE_IMAGE_BLOCKING + char *p; +#endif /* def FEATURE_IMAGE_BLOCKING */ + struct http_response *rsp; + + /* + * If it's not blocked, don't block it ;-) + */ + if ((csp->action->flags & ACTION_BLOCK) == 0) + { + return NULL; + } + + /* + * Else, prepare a response + */ + if (NULL == (rsp = alloc_http_response())) + { + return cgi_error_memory(); + } + + /* + * If it's an image-url, send back an image or redirect + * as specified by the relevant +image action + */ +#ifdef FEATURE_IMAGE_BLOCKING + if (((csp->action->flags & ACTION_IMAGE_BLOCKER) != 0) + && is_imageurl(csp)) + { + /* determine HOW images should be blocked */ + p = csp->action->string[ACTION_STRING_IMAGE_BLOCKER]; + +#if 1 /* Two alternative strategies, use this one for now: */ + + /* and handle accordingly: */ + if ((p == NULL) || (0 == strcmpic(p, "pattern"))) + { + rsp->body = bindup(image_pattern_data, image_pattern_length); + if (rsp->body == NULL) + { + free_http_response(rsp); + return cgi_error_memory(); + } + rsp->content_length = image_pattern_length; + + if (enlist_unique_header(rsp->headers, "Content-Type", BUILTIN_IMAGE_MIMETYPE)) + { + free_http_response(rsp); + return cgi_error_memory(); + } + } + + else if (0 == strcmpic(p, "blank")) + { + rsp->body = bindup(image_blank_data, image_blank_length); + if (rsp->body == NULL) + { + free_http_response(rsp); + return cgi_error_memory(); + } + rsp->content_length = image_blank_length; + + if (enlist_unique_header(rsp->headers, "Content-Type", BUILTIN_IMAGE_MIMETYPE)) + { + free_http_response(rsp); + return cgi_error_memory(); + } + } + + else + { + rsp->status = strdup("302 Local Redirect from Privoxy"); + if (rsp->status == NULL) + { + free_http_response(rsp); + return cgi_error_memory(); + } + + if (enlist_unique_header(rsp->headers, "Location", p)) + { + free_http_response(rsp); + return cgi_error_memory(); + } + } + +#else /* Following code is disabled for now */ + + /* and handle accordingly: */ + if ((p == NULL) || (0 == strcmpic(p, "pattern"))) + { + p = CGI_PREFIX "send-banner?type=pattern"; + } + else if (0 == strcmpic(p, "blank")) + { + p = CGI_PREFIX "send-banner?type=blank"; + } + rsp->status = strdup("302 Local Redirect from Privoxy"); + if (rsp->status == NULL) + { + free_http_response(rsp); + return cgi_error_memory(); + } + + if (enlist_unique_header(rsp->headers, "Location", p)) + { + free_http_response(rsp); + return cgi_error_memory(); + } +#endif /* Preceeding code is disabled for now */ + } + else +#endif /* def FEATURE_IMAGE_BLOCKING */ + + /* + * Else, generate an HTML "blocked" message: + */ + { + jb_err err; + struct map * exports; + + /* + * Workaround for stupid Netscape bug which prevents + * pages from being displayed if loading a referenced + * JavaScript or style sheet fails. So make it appear + * as if it succeeded. + */ + if ( NULL != (p = get_header_value(csp->headers, "User-Agent:")) + && !strncmpic(p, "mozilla", 7) /* Catch Netscape but */ + && !strstr(p, "Gecko") /* save Mozilla, */ + && !strstr(p, "compatible") /* MSIE */ + && !strstr(p, "Opera")) /* and Opera. */ + { + rsp->status = strdup("200 Request for blocked URL"); + } + else + { + rsp->status = strdup("404 Request for blocked URL"); + } + + if (rsp->status == NULL) + { + free_http_response(rsp); + return cgi_error_memory(); + } + + exports = default_exports(csp, NULL); + if (exports == NULL) + { + free_http_response(rsp); + return cgi_error_memory(); + } + +#ifdef FEATURE_FORCE_LOAD + err = map(exports, "force-prefix", 1, FORCE_PREFIX, 1); + if (csp->http->ssl != 0) +#endif /* ndef FEATURE_FORCE_LOAD */ + { + err = map_block_killer(exports, "force-support"); + } + + if (!err) err = map(exports, "protocol", 1, csp->http->ssl ? "https://" : "http://", 1); + if (!err) err = map(exports, "hostport", 1, html_encode(csp->http->hostport), 0); + if (!err) err = map(exports, "path", 1, html_encode(csp->http->path), 0); + + if (err) + { + free_map(exports); + free_http_response(rsp); + return cgi_error_memory(); + } + + err = template_fill_for_cgi(csp, "blocked", exports, rsp); + if (err) + { + free_http_response(rsp); + return cgi_error_memory(); + } + } + + return finish_http_response(rsp); + +} + + +#ifdef FEATURE_TRUST +/********************************************************************* + * + * Function : trust_url FIXME: I should be called distrust_url + * + * Description : Calls is_untrusted_url to determine if the URL is trusted + * and if not, returns a HTTP 304 response with a reject message. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : NULL => trusted, else http_response. + * + *********************************************************************/ +struct http_response *trust_url(struct client_state *csp) +{ + struct http_response *rsp; + struct map * exports; + char buf[BUFFER_SIZE]; + char *p; + struct url_spec **tl; + struct url_spec *t; + jb_err err; + + /* + * Don't bother to work on trusted URLs + */ + if (!is_untrusted_url(csp)) + { + return NULL; + } + + /* + * Else, prepare a response: + */ + if (NULL == (rsp = alloc_http_response())) + { + return cgi_error_memory(); + } + + exports = default_exports(csp, NULL); + if (exports == NULL) + { + free_http_response(rsp); + return cgi_error_memory(); + } + + /* + * Export the protocol, host, port, and referrer information + */ + err = map(exports, "hostport", 1, csp->http->hostport, 1); + if (!err) err = map(exports, "protocol", 1, csp->http->ssl ? "https://" : "http://", 1); + if (!err) err = map(exports, "path", 1, csp->http->path, 1); + + if (NULL != (p = get_header_value(csp->headers, "Referer:"))) + { + if (!err) err = map(exports, "referrer", 1, html_encode(p), 0); + } + else + { + if (!err) err = map(exports, "referrer", 1, "unknown", 1); + } + + if (err) + { + free_map(exports); + free_http_response(rsp); + return cgi_error_memory(); + } + + /* + * Export the trust list + */ + p = strdup(""); + for (tl = csp->config->trust_list; (t = *tl) != NULL ; tl++) + { + sprintf(buf, "
  • %s
  • \n", t->spec); + string_append(&p, buf); + } + err = map(exports, "trusted-referrers", 1, p, 0); + + if (err) + { + free_map(exports); + free_http_response(rsp); + return cgi_error_memory(); + } + + /* + * Export the trust info, if available + */ + if (csp->config->trust_info->first) + { + struct list_entry *l; + + p = strdup(""); + for (l = csp->config->trust_info->first; l ; l = l->next) + { + sprintf(buf, "
  • %s
    \n",l->str, l->str); + string_append(&p, buf); + } + err = map(exports, "trust-info", 1, p, 0); + } + else + { + err = map_block_killer(exports, "have-trust-info"); + } + + if (err) + { + free_map(exports); + free_http_response(rsp); + return cgi_error_memory(); + } + + /* + * Export the force prefix or the force conditional block killer + */ +#ifdef FEATURE_FORCE_LOAD + err = map(exports, "force-prefix", 1, FORCE_PREFIX, 1); +#else /* ifndef FEATURE_FORCE_LOAD */ + err = map_block_killer(exports, "force-support"); +#endif /* ndef FEATURE_FORCE_LOAD */ + + if (err) + { + free_map(exports); + free_http_response(rsp); + return cgi_error_memory(); + } + + /* + * Build the response + */ + err = template_fill_for_cgi(csp, "untrusted", exports, rsp); + if (err) + { + free_http_response(rsp); + return cgi_error_memory(); + } + + return finish_http_response(rsp); +} +#endif /* def FEATURE_TRUST */ + + +#ifdef FEATURE_FAST_REDIRECTS +/********************************************************************* + * + * Function : redirect_url + * + * Description : Checks for redirection URLs and returns a HTTP redirect + * to the destination URL, if necessary + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : NULL if URL was clean, HTTP redirect otherwise. + * + *********************************************************************/ +struct http_response *redirect_url(struct client_state *csp) +{ + char *p, *q; + struct http_response *rsp; + + p = q = csp->http->path; + log_error(LOG_LEVEL_REDIRECTS, "checking path for redirects: %s", p); + + /* + * find the last URL encoded in the request + */ + while ((p = strstr(p, "http://")) != NULL) + { + q = p++; + } + + /* + * if there was any, generate and return a HTTP redirect + */ + if (q != csp->http->path) + { + log_error(LOG_LEVEL_REDIRECTS, "redirecting to: %s", q); + + if (NULL == (rsp = alloc_http_response())) + { + return cgi_error_memory(); + } + + if ( enlist_unique_header(rsp->headers, "Location", q) + || (NULL == (rsp->status = strdup("302 Local Redirect from Privoxy"))) ) + { + free_http_response(rsp); + return cgi_error_memory(); + } + + return finish_http_response(rsp); + } + else + { + return NULL; + } + +} +#endif /* def FEATURE_FAST_REDIRECTS */ + + +#ifdef FEATURE_IMAGE_BLOCKING +/********************************************************************* + * + * Function : is_imageurl + * + * Description : Given a URL, decide whether it is an image or not, + * using either the info from a previous +image action + * or, #ifdef FEATURE_IMAGE_DETECT_MSIE, the info from + * the browser's accept header. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : True (nonzero) if URL is an image, false (0) + * otherwise + * + *********************************************************************/ +int is_imageurl(struct client_state *csp) +{ +#ifdef FEATURE_IMAGE_DETECT_MSIE + char *tmp; + + tmp = get_header_value(csp->headers, "User-Agent:"); + if (tmp && strstr(tmp, "MSIE")) + { + tmp = get_header_value(csp->headers, "Accept:"); + if (tmp && strstr(tmp, "image/gif")) + { + /* Client will accept HTML. If this seems counterintuitive, + * blame Microsoft. + */ + return(0); + } + else + { + return(1); + } + } +#endif /* def FEATURE_IMAGE_DETECT_MSIE */ + + return ((csp->action->flags & ACTION_IMAGE) != 0); + +} +#endif /* def FEATURE_IMAGE_BLOCKING */ + + +#ifdef FEATURE_TRUST +/********************************************************************* + * + * Function : is_untrusted_url + * + * Description : Should we "distrust" this URL (and block it)? + * + * Yes if it matches a line in the trustfile, or if the + * referrer matches a line starting with "+" in the + * trustfile. + * No otherwise. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : 0 => trusted, 1 => untrusted + * + *********************************************************************/ +int is_untrusted_url(struct client_state *csp) +{ + struct file_list *fl; + struct block_spec *b; + struct url_spec **trusted_url; + struct http_request rhttp[1]; + const char * referer; + jb_err err; + + /* + * If we don't have a trustlist, we trust everybody + */ + if (((fl = csp->tlist) == NULL) || ((b = fl->f) == NULL)) + { + return 0; + } + + memset(rhttp, '\0', sizeof(*rhttp)); + + /* + * Do we trust the request URL itself? + */ + for (b = b->next; b ; b = b->next) + { + if (url_match(b->url, csp->http)) + { + return b->reject; + } + } + + if (NULL == (referer = get_header_value(csp->headers, "Referer:"))) + { + /* no referrer was supplied */ + return 1; + } + + + /* + * If not, do we maybe trust its referrer? + */ + err = parse_http_url(referer, rhttp, csp); + if (err) + { + return 1; + } + + for (trusted_url = csp->config->trust_list; *trusted_url != NULL; trusted_url++) + { + if (url_match(*trusted_url, rhttp)) + { + /* if the URL's referrer is from a trusted referrer, then + * add the target spec to the trustfile as an unblocked + * domain and return NULL (which means it's OK). + */ + + FILE *fp; + + if (NULL != (fp = fopen(csp->config->trustfile, "a"))) + { + char * path; + char * path_end; + char * new_entry = strdup("~"); + + string_append(&new_entry, csp->http->hostport); + + path = csp->http->path; + if ( (path[0] == '/') + && (path[1] == '~') + && ((path_end = strchr(path + 2, '/')) != NULL)) + { + /* since this path points into a user's home space + * be sure to include this spec in the trustfile. + */ + int path_len = path_end - path; /* save offset */ + path = strdup(path); /* Copy string */ + if (path != NULL) + { + path_end = path + path_len; /* regenerate ptr to new buffer */ + *(path_end + 1) = '\0'; /* Truncate path after '/' */ + } + string_join(&new_entry, path); + } + + if (new_entry != NULL) + { + fprintf(fp, "%s\n", new_entry); + free(new_entry); + } + else + { + /* FIXME: No way to handle out-of memory, so mostly ignoring it */ + log_error(LOG_LEVEL_ERROR, "Out of memory adding pattern to trust file"); + } + + fclose(fp); + } + return 0; + } + } + return 1; +} +#endif /* def FEATURE_TRUST */ + + +/********************************************************************* + * + * Function : pcrs_filter_response + * + * Description : Ecexute all text substitutions from all applying + * +filter actions on the text buffer that's been accumulated + * in csp->iob->buf. If this changes the contents, set + * csp->content_length to the modified size and raise the + * CSP_FLAG_MODIFIED flag. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : a pointer to the (newly allocated) modified buffer. + * or NULL if there were no hits or something went wrong + * + *********************************************************************/ +char *pcrs_filter_response(struct client_state *csp) +{ + int hits=0; + size_t size; + + char *old = csp->iob->cur, *new = NULL; + pcrs_job *job; + + struct file_list *fl; + struct re_filterfile_spec *b; + struct list_entry *filtername; + + /* + * Sanity first + */ + if (csp->iob->cur >= csp->iob->eod) + { + return(NULL); + } + size = csp->iob->eod - csp->iob->cur; + + if ( ( NULL == (fl = csp->rlist) ) || ( NULL == fl->f) ) + { + log_error(LOG_LEVEL_ERROR, "Unable to get current state of regexp filtering."); + return(NULL); + } + + /* + * If the body has a "chunked" transfer-encoding, + * get rid of it first, adjusting size and iob->eod + */ + if (csp->flags & CSP_FLAG_CHUNKED) + { + log_error(LOG_LEVEL_RE_FILTER, "Need to de-chunk first"); + if (0 == (size = remove_chunked_transfer_coding(csp->iob->cur, size))) + { + return(NULL); + } + csp->iob->eod = csp->iob->cur + size; + csp->flags |= CSP_FLAG_MODIFIED; + } + + /* + * For all applying +filter actions, look if a filter by that + * name exists and if yes, execute it's pcrs_joblist on the + * buffer. + */ + for (b = fl->f; b; b = b->next) + { + for (filtername = csp->action->multi[ACTION_MULTI_FILTER]->first; + filtername ; filtername = filtername->next) + { + if (strcmp(b->name, filtername->str) == 0) + { + int current_hits = 0; + + if ( NULL == b->joblist ) + { + log_error(LOG_LEVEL_RE_FILTER, "Filter %s has empty joblist. Nothing to do.", b->name); + return(NULL); + } + + log_error(LOG_LEVEL_RE_FILTER, "re_filtering %s%s (size %d) with filter %s...", + csp->http->hostport, csp->http->path, size, b->name); + + /* Apply all jobs from the joblist */ + for (job = b->joblist; NULL != job; job = job->next) + { + current_hits += pcrs_execute(job, old, size, &new, &size); + if (old != csp->iob->cur) free(old); + old=new; + } + + log_error(LOG_LEVEL_RE_FILTER, " ...produced %d hits (new size %d).", current_hits, size); + hits += current_hits; + } + } + } + + /* + * If there were no hits, destroy our copy and let + * chat() use the original in csp->iob + */ + if (!hits) + { + free(new); + return(NULL); + } + + csp->flags |= CSP_FLAG_MODIFIED; + csp->content_length = size; + IOB_RESET(csp); + + return(new); + +} + + +/********************************************************************* + * + * Function : gif_deanimate_response + * + * Description : Deanimate the GIF image that has been accumulated in + * csp->iob->buf, set csp->content_length to the modified + * size and raise the CSP_FLAG_MODIFIED flag. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : a pointer to the (newly allocated) modified buffer. + * or NULL in case something went wrong. + * + *********************************************************************/ +char *gif_deanimate_response(struct client_state *csp) +{ + struct binbuffer *in, *out; + char *p; + size_t size = csp->iob->eod - csp->iob->cur; + + /* + * If the body has a "chunked" transfer-encoding, + * get rid of it first, adjusting size and iob->eod + */ + if (csp->flags & CSP_FLAG_CHUNKED) + { + log_error(LOG_LEVEL_DEANIMATE, "Need to de-chunk first"); + if (0 == (size = remove_chunked_transfer_coding(csp->iob->cur, size))) + { + return(NULL); + } + csp->iob->eod = csp->iob->cur + size; + csp->flags |= CSP_FLAG_MODIFIED; + } + + if ( (NULL == (in = (struct binbuffer *)zalloc(sizeof *in ))) + || (NULL == (out = (struct binbuffer *)zalloc(sizeof *out))) ) + { + log_error(LOG_LEVEL_DEANIMATE, "failed! (no mem)"); + return NULL; + } + + in->buffer = csp->iob->cur; + in->size = size; + + if (gif_deanimate(in, out, strncmp("last", csp->action->string[ACTION_STRING_DEANIMATE], 4))) + { + log_error(LOG_LEVEL_DEANIMATE, "failed! (gif parsing)"); + free(in); + buf_free(out); + return(NULL); + } + else + { + if ((int)size == out->offset) + { + log_error(LOG_LEVEL_DEANIMATE, "GIF not changed."); + } + else + { + log_error(LOG_LEVEL_DEANIMATE, "Success! GIF shrunk from %d bytes to %d.", size, out->offset); + } + csp->content_length = out->offset; + csp->flags |= CSP_FLAG_MODIFIED; + p = out->buffer; + free(in); + free(out); + return(p); + } + +} + + +/********************************************************************* + * + * Function : remove_chunked_transfer_coding + * + * Description : In-situ remove the "chunked" transfer coding as defined + * in rfc2616 from a buffer. + * + * Parameters : + * 1 : buffer = Pointer to the text buffer + * 2 : size = Number of bytes to be processed + * + * Returns : The new size, i.e. the number of bytes from buffer which + * are occupied by the stripped body, or 0 in case something + * went wrong + * + *********************************************************************/ +int remove_chunked_transfer_coding(char *buffer, const size_t size) +{ + size_t newsize = 0; + unsigned int chunksize = 0; + char *from_p, *to_p; + + assert(buffer); + from_p = to_p = buffer; + + if (sscanf(buffer, "%x", &chunksize) != 1) + { + log_error(LOG_LEVEL_ERROR, "Invalid first chunksize while stripping \"chunked\" transfer coding"); + return(0); + } + + while (chunksize > 0) + { + if (NULL == (from_p = strstr(from_p, "\r\n"))) + { + log_error(LOG_LEVEL_ERROR, "Parse error while stripping \"chunked\" transfer coding"); + return(0); + } + newsize += chunksize; + from_p += 2; + + memmove(to_p, from_p, (size_t) chunksize); + to_p = buffer + newsize; + from_p += chunksize + 2; + + if (sscanf(from_p, "%x", &chunksize) != 1) + { + log_error(LOG_LEVEL_ERROR, "Parse error while stripping \"chunked\" transfer coding"); + return(0); + } + } + + /* FIXME: Should this get its own loglevel? */ + log_error(LOG_LEVEL_RE_FILTER, "De-chunking successful. Shrunk from %d to %d\n", size, newsize); + return(newsize); + +} + + +/********************************************************************* + * + * Function : url_actions + * + * Description : Gets the actions for this URL. + * + * Parameters : + * 1 : http = http_request request for blocked URLs + * 2 : csp = Current client state (buffers, headers, etc...) + * + * Returns : N/A + * + *********************************************************************/ +void url_actions(struct http_request *http, + struct client_state *csp) +{ + struct file_list *fl; + struct url_actions *b; + int i; + + init_current_action(csp->action); + + for (i = 0; i < MAX_ACTION_FILES; i++) + { + if (((fl = csp->actions_list[i]) == NULL) || ((b = fl->f) == NULL)) + { + return; + } + + apply_url_actions(csp->action, http, b); + } + + return; +} + + +/********************************************************************* + * + * Function : apply_url_actions + * + * Description : Applies a list of URL actions. + * + * Parameters : + * 1 : action = Destination. + * 2 : http = Current URL + * 3 : b = list of URL actions to apply + * + * Returns : N/A + * + *********************************************************************/ +void apply_url_actions(struct current_action_spec *action, + struct http_request *http, + struct url_actions *b) +{ + if (b == NULL) + { + /* Should never happen */ + return; + } + + for (b = b->next; NULL != b; b = b->next) + { + if (url_match(b->url, http)) + { + merge_current_action(action, b->action); + } + } +} + + +/********************************************************************* + * + * Function : forward_url + * + * Description : Should we forward this to another proxy? + * + * Parameters : + * 1 : http = http_request request for current URL + * 2 : csp = Current client state (buffers, headers, etc...) + * + * Returns : Pointer to forwarding information. + * + *********************************************************************/ +const struct forward_spec * forward_url(struct http_request *http, + struct client_state *csp) +{ + static const struct forward_spec fwd_default[1] = { FORWARD_SPEC_INITIALIZER }; + struct forward_spec *fwd = csp->config->forward; + + if (fwd == NULL) + { + return fwd_default; + } + + while (fwd != NULL) + { + if (url_match(fwd->url, http)) + { + return fwd; + } + fwd = fwd->next; + } + + return fwd_default; +} + + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/filters.h b/src/filters.h new file mode 100644 index 00000000..33f4e49b --- /dev/null +++ b/src/filters.h @@ -0,0 +1,285 @@ +#ifndef FILTERS_H_INCLUDED +#define FILTERS_H_INCLUDED +#define FILTERS_H_VERSION "$Id: filters.h,v 1.20 2002/04/02 14:56:16 oes Exp $" +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/filters.h,v $ + * + * Purpose : Declares functions to parse/crunch headers and pages. + * Functions declared include: + * `acl_addr', `add_stats', `block_acl', `block_imageurl', + * `block_url', `url_actions', `filter_popups', `forward_url' + * `ij_untrusted_url', `intercept_url', `re_process_buffer', + * `show_proxy_args', and `trust_url' + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: filters.h,v $ + * Revision 1.20 2002/04/02 14:56:16 oes + * Bugfix: is_untrusted_url() and trust_url() now depend on FEATURE_TRUST, not FEATURE_COOKIE_JAR + * + * Revision 1.19 2002/03/26 22:29:54 swa + * we have a new homepage! + * + * Revision 1.18 2002/03/25 22:12:45 oes + * Added fix for undefined INADDR_NONE on Solaris by Bart Schelstraete + * + * Revision 1.17 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.16 2002/01/17 21:01:02 jongfoster + * Moving all our URL and URL pattern parsing code to urlmatch.c. + * + * Revision 1.15 2001/10/10 16:44:16 oes + * Added match_portlist function + * + * Revision 1.14 2001/10/07 15:41:40 oes + * Added prototype for remove_chunked_transfer_coding + * + * Revision 1.13 2001/07/30 22:08:36 jongfoster + * Tidying up #defines: + * - All feature #defines are now of the form FEATURE_xxx + * - Permanently turned off WIN_GUI_EDIT + * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS + * + * Revision 1.12 2001/07/29 19:01:11 jongfoster + * Changed _FILENAME_H to FILENAME_H_INCLUDED. + * Added forward declarations for needed structures. + * + * Revision 1.11 2001/07/13 14:00:18 oes + * - Introduced gif_deanimate_response + * - Renamed re_process_buffer to pcrs_filter_response + * - Removed all #ifdef PCRS + * + * Revision 1.10 2001/06/29 13:29:01 oes + * Cleaned up and updated to reflect the changesin + * filters.c + * + * Revision 1.9 2001/06/07 23:10:53 jongfoster + * Replacing struct gateway with struct forward_spec + * + * Revision 1.8 2001/06/03 19:12:00 oes + * extracted-CGI relevant stuff + * + * Revision 1.7 2001/05/31 21:21:30 jongfoster + * Permissionsfile / actions file changes: + * - Changed "permission" to "action" throughout + * - changes to file format to allow string parameters + * - Moved helper functions to actions.c + * + * Revision 1.6 2001/05/29 09:50:24 jongfoster + * Unified blocklist/imagelist/permissionslist. + * File format is still under discussion, but the internal changes + * are (mostly) done. + * + * Also modified interceptor behaviour: + * - We now intercept all URLs beginning with one of the following + * prefixes (and *only* these prefixes): + * * http://i.j.b/ + * * http://ijbswa.sf.net/config/ + * * http://ijbswa.sourceforge.net/config/ + * - New interceptors "home page" - go to http://i.j.b/ to see it. + * - Internal changes so that intercepted and fast redirect pages + * are not replaced with an image. + * - Interceptors now have the option to send a binary page direct + * to the client. (i.e. ijb-send-banner uses this) + * - Implemented show-url-info interceptor. (Which is why I needed + * the above interceptors changes - a typical URL is + * "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif". + * The previous mechanism would not have intercepted that, and + * if it had been intercepted then it then it would have replaced + * it with an image.) + * + * Revision 1.5 2001/05/27 22:17:04 oes + * + * - re_process_buffer no longer writes the modified buffer + * to the client, which was very ugly. It now returns the + * buffer, which it is then written by chat. + * + * - content_length now adjusts the Content-Length: header + * for modified documents rather than crunch()ing it. + * (Length info in csp->content_length, which is 0 for + * unmodified documents) + * + * - For this to work, sed() is called twice when filtering. + * + * Revision 1.4 2001/05/26 15:26:15 jongfoster + * ACL feature now provides more security by immediately dropping + * connections from untrusted hosts. + * + * Revision 1.3 2001/05/22 18:46:04 oes + * + * - Enabled filtering banners by size rather than URL + * by adding patterns that replace all standard banner + * sizes with the "Junkbuster" gif to the re_filterfile + * + * - Enabled filtering WebBugs by providing a pattern + * which kills all 1x1 images + * + * - Added support for PCRE_UNGREEDY behaviour to pcrs, + * which is selected by the (nonstandard and therefore + * capital) letter 'U' in the option string. + * It causes the quantifiers to be ungreedy by default. + * Appending a ? turns back to greedy (!). + * + * - Added a new interceptor ijb-send-banner, which + * sends back the "Junkbuster" gif. Without imagelist or + * MSIE detection support, or if tinygif = 1, or the + * URL isn't recognized as an imageurl, a lame HTML + * explanation is sent instead. + * + * - Added new feature, which permits blocking remote + * script redirects and firing back a local redirect + * to the browser. + * The feature is conditionally compiled, i.e. it + * can be disabled with --disable-fast-redirects, + * plus it must be activated by a "fast-redirects" + * line in the config file, has its own log level + * and of course wants to be displayed by show-proxy-args + * Note: Boy, all the #ifdefs in 1001 locations and + * all the fumbling with configure.in and acconfig.h + * were *way* more work than the feature itself :-( + * + * - Because a generic redirect template was needed for + * this, tinygif = 3 now uses the same. + * + * - Moved GIFs, and other static HTTP response templates + * to project.h + * + * - Some minor fixes + * + * - Removed some >400 CRs again (Jon, you really worked + * a lot! ;-) + * + * Revision 1.2 2001/05/20 01:21:20 jongfoster + * Version 2.9.4 checkin. + * - Merged popupfile and cookiefile, and added control over PCRS + * filtering, in new "permissionsfile". + * - Implemented LOG_LEVEL_FATAL, so that if there is a configuration + * file error you now get a message box (in the Win32 GUI) rather + * than the program exiting with no explanation. + * - Made killpopup use the PCRS MIME-type checking and HTTP-header + * skipping. + * - Removed tabs from "config" + * - Moved duplicated url parsing code in "loaders.c" to a new funcition. + * - Bumped up version number. + * + * Revision 1.1.1.1 2001/05/15 13:58:52 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#include "project.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +struct access_control_addr; +struct client_state; +struct http_request; +struct http_response; +struct current_action_spec; +struct url_actions; +struct url_spec; + + +/* + * ACL checking + */ +#ifdef FEATURE_ACL +extern int block_acl(struct access_control_addr *dst, struct client_state *csp); +extern int acl_addr(char *aspec, struct access_control_addr *aca); +#endif /* def FEATURE_ACL */ +extern int match_portlist(const char *portlist, int port); + +/* + * Interceptors + */ +extern struct http_response *block_url(struct client_state *csp); +extern struct http_response *redirect_url(struct client_state *csp); +#ifdef FEATURE_TRUST +extern struct http_response *trust_url(struct client_state *csp); +#endif /* def FEATURE_TRUST */ + +/* + * Request inspectors + */ +#ifdef FEATURE_TRUST +extern int is_untrusted_url(struct client_state *csp); +#endif /* def FEATURE_TRUST */ +#ifdef FEATURE_IMAGE_BLOCKING +extern int is_imageurl(struct client_state *csp); +#endif /* def FEATURE_IMAGE_BLOCKING */ + +/* + * Determining applicable actions + */ +extern void url_actions(struct http_request *http, + struct client_state *csp); +extern void apply_url_actions(struct current_action_spec *action, + struct http_request *http, + struct url_actions *b); +/* + * Determining parent proxies + */ +extern const struct forward_spec *forward_url(struct http_request *http, struct client_state *csp); + +/* + * Content modification + */ +extern char *pcrs_filter_response(struct client_state *csp); +extern char *gif_deanimate_response(struct client_state *csp); +extern int remove_chunked_transfer_coding(char *buffer, const size_t size); + +/* + * Solaris fix: + */ +#ifndef INADDR_NONE +#define INADDR_NONE -1 +#endif + +/* + * Revision control strings from this header and associated .c file + */ +extern const char filters_rcs[]; +extern const char filters_h_rcs[]; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ndef FILTERS_H_INCLUDED */ + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/gateway.c b/src/gateway.c new file mode 100644 index 00000000..6ff762a9 --- /dev/null +++ b/src/gateway.c @@ -0,0 +1,399 @@ +const char gateway_rcs[] = "$Id: gateway.c,v 1.16 2002/05/12 21:36:29 jongfoster Exp $"; +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/gateway.c,v $ + * + * Purpose : Contains functions to connect to a server, possibly + * using a "forwarder" (i.e. HTTP proxy and/or a SOCKS4 + * proxy). + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: gateway.c,v $ + * Revision 1.16 2002/05/12 21:36:29 jongfoster + * Correcting function comments + * + * Revision 1.15 2002/03/26 22:29:54 swa + * we have a new homepage! + * + * Revision 1.14 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.13 2002/03/13 00:29:59 jongfoster + * Killing warnings + * + * Revision 1.12 2002/03/09 20:03:52 jongfoster + * - Making various functions return int rather than size_t. + * (Undoing a recent change). Since size_t is unsigned on + * Windows, functions like read_socket that return -1 on + * error cannot return a size_t. + * + * THIS WAS A MAJOR BUG - it caused frequent, unpredictable + * crashes, and also frequently caused JB to jump to 100% + * CPU and stay there. (Because it thought it had just + * read ((unsigned)-1) == 4Gb of data...) + * + * - The signature of write_socket has changed, it now simply + * returns success=0/failure=nonzero. + * + * - Trying to get rid of a few warnings --with-debug on + * Windows, I've introduced a new type "jb_socket". This is + * used for the socket file descriptors. On Windows, this + * is SOCKET (a typedef for unsigned). Everywhere else, it's + * an int. The error value can't be -1 any more, so it's + * now JB_INVALID_SOCKET (which is -1 on UNIX, and in + * Windows it maps to the #define INVALID_SOCKET.) + * + * - The signature of bind_port has changed. + * + * Revision 1.11 2002/03/08 17:46:04 jongfoster + * Fixing int/size_t warnings + * + * Revision 1.10 2002/03/07 03:50:19 oes + * - Improved handling of failed DNS lookups + * - Fixed compiler warnings + * + * Revision 1.9 2001/10/25 03:40:48 david__schmidt + * Change in porting tactics: OS/2's EMX porting layer doesn't allow multiple + * threads to call select() simultaneously. So, it's time to do a real, live, + * native OS/2 port. See defines for __EMX__ (the porting layer) vs. __OS2__ + * (native). Both versions will work, but using __OS2__ offers multi-threading. + * + * Revision 1.8 2001/09/13 20:10:12 jongfoster + * Fixing missing #include under Windows + * + * Revision 1.7 2001/09/12 17:58:26 steudten + * + * add #include + * + * Revision 1.6 2001/09/10 10:41:16 oes + * Added #include in.h + * + * Revision 1.5 2001/07/29 18:47:57 jongfoster + * Adding missing #include project.h + * + * Revision 1.4 2001/07/24 12:47:06 oes + * Applied BeOS support update by Eugenia + * + * Revision 1.3 2001/06/09 10:55:28 jongfoster + * Changing BUFSIZ ==> BUFFER_SIZE + * + * Revision 1.2 2001/06/07 23:11:38 jongfoster + * Removing gateways[] list - no longer used. + * Replacing function pointer in struct gateway with a directly + * called function forwarded_connect(), which can do the common + * task of deciding whether to connect to the web server or HTTP + * proxy. + * Replacing struct gateway with struct forward_spec + * Fixing bug with SOCKS4A and HTTP proxy server in combination. + * It was a bug which led to the connection being made to the web + * server rather than the HTTP proxy, and also a buffer overrun. + * + * Revision 1.1.1.1 2001/05/15 13:58:54 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#include "config.h" + +#include +#include + +#ifndef _WIN32 +#include +#endif + +#include +#include + +#ifdef _WIN32 +#include +#endif /* def _WIN32 */ + +#ifdef __BEOS__ +#include +#endif /* def __BEOS__ */ + +#ifdef __OS2__ +#include +#endif /* def __OS2__ */ + +#include "project.h" +#include "jcc.h" +#include "errlog.h" +#include "jbsockets.h" +#include "gateway.h" + +const char gateway_h_rcs[] = GATEWAY_H_VERSION; + +static jb_socket socks4_connect(const struct forward_spec * fwd, + const char * target_host, + int target_port, + struct client_state *csp); + + +#define SOCKS_REQUEST_GRANTED 90 +#define SOCKS_REQUEST_REJECT 91 +#define SOCKS_REQUEST_IDENT_FAILED 92 +#define SOCKS_REQUEST_IDENT_CONFLICT 93 + +/* structure of a socks client operation */ +struct socks_op { + unsigned char vn; /* socks version number */ + unsigned char cd; /* command code */ + unsigned char dstport[2]; /* destination port */ + unsigned char dstip[4]; /* destination address */ + unsigned char userid; /* first byte of userid */ + /* more bytes of the userid follow, terminated by a NULL */ +}; + +/* structure of a socks server reply */ +struct socks_reply { + unsigned char vn; /* socks version number */ + unsigned char cd; /* command code */ + unsigned char dstport[2]; /* destination port */ + unsigned char dstip[4]; /* destination address */ +}; + +static const char socks_userid[] = "anonymous"; + + +/********************************************************************* + * + * Function : forwarded_connect + * + * Description : Connect to a specified web server, possibly via + * a HTTP proxy and/or a SOCKS proxy. + * + * Parameters : + * 1 : fwd = the proxies to use when connecting. + * 2 : http = the http request and apropos headers + * 3 : csp = Current client state (buffers, headers, etc...) + * + * Returns : JB_INVALID_SOCKET => failure, else it is the socket file descriptor. + * + *********************************************************************/ +jb_socket forwarded_connect(const struct forward_spec * fwd, + struct http_request *http, + struct client_state *csp) +{ + const char * dest_host; + int dest_port; + + /* Figure out if we need to connect to the web server or a HTTP proxy. */ + if (fwd->forward_host) + { + /* HTTP proxy */ + dest_host = fwd->forward_host; + dest_port = fwd->forward_port; + } + else + { + /* Web server */ + dest_host = http->host; + dest_port = http->port; + } + + /* Connect, maybe using a SOCKS proxy */ + switch (fwd->type) + { + case SOCKS_NONE: + return (connect_to(dest_host, dest_port, csp)); + + case SOCKS_4: + case SOCKS_4A: + return (socks4_connect(fwd, dest_host, dest_port, csp)); + + default: + /* Should never get here */ + log_error(LOG_LEVEL_FATAL, "SOCKS4 impossible internal error - bad SOCKS type."); + errno = EINVAL; + return(JB_INVALID_SOCKET); + } +} + + +/********************************************************************* + * + * Function : socks4_connect + * + * Description : Connect to the SOCKS server, and connect through + * it to the specified server. This handles + * all the SOCKS negotiation, and returns a file + * descriptor for a socket which can be treated as a + * normal (non-SOCKS) socket. + * + * Parameters : + * 1 : fwd = Specifies the SOCKS proxy to use. + * 2 : target_host = The final server to connect to. + * 3 : target_port = The final port to connect to. + * 4 : csp = Current client state (buffers, headers, etc...) + * + * Returns : JB_INVALID_SOCKET => failure, else a socket file descriptor. + * + *********************************************************************/ +static jb_socket socks4_connect(const struct forward_spec * fwd, + const char * target_host, + int target_port, + struct client_state *csp) +{ + int web_server_addr; + char cbuf[BUFFER_SIZE]; + char sbuf[BUFFER_SIZE]; + struct socks_op *c = (struct socks_op *)cbuf; + struct socks_reply *s = (struct socks_reply *)sbuf; + size_t n; + size_t csiz; + jb_socket sfd; + int err = 0; + char *errstr; + + if ((fwd->gateway_host == NULL) || (*fwd->gateway_host == '\0')) + { + log_error(LOG_LEVEL_CONNECT, "socks4_connect: NULL gateway host specified"); + err = 1; + } + + if (fwd->gateway_port <= 0) + { + log_error(LOG_LEVEL_CONNECT, "socks4_connect: invalid gateway port specified"); + err = 1; + } + + if (err) + { + errno = EINVAL; + return(JB_INVALID_SOCKET); + } + + /* build a socks request for connection to the web server */ + + strcpy((char *)&(c->userid), socks_userid); + + csiz = sizeof(*c) + sizeof(socks_userid) - 1; + + switch (fwd->type) + { + case SOCKS_4: + web_server_addr = htonl(resolve_hostname_to_ip(target_host)); + if (web_server_addr == INADDR_NONE) + { + log_error(LOG_LEVEL_CONNECT, "socks4_connect: could not resolve target host %s", target_host); + return(JB_INVALID_SOCKET); + } + break; + case SOCKS_4A: + web_server_addr = 0x00000001; + n = csiz + strlen(target_host) + 1; + if (n > sizeof(cbuf)) + { + errno = EINVAL; + return(JB_INVALID_SOCKET); + } + strcpy(cbuf + csiz, target_host); + csiz = n; + break; + default: + /* Should never get here */ + log_error(LOG_LEVEL_FATAL, "SOCKS4 impossible internal error - bad SOCKS type."); + errno = EINVAL; + return(JB_INVALID_SOCKET); + } + + c->vn = 4; + c->cd = 1; + c->dstport[0] = (target_port >> 8 ) & 0xff; + c->dstport[1] = (target_port ) & 0xff; + c->dstip[0] = (web_server_addr >> 24 ) & 0xff; + c->dstip[1] = (web_server_addr >> 16 ) & 0xff; + c->dstip[2] = (web_server_addr >> 8 ) & 0xff; + c->dstip[3] = (web_server_addr ) & 0xff; + + /* pass the request to the socks server */ + sfd = connect_to(fwd->gateway_host, fwd->gateway_port, csp); + + if (sfd == JB_INVALID_SOCKET) + { + return(JB_INVALID_SOCKET); + } + + if (write_socket(sfd, (char *)c, csiz)) + { + log_error(LOG_LEVEL_CONNECT, "SOCKS4 negotiation write failed..."); + close_socket(sfd); + return(JB_INVALID_SOCKET); + } + + if (read_socket(sfd, sbuf, sizeof(sbuf)) != sizeof(*s)) + { + log_error(LOG_LEVEL_CONNECT, "SOCKS4 negotiation read failed..."); + close_socket(sfd); + return(JB_INVALID_SOCKET); + } + + switch (s->cd) + { + case SOCKS_REQUEST_GRANTED: + return(sfd); + break; + case SOCKS_REQUEST_REJECT: + errstr = "SOCKS request rejected or failed"; + errno = EINVAL; + break; + case SOCKS_REQUEST_IDENT_FAILED: + errstr = "SOCKS request rejected because " + "SOCKS server cannot connect to identd on the client"; + errno = EACCES; + break; + case SOCKS_REQUEST_IDENT_CONFLICT: + errstr = "SOCKS request rejected because " + "the client program and identd report " + "different user-ids"; + errno = EACCES; + break; + default: + errstr = cbuf; + errno = ENOENT; + sprintf(errstr, + "SOCKS request rejected for reason code %d\n", s->cd); + } + + log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s ...", errstr); + + close_socket(sfd); + return(JB_INVALID_SOCKET); + +} + + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/gateway.h b/src/gateway.h new file mode 100644 index 00000000..8b39690c --- /dev/null +++ b/src/gateway.h @@ -0,0 +1,126 @@ +#ifndef GATEWAY_H_INCLUDED +#define GATEWAY_H_INCLUDED +#define GATEWAY_H_VERSION "$Id: gateway.h,v 1.7 2002/03/26 22:29:54 swa Exp $" +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/gateway.h,v $ + * + * Purpose : Contains functions to connect to a server, possibly + * using a "gateway" (i.e. HTTP proxy and/or SOCKS4 + * proxy). Also contains the list of gateway types. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: gateway.h,v $ + * Revision 1.7 2002/03/26 22:29:54 swa + * we have a new homepage! + * + * Revision 1.6 2002/03/25 22:12:45 oes + * Added fix for undefined INADDR_NONE on Solaris by Bart Schelstraete + * + * Revision 1.5 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.4 2002/03/09 20:03:52 jongfoster + * - Making various functions return int rather than size_t. + * (Undoing a recent change). Since size_t is unsigned on + * Windows, functions like read_socket that return -1 on + * error cannot return a size_t. + * + * THIS WAS A MAJOR BUG - it caused frequent, unpredictable + * crashes, and also frequently caused JB to jump to 100% + * CPU and stay there. (Because it thought it had just + * read ((unsigned)-1) == 4Gb of data...) + * + * - The signature of write_socket has changed, it now simply + * returns success=0/failure=nonzero. + * + * - Trying to get rid of a few warnings --with-debug on + * Windows, I've introduced a new type "jb_socket". This is + * used for the socket file descriptors. On Windows, this + * is SOCKET (a typedef for unsigned). Everywhere else, it's + * an int. The error value can't be -1 any more, so it's + * now JB_INVALID_SOCKET (which is -1 on UNIX, and in + * Windows it maps to the #define INVALID_SOCKET.) + * + * - The signature of bind_port has changed. + * + * Revision 1.3 2001/07/29 18:58:15 jongfoster + * Removing nested #includes, adding forward declarations for needed + * structures, and changing the #define _FILENAME_H to FILENAME_H_INCLUDED. + * + * Revision 1.2 2001/06/07 23:12:14 jongfoster + * Removing gateways[] list - no longer used. + * Replacing function pointer in struct gateway with a directly + * called function forwarded_connect(), which can do the common + * task of deciding whether to connect to the web server or HTTP + * proxy. + * Replacing struct gateway with struct forward_spec + * + * Revision 1.1.1.1 2001/05/15 13:58:54 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#ifdef __cplusplus +extern "C" { +#endif + +struct forward_spec; +struct http_request; +struct client_state; + +extern jb_socket forwarded_connect(const struct forward_spec * fwd, + struct http_request *http, + struct client_state *csp); + +/* + * Solaris fix + */ +#ifndef INADDR_NONE +#define INADDR_NONE -1 +#endif + +/* + * Revision control strings from this header and associated .c file + */ +extern const char gateway_rcs[]; +extern const char gateway_h_rcs[]; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ndef GATEWAY_H_INCLUDED */ + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/jbsockets.c b/src/jbsockets.c new file mode 100644 index 00000000..bfc025c9 --- /dev/null +++ b/src/jbsockets.c @@ -0,0 +1,820 @@ +const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.36 2002/05/26 23:23:10 joergs Exp $"; +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/jbsockets.c,v $ + * + * Purpose : Contains wrappers for system-specific sockets code, + * so that the rest of Junkbuster can be more + * OS-independent. Contains #ifdefs to make this work + * on many platforms. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: jbsockets.c,v $ + * Revision 1.36 2002/05/26 23:23:10 joergs + * AmigaOS: Fixed wrong type for len in write_socket() + * + * Revision 1.35 2002/04/26 15:50:04 joergs + * AmigaOS: No socklen_t, added AMIGA to the systems using int instead. + * + * Revision 1.34 2002/04/08 20:31:41 swa + * fixed JB spelling + * + * Revision 1.33 2002/04/03 16:02:18 gliptak + * Correcting compile warning with older gcc + * + * Revision 1.32 2002/03/31 17:18:59 jongfoster + * Win32 only: Enabling STRICT to fix a VC++ compile warning. + * + * Revision 1.31 2002/03/29 03:33:13 david__schmidt + * Fix Mac OSX compiler warnings + * + * Revision 1.30 2002/03/27 14:32:43 david__schmidt + * More compiler warning message maintenance + * + * Revision 1.29 2002/03/26 22:29:54 swa + * we have a new homepage! + * + * Revision 1.28 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.27 2002/03/13 00:27:05 jongfoster + * Killing warnings + * + * Revision 1.26 2002/03/11 22:07:02 david__schmidt + * OS/2 port maintenance: + * - Fixed EMX build - it had decayed a little + * - Fixed inexplicable crash during FD_ZERO - must be due to a bad macro. + * substituted a memset for now. + * + * Revision 1.25 2002/03/09 20:03:52 jongfoster + * - Making various functions return int rather than size_t. + * (Undoing a recent change). Since size_t is unsigned on + * Windows, functions like read_socket that return -1 on + * error cannot return a size_t. + * + * THIS WAS A MAJOR BUG - it caused frequent, unpredictable + * crashes, and also frequently caused JB to jump to 100% + * CPU and stay there. (Because it thought it had just + * read ((unsigned)-1) == 4Gb of data...) + * + * - The signature of write_socket has changed, it now simply + * returns success=0/failure=nonzero. + * + * - Trying to get rid of a few warnings --with-debug on + * Windows, I've introduced a new type "jb_socket". This is + * used for the socket file descriptors. On Windows, this + * is SOCKET (a typedef for unsigned). Everywhere else, it's + * an int. The error value can't be -1 any more, so it's + * now JB_INVALID_SOCKET (which is -1 on UNIX, and in + * Windows it maps to the #define INVALID_SOCKET.) + * + * - The signature of bind_port has changed. + * + * Revision 1.24 2002/03/07 03:51:36 oes + * - Improved handling of failed DNS lookups + * - Fixed compiler warnings etc + * + * Revision 1.23 2002/03/05 00:36:01 jongfoster + * Fixing bug 514988 - unable to restart Junkbuster + * + * Revision 1.22 2002/03/04 02:08:02 david__schmidt + * Enable web editing of actions file on OS/2 (it had been broken all this time!) + * + * Revision 1.21 2002/01/09 14:32:33 oes + * Added support for gethostbyname_r and gethostbyaddr_r. + * + * Revision 1.20 2001/11/16 00:48:48 jongfoster + * Enabling duplicate-socket detection for all platforms, not + * just Win32. + * + * Revision 1.19 2001/10/25 03:40:47 david__schmidt + * Change in porting tactics: OS/2's EMX porting layer doesn't allow multiple + * threads to call select() simultaneously. So, it's time to do a real, live, + * native OS/2 port. See defines for __EMX__ (the porting layer) vs. __OS2__ + * (native). Both versions will work, but using __OS2__ offers multi-threading. + * + * Revision 1.18 2001/09/21 23:02:02 david__schmidt + * Cleaning up 2 compiler warnings on OS/2. + * + * Revision 1.17 2001/09/13 20:11:46 jongfoster + * Fixing 2 compiler warnings under Win32 + * + * Revision 1.16 2001/07/30 22:08:36 jongfoster + * Tidying up #defines: + * - All feature #defines are now of the form FEATURE_xxx + * - Permanently turned off WIN_GUI_EDIT + * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS + * + * Revision 1.15 2001/07/29 17:40:43 jongfoster + * Fixed compiler warning by adding a cast + * + * Revision 1.14 2001/07/18 13:47:59 oes + * Eliminated dirty hack for getsockbyname() + * + * Revision 1.13 2001/07/15 13:56:57 jongfoster + * Removing unused local variable. + * + * Revision 1.12 2001/07/01 17:04:11 oes + * Bugfix: accept_connection no longer uses the obsolete hstrerror() function + * + * Revision 1.11 2001/06/29 21:45:41 oes + * Indentation, CRLF->LF, Tab-> Space + * + * Revision 1.10 2001/06/29 13:29:15 oes + * - Added remote (server) host IP to csp->http->host_ip_addr_str + * - Added detection of local socket IP and fqdn + * - Removed logentry from cancelled commit + * + * Revision 1.9 2001/06/07 23:06:09 jongfoster + * The host parameter to connect_to() is now const. + * + * Revision 1.8 2001/06/03 19:12:07 oes + * filled comment + * + * Revision 1.7 2001/05/28 16:14:00 jongfoster + * Fixing bug in LOG_LEVEL_LOG + * + * Revision 1.6 2001/05/26 17:28:32 jongfoster + * Fixed LOG_LEVEL_LOG + * + * Revision 1.5 2001/05/26 15:26:15 jongfoster + * ACL feature now provides more security by immediately dropping + * connections from untrusted hosts. + * + * Revision 1.4 2001/05/26 00:37:42 jongfoster + * Cosmetic indentation correction. + * + * Revision 1.3 2001/05/25 21:57:54 jongfoster + * Now gives a warning under Windows if you try to bind + * it to a port that's already in use. + * + * Revision 1.2 2001/05/17 23:01:01 oes + * - Cleaned CRLF's from the sources and related files + * + * Revision 1.1.1.1 2001/05/15 13:58:54 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 + +#ifndef STRICT +#define STRICT +#endif +#include +#include +#include + +#else + +#ifndef __OS2__ +#include +#endif +#include +#include +#include +#include +#include + +#ifndef __BEOS__ +#include +#ifndef __OS2__ +#include +#endif +#else +#include +#endif + +#if defined(__EMX__) || defined (__OS2__) +#include /* OS/2/EMX needs a little help with select */ +#ifdef __OS2__ +#include +#endif +#endif + +#endif + +#include "project.h" +#include "jbsockets.h" +#include "filters.h" +#include "errlog.h" + +const char jbsockets_h_rcs[] = JBSOCKETS_H_VERSION; + + +/********************************************************************* + * + * Function : connect_to + * + * Description : Open a socket and connect to it. Will check + * that this is allowed according to ACL. + * + * Parameters : + * 1 : host = hostname to connect to + * 2 : portnum = port to connent on + * 3 : csp = Current client state (buffers, headers, etc...) + * Not modified, only used for source IP and ACL. + * + * Returns : JB_INVALID_SOCKET => failure, else it is the socket + * file descriptor. + * + *********************************************************************/ +jb_socket connect_to(const char *host, int portnum, struct client_state *csp) +{ + struct sockaddr_in inaddr; + jb_socket fd; + int addr; + fd_set wfds; + struct timeval tv[1]; +#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) + int flags; +#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) */ + +#ifdef FEATURE_ACL + struct access_control_addr dst[1]; +#endif /* def FEATURE_ACL */ + + memset((char *)&inaddr, 0, sizeof inaddr); + + if ((addr = resolve_hostname_to_ip(host)) == INADDR_NONE) + { + csp->http->host_ip_addr_str = strdup("unknown"); + return(JB_INVALID_SOCKET); + } + +#ifdef FEATURE_ACL + dst->addr = ntohl((unsigned long) addr); + dst->port = portnum; + + if (block_acl(dst, csp)) + { +#ifdef __OS2__ + errno = SOCEPERM; +#else + errno = EPERM; +#endif + return(JB_INVALID_SOCKET); + } +#endif /* def FEATURE_ACL */ + + inaddr.sin_addr.s_addr = addr; + inaddr.sin_family = AF_INET; + csp->http->host_ip_addr_str = strdup(inet_ntoa(inaddr.sin_addr)); + +#ifndef _WIN32 + if (sizeof(inaddr.sin_port) == sizeof(short)) +#endif /* ndef _WIN32 */ + { + inaddr.sin_port = htons((unsigned short) portnum); + } +#ifndef _WIN32 + else + { + inaddr.sin_port = htonl((unsigned long)portnum); + } +#endif /* ndef _WIN32 */ + +#ifdef _WIN32 + if ((fd = socket(inaddr.sin_family, SOCK_STREAM, 0)) == JB_INVALID_SOCKET) +#else + if ((fd = socket(inaddr.sin_family, SOCK_STREAM, 0)) < 0) +#endif + { + return(JB_INVALID_SOCKET); + } + +#ifdef TCP_NODELAY + { /* turn off TCP coalescence */ + int mi = 1; + setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (char *) &mi, sizeof (int)); + } +#endif /* def TCP_NODELAY */ + +#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) + if ((flags = fcntl(fd, F_GETFL, 0)) != -1) + { + flags |= O_NDELAY; + fcntl(fd, F_SETFL, flags); + } +#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */ + + while (connect(fd, (struct sockaddr *) & inaddr, sizeof inaddr) == JB_INVALID_SOCKET) + { +#ifdef _WIN32 + if (errno == WSAEINPROGRESS) +#elif __OS2__ + if (sock_errno() == EINPROGRESS) +#else /* ifndef _WIN32 */ + if (errno == EINPROGRESS) +#endif /* ndef _WIN32 || __OS2__ */ + { + break; + } + +#ifdef __OS2__ + if (sock_errno() != EINTR) +#else + if (errno != EINTR) +#endif /* __OS2__ */ + { + close_socket(fd); + return(JB_INVALID_SOCKET); + } + } + +#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) + if (flags != -1) + { + flags &= ~O_NDELAY; + fcntl(fd, F_SETFL, flags); + } +#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */ + + /* wait for connection to complete */ + FD_ZERO(&wfds); + FD_SET(fd, &wfds); + + tv->tv_sec = 30; + tv->tv_usec = 0; + + /* MS Windows uses int, not SOCKET, for the 1st arg of select(). Wierd! */ + if (select((int)fd + 1, NULL, &wfds, NULL, tv) <= 0) + { + close_socket(fd); + return(JB_INVALID_SOCKET); + } + return(fd); + +} + + +/********************************************************************* + * + * Function : write_socket + * + * Description : Write the contents of buf (for n bytes) to socket fd. + * + * Parameters : + * 1 : fd = file descriptor (aka. handle) of socket to write to. + * 2 : buf = pointer to data to be written. + * 3 : len = length of data to be written to the socket "fd". + * + * Returns : 0 on success (entire buffer sent). + * nonzero on error. + * + *********************************************************************/ +#ifdef AMIGA +int write_socket(jb_socket fd, const char *buf, ssize_t len) +#else +int write_socket(jb_socket fd, const char *buf, size_t len) +#endif +{ + if (len == 0) + { + return 0; + } + + if (len < 0) + { + return 1; + } + + log_error(LOG_LEVEL_LOG, "%N", len, buf); + +#if defined(_WIN32) + return (send(fd, buf, (int)len, 0) != (int)len); +#elif defined(__BEOS__) || defined(AMIGA) + return (send(fd, buf, len, 0) != len); +#elif defined(__OS2__) + /* + * Break the data up into SOCKET_SEND_MAX chunks for sending... + * OS/2 seemed to complain when the chunks were too large. + */ +#define SOCKET_SEND_MAX 65000 + { + int write_len = 0, send_len, send_rc = 0, i = 0; + while ((i < len) && (send_rc != -1)) + { + if ((i + SOCKET_SEND_MAX) > len) + send_len = len - i; + else + send_len = SOCKET_SEND_MAX; + send_rc = send(fd,(char*)buf + i, send_len, 0); + if (send_rc == -1) + return 1; + i = i + send_len; + } + return 0; + } +#else + return (write(fd, buf, len) != len); +#endif + +} + + +/********************************************************************* + * + * Function : read_socket + * + * Description : Read from a TCP/IP socket in a platform independent way. + * + * Parameters : + * 1 : fd = file descriptor of the socket to read + * 2 : buf = pointer to buffer where data will be written + * Must be >= len bytes long. + * 3 : len = maximum number of bytes to read + * + * Returns : On success, the number of bytes read is returned (zero + * indicates end of file), and the file position is advanced + * by this number. It is not an error if this number is + * smaller than the number of bytes requested; this may hap- + * pen for example because fewer bytes are actually available + * right now (maybe because we were close to end-of-file, or + * because we are reading from a pipe, or from a terminal, + * or because read() was interrupted by a signal). On error, + * -1 is returned, and errno is set appropriately. In this + * case it is left unspecified whether the file position (if + * any) changes. + * + *********************************************************************/ +int read_socket(jb_socket fd, char *buf, int len) +{ + if (len <= 0) + { + return(0); + } + +#if defined(_WIN32) + return(recv(fd, buf, len, 0)); +#elif defined(__BEOS__) || defined(AMIGA) || defined(__OS2__) + return(recv(fd, buf, (size_t)len, 0)); +#else + return(read(fd, buf, (size_t)len)); +#endif +} + + +/********************************************************************* + * + * Function : close_socket + * + * Description : Closes a TCP/IP socket + * + * Parameters : + * 1 : fd = file descriptor of socket to be closed + * + * Returns : void + * + *********************************************************************/ +void close_socket(jb_socket fd) +{ +#if defined(_WIN32) || defined(__BEOS__) + closesocket(fd); +#elif defined(AMIGA) + CloseSocket(fd); +#elif defined(__OS2__) + soclose(fd); +#else + close(fd); +#endif + +} + + +/********************************************************************* + * + * Function : bind_port + * + * Description : Call socket, set socket options, and listen. + * Called by listen_loop to "boot up" our proxy address. + * + * Parameters : + * 1 : hostnam = TCP/IP address to bind/listen to + * 2 : portnum = port to listen on + * 3 : pfd = pointer used to return file descriptor. + * + * Returns : if success, returns 0 and sets *pfd. + * if failure, returns -3 if address is in use, + * -2 if address unresolvable, + * -1 otherwise + *********************************************************************/ +int bind_port(const char *hostnam, int portnum, jb_socket *pfd) +{ + struct sockaddr_in inaddr; + jb_socket fd; +#ifndef _WIN32 + int one = 1; +#endif /* ndef _WIN32 */ + + *pfd = JB_INVALID_SOCKET; + + memset((char *)&inaddr, '\0', sizeof inaddr); + + inaddr.sin_family = AF_INET; + inaddr.sin_addr.s_addr = resolve_hostname_to_ip(hostnam); + + if (inaddr.sin_addr.s_addr == INADDR_NONE) + { + return(-2); + } + +#ifndef _WIN32 + if (sizeof(inaddr.sin_port) == sizeof(short)) +#endif /* ndef _WIN32 */ + { + inaddr.sin_port = htons((unsigned short) portnum); + } +#ifndef _WIN32 + else + { + inaddr.sin_port = htonl((unsigned long) portnum); + } +#endif /* ndef _WIN32 */ + + fd = socket(AF_INET, SOCK_STREAM, 0); + +#ifdef _WIN32 + if (fd == JB_INVALID_SOCKET) +#else + if (fd < 0) +#endif + { + return(-1); + } + +#ifndef _WIN32 + /* + * This is not needed for Win32 - in fact, it stops + * duplicate instances of Junkbuster from being caught. + * + * On UNIX, we assume the user is sensible enough not + * to start Junkbuster multiple times on the same IP. + * Without this, stopping and restarting Junkbuster + * from a script fails. + * Note: SO_REUSEADDR is meant to only take over + * sockets which are *not* in listen state in Linux, + * e.g. sockets in TIME_WAIT. YMMV. + */ + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)); +#endif /* ndef _WIN32 */ + + if (bind (fd, (struct sockaddr *)&inaddr, sizeof(inaddr)) < 0) + { + close_socket (fd); +#ifdef _WIN32 + if (errno == WSAEADDRINUSE) +#else + if (errno == EADDRINUSE) +#endif + { + return(-3); + } + else + { + return(-1); + } + } + + while (listen(fd, 5) == -1) + { + if (errno != EINTR) + { + return(-1); + } + } + + *pfd = fd; + return 0; + +} + + +/********************************************************************* + * + * Function : accept_connection + * + * Description : Accepts a connection on a socket. Socket must have + * been created using bind_port(). + * + * Parameters : + * 1 : csp = Client state, cfd, ip_addr_str, and + * ip_addr_long will be set by this routine. + * 2 : fd = file descriptor returned from bind_port + * + * Returns : when a connection is accepted, it returns 1 (TRUE). + * On an error it returns 0 (FALSE). + * + *********************************************************************/ +int accept_connection(struct client_state * csp, jb_socket fd) +{ + struct sockaddr_in client, server; + struct hostent *host = NULL; + jb_socket afd; +#if defined(_WIN32) || defined(__OS2__) || defined(__APPLE_CC__) || defined(AMIGA) + /* Wierdness - fix a warning. */ + int c_length, s_length; +#else + socklen_t c_length, s_length; +#endif +#if defined(HAVE_GETHOSTBYADDR_R_8_ARGS) || defined(HAVE_GETHOSTBYADDR_R_7_ARGS) || defined(HAVE_GETHOSTBYADDR_R_5_ARGS) + struct hostent result; +#if defined(HAVE_GETHOSTBYADDR_R_5_ARGS) + struct hostent_data hdata; +#else + char hbuf[HOSTENT_BUFFER_SIZE]; + int thd_err; +#endif /* def HAVE_GETHOSTBYADDR_R_5_ARGS */ +#endif /* def HAVE_GETHOSTBYADDR_R_(8|7|5)_ARGS */ + + c_length = s_length = sizeof(client); + +#ifdef _WIN32 + afd = accept (fd, (struct sockaddr *) &client, &c_length); + if (afd == JB_INVALID_SOCKET) + { + return 0; + } +#else + do + { + afd = accept (fd, (struct sockaddr *) &client, &c_length); + } while (afd < 1 && errno == EINTR); + if (afd < 0) + { + return 0; + } +#endif + + /* + * Determine the IP-Adress that the client used to reach us + * and the hostname associated with that address + */ + if (!getsockname(afd, (struct sockaddr *) &server, &s_length)) + { + csp->my_ip_addr_str = strdup(inet_ntoa(server.sin_addr)); +#if defined(HAVE_GETHOSTBYADDR_R_8_ARGS) + gethostbyaddr_r((const char *)&server.sin_addr, + sizeof(server.sin_addr), AF_INET, + &result, hbuf, HOSTENT_BUFFER_SIZE, + &host, &thd_err); +#elif defined(HAVE_GETHOSTBYADDR_R_7_ARGS) + host = gethostbyaddr_r((const char *)&server.sin_addr, + sizeof(server.sin_addr), AF_INET, + &result, hbuf, HOSTENT_BUFFER_SIZE, &thd_err); +#elif defined(HAVE_GETHOSTBYADDR_R_5_ARGS) + if (0 == gethostbyaddr_r((const char *)&server.sin_addr, + sizeof(server.sin_addr), AF_INET, + &result, &hdata)) + { + host = &result; + } + else + { + host = NULL; + } +#else + host = gethostbyaddr((const char *)&server.sin_addr, + sizeof(server.sin_addr), AF_INET); +#endif + if (host == NULL) + { + log_error(LOG_LEVEL_ERROR, "Unable to get my own hostname: %E\n"); + } + else + { + csp->my_hostname = strdup(host->h_name); + } + } + + csp->cfd = afd; + csp->ip_addr_str = strdup(inet_ntoa(client.sin_addr)); + csp->ip_addr_long = ntohl(client.sin_addr.s_addr); + + return 1; + +} + + +/********************************************************************* + * + * Function : resolve_hostname_to_ip + * + * Description : Resolve a hostname to an internet tcp/ip address. + * NULL or an empty string resolve to INADDR_ANY. + * + * Parameters : + * 1 : host = hostname to resolve + * + * Returns : INADDR_NONE => failure, INADDR_ANY or tcp/ip address if succesful. + * + *********************************************************************/ +unsigned long resolve_hostname_to_ip(const char *host) +{ + struct sockaddr_in inaddr; + struct hostent *hostp; +#if defined(HAVE_GETHOSTBYNAME_R_6_ARGS) || defined(HAVE_GETHOSTBYNAME_R_5_ARGS) || defined(HAVE_GETHOSTBYNAME_R_3_ARGS) + struct hostent result; +#if defined(HAVE_GETHOSTBYNAME_R_6_ARGS) || defined(HAVE_GETHOSTBYNAME_R_5_ARGS) + char hbuf[HOSTENT_BUFFER_SIZE]; + int thd_err; +#else /* defined(HAVE_GETHOSTBYNAME_R_3_ARGS) */ + struct hostent_data hdata; +#endif /* def HAVE_GETHOSTBYNAME_R_(6|5)_ARGS */ +#endif /* def HAVE_GETHOSTBYNAME_R_(6|5|3)_ARGS */ + + if ((host == NULL) || (*host == '\0')) + { + return(INADDR_ANY); + } + + memset((char *) &inaddr, 0, sizeof inaddr); + + if ((inaddr.sin_addr.s_addr = inet_addr(host)) == -1) + { +#if defined(HAVE_GETHOSTBYNAME_R_6_ARGS) + gethostbyname_r(host, &result, hbuf, + HOSTENT_BUFFER_SIZE, &hostp, &thd_err); +#elif defined(HAVE_GETHOSTBYNAME_R_5_ARGS) + hostp = gethostbyname_r(host, &result, hbuf, + HOSTENT_BUFFER_SIZE, &thd_err); +#elif defined(HAVE_GETHOSTBYNAME_R_3_ARGS) + if (0 == gethostbyname_r(host, &result, &hdata)) + { + hostp = &result; + } + else + { + hostp = NULL; + } +#else + hostp = gethostbyname(host); +#endif /* def HAVE_GETHOSTBYNAME_R_(6|5|3)_ARGS */ + if (hostp == NULL) + { + errno = EINVAL; + log_error(LOG_LEVEL_ERROR, "could not resolve hostname %s", host); + return(INADDR_NONE); + } + if (hostp->h_addrtype != AF_INET) + { +#ifdef _WIN32 + errno = WSAEPROTOTYPE; +#else + errno = EPROTOTYPE; +#endif + log_error(LOG_LEVEL_ERROR, "hostname %s resolves to unknown address type.", host); + return(INADDR_NONE); + } + memcpy( + (char *) &inaddr.sin_addr, + (char *) hostp->h_addr, + sizeof(inaddr.sin_addr) + ); + } + return(inaddr.sin_addr.s_addr); + +} + + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/jbsockets.h b/src/jbsockets.h new file mode 100644 index 00000000..8a95bb28 --- /dev/null +++ b/src/jbsockets.h @@ -0,0 +1,133 @@ +#ifndef JBSOCKETS_H_INCLUDED +#define JBSOCKETS_H_INCLUDED +#define JBSOCKETS_H_VERSION "$Id: jbsockets.h,v 1.10 2002/05/26 23:23:10 joergs Exp $" +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/jbsockets.h,v $ + * + * Purpose : Contains wrappers for system-specific sockets code, + * so that the rest of Junkbuster can be more + * OS-independent. Contains #ifdefs to make this work + * on many platforms. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: jbsockets.h,v $ + * Revision 1.10 2002/05/26 23:23:10 joergs + * AmigaOS: Fixed wrong type for len in write_socket() + * + * Revision 1.9 2002/04/08 20:31:41 swa + * fixed JB spelling + * + * Revision 1.8 2002/03/26 22:29:54 swa + * we have a new homepage! + * + * Revision 1.7 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.6 2002/03/13 00:27:05 jongfoster + * Killing warnings + * + * Revision 1.5 2002/03/09 20:03:52 jongfoster + * - Making various functions return int rather than size_t. + * (Undoing a recent change). Since size_t is unsigned on + * Windows, functions like read_socket that return -1 on + * error cannot return a size_t. + * + * THIS WAS A MAJOR BUG - it caused frequent, unpredictable + * crashes, and also frequently caused JB to jump to 100% + * CPU and stay there. (Because it thought it had just + * read ((unsigned)-1) == 4Gb of data...) + * + * - The signature of write_socket has changed, it now simply + * returns success=0/failure=nonzero. + * + * - Trying to get rid of a few warnings --with-debug on + * Windows, I've introduced a new type "jb_socket". This is + * used for the socket file descriptors. On Windows, this + * is SOCKET (a typedef for unsigned). Everywhere else, it's + * an int. The error value can't be -1 any more, so it's + * now JB_INVALID_SOCKET (which is -1 on UNIX, and in + * Windows it maps to the #define INVALID_SOCKET.) + * + * - The signature of bind_port has changed. + * + * Revision 1.4 2002/03/07 03:51:36 oes + * - Improved handling of failed DNS lookups + * - Fixed compiler warnings etc + * + * Revision 1.3 2001/07/29 19:01:11 jongfoster + * Changed _FILENAME_H to FILENAME_H_INCLUDED. + * Added forward declarations for needed structures. + * + * Revision 1.2 2001/06/07 23:06:09 jongfoster + * The host parameter to connect_to() is now const. + * + * Revision 1.1.1.1 2001/05/15 13:58:54 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#include "project.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct client_state; + +extern jb_socket connect_to(const char *host, int portnum, struct client_state *csp); +#ifdef AMIGA +extern int write_socket(jb_socket fd, const char *buf, ssize_t n); +#else +extern int write_socket(jb_socket fd, const char *buf, size_t n); +#endif +extern int read_socket(jb_socket fd, char *buf, int n); +extern void close_socket(jb_socket fd); + +extern int bind_port(const char *hostnam, int portnum, jb_socket *pfd); +extern int accept_connection(struct client_state * csp, jb_socket fd); + +extern unsigned long resolve_hostname_to_ip(const char *host); + +/* Revision control strings from this header and associated .c file */ +extern const char jbsockets_rcs[]; +extern const char jbsockets_h_rcs[]; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ndef JBSOCKETS_H_INCLUDED */ + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/jcc.c b/src/jcc.c new file mode 100644 index 00000000..5dd10e03 --- /dev/null +++ b/src/jcc.c @@ -0,0 +1,2364 @@ +const char jcc_rcs[] = "$Id: jcc.c,v 1.92 2002/05/08 16:00:46 oes Exp $"; +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/jcc.c,v $ + * + * Purpose : Main file. Contains main() method, main loop, and + * the main connection-handling function. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: jcc.c,v $ + * Revision 1.92 2002/05/08 16:00:46 oes + * Chat's buffer handling: + * - Fixed bug with unchecked out-of-mem conditions + * while reading client request & server headers + * - No longer predict if the buffer limit will be exceeded + * in the next read -- check add_to_iob's new + * return code. If buffer couldn't be extended + * (policy or out-of-mem) while + * - reading from client: abort + * - reading server headers: send error page + * - buffering server body for filter: flush, + * and if that fails: send error page + * + * Revision 1.91 2002/04/08 20:35:58 swa + * fixed JB spelling + * + * Revision 1.90 2002/04/02 14:57:28 oes + * Made sending wafers independent of FEATURE_COOKIE_JAR + * + * Revision 1.89 2002/03/31 17:18:59 jongfoster + * Win32 only: Enabling STRICT to fix a VC++ compile warning. + * + * Revision 1.88 2002/03/27 14:32:43 david__schmidt + * More compiler warning message maintenance + * + * Revision 1.87 2002/03/26 22:29:54 swa + * we have a new homepage! + * + * Revision 1.86 2002/03/25 17:04:55 david__schmidt + * Workaround for closing the jarfile before load_config() comes around again + * + * Revision 1.85 2002/03/24 15:23:33 jongfoster + * Name changes + * + * Revision 1.84 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.83 2002/03/16 23:54:06 jongfoster + * Adding graceful termination feature, to help look for memory leaks. + * If you enable this (which, by design, has to be done by hand + * editing config.h) and then go to http://i.j.b/die, then the program + * will exit cleanly after the *next* request. It should free all the + * memory that was used. + * + * Revision 1.82 2002/03/13 00:27:05 jongfoster + * Killing warnings + * + * Revision 1.81 2002/03/12 01:42:50 oes + * Introduced modular filters + * + * Revision 1.80 2002/03/11 22:07:05 david__schmidt + * OS/2 port maintenance: + * - Fixed EMX build - it had decayed a little + * - Fixed inexplicable crash during FD_ZERO - must be due to a bad macro. + * substituted a memset for now. + * + * Revision 1.79 2002/03/09 20:03:52 jongfoster + * - Making various functions return int rather than size_t. + * (Undoing a recent change). Since size_t is unsigned on + * Windows, functions like read_socket that return -1 on + * error cannot return a size_t. + * + * THIS WAS A MAJOR BUG - it caused frequent, unpredictable + * crashes, and also frequently caused JB to jump to 100% + * CPU and stay there. (Because it thought it had just + * read ((unsigned)-1) == 4Gb of data...) + * + * - The signature of write_socket has changed, it now simply + * returns success=0/failure=nonzero. + * + * - Trying to get rid of a few warnings --with-debug on + * Windows, I've introduced a new type "jb_socket". This is + * used for the socket file descriptors. On Windows, this + * is SOCKET (a typedef for unsigned). Everywhere else, it's + * an int. The error value can't be -1 any more, so it's + * now JB_INVALID_SOCKET (which is -1 on UNIX, and in + * Windows it maps to the #define INVALID_SOCKET.) + * + * - The signature of bind_port has changed. + * + * Revision 1.78 2002/03/08 21:35:04 oes + * Added optional group supplement to --user option. Will now use default group of user if no group given + * + * Revision 1.77 2002/03/07 03:52:06 oes + * - Fixed compiler warnings etc + * - Improved handling of failed DNS lookups + * + * Revision 1.76 2002/03/06 22:54:35 jongfoster + * Automated function-comment nitpicking. + * + * Revision 1.75 2002/03/06 10:02:19 oes + * Fixed stupid bug when --user was not given + * + * Revision 1.74 2002/03/06 00:49:31 jongfoster + * Fixing warning on Windows + * Making #ifdefs that refer to the same variable consistently + * use #ifdef unix rather than mixing #ifdef unix & #ifndef OS2 + * + * Revision 1.73 2002/03/05 23:57:30 hal9 + * Stray character 's' on line 1618 was breaking build. + * + * Revision 1.72 2002/03/05 21:33:45 david__schmidt + * - Re-enable OS/2 building after new parms were added + * - Fix false out of memory report when resolving CGI templates when no IP + * address is available of failed attempt (a la no such domain) + * + * Revision 1.71 2002/03/05 18:13:56 oes + * Added --user option + * + * Revision 1.70 2002/03/05 04:52:42 oes + * Deleted non-errlog debugging code + * + * Revision 1.69 2002/03/04 23:50:00 jongfoster + * Splitting off bind_port() call into bind_port_helper(), with + * improved logging. + * + * Revision 1.68 2002/03/04 20:17:32 oes + * Fixed usage info + * + * Revision 1.67 2002/03/04 18:18:57 oes + * - Removed _DEBUG mode + * - Cleand up cmdline parsing + * - Introduced --no-daemon, --pidfile options + * - Cleaned up signal handling: + * - Terminate cleanly on INT, TERM and ABRT + * - Schedule logfile for re-opening on HUP + * - Ignore CHLD and PIPE + * - Leave the rest with their default handlers + * - Uniform handler registration + * - Added usage() function + * - Played styleguide police + * + * Revision 1.66 2002/03/03 15:06:55 oes + * Re-enabled automatic config reloading + * + * Revision 1.65 2002/03/03 14:49:11 oes + * Fixed CLF logging: Now uses client's original HTTP request + * + * Revision 1.64 2002/03/03 09:18:03 joergs + * Made jumbjuster work on AmigaOS again. + * + * Revision 1.63 2002/03/02 04:14:50 david__schmidt + * Clean up a little CRLF unpleasantness that suddenly appeared + * + * Revision 1.62 2002/02/20 23:17:23 jongfoster + * Detecting some out-of memory conditions and exiting with a log message. + * + * Revision 1.61 2002/01/17 21:01:52 jongfoster + * Moving all our URL and URL pattern parsing code to urlmatch.c. + * + * Revision 1.60 2001/12/30 14:07:32 steudten + * - Add signal handling (unix) + * - Add SIGHUP handler (unix) + * - Add creation of pidfile (unix) + * - Add action 'top' in rc file (RH) + * - Add entry 'SIGNALS' to manpage + * - Add exit message to logfile (unix) + * + * Revision 1.59 2001/12/13 14:07:18 oes + * Fixed Bug: 503 error page now sent OK + * + * Revision 1.58 2001/11/30 23:37:24 jongfoster + * Renaming the Win32 config file to config.txt - this is almost the + * same as the corresponding UNIX name "config" + * + * Revision 1.57 2001/11/16 00:47:43 jongfoster + * Changing the tty-disconnection code to use setsid(). + * + * Revision 1.56 2001/11/13 20:20:54 jongfoster + * Tabs->spaces, fixing a bug with missing {} around an if() + * + * Revision 1.55 2001/11/13 20:14:53 jongfoster + * Patch for FreeBSD setpgrp() as suggested by Alexander Lazic + * + * Revision 1.54 2001/11/07 00:03:14 steudten + * Give reliable return value if an error + * occurs not just 0 with new daemon mode. + * + * Revision 1.53 2001/11/05 21:41:43 steudten + * Add changes to be a real daemon just for unix os. + * (change cwd to /, detach from controlling tty, set + * process group and session leader to the own process. + * Add DBG() Macro. + * Add some fatal-error log message for failed malloc(). + * Add '-d' if compiled with 'configure --with-debug' to + * enable debug output. + * + * Revision 1.52 2001/10/26 20:11:20 jongfoster + * Fixing type mismatch + * + * Revision 1.51 2001/10/26 17:38:28 oes + * Cosmetics + * + * Revision 1.50 2001/10/25 03:40:48 david__schmidt + * Change in porting tactics: OS/2's EMX porting layer doesn't allow multiple + * threads to call select() simultaneously. So, it's time to do a real, live, + * native OS/2 port. See defines for __EMX__ (the porting layer) vs. __OS2__ + * (native). Both versions will work, but using __OS2__ offers multi-threading. + * + * Revision 1.49 2001/10/23 21:41:35 jongfoster + * Added call to initialize the (statically-allocated of course) + * "out of memory" CGI response. + * + * Revision 1.48 2001/10/10 19:56:46 jongfoster + * Moving some code that wasn't cookie-related out of an #ifdef + * FEATURE_COOKIE_JAR + * + * Revision 1.47 2001/10/10 16:44:36 oes + * Added CONNECT destination port limitation check + * + * Revision 1.46 2001/10/08 15:17:41 oes + * Re-enabled SSL forwarding + * + * Revision 1.45 2001/10/07 15:42:11 oes + * Replaced 6 boolean members of csp with one bitmap (csp->flags) + * + * Moved downgrading of the HTTP version from parse_http_request to + * chat(), since we can't decide if it is necessary before we have + * determined the actions for the URL. The HTTP command is now + * *always* re-built so the repairs need no longer be special-cased. + * + * filter_popups now gets a csp pointer so it can raise the new + * CSP_FLAG_MODIFIED flag. + * + * Bugfix + * + * Added configurable size limit for the IOB. If the IOB grows so + * large that the next read would exceed the limit, the header + * is generated, and the header & unfiltered buffer are flushed + * to the client. Chat then continues in non-buffering, + * non-filtering body mode. + * + * Revision 1.44 2001/10/02 18:13:57 oes + * Ooops + * + * Revision 1.43 2001/10/02 15:32:13 oes + * Moved generation of hdr + * + * Revision 1.42 2001/09/21 23:02:02 david__schmidt + * Cleaning up 2 compiler warnings on OS/2. + * + * Revision 1.41 2001/09/16 17:05:14 jongfoster + * Removing unused #include showarg.h + * + * Revision 1.40 2001/09/16 15:41:45 jongfoster + * Fixing signed/unsigned comparison warning. + * + * Revision 1.39 2001/09/16 13:21:27 jongfoster + * Changes to use new list functions. + * + * Revision 1.38 2001/09/16 13:01:46 jongfoster + * Removing redundant function call that zeroed zalloc()'d memory. + * + * Revision 1.37 2001/09/10 11:12:24 oes + * Deleted unused variable + * + * Revision 1.36 2001/09/10 10:56:15 oes + * Silenced compiler warnings + * + * Revision 1.35 2001/07/31 14:44:22 oes + * Deleted unused size parameter from filter_popups() + * + * Revision 1.34 2001/07/30 22:08:36 jongfoster + * Tidying up #defines: + * - All feature #defines are now of the form FEATURE_xxx + * - Permanently turned off WIN_GUI_EDIT + * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS + * + * Revision 1.33 2001/07/29 19:32:00 jongfoster + * Renaming _main() [mingw32 only] to real_main(), for ANSI compliance. + * + * Revision 1.32 2001/07/29 18:47:05 jongfoster + * Adding missing #include "loadcfg.h" + * + * Revision 1.31 2001/07/29 12:17:48 oes + * Applied pthread fix by Paul Lieverse + * + * Revision 1.30 2001/07/25 22:57:13 jongfoster + * __BEOS__ no longer overrides FEATURE_PTHREAD. + * This is because FEATURE_PTHREAD will soon be widely used, so I + * want to keep it simple. + * + * Revision 1.29 2001/07/24 12:47:06 oes + * Applied BeOS support update by Eugenia + * + * Revision 1.28 2001/07/23 13:26:12 oes + * Fixed bug in popup-killing for the first read that caused binary garbage to be sent between headers and body + * + * Revision 1.27 2001/07/19 19:09:47 haroon + * - Added code to take care of the situation where while processing the first + * server response (which includes the server header), after finding the end + * of the headers we were not looking past the end of the headers for + * content modification. I enabled it for filter_popups. + * Someone else should look to see if other similar operations should be + * done to the discarded portion of the buffer. + * + * Note 2001/07/20: No, the other content modification mechanisms will process + * the whole iob later anyway. --oes + * + * Revision 1.26 2001/07/18 12:31:36 oes + * cosmetics + * + * Revision 1.25 2001/07/15 19:43:49 jongfoster + * Supports POSIX threads. + * Also removed some unused #includes. + * + * Revision 1.24 2001/07/13 14:00:40 oes + * - Generic content modification scheme: + * Each feature has its own applicability flag that is set + * from csp->action->flags. + * Replaced the "filtering" int flag , by a function pointer + * "content_filter" to the function that will do the content + * modification. If it is != NULL, the document will be buffered + * and processed through *content_filter, which must set + * csp->content_length and return a modified copy of the body + * or return NULL (on failiure). + * - Changed csp->is_text to the more generic bitmap csp->content_type + * which can currently take the valued CT_TEXT or CT_GIF + * - Reformatting etc + * - Removed all #ifdef PCRS + * + * Revision 1.23 2001/07/02 02:28:25 iwanttokeepanon + * Added "#ifdef ACL_FILES" conditional compilation to line 1291 to exclude + * the `block_acl' call. This prevents a compilation error when the user + * does not wish to use the "ACL" feature. + * + * Revision 1.22 2001/06/29 21:45:41 oes + * Indentation, CRLF->LF, Tab-> Space + * + * Revision 1.21 2001/06/29 13:29:36 oes + * - Cleaned up, improved comments + * - Unified all possible interceptors (CGI, + * block, trust, fast_redirect) in one + * place, with one (CGI) answer generation + * mechansim. Much clearer now. + * - Removed the GIF image generation, which + * is now done in filters.c:block_url() + * - Made error conditions like domain lookup + * failiure or (various) problems while talking + * to the server use cgi.c:error_response() + * instead of generating HTML/HTTP in chat() (yuck!) + * - Removed logentry from cancelled commit + * + * Revision 1.20 2001/06/09 10:55:28 jongfoster + * Changing BUFSIZ ==> BUFFER_SIZE + * + * Revision 1.19 2001/06/07 23:12:52 jongfoster + * Replacing function pointer in struct gateway with a directly + * called function forwarded_connect(). + * Replacing struct gateway with struct forward_spec + * + * Revision 1.18 2001/06/03 19:12:16 oes + * introduced new cgi handling + * + * Revision 1.17 2001/06/01 20:07:23 jongfoster + * Now uses action +image-blocker{} rather than config->tinygif + * + * Revision 1.16 2001/06/01 18:49:17 jongfoster + * Replaced "list_share" with "list" - the tiny memory gain was not + * worth the extra complexity. + * + * Revision 1.15 2001/05/31 21:24:47 jongfoster + * Changed "permission" to "action" throughout. + * Removed DEFAULT_USER_AGENT - it must now be specified manually. + * Moved vanilla wafer check into chat(), since we must now + * decide whether or not to add it based on the URL. + * + * Revision 1.14 2001/05/29 20:14:01 joergs + * AmigaOS bugfix: PCRS needs a lot of stack, stacksize for child threads + * increased. + * + * Revision 1.13 2001/05/29 09:50:24 jongfoster + * Unified blocklist/imagelist/permissionslist. + * File format is still under discussion, but the internal changes + * are (mostly) done. + * + * Also modified interceptor behaviour: + * - We now intercept all URLs beginning with one of the following + * prefixes (and *only* these prefixes): + * * http://i.j.b/ + * * http://ijbswa.sf.net/config/ + * * http://ijbswa.sourceforge.net/config/ + * - New interceptors "home page" - go to http://i.j.b/ to see it. + * - Internal changes so that intercepted and fast redirect pages + * are not replaced with an image. + * - Interceptors now have the option to send a binary page direct + * to the client. (i.e. ijb-send-banner uses this) + * - Implemented show-url-info interceptor. (Which is why I needed + * the above interceptors changes - a typical URL is + * "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif". + * The previous mechanism would not have intercepted that, and + * if it had been intercepted then it then it would have replaced + * it with an image.) + * + * Revision 1.12 2001/05/27 22:17:04 oes + * + * - re_process_buffer no longer writes the modified buffer + * to the client, which was very ugly. It now returns the + * buffer, which it is then written by chat. + * + * - content_length now adjusts the Content-Length: header + * for modified documents rather than crunch()ing it. + * (Length info in csp->content_length, which is 0 for + * unmodified documents) + * + * - For this to work, sed() is called twice when filtering. + * + * Revision 1.11 2001/05/26 17:27:53 jongfoster + * Added support for CLF and fixed LOG_LEVEL_LOG. + * Also did CRLF->LF fix of my previous patch. + * + * Revision 1.10 2001/05/26 15:26:15 jongfoster + * ACL feature now provides more security by immediately dropping + * connections from untrusted hosts. + * + * Revision 1.9 2001/05/26 00:28:36 jongfoster + * Automatic reloading of config file. + * Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32). + * Most of the global variables have been moved to a new + * struct configuration_spec, accessed through csp->config->globalname + * Most of the globals remaining are used by the Win32 GUI. + * + * Revision 1.8 2001/05/25 22:43:18 jongfoster + * Fixing minor memory leak and buffer overflow. + * + * Revision 1.7 2001/05/25 22:34:30 jongfoster + * Hard tabs->Spaces + * + * Revision 1.6 2001/05/23 00:13:58 joergs + * AmigaOS support fixed. + * + * Revision 1.5 2001/05/22 18:46:04 oes + * + * - Enabled filtering banners by size rather than URL + * by adding patterns that replace all standard banner + * sizes with the "Junkbuster" gif to the re_filterfile + * + * - Enabled filtering WebBugs by providing a pattern + * which kills all 1x1 images + * + * - Added support for PCRE_UNGREEDY behaviour to pcrs, + * which is selected by the (nonstandard and therefore + * capital) letter 'U' in the option string. + * It causes the quantifiers to be ungreedy by default. + * Appending a ? turns back to greedy (!). + * + * - Added a new interceptor ijb-send-banner, which + * sends back the "Junkbuster" gif. Without imagelist or + * MSIE detection support, or if tinygif = 1, or the + * URL isn't recognized as an imageurl, a lame HTML + * explanation is sent instead. + * + * - Added new feature, which permits blocking remote + * script redirects and firing back a local redirect + * to the browser. + * The feature is conditionally compiled, i.e. it + * can be disabled with --disable-fast-redirects, + * plus it must be activated by a "fast-redirects" + * line in the config file, has its own log level + * and of course wants to be displayed by show-proxy-args + * Note: Boy, all the #ifdefs in 1001 locations and + * all the fumbling with configure.in and acconfig.h + * were *way* more work than the feature itself :-( + * + * - Because a generic redirect template was needed for + * this, tinygif = 3 now uses the same. + * + * - Moved GIFs, and other static HTTP response templates + * to project.h + * + * - Some minor fixes + * + * - Removed some >400 CRs again (Jon, you really worked + * a lot! ;-) + * + * Revision 1.4 2001/05/21 19:34:01 jongfoster + * Made failure to bind() a fatal error. + * + * Revision 1.3 2001/05/20 01:21:20 jongfoster + * Version 2.9.4 checkin. + * - Merged popupfile and cookiefile, and added control over PCRS + * filtering, in new "permissionsfile". + * - Implemented LOG_LEVEL_FATAL, so that if there is a configuration + * file error you now get a message box (in the Win32 GUI) rather + * than the program exiting with no explanation. + * - Made killpopup use the PCRS MIME-type checking and HTTP-header + * skipping. + * - Removed tabs from "config" + * - Moved duplicated url parsing code in "loaders.c" to a new funcition. + * - Bumped up version number. + * + * Revision 1.2 2001/05/17 22:34:44 oes + * - Added hint on GIF char array generation to jcc.c + * - Cleaned CRLF's from the sources and related files + * - Repaired logging for REF and FRC + * + * Revision 1.1.1.1 2001/05/15 13:58:56 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef FEATURE_PTHREAD +#include +#endif /* def FEATURE_PTHREAD */ + +#ifdef _WIN32 +# ifndef FEATURE_PTHREAD +# ifndef STRICT +# define STRICT +# endif +# include +# include +# endif /* ndef FEATURE_PTHREAD */ + +# include "win32.h" +# ifndef _WIN_CONSOLE +# include "w32log.h" +# endif /* ndef _WIN_CONSOLE */ + +#else /* ifndef _WIN32 */ + +# if !defined (__OS2__) +# include +# include +# endif /* ndef __OS2__ */ +# include +# include +# include + +#ifdef sun +#include +#endif /* sun */ + +#ifdef unix +#include +#include +#endif + +# include + +# ifdef __BEOS__ +# include /* BeOS has select() for sockets only. */ +# include /* declarations for threads and stuff. */ +# endif + +# if defined(__EMX__) || defined(__OS2__) +# include /* OS/2/EMX needs a little help with select */ +# endif +# ifdef __OS2__ +#define INCL_DOS +# include +#define bzero(B,N) memset(B,0x00,n) +# endif + +# ifndef FD_ZERO +# include +# endif + +#endif + +#include "project.h" +#include "list.h" +#include "jcc.h" +#include "filters.h" +#include "loaders.h" +#include "parsers.h" +#include "killpopup.h" +#include "miscutil.h" +#include "errlog.h" +#include "jbsockets.h" +#include "gateway.h" +#include "actions.h" +#include "cgi.h" +#include "loadcfg.h" +#include "urlmatch.h" + +const char jcc_h_rcs[] = JCC_H_VERSION; +const char project_h_rcs[] = PROJECT_H_VERSION; + +int no_daemon = 0; +struct client_state clients[1]; +struct file_list files[1]; + +#ifdef FEATURE_STATISTICS +int urls_read = 0; /* total nr of urls read inc rejected */ +int urls_rejected = 0; /* total nr of urls rejected */ +#endif /* def FEATURE_STATISTICS */ + +#ifdef FEATURE_GRACEFUL_TERMINATION +int g_terminate = 0; +#endif + +static void listen_loop(void); +static void chat(struct client_state *csp); +#ifdef AMIGA +void serve(struct client_state *csp); +#else /* ifndef AMIGA */ +static void serve(struct client_state *csp); +#endif /* def AMIGA */ + +#ifdef __BEOS__ +static int32 server_thread(void *data); +#endif /* def __BEOS__ */ + +#ifdef _WIN32 +#define sleep(N) Sleep(((N) * 1000)) +#endif + +#ifdef __OS2__ +#define sleep(N) DosSleep(((N) * 100)) +#endif + +#if defined(unix) || defined(__EMX__) +const char *basedir; +const char *pidfile = NULL; +int received_hup_signal = 0; +#endif /* defined unix */ + +/* The vanilla wafer. */ +static const char VANILLA_WAFER[] = + "NOTICE=TO_WHOM_IT_MAY_CONCERN_" + "Do_not_send_me_any_copyrighted_information_other_than_the_" + "document_that_I_am_requesting_or_any_of_its_necessary_components._" + "In_particular_do_not_send_me_any_cookies_that_" + "are_subject_to_a_claim_of_copyright_by_anybody._" + "Take_notice_that_I_refuse_to_be_bound_by_any_license_condition_" + "(copyright_or_otherwise)_applying_to_any_cookie._"; + + +#if !defined(_WIN32) && !defined(__OS2__) && !defined(AMIGA) +/********************************************************************* + * + * Function : sig_handler + * + * Description : Signal handler for different signals. + * Exit gracefully on ABRT, TERM and INT + * or set a flag that will cause the errlog + * to be reopened by the main thread on HUP. + * + * Parameters : + * 1 : the_signal = the signal cause this function to call + * + * Returns : - + * + *********************************************************************/ +static void sig_handler(int the_signal) +{ + switch(the_signal) + { + case SIGABRT: + case SIGTERM: + case SIGINT: + log_error(LOG_LEVEL_INFO, "exiting by signal %d .. bye", the_signal); +#if defined(unix) + unlink(pidfile); +#endif /* unix */ + exit(the_signal); + break; + + case SIGHUP: + received_hup_signal = 1; + break; + + default: + /* + * We shouldn't be here, unless we catch signals + * in main() that we can't handle here! + */ + log_error(LOG_LEVEL_FATAL, "sig_handler: exiting on unexpected signal %d", the_signal); + } + return; + +} +#endif + + +/********************************************************************* + * + * Function : chat + * + * Description : Once a connection to the client has been accepted, + * this function is called (via serve()) to handle the + * main business of the communication. When this + * function returns, the caller must close the client + * socket handle. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : On success, the number of bytes written are returned (zero + * indicates nothing was written). On error, -1 is returned, + * and errno is set appropriately. If count is zero and the + * file descriptor refers to a regular file, 0 will be + * returned without causing any other effect. For a special + * file, the results are not portable. + * + *********************************************************************/ +static void chat(struct client_state *csp) +{ +/* + * This next lines are a little ugly, but they simplifies the if statements + * below. Basically if TOGGLE, then we want the if to test if the + * CSP_FLAG_TOGGLED_ON flag ist set, else we don't. And if FEATURE_FORCE_LOAD, + * then we want the if to test for CSP_FLAG_FORCED , else we don't + */ +#ifdef FEATURE_TOGGLE +# define IS_TOGGLED_ON_AND (csp->flags & CSP_FLAG_TOGGLED_ON) && +#else /* ifndef FEATURE_TOGGLE */ +# define IS_TOGGLED_ON_AND +#endif /* ndef FEATURE_TOGGLE */ +#ifdef FEATURE_FORCE_LOAD +# define IS_NOT_FORCED_AND !(csp->flags & CSP_FLAG_FORCED) && +#else /* ifndef FEATURE_FORCE_LOAD */ +# define IS_NOT_FORCED_AND +#endif /* def FEATURE_FORCE_LOAD */ + +#define IS_ENABLED_AND IS_TOGGLED_ON_AND IS_NOT_FORCED_AND + + char buf[BUFFER_SIZE]; + char *hdr; + char *p; + char *req; + fd_set rfds; + int n; + jb_socket maxfd; + int server_body; + int ms_iis5_hack = 0; + int byte_count = 0; + const struct forward_spec * fwd; + struct http_request *http; + int len; /* for buffer sizes */ +#ifdef FEATURE_KILL_POPUPS + int block_popups; /* bool, 1==will block popups */ + int block_popups_now = 0; /* bool, 1==currently blocking popups */ +#endif /* def FEATURE_KILL_POPUPS */ + + int pcrs_filter; /* bool, 1==will filter through pcrs */ + int gif_deanimate; /* bool, 1==will deanimate gifs */ + + /* Function that does the content filtering for the current request */ + char *(*content_filter)() = NULL; + + /* Skeleton for HTTP response, if we should intercept the request */ + struct http_response *rsp; + + http = csp->http; + + /* + * Read the client's request. Note that since we're not using select() we + * could get blocked here if a client connected, then didn't say anything! + */ + + for (;;) + { + len = read_socket(csp->cfd, buf, sizeof(buf)); + + if (len <= 0) break; /* error! */ + + /* + * If there is no memory left for buffering the + * request, there is nothing we can do but hang up + */ + if (add_to_iob(csp, buf, len)) + { + return; + } + + req = get_header(csp); + + if (req == NULL) + { + break; /* no HTTP request! */ + } + + if (*req == '\0') + { + continue; /* more to come! */ + } + +#ifdef FEATURE_FORCE_LOAD + /* If this request contains the FORCE_PREFIX, + * better get rid of it now and set the force flag --oes + */ + + if (strstr(req, FORCE_PREFIX)) + { + strclean(req, FORCE_PREFIX); + log_error(LOG_LEVEL_FORCE, "Enforcing request \"%s\".\n", req); + csp->flags |= CSP_FLAG_FORCED; + } + +#endif /* def FEATURE_FORCE_LOAD */ + + parse_http_request(req, http, csp); + freez(req); + break; + } + + if (http->cmd == NULL) + { + strcpy(buf, CHEADER); + write_socket(csp->cfd, buf, strlen(buf)); + + log_error(LOG_LEVEL_CLF, "%s - - [%T] \" \" 400 0", csp->ip_addr_str); + + return; + } + + /* decide how to route the HTTP request */ + + if ((fwd = forward_url(http, csp)) == NULL) + { + log_error(LOG_LEVEL_FATAL, "gateway spec is NULL!?!? This can't happen!"); + /* Never get here - LOG_LEVEL_FATAL causes program exit */ + } + + /* build the http request to send to the server + * we have to do one of the following: + * + * create = use the original HTTP request to create a new + * HTTP request that has either the path component + * without the http://domainspec (w/path) or the + * full orininal URL (w/url) + * Note that the path and/or the HTTP version may + * have been altered by now. + * + * connect = Open a socket to the host:port of the server + * and short-circuit server and client socket. + * + * pass = Pass the request unchanged if forwarding a CONNECT + * request to a parent proxy. Note that we'll be sending + * the CFAIL message ourselves if connecting to the parent + * fails, but we won't send a CSUCCEED message if it works, + * since that would result in a double message (ours and the + * parent's). After sending the request to the parent, we simply + * tunnel. + * + * here's the matrix: + * SSL + * 0 1 + * +--------+--------+ + * | | | + * 0 | create | connect| + * | w/path | | + * Forwarding +--------+--------+ + * | | | + * 1 | create | pass | + * | w/url | | + * +--------+--------+ + * + */ + + /* + * Determine the actions for this URL + */ +#ifdef FEATURE_TOGGLE + if (!(csp->flags & CSP_FLAG_TOGGLED_ON)) + { + /* Most compatible set of actions (i.e. none) */ + init_current_action(csp->action); + } + else +#endif /* ndef FEATURE_TOGGLE */ + { + url_actions(http, csp); + } + + + /* + * Check if a CONNECT request is allowable: + * In the absence of a +limit-connect action, allow only port 443. + * If there is an action, allow whatever matches the specificaton. + */ + if(http->ssl) + { + if( ( !(csp->action->flags & ACTION_LIMIT_CONNECT) && csp->http->port != 443) + || (csp->action->flags & ACTION_LIMIT_CONNECT + && !match_portlist(csp->action->string[ACTION_STRING_LIMIT_CONNECT], csp->http->port)) ) + { + strcpy(buf, CFORBIDDEN); + write_socket(csp->cfd, buf, strlen(buf)); + + log_error(LOG_LEVEL_CONNECT, "Denying suspicious CONNECT request from %s", csp->ip_addr_str); + log_error(LOG_LEVEL_CLF, "%s - - [%T] \" \" 403 0", csp->ip_addr_str); + + return; + } + } + + + /* + * Downgrade http version from 1.1 to 1.0 if +downgrade + * action applies + */ + if ( (http->ssl == 0) + && (!strcmpic(http->ver, "HTTP/1.1")) + && (csp->action->flags & ACTION_DOWNGRADE)) + { + freez(http->ver); + http->ver = strdup("HTTP/1.0"); + + if (http->ver == NULL) + { + log_error(LOG_LEVEL_FATAL, "Out of memory downgrading HTTP version"); + } + } + + /* + * Save a copy of the original request for logging + */ + http->ocmd = strdup(http->cmd); + + if (http->ocmd == NULL) + { + log_error(LOG_LEVEL_FATAL, "Out of memory copying HTTP request line"); + } + + /* + * (Re)build the HTTP request for non-SSL requests. + * If forwarding, use the whole URL, else, use only the path. + */ + if (http->ssl == 0) + { + freez(http->cmd); + + http->cmd = strdup(http->gpc); + string_append(&http->cmd, " "); + + if (fwd->forward_host) + { + string_append(&http->cmd, http->url); + } + else + { + string_append(&http->cmd, http->path); + } + + string_append(&http->cmd, " "); + string_append(&http->cmd, http->ver); + + if (http->cmd == NULL) + { + log_error(LOG_LEVEL_FATAL, "Out of memory rewiting SSL command"); + } + } + enlist(csp->headers, http->cmd); + + + /* + * If the user has not supplied any wafers, and the user has not + * told us to suppress the vanilla wafer, then send the vanilla wafer. + */ + if (list_is_empty(csp->action->multi[ACTION_MULTI_WAFER]) + && ((csp->action->flags & ACTION_VANILLA_WAFER) != 0)) + { + enlist(csp->action->multi[ACTION_MULTI_WAFER], VANILLA_WAFER); + } + + +#ifdef FEATURE_KILL_POPUPS + block_popups = ((csp->action->flags & ACTION_NO_POPUPS) != 0); +#endif /* def FEATURE_KILL_POPUPS */ + + pcrs_filter = (csp->rlist != NULL) && /* There are expressions to be used */ + (!list_is_empty(csp->action->multi[ACTION_MULTI_FILTER])); + + gif_deanimate = ((csp->action->flags & ACTION_DEANIMATE) != 0); + + /* grab the rest of the client's headers */ + + for (;;) + { + if ( ( ( p = get_header(csp) ) != NULL) && ( *p == '\0' ) ) + { + len = read_socket(csp->cfd, buf, sizeof(buf)); + if (len <= 0) + { + log_error(LOG_LEVEL_ERROR, "read from client failed: %E"); + return; + } + + /* + * If there is no memory left for buffering the + * request, there is nothing we can do but hang up + */ + if (add_to_iob(csp, buf, len)) + { + return; + } + continue; + } + + if (p == NULL) break; + + enlist(csp->headers, p); + freez(p); + } + /* + * We have a request. Now, check to see if we need to + * intercept it, i.e. If .. + */ + + if ( + /* a CGI call was detected and answered */ + (NULL != (rsp = dispatch_cgi(csp))) + + /* or we are enabled and... */ + || (IS_ENABLED_AND ( + + /* ..the request was blocked */ + ( NULL != (rsp = block_url(csp))) + + /* ..or untrusted */ +#ifdef FEATURE_TRUST + || ( NULL != (rsp = trust_url(csp))) +#endif /* def FEATURE_TRUST */ + + /* ..or a fast redirect kicked in */ +#ifdef FEATURE_FAST_REDIRECTS + || (((csp->action->flags & ACTION_FAST_REDIRECTS) != 0) && + (NULL != (rsp = redirect_url(csp)))) +#endif /* def FEATURE_FAST_REDIRECTS */ + )) + ) + { + /* Write the answer to the client */ + if (write_socket(csp->cfd, rsp->head, rsp->head_length) + || write_socket(csp->cfd, rsp->body, rsp->content_length)) + { + log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host); + } + +#ifdef FEATURE_STATISTICS + /* Count as a rejected request */ + csp->flags |= CSP_FLAG_REJECTED; +#endif /* def FEATURE_STATISTICS */ + + /* Log (FIXME: All intercept reasons apprear as "crunch" with Status 200) */ + log_error(LOG_LEVEL_GPC, "%s%s crunch!", http->hostport, http->path); + log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 3", csp->ip_addr_str, http->ocmd); + + /* Clean up and return */ + free_http_response(rsp); + return; + } + + log_error(LOG_LEVEL_GPC, "%s%s", http->hostport, http->path); + + if (fwd->forward_host) + { + log_error(LOG_LEVEL_CONNECT, "via %s:%d to: %s", + fwd->forward_host, fwd->forward_port, http->hostport); + } + else + { + log_error(LOG_LEVEL_CONNECT, "to %s", http->hostport); + } + + /* here we connect to the server, gateway, or the forwarder */ + + csp->sfd = forwarded_connect(fwd, http, csp); + + if (csp->sfd == JB_INVALID_SOCKET) + { + log_error(LOG_LEVEL_CONNECT, "connect to: %s failed: %E", + http->hostport); + + if (errno == EINVAL) + { + rsp = error_response(csp, "no-such-domain", errno); + + log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 404 0", + csp->ip_addr_str, http->ocmd); + } + else + { + rsp = error_response(csp, "connect-failed", errno); + + log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 503 0", + csp->ip_addr_str, http->ocmd); + } + + + /* Write the answer to the client */ + if(rsp) + { + if (write_socket(csp->cfd, rsp->head, rsp->head_length) + || write_socket(csp->cfd, rsp->body, rsp->content_length)) + { + log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host); + } + } + + free_http_response(rsp); + return; + } + + log_error(LOG_LEVEL_CONNECT, "OK"); + + hdr = sed(client_patterns, add_client_headers, csp); + if (hdr == NULL) + { + /* FIXME Should handle error properly */ + log_error(LOG_LEVEL_FATAL, "Out of memory parsing client header"); + } + + list_remove_all(csp->headers); + + if (fwd->forward_host || (http->ssl == 0)) + { + /* write the client's (modified) header to the server + * (along with anything else that may be in the buffer) + */ + + if (write_socket(csp->sfd, hdr, strlen(hdr)) + || (flush_socket(csp->sfd, csp) < 0)) + { + log_error(LOG_LEVEL_CONNECT, "write header to: %s failed: %E", + http->hostport); + + log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 503 0", + csp->ip_addr_str, http->ocmd); + + rsp = error_response(csp, "connect-failed", errno); + + if(rsp) + { + if (write_socket(csp->cfd, rsp->head, rsp->head_length) + || write_socket(csp->cfd, rsp->body, rsp->content_length)) + { + log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host); + } + } + + free_http_response(rsp); + freez(hdr); + return; + } + } + else + { + /* + * We're running an SSL tunnel and we're not forwarding, + * so just send the "connect succeeded" message to the + * client, flush the rest, and get out of the way. + */ + log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 2\n", + csp->ip_addr_str, http->ocmd); + + if (write_socket(csp->cfd, CSUCCEED, sizeof(CSUCCEED)-1)) + { + freez(hdr); + return; + } + IOB_RESET(csp); + } + + /* we're finished with the client's header */ + freez(hdr); + + maxfd = ( csp->cfd > csp->sfd ) ? csp->cfd : csp->sfd; + + /* pass data between the client and server + * until one or the other shuts down the connection. + */ + + server_body = 0; + + for (;;) + { +#ifdef __OS2__ + /* + * FD_ZERO here seems to point to an errant macro which crashes. + * So do this by hand for now... + */ + memset(&rfds,0x00,sizeof(fd_set)); +#else + FD_ZERO(&rfds); +#endif + FD_SET(csp->cfd, &rfds); + FD_SET(csp->sfd, &rfds); + + n = select((int)maxfd+1, &rfds, NULL, NULL, NULL); + + if (n < 0) + { + log_error(LOG_LEVEL_ERROR, "select() failed!: %E"); + return; + } + + /* this is the body of the browser's request + * just read it and write it. + */ + + if (FD_ISSET(csp->cfd, &rfds)) + { + len = read_socket(csp->cfd, buf, sizeof(buf)); + + if (len <= 0) + { + break; /* "game over, man" */ + } + + if (write_socket(csp->sfd, buf, (size_t)len)) + { + log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host); + return; + } + continue; + } + + /* + * The server wants to talk. It could be the header or the body. + * If `hdr' is null, then it's the header otherwise it's the body. + * FIXME: Does `hdr' really mean `host'? No. + */ + + + if (FD_ISSET(csp->sfd, &rfds)) + { + fflush( 0 ); + len = read_socket(csp->sfd, buf, sizeof(buf) - 1); + + if (len < 0) + { + log_error(LOG_LEVEL_ERROR, "read from: %s failed: %E", http->host); + + log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 503 0", + csp->ip_addr_str, http->ocmd); + + rsp = error_response(csp, "connect-failed", errno); + + if(rsp) + { + if (write_socket(csp->cfd, rsp->head, rsp->head_length) + || write_socket(csp->cfd, rsp->body, rsp->content_length)) + { + log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host); + } + } + + free_http_response(rsp); + return; + } + + /* Add a trailing zero. This lets filter_popups + * use string operations. + */ + buf[len] = '\0'; + +#ifdef FEATURE_KILL_POPUPS + /* Filter the popups on this read. */ + if (block_popups_now) + { + filter_popups(buf, csp); + } +#endif /* def FEATURE_KILL_POPUPS */ + + /* Normally, this would indicate that we've read + * as much as the server has sent us and we can + * close the client connection. However, Microsoft + * in its wisdom has released IIS/5 with a bug that + * prevents it from sending the trailing \r\n in + * a 302 redirect header (and possibly other headers). + * To work around this if we've haven't parsed + * a full header we'll append a trailing \r\n + * and see if this now generates a valid one. + * + * This hack shouldn't have any impacts. If we've + * already transmitted the header or if this is a + * SSL connection, then we won't bother with this + * hack. So we only work on partially received + * headers. If we append a \r\n and this still + * doesn't generate a valid header, then we won't + * transmit anything to the client. + */ + if (len == 0) + { + + if (server_body || http->ssl) + { + /* + * If we have been buffering up the document, + * now is the time to apply content modification + * and send the result to the client. + */ + if (content_filter) + { + /* + * If the content filter fails, use the original + * buffer and length. + * (see p != NULL ? p : csp->iob->cur below) + */ + if (NULL == (p = (*content_filter)(csp))) + { + csp->content_length = csp->iob->eod - csp->iob->cur; + } + + hdr = sed(server_patterns, add_server_headers, csp); + if (hdr == NULL) + { + /* FIXME Should handle error properly */ + log_error(LOG_LEVEL_FATAL, "Out of memory parsing server header"); + } + + if (write_socket(csp->cfd, hdr, strlen(hdr)) + || write_socket(csp->cfd, p != NULL ? p : csp->iob->cur, csp->content_length)) + { + log_error(LOG_LEVEL_ERROR, "write modified content to client failed: %E"); + return; + } + + freez(hdr); + if (NULL != p) { + freez(p); + } + } + + break; /* "game over, man" */ + } + + /* + * This is NOT the body, so + * Let's pretend the server just sent us a blank line. + */ + len = sprintf(buf, "\r\n"); + + /* + * Now, let the normal header parsing algorithm below do its + * job. If it fails, we'll exit instead of continuing. + */ + + ms_iis5_hack = 1; + } + + /* + * If this is an SSL connection or we're in the body + * of the server document, just write it to the client, + * unless we need to buffer the body for later content-filtering + */ + + if (server_body || http->ssl) + { + if (content_filter) + { + /* + * If there is no memory left for buffering the content, or the buffer limit + * has been reached, switch to non-filtering mode, i.e. make & write the + * header, flush the iob and buf, and get out of the way. + */ + if (add_to_iob(csp, buf, len)) + { + size_t hdrlen; + int flushed; + + log_error(LOG_LEVEL_ERROR, "Flushing header and buffers. Stepping back from filtering."); + + hdr = sed(server_patterns, add_server_headers, csp); + if (hdr == NULL) + { + /* + * Memory is too tight to even generate the header. + * Send our static "Out-of-memory" page. + */ + log_error(LOG_LEVEL_ERROR, "Out of memory while trying to flush."); + rsp = cgi_error_memory(); + + if (write_socket(csp->cfd, rsp->head, rsp->head_length) + || write_socket(csp->cfd, rsp->body, rsp->content_length)) + { + log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host); + } + return; + } + + hdrlen = strlen(hdr); + + if (write_socket(csp->cfd, hdr, hdrlen) + || ((flushed = flush_socket(csp->cfd, csp)) < 0) + || (write_socket(csp->cfd, buf, len))) + { + log_error(LOG_LEVEL_CONNECT, "Flush header and buffers to client failed: %E"); + + freez(hdr); + return; + } + + byte_count += hdrlen + flushed + len; + freez(hdr); + content_filter = NULL; + server_body = 1; + + } + } + else + { + if (write_socket(csp->cfd, buf, (size_t)len)) + { + log_error(LOG_LEVEL_ERROR, "write to client failed: %E"); + return; + } + } + byte_count += len; + continue; + } + else + { + /* we're still looking for the end of the + * server's header ... (does that make header + * parsing an "out of body experience" ? + */ + + /* + * buffer up the data we just read. If that fails, + * there's little we can do but send our static + * out-of-memory page. + */ + if (add_to_iob(csp, buf, len)) + { + log_error(LOG_LEVEL_ERROR, "Out of memory while looking for end of server headers."); + rsp = cgi_error_memory(); + + if (write_socket(csp->cfd, rsp->head, rsp->head_length) + || write_socket(csp->cfd, rsp->body, rsp->content_length)) + { + log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host); + } + return; + } + + /* get header lines from the iob */ + + while ((p = get_header(csp)) != NULL) + { + if (*p == '\0') + { + /* see following note */ + break; + } + enlist(csp->headers, p); + freez(p); + } + + /* NOTE: there are no "empty" headers so + * if the pointer `p' is not NULL we must + * assume that we reached the end of the + * buffer before we hit the end of the header. + */ + + if (p) + { + if (ms_iis5_hack) + { + /* Well, we tried our MS IIS/5 + * hack and it didn't work. + * The header is incomplete + * and there isn't anything + * we can do about it. + */ + break; + } + else + { + /* Since we have to wait for + * more from the server before + * we can parse the headers + * we just continue here. + */ + continue; + } + } + + /* we have now received the entire header. + * filter it and send the result to the client + */ + + hdr = sed(server_patterns, add_server_headers, csp); + if (hdr == NULL) + { + /* FIXME Should handle error properly */ + log_error(LOG_LEVEL_FATAL, "Out of memory parsing server header"); + } + +#ifdef FEATURE_KILL_POPUPS + /* Start blocking popups if appropriate. */ + + if ((csp->content_type & CT_TEXT) && /* It's a text / * MIME-Type */ + !http->ssl && /* We talk plaintext */ + block_popups) /* Policy allows */ + { + block_popups_now = 1; + /* + * Filter the part of the body that came in the same read + * as the last headers: + */ + filter_popups(csp->iob->cur, csp); + } + +#endif /* def FEATURE_KILL_POPUPS */ + + /* Buffer and pcrs filter this if appropriate. */ + + if ((csp->content_type & CT_TEXT) && /* It's a text / * MIME-Type */ + !http->ssl && /* We talk plaintext */ + pcrs_filter) /* Policy allows */ + { + content_filter = pcrs_filter_response; + } + + /* Buffer and gif_deanimate this if appropriate. */ + + if ((csp->content_type & CT_GIF) && /* It's a image/gif MIME-Type */ + !http->ssl && /* We talk plaintext */ + gif_deanimate) /* Policy allows */ + { + content_filter = gif_deanimate_response; + } + + /* + * Only write if we're not buffering for content modification + */ + if (!content_filter) + { + /* write the server's (modified) header to + * the client (along with anything else that + * may be in the buffer) + */ + + if (write_socket(csp->cfd, hdr, strlen(hdr)) + || ((len = flush_socket(csp->cfd, csp)) < 0)) + { + log_error(LOG_LEVEL_CONNECT, "write header to client failed: %E"); + + /* the write failed, so don't bother + * mentioning it to the client... + * it probably can't hear us anyway. + */ + freez(hdr); + return; + } + + byte_count += len; + } + + /* we're finished with the server's header */ + + freez(hdr); + server_body = 1; + + /* If this was a MS IIS/5 hack then it means + * the server has already closed the + * connection. Nothing more to read. Time + * to bail. + */ + if (ms_iis5_hack) + { + break; + } + } + continue; + } + + return; /* huh? we should never get here */ + } + + log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 %d", + csp->ip_addr_str, http->ocmd, byte_count); +} + + +/********************************************************************* + * + * Function : serve + * + * Description : This is little more than chat. We only "serve" to + * to close any socket that chat may have opened. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : N/A + * + *********************************************************************/ +#ifdef AMIGA +void serve(struct client_state *csp) +#else /* ifndef AMIGA */ +static void serve(struct client_state *csp) +#endif /* def AMIGA */ +{ + chat(csp); + close_socket(csp->cfd); + + if (csp->sfd != JB_INVALID_SOCKET) + { + close_socket(csp->sfd); + } + + csp->flags &= ~CSP_FLAG_ACTIVE; + +} + + +#ifdef __BEOS__ +/********************************************************************* + * + * Function : server_thread + * + * Description : We only exist to call `serve' in a threaded environment. + * + * Parameters : + * 1 : data = Current client state (buffers, headers, etc...) + * + * Returns : Always 0. + * + *********************************************************************/ +static int32 server_thread(void *data) +{ + serve((struct client_state *) data); + return 0; + +} +#endif + + +/********************************************************************* + * + * Function : usage + * + * Description : Print usage info & exit. + * + * Parameters : Pointer to argv[0] for identifying ourselves + * + * Returns : No. ,-) + * + *********************************************************************/ +void usage(const char *myname) +{ + printf("Privoxy version " VERSION " (" HOME_PAGE_URL ")\n" + "Usage: %s [--help] [--version] [--no-daemon] [--pidfile pidfile] [--user user[.group]] [configfile]\n" + "Aborting.\n", myname); + + exit(2); + +} + + +/********************************************************************* + * + * Function : main + * + * Description : Load the config file and start the listen loop. + * This function is a lot more *sane* with the `load_config' + * and `listen_loop' functions; although it stills does + * a *little* too much for my taste. + * + * Parameters : + * 1 : argc = Number of parameters (including $0). + * 2 : argv = Array of (char *)'s to the parameters. + * + * Returns : 1 if : can't open config file, unrecognized directive, + * stats requested in multi-thread mode, can't open the + * log file, can't open the jar file, listen port is invalid, + * any load fails, and can't bind port. + * + * Else main never returns, the process must be signaled + * to terminate execution. Or, on Windows, use the + * "File", "Exit" menu option. + * + *********************************************************************/ +#ifdef __MINGW32__ +int real_main(int argc, const char *argv[]) +#else +int main(int argc, const char *argv[]) +#endif +{ + int argc_pos = 0; +#ifdef unix + struct passwd *pw = NULL; + struct group *grp = NULL; + char *p; +#endif + + Argc = argc; + Argv = argv; + + configfile = +#if !defined(_WIN32) + "config" +#else + "config.txt" +#endif + ; + + /* + * Parse the command line arguments + */ + while (++argc_pos < argc) + { +#if !defined(_WIN32) || defined(_WIN_CONSOLE) + + if (strcmp(argv[argc_pos], "--help") == 0) + { + usage(argv[0]); + } + + else if(strcmp(argv[argc_pos], "--version") == 0) + { + printf("Privoxy version " VERSION " (" HOME_PAGE_URL ")\n"); + exit(0); + } + + else if (strcmp(argv[argc_pos], "--no-daemon" ) == 0) + { + no_daemon = 1; + } +#if defined(unix) + else if (strcmp(argv[argc_pos], "--pidfile" ) == 0) + { + if (++argc_pos == argc) usage(argv[0]); + pidfile = strdup(argv[argc_pos]); + } + + else if (strcmp(argv[argc_pos], "--user" ) == 0) + { + if (++argc_pos == argc) usage(argv[argc_pos]); + + if ((NULL != (p = strchr(argv[argc_pos], '.'))) && *(p + 1) != '0') + { + *p++ = '\0'; + if (NULL == (grp = getgrnam(p))) + { + log_error(LOG_LEVEL_FATAL, "Group %s not found.", p); + } + } + + if (NULL == (pw = getpwnam(argv[argc_pos]))) + { + log_error(LOG_LEVEL_FATAL, "User %s not found.", argv[argc_pos]); + } + + if (p != NULL) *--p = '\0'; + } +#endif /* defined(unix) */ + else +#endif /* defined(_WIN32) && !defined(_WIN_CONSOLE) */ + { + configfile = argv[argc_pos]; + } + + } /* -END- while (more arguments) */ + +#if defined(unix) + if ( *configfile != '/' ) + { + char *abs_file; + + /* make config-filename absolute here */ + if ( !(basedir = getcwd( NULL, 1024 ))) + { + perror("get working dir failed"); + exit( 1 ); + } + + if ( !(abs_file = malloc( strlen( basedir ) + strlen( configfile ) + 5 ))) + { + perror("malloc failed"); + exit( 1 ); + } + strcpy( abs_file, basedir ); + strcat( abs_file, "/" ); + strcat( abs_file, configfile ); + configfile = abs_file; + } +#endif /* defined unix */ + + + files->next = NULL; + +#ifdef AMIGA + InitAmiga(); +#elif defined(_WIN32) + InitWin32(); +#endif + + /* + * Unix signal handling + * + * Catch the abort, interrupt and terminate signals for a graceful exit + * Catch the hangup signal so the errlog can be reopened. + * Ignore the broken pipe and child signals + * FIXME: Isn't ignoring the default for SIGCHLD anyway and why ignore SIGPIPE? + */ +#if !defined(_WIN32) && !defined(__OS2__) && !defined(AMIGA) +{ + int idx; + const int catched_signals[] = { SIGABRT, SIGTERM, SIGINT, SIGHUP, 0 }; + const int ignored_signals[] = { SIGPIPE, SIGCHLD, 0 }; + + for (idx = 0; catched_signals[idx] != 0; idx++) + { + if (signal(catched_signals[idx], sig_handler) == SIG_ERR) + { + log_error(LOG_LEVEL_FATAL, "Can't set signal-handler for signal %d: %E", catched_signals[idx]); + } + } + + for (idx = 0; ignored_signals[idx] != 0; idx++) + { + if (signal(ignored_signals[idx], SIG_IGN) == SIG_ERR) + { + log_error(LOG_LEVEL_FATAL, "Can't set ignore-handler for signal %d: %E", ignored_signals[idx]); + } + } + +} +#else /* ifdef _WIN32 */ +# ifdef _WIN_CONSOLE + /* + * We *are* in a windows console app. + * Print a verbose messages about FAQ's and such + */ + printf(win32_blurb); +# endif /* def _WIN_CONSOLE */ +#endif /* def _WIN32 */ + + + /* Initialize the CGI subsystem */ + cgi_init_error_messages(); + + /* + * If runnig on unix and without the --nodaemon + * option, become a daemon. I.e. fork, detach + * from tty and get process group leadership + */ +#if defined(unix) +{ + pid_t pid = 0; +#if 0 + int fd; +#endif + + if (!no_daemon) + { + pid = fork(); + + if ( pid < 0 ) /* error */ + { + perror("fork"); + exit( 3 ); + } + else if ( pid != 0 ) /* parent */ + { + int status; + pid_t wpid; + /* + * must check for errors + * child died due to missing files aso + */ + sleep( 1 ); + wpid = waitpid( pid, &status, WNOHANG ); + if ( wpid != 0 ) + { + exit( 1 ); + } + exit( 0 ); + } + /* child */ +#if 1 + /* Should be more portable, but not as well tested */ + setsid(); +#else /* !1 */ +#ifdef __FreeBSD__ + setpgrp(0,0); +#else /* ndef __FreeBSD__ */ + setpgrp(); +#endif /* ndef __FreeBSD__ */ + fd = open("/dev/tty", O_RDONLY); + if ( fd ) + { + /* no error check here */ + ioctl( fd, TIOCNOTTY,0 ); + close ( fd ); + } +#endif /* 1 */ + /* FIXME: should close stderr (fd 2) here too, but the test + * for existence + * and load config file is done in listen_loop() and puts + * some messages on stderr there. + */ + + close( 0 ); + close( 1 ); + chdir("/"); + + } /* -END- if (!no_daemon) */ + + /* + * As soon as we have written the PID file, we can switch + * to the user and group ID indicated by the --user option + */ + write_pid_file(); + + if (NULL != pw) + { + if (((NULL != grp) && setgid(grp->gr_gid)) || (setgid(pw->pw_gid))) + { + log_error(LOG_LEVEL_FATAL, "Cannot setgid(): Insufficient permissions."); + } + if (setuid(pw->pw_uid)) + { + log_error(LOG_LEVEL_FATAL, "Cannot setuid(): Insufficient permissions."); + } + } +} +#endif /* defined unix */ + + listen_loop(); + + /* NOTREACHED */ + return(-1); + +} + + +/********************************************************************* + * + * Function : bind_port_helper + * + * Description : Bind the listen port. Handles logging, and aborts + * on failure. + * + * Parameters : + * 1 : config = Privoxy configuration. Specifies port + * to bind to. + * + * Returns : Port that was opened. + * + *********************************************************************/ +static jb_socket bind_port_helper(struct configuration_spec * config) +{ + int result; + jb_socket bfd; + + if ( (config->haddr != NULL) + && (config->haddr[0] == '1') + && (config->haddr[1] == '2') + && (config->haddr[2] == '7') + && (config->haddr[3] == '.') ) + { + log_error(LOG_LEVEL_INFO, "Listening on port %d for local connections only", + config->hport); + } + else if (config->haddr == NULL) + { + log_error(LOG_LEVEL_INFO, "Listening on port %d on all IP addresses", + config->hport); + } + else + { + log_error(LOG_LEVEL_INFO, "Listening on port %d on IP address %s", + config->hport, config->haddr); + } + + result = bind_port(config->haddr, config->hport, &bfd); + + if (result < 0) + { + switch(result) + { + case -3 : + log_error(LOG_LEVEL_FATAL, "can't bind to %s:%d: " + "There may be another Privoxy or some other " + "proxy running on port %d", + (NULL != config->haddr) ? config->haddr : "INADDR_ANY", + config->hport, config->hport); + + case -2 : + log_error(LOG_LEVEL_FATAL, "can't bind to %s:%d: " + "The hostname is not resolvable", + (NULL != config->haddr) ? config->haddr : "INADDR_ANY", config->hport); + + default : + log_error(LOG_LEVEL_FATAL, "can't bind to %s:%d: because %E", + (NULL != config->haddr) ? config->haddr : "INADDR_ANY", config->hport); + } + + /* shouldn't get here */ + return JB_INVALID_SOCKET; + } + + config->need_bind = 0; + + return bfd; +} + + +/********************************************************************* + * + * Function : listen_loop + * + * Description : bind the listen port and enter a "FOREVER" listening loop. + * + * Parameters : N/A + * + * Returns : Never. + * + *********************************************************************/ +static void listen_loop(void) +{ + struct client_state *csp = NULL; + jb_socket bfd; + struct configuration_spec * config; + + config = load_config(); + + bfd = bind_port_helper(config); + +#ifdef FEATURE_GRACEFUL_TERMINATION + while (!g_terminate) +#else + for (;;) +#endif + { +#if !defined(FEATURE_PTHREAD) && !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) + while (waitpid(-1, NULL, WNOHANG) > 0) + { + /* zombie children */ + } +#endif /* !defined(FEATURE_PTHREAD) && !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) */ + + /* + * Free data that was used by died threads + */ + sweep(); + +#if defined(unix) + /* + * Re-open the errlog after HUP signal + */ + if (received_hup_signal) + { + init_error_log(Argv[0], config->logfile, config->debug); + received_hup_signal = 0; + } +#endif + +#ifdef __OS2__ +#ifdef FEATURE_COOKIE_JAR + /* + * Need a workaround here: we have to fclose() the jarfile, or we die because it's + * already open. I think unload_configfile() is not being run, which should do + * this work. Until that can get resolved, we'll use this workaround. + */ + if (csp) + if(csp->config) + if (csp->config->jar) + { + fclose(csp->config->jar); + csp->config->jar = NULL; + } +#endif /* FEATURE_COOKIE_JAR */ +#endif /* __OS2__ */ + + if ( NULL == (csp = (struct client_state *) zalloc(sizeof(*csp))) ) + { + log_error(LOG_LEVEL_FATAL, "malloc(%d) for csp failed: %E", sizeof(*csp)); + continue; + } + + csp->flags |= CSP_FLAG_ACTIVE; + csp->sfd = JB_INVALID_SOCKET; + + csp->config = config = load_config(); + + if ( config->need_bind ) + { + /* + * Since we were listening to the "old port", we will not see + * a "listen" param change until the next IJB request. So, at + * least 1 more request must be made for us to find the new + * setting. I am simply closing the old socket and binding the + * new one. + * + * Which-ever is correct, we will serve 1 more page via the + * old settings. This should probably be a "show-proxy-args" + * request. This should not be a so common of an operation + * that this will hurt people's feelings. + */ + + close_socket(bfd); + + bfd = bind_port_helper(config); + } + + log_error(LOG_LEVEL_CONNECT, "accept connection ... "); + + if (!accept_connection(csp, bfd)) + { + log_error(LOG_LEVEL_CONNECT, "accept failed: %E"); + +#ifdef AMIGA + if(!childs) + { + exit(1); + } +#endif + freez(csp); + continue; + } + else + { + log_error(LOG_LEVEL_CONNECT, "OK"); + } + +#ifdef FEATURE_TOGGLE + if (g_bToggleIJB) + { + csp->flags |= CSP_FLAG_TOGGLED_ON; + } +#endif /* def FEATURE_TOGGLE */ + + if (run_loader(csp)) + { + log_error(LOG_LEVEL_FATAL, "a loader failed - must exit"); + /* Never get here - LOG_LEVEL_FATAL causes program exit */ + } + +#ifdef FEATURE_ACL + if (block_acl(NULL,csp)) + { + log_error(LOG_LEVEL_CONNECT, "Connection dropped due to ACL"); + close_socket(csp->cfd); + freez(csp); + continue; + } +#endif /* def FEATURE_ACL */ + + /* add it to the list of clients */ + csp->next = clients->next; + clients->next = csp; + + if (config->multi_threaded) + { + int child_id; + +/* this is a switch () statment in the C preprocessor - ugh */ +#undef SELECTED_ONE_OPTION + +/* Use Pthreads in preference to native code */ +#if defined(FEATURE_PTHREAD) && !defined(SELECTED_ONE_OPTION) +#define SELECTED_ONE_OPTION + { + pthread_t the_thread; + pthread_attr_t attrs; + + pthread_attr_init(&attrs); + pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED); + child_id = (pthread_create(&the_thread, &attrs, + (void*)serve, csp) ? -1 : 0); + pthread_attr_destroy(&attrs); + } +#endif + +#if defined(_WIN32) && !defined(_CYGWIN) && !defined(SELECTED_ONE_OPTION) +#define SELECTED_ONE_OPTION + child_id = _beginthread( + (void (*)(void *))serve, + 64 * 1024, + csp); +#endif + +#if defined(__OS2__) && !defined(SELECTED_ONE_OPTION) +#define SELECTED_ONE_OPTION + child_id = _beginthread( + (void(* _Optlink)(void*))serve, + NULL, + 64 * 1024, + csp); +#endif + +#if defined(__BEOS__) && !defined(SELECTED_ONE_OPTION) +#define SELECTED_ONE_OPTION + { + thread_id tid = spawn_thread + (server_thread, "server", B_NORMAL_PRIORITY, csp); + + if ((tid >= 0) && (resume_thread(tid) == B_OK)) + { + child_id = (int) tid; + } + else + { + child_id = -1; + } + } +#endif + +#if defined(AMIGA) && !defined(SELECTED_ONE_OPTION) +#define SELECTED_ONE_OPTION + csp->cfd = ReleaseSocket(csp->cfd, -1); + if((child_id = (int)CreateNewProcTags( + NP_Entry, (ULONG)server_thread, + NP_Output, Output(), + NP_CloseOutput, FALSE, + NP_Name, (ULONG)"privoxy child", + NP_StackSize, 200*1024, + TAG_DONE))) + { + childs++; + ((struct Task *)child_id)->tc_UserData = csp; + Signal((struct Task *)child_id, SIGF_SINGLE); + Wait(SIGF_SINGLE); + } +#endif + +#if !defined(SELECTED_ONE_OPTION) + child_id = fork(); + + /* This block is only needed when using fork(). + * When using threads, the server thread was + * created and run by the call to _beginthread(). + */ + if (child_id == 0) /* child */ + { + serve(csp); + _exit(0); + + } + else if (child_id > 0) /* parent */ + { + /* in a fork()'d environment, the parent's + * copy of the client socket and the CSP + * are not used. + */ + +#if !defined(_WIN32) && defined(__CYGWIN__) + wait( NULL ); +#endif /* !defined(_WIN32) && defined(__CYGWIN__) */ + close_socket(csp->cfd); + csp->flags &= ~CSP_FLAG_ACTIVE; + } +#endif + +#undef SELECTED_ONE_OPTION +/* end of cpp switch () */ + + if (child_id < 0) /* failed */ + { + char buf[BUFFER_SIZE]; + + log_error(LOG_LEVEL_ERROR, "can't fork: %E"); + + sprintf(buf , "Privoxy: can't fork: errno = %d", errno); + + write_socket(csp->cfd, buf, strlen(buf)); + close_socket(csp->cfd); + csp->flags &= ~CSP_FLAG_ACTIVE; + sleep(5); + continue; + } + } + else + { + serve(csp); + } + } + + /* NOTREACHED unless FEATURE_GRACEFUL_TERMINATION is defined */ + + /* Clean up. Aim: free all memory (no leaks) */ +#ifdef FEATURE_GRACEFUL_TERMINATION + + log_error(LOG_LEVEL_ERROR, "Graceful termination requested"); + + unload_current_config_file(); + unload_current_actions_file(); + unload_current_re_filterfile(); +#ifdef FEATURE_TRUST + unload_current_trust_file(); +#endif + + if (config->multi_threaded) + { + int i = 60; + do + { + sleep(1); + sweep(); + } while ((clients->next != NULL) && (--i > 0)); + + if (i <= 0) + { + log_error(LOG_LEVEL_ERROR, "Graceful termination failed - still some live clients after 1 minute wait."); + } + } + sweep(); + sweep(); + +#if defined(unix) + free(basedir); +#endif +#if defined(_WIN32) && !defined(_WIN_CONSOLE) + /* Cleanup - remove taskbar icon etc. */ + TermLogWindow(); +#endif + + exit(0); +#endif /* FEATURE_GRACEFUL_TERMINATION */ + +} + + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/jcc.h b/src/jcc.h new file mode 100644 index 00000000..9cdab39a --- /dev/null +++ b/src/jcc.h @@ -0,0 +1,143 @@ +#ifndef JCC_H_INCLUDED +#define JCC_H_INCLUDED +#define JCC_H_VERSION "$Id: jcc.h,v 1.12 2002/03/26 22:29:55 swa Exp $" +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/jcc.h,v $ + * + * Purpose : Main file. Contains main() method, main loop, and + * the main connection-handling function. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: jcc.h,v $ + * Revision 1.12 2002/03/26 22:29:55 swa + * we have a new homepage! + * + * Revision 1.11 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.10 2002/03/16 23:54:06 jongfoster + * Adding graceful termination feature, to help look for memory leaks. + * If you enable this (which, by design, has to be done by hand + * editing config.h) and then go to http://i.j.b/die, then the program + * will exit cleanly after the *next* request. It should free all the + * memory that was used. + * + * Revision 1.9 2002/03/07 03:52:44 oes + * Set logging to tty for --no-daemon mode + * + * Revision 1.8 2002/03/04 18:19:49 oes + * Added extern const char *pidfile + * + * Revision 1.7 2001/11/05 21:41:43 steudten + * Add changes to be a real daemon just for unix os. + * (change cwd to /, detach from controlling tty, set + * process group and session leader to the own process. + * Add DBG() Macro. + * Add some fatal-error log message for failed malloc(). + * Add '-d' if compiled with 'configure --with-debug' to + * enable debug output. + * + * Revision 1.6 2001/07/30 22:08:36 jongfoster + * Tidying up #defines: + * - All feature #defines are now of the form FEATURE_xxx + * - Permanently turned off WIN_GUI_EDIT + * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS + * + * Revision 1.5 2001/07/29 19:32:00 jongfoster + * Renaming _main() [mingw32 only] to real_main(), for ANSI compliance. + * + * Revision 1.4 2001/07/29 18:58:15 jongfoster + * Removing nested #includes, adding forward declarations for needed + * structures, and changing the #define _FILENAME_H to FILENAME_H_INCLUDED. + * + * Revision 1.3 2001/07/18 12:31:58 oes + * moved #define freez from jcc.h to project.h + * + * Revision 1.2 2001/05/31 21:24:47 jongfoster + * Changed "permission" to "action" throughout. + * Removed DEFAULT_USER_AGENT - it must now be specified manually. + * Moved vanilla wafer check into chat(), since we must now + * decide whether or not to add it based on the URL. + * + * Revision 1.1.1.1 2001/05/15 13:58:56 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#ifdef __cplusplus +extern "C" { +#endif + +struct client_state; +struct file_list; + +/* Global variables */ + +#ifdef FEATURE_STATISTICS +extern int urls_read; +extern int urls_rejected; +#endif /*def FEATURE_STATISTICS*/ + +extern struct client_state clients[]; +extern struct file_list files[]; + +#ifdef unix +extern const char *pidfile; +#endif +extern int no_daemon; + +#ifdef FEATURE_GRACEFUL_TERMINATION +extern int g_terminate; +#endif + +/* Functions */ + +#ifdef __MINGW32__ +int real_main(int argc, const char *argv[]); +#else +int main(int argc, const char *argv[]); +#endif + +/* Revision control strings from this header and associated .c file */ +extern const char jcc_rcs[]; +extern const char jcc_h_rcs[]; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ndef JCC_H_INCLUDED */ + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/killpopup.c b/src/killpopup.c new file mode 100644 index 00000000..bc239a4a --- /dev/null +++ b/src/killpopup.c @@ -0,0 +1,216 @@ +const char killpopup_rcs[] = "$Id: killpopup.c,v 1.16 2002/03/26 22:29:55 swa Exp $"; +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/killpopup.c,v $ + * + * Purpose : Handles the filtering of popups. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: killpopup.c,v $ + * Revision 1.16 2002/03/26 22:29:55 swa + * we have a new homepage! + * + * Revision 1.15 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.14 2002/03/07 03:46:53 oes + * Fixed compiler warnings etc + * + * Revision 1.13 2001/11/13 00:16:40 jongfoster + * Replacing references to malloc.h with the standard stdlib.h + * (See ANSI or K&R 2nd Ed) + * + * Revision 1.12 2001/10/25 03:40:48 david__schmidt + * Change in porting tactics: OS/2's EMX porting layer doesn't allow multiple + * threads to call select() simultaneously. So, it's time to do a real, live, + * native OS/2 port. See defines for __EMX__ (the porting layer) vs. __OS2__ + * (native). Both versions will work, but using __OS2__ offers multi-threading. + * + * Revision 1.11 2001/10/07 15:42:41 oes + * filter_popups now gets a csp pointer so it can raise the new + * CSP_FLAG_MODIFIED flag. + * + * Revision 1.10 2001/09/22 16:34:44 jongfoster + * Removing unneeded #includes + * + * Revision 1.9 2001/07/31 14:44:22 oes + * Deleted unused size parameter from filter_popups() + * + * Revision 1.8 2001/07/30 22:08:36 jongfoster + * Tidying up #defines: + * - All feature #defines are now of the form FEATURE_xxx + * - Permanently turned off WIN_GUI_EDIT + * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS + * + * Revision 1.7 2001/07/20 19:29:25 haroon + * - In v1.5 forgot to add that I implemented LOG_LEVEL_POPUPS in errlog.c, + * errlog.h and killpopup.c. In that case, it is superfluous to have define for + * POPUP_VERBOSE, so I removed the defines and logging is now done + * via log_error(LOG_LEVEL_POPUPS, ....) + * + * Revision 1.6 2001/07/19 19:11:35 haroon + * - Implemented Guy's idea of replacing window.open( with 1;''.concat( + * - Implemented Guy's idea of replacing .resizeTo( with .scrollTo( + * + * Revision 1.5 2001/07/18 15:02:52 haroon + * improved nuking of window.open + * + * Revision 1.4 2001/06/29 13:29:55 oes + * Added FIXMEs (and didn't repair, hehe) + * + * Revision 1.3 2001/05/22 18:56:28 oes + * CRLF -> LF + * + * Revision 1.2 2001/05/20 01:21:20 jongfoster + * Version 2.9.4 checkin. + * - Merged popupfile and cookiefile, and added control over PCRS + * filtering, in new "permissionsfile". + * - Implemented LOG_LEVEL_FATAL, so that if there is a configuration + * file error you now get a message box (in the Win32 GUI) rather + * than the program exiting with no explanation. + * - Made killpopup use the PCRS MIME-type checking and HTTP-header + * skipping. + * - Removed tabs from "config" + * - Moved duplicated url parsing code in "loaders.c" to a new funcition. + * - Bumped up version number. + * + * Revision 1.1.1.1 2001/05/15 13:58:58 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#if !defined(_WIN32) && !defined(__OS2__) +#include +#endif + +#include "project.h" +#include "killpopup.h" +#include "errlog.h" + +const char killpopup_h_rcs[] = KILLPOPUP_H_VERSION; + +#ifdef FEATURE_KILL_POPUPS + +/********************************************************************* + * + * Function : filter_popups + * + * Description : Filter the block of data that's been read from the server + * for javascript popup code and replace by syntactically + * neutral code of the same size. + * Raise the CSP_FLAG_MODIFIED flag on success. + * + * Parameters : + * 1 : buff = Buffer to scan and modify. Null terminated. + * 2 : csp = Client state pointer + * + * Returns : void + * + *********************************************************************/ +void filter_popups(char *buff, struct client_state *csp) +{ + char *start_p = NULL; + char *close_p = NULL; + char *p = NULL; + + /* + * replace the window.open( with a harmless JavaScript replacement + * (notice the two single quotes) + */ + while ((start_p = strstr(buff, "window.open(")) != NULL) + { + if (start_p) + { + strncpy(start_p, "1;''.concat(", 12); + log_error(LOG_LEVEL_POPUPS, "Blocked popup window open"); + csp->flags |= CSP_FLAG_MODIFIED; + } + } + + /* + * replace the .resizeTo( with a harmless JavaScript replacement + */ + while ((start_p = strstr(buff, ".resizeTo(")) != NULL) + { + if (start_p) + { + strncpy(start_p, ".scrollTo(", 10); + log_error(LOG_LEVEL_POPUPS, "Blocked popup window resize"); + csp->flags |= CSP_FLAG_MODIFIED; + } + } + + /* + * Filter onUnload and onExit + */ + start_p = strstr(buff, "'); + if (close_p) + { + /* we are now between */ + p = strstr(start_p, "onUnload"); + if (p) + { + strncpy(p, "_nU_", 4); + csp->flags |= CSP_FLAG_MODIFIED; + } + p = strstr(start_p, "onExit"); + if (p) + { + strncpy(p, "_nE_", 4); + csp->flags |= CSP_FLAG_MODIFIED; + } + } + } + +} + +#endif /* def FEATURE_KILL_POPUPS */ + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/killpopup.h b/src/killpopup.h new file mode 100644 index 00000000..76f62c4b --- /dev/null +++ b/src/killpopup.h @@ -0,0 +1,106 @@ +#ifndef KILLPOPUP_H_INCLUDED +#define KILLPOPUP_H_INCLUDED +#define KILLPOPUP_H_VERSION "$Id: killpopup.h,v 1.8 2002/03/26 22:29:55 swa Exp $" +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/killpopup.h,v $ + * + * Purpose : Handles the filtering of popups. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: killpopup.h,v $ + * Revision 1.8 2002/03/26 22:29:55 swa + * we have a new homepage! + * + * Revision 1.7 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.6 2001/10/07 15:42:41 oes + * filter_popups now gets a csp pointer so it can raise the new + * CSP_FLAG_MODIFIED flag. + * + * Revision 1.5 2001/07/31 14:44:22 oes + * Deleted unused size parameter from filter_popups() + * + * Revision 1.4 2001/07/30 22:08:36 jongfoster + * Tidying up #defines: + * - All feature #defines are now of the form FEATURE_xxx + * - Permanently turned off WIN_GUI_EDIT + * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS + * + * Revision 1.3 2001/07/29 18:59:21 jongfoster + * - Changing #define _KILLPOPUP_H to KILLPOPUP_H_INCLUDED + * - Adding extern "C" {} + * + * Revision 1.2 2001/05/20 01:21:20 jongfoster + * Version 2.9.4 checkin. + * - Merged popupfile and cookiefile, and added control over PCRS + * filtering, in new "permissionsfile". + * - Implemented LOG_LEVEL_FATAL, so that if there is a configuration + * file error you now get a message box (in the Win32 GUI) rather + * than the program exiting with no explanation. + * - Made killpopup use the PCRS MIME-type checking and HTTP-header + * skipping. + * - Removed tabs from "config" + * - Moved duplicated url parsing code in "loaders.c" to a new funcition. + * - Bumped up version number. + * + * Revision 1.1.1.1 2001/05/15 13:58:58 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#include "project.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef FEATURE_KILL_POPUPS + +extern void filter_popups(char *buff, struct client_state *csp); + +#endif /* def FEATURE_KILL_POPUPS */ + +/* Revision control strings from this header and associated .c file */ +extern const char killpopup_rcs[]; +extern const char killpopup_h_rcs[]; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ndef KILLPOPUP_H_INCLUDED */ + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/list.c b/src/list.c new file mode 100644 index 00000000..c8556d4c --- /dev/null +++ b/src/list.c @@ -0,0 +1,1067 @@ +const char list_rcs[] = "$Id: list.c,v 1.15 2002/03/26 22:29:55 swa Exp $"; +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/list.c,v $ + * + * Purpose : Declares functions to handle lists. + * Functions declared include: + * `destroy_list', `enlist' and `list_to_text' + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: list.c,v $ + * Revision 1.15 2002/03/26 22:29:55 swa + * we have a new homepage! + * + * Revision 1.14 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.13 2002/03/07 03:46:17 oes + * Fixed compiler warnings + * + * Revision 1.12 2001/10/25 03:40:48 david__schmidt + * Change in porting tactics: OS/2's EMX porting layer doesn't allow multiple + * threads to call select() simultaneously. So, it's time to do a real, live, + * native OS/2 port. See defines for __EMX__ (the porting layer) vs. __OS2__ + * (native). Both versions will work, but using __OS2__ offers multi-threading. + * + * Revision 1.11 2001/10/23 21:21:03 jongfoster + * New error handling - error codes are now jb_errs, not ints. + * Changed the way map() handles out-of-memory, to dramatically + * reduce the amount of error-checking clutter needed. + * + * Revision 1.10 2001/09/16 17:30:24 jongfoster + * Fixing a compiler warning. + * + * Revision 1.9 2001/09/16 13:20:29 jongfoster + * Rewrite of list library. Now has seperate header and list_entry + * structures. Also added a large sprinking of assert()s to the list + * code. + * + * Revision 1.8 2001/08/07 14:00:20 oes + * Fixed comment + * + * Revision 1.7 2001/08/05 16:06:20 jongfoster + * Modifiying "struct map" so that there are now separate header and + * "map_entry" structures. This means that functions which modify a + * map no longer need to return a pointer to the modified map. + * Also, it no longer reverses the order of the entries (which may be + * important with some advanced template substitutions). + * + * Revision 1.6 2001/07/31 14:44:51 oes + * list_to_text() now appends empty line at end + * + * Revision 1.5 2001/06/29 21:45:41 oes + * Indentation, CRLF->LF, Tab-> Space + * + * Revision 1.4 2001/06/29 13:30:22 oes + * - Added Convenience function enlist_unique_header(), + * which takes the Header name and value as separate + * arguments and thus saves the pain of sprintf()ing + * and determining the Header name length to enlist_unique + * - Improved comments + * - Removed logentry from cancelled commit + * + * Revision 1.3 2001/06/03 19:12:24 oes + * functions for new struct map, extended enlist_unique + * + * Revision 1.2 2001/06/01 18:49:17 jongfoster + * Replaced "list_share" with "list" - the tiny memory gain was not + * worth the extra complexity. + * + * Revision 1.1 2001/05/31 21:11:53 jongfoster + * - Moved linked list support to new "list.c" file. + * Structure definitions are still in project.h, + * function prototypes are now in "list.h". + * - Added support for "struct list_share", which is identical + * to "struct list" except it saves memory by not duplicating + * the strings. Obviously, this only works if there is some + * other way of managing the memory used by the strings. + * (These list_share lists are used for lists which last + * for only 1 request, and where all the list entries are + * just coming directly from entries in the actionsfile.) + * Note that you still need to destroy list_share lists + * properly to free the nodes - it's only the strings + * which are shared. + * + * + *********************************************************************/ + + +#include "config.h" + +#ifndef _WIN32 +/* FIXME: The following headers are not needed for Win32. Are they + * needed on other platforms? + */ +#include +#include +#include +#include +#endif +#include + +#if !defined(_WIN32) && !defined(__OS2__) +#include +#endif + +#include + +#include "project.h" +#include "list.h" +#include "miscutil.h" + +const char list_h_rcs[] = LIST_H_VERSION; + + +static int list_is_valid (const struct list *the_list); + + +/********************************************************************* + * + * Function : list_init + * + * Description : Create a new, empty list in user-allocated memory. + * Caller should allocate a "struct list" variable, + * then pass it to this function. + * (Implementation note: Rather than calling this + * function, you can also just memset the memory to + * zero, e.g. if you have a larger structure you + * want to initialize quickly. However, that isn't + * really good design.) + * + * Parameters : + * 1 : the_list = pointer to list + * + * Returns : N/A + * + *********************************************************************/ +void init_list(struct list *the_list) +{ + memset(the_list, '\0', sizeof(*the_list)); +} + + +/********************************************************************* + * + * Function : destroy_list + * + * Description : Destroy a string list (opposite of list_init). + * On return, the memory used by the list entries has + * been freed, but not the memory used by the_list + * itself. You should not re-use the_list without + * calling list_init(). + * + * (Implementation note: You *can* reuse the_list + * without calling list_init(), but please don't. + * If you want to remove all entries from a list + * and still have a usable list, then use + * list_remove_all().) + * + * Parameters : + * 1 : the_list = pointer to list + * + * Returns : N/A + * + *********************************************************************/ +void destroy_list (struct list *the_list) +{ + struct list_entry *cur_entry, *next_entry; + + assert(the_list); + + for (cur_entry = the_list->first; cur_entry ; cur_entry = next_entry) + { + next_entry = cur_entry->next; + freez(cur_entry->str); + free(cur_entry); + } + + the_list->first = NULL; + the_list->last = NULL; +} + + +/********************************************************************* + * + * Function : list_is_valid + * + * Description : Check that a string list is valid. The intended + * usage is "assert(list_is_valid(the_list))". + * Currently this checks that "the_list->last" + * is correct, and that the list dosn't contain + * circular references. It is likely to crash if + * it's passed complete garbage. + * + * Parameters : + * 1 : the_list = pointer to list. Must be non-null. + * + * Returns : 1 if list is valid, 0 otherwise. + * + *********************************************************************/ +static int list_is_valid (const struct list *the_list) +{ + /* + * If you don't want this check, just change the line below + * from "#if 1" to "#if 0". + */ +#if 1 + const struct list_entry *cur_entry; + const struct list_entry *last_entry = NULL; + int length = 0; + + assert(the_list); + + for (cur_entry = the_list->first; cur_entry ; cur_entry = cur_entry->next) + { + last_entry = cur_entry; + + if (cur_entry->str) + { + /* + * Just check that this string can be accessed - i.e. it's a valid + * pointer. + */ + strlen(cur_entry->str); + } + + /* + * Check for looping back to first + */ + if ((length != 0) && (cur_entry == the_list->first)) + { + return 0; + } + + /* + * Arbitrarily limit length to prevent infinite loops. + */ + if (++length > 1000) + { + return 0; + } + + /* + * Check this isn't marked as the last entry, unless of course it's + * *really* the last entry. + */ + if ((the_list->last == cur_entry) && (cur_entry->next != NULL)) + { + /* This is the last entry, but there's data after it !!?? */ + return 0; + } + } + + return (the_list->last == last_entry); +#else + return 1; +#endif +} + +/********************************************************************* + * + * Function : enlist + * + * Description : Append a string into a specified string list. + * + * Parameters : + * 1 : the_list = pointer to list + * 2 : str = string to add to the list (maybe NULL) + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory error. + * On error, the_list will be unchanged. + * + *********************************************************************/ +jb_err enlist(struct list *the_list, const char *str) +{ + struct list_entry *cur; + + assert(the_list); + assert(list_is_valid(the_list)); + + if (NULL == (cur = (struct list_entry *)zalloc(sizeof(*cur)))) + { + return JB_ERR_MEMORY; + } + + if (str) + { + if (NULL == (cur->str = strdup(str))) + { + free(cur); + return JB_ERR_MEMORY; + } + } + /* else { cur->str = NULL; } - implied by zalloc */ + + /* cur->next = NULL; - implied by zalloc */ + + if (the_list->last) + { + the_list->last->next = cur; + the_list->last = cur; + } + else + { + the_list->first = cur; + the_list->last = cur; + } + + assert(list_is_valid(the_list)); + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : enlist_first + * + * Description : Append a string as first element into a specified + * string list. + * + * Parameters : + * 1 : the_list = pointer to list + * 2 : str = string to add to the list (maybe NULL) + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory error. + * On error, the_list will be unchanged. + * + *********************************************************************/ +jb_err enlist_first(struct list *the_list, const char *str) +{ + struct list_entry *cur; + + assert(the_list); + assert(list_is_valid(the_list)); + + if (NULL == (cur = (struct list_entry *)zalloc(sizeof(*cur)))) + { + return JB_ERR_MEMORY; + } + + if (str) + { + if (NULL == (cur->str = strdup(str))) + { + free(cur); + return JB_ERR_MEMORY; + } + } + /* else { cur->str = NULL; } - implied by zalloc */ + + cur->next = the_list->first; + + the_list->first = cur; + if (the_list->last == NULL) + { + the_list->last = cur; + } + + assert(list_is_valid(the_list)); + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : enlist_unique + * + * Description : Append a string into a specified string list, + * if & only if it's not there already. + * If the num_significant_chars argument is nonzero, + * only compare up to the nth character. + * + * Parameters : + * 1 : the_list = pointer to list + * 2 : str = string to add to the list + * 3 : num_significant_chars = number of chars to use + * for uniqueness test, or 0 to require an exact match. + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory error. + * On error, the_list will be unchanged. + * "Success" does not indicate whether or not the + * item was already in the list. + * + *********************************************************************/ +jb_err enlist_unique(struct list *the_list, const char *str, + size_t num_significant_chars) +{ + struct list_entry *cur_entry; + + assert(the_list); + assert(list_is_valid(the_list)); + assert(str); + assert(num_significant_chars >= 0); + assert(num_significant_chars <= strlen(str)); + + if (num_significant_chars > 0) + { + for (cur_entry = the_list->first; cur_entry != NULL; cur_entry = cur_entry->next) + { + if ( (cur_entry->str != NULL) + && (0 == strncmp(str, cur_entry->str, num_significant_chars))) + { + /* Already there */ + return JB_ERR_OK; + } + } + } + else + { + /* Test whole string */ + for (cur_entry = the_list->first; cur_entry != NULL; cur_entry = cur_entry->next) + { + if ( (cur_entry->str != NULL) && (0 == strcmp(str, cur_entry->str))) + { + /* Already there */ + return JB_ERR_OK; + } + } + } + + return enlist(the_list, str); +} + + +/********************************************************************* + * + * Function : enlist_unique_header + * + * Description : Make a HTTP header from the two strings name and value, + * and append the result into a specified string list, + * if & only if there isn't already a header with that name. + * + * Parameters : + * 1 : the_list = pointer to list + * 2 : name = HTTP header name (e.g. "Content-type") + * 3 : value = HTTP header value (e.g. "text/html") + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory error. + * On error, the_list will be unchanged. + * "Success" does not indicate whether or not the + * header was already in the list. + * + *********************************************************************/ +jb_err enlist_unique_header(struct list *the_list, const char *name, + const char *value) +{ + size_t length; + jb_err result; + char *str; + + assert(the_list); + assert(list_is_valid(the_list)); + assert(name); + assert(value); + + length = strlen(name) + 2; + if (NULL == (str = (char *)malloc(length + strlen(value) + 1))) + { + return JB_ERR_MEMORY; + } + strcpy(str, name); + str[length - 2] = ':'; + str[length - 1] = ' '; + strcpy(str + length, value); + + result = enlist_unique(the_list, str, length); + + free(str); + + assert(list_is_valid(the_list)); + + return result; + +} + + +/********************************************************************* + * + * Function : list_remove_all + * + * Description : Remove all entries from a list. On return, the_list + * is a valid, empty list. Note that this is similar + * to destroy_list(), but the difference is that this + * function guarantees that the list structure is still + * valid after the call. + * + * Parameters : + * 1 : the_list = pointer to list + * + * Returns : N/A + * + *********************************************************************/ +void list_remove_all(struct list *the_list) +{ + struct list_entry *cur_entry; + struct list_entry *next_entry; + + assert(the_list); + assert(list_is_valid(the_list)); + + for (cur_entry = the_list->first; cur_entry ; cur_entry = next_entry) + { + next_entry = cur_entry->next; + freez(cur_entry->str); + free(cur_entry); + } + + the_list->first = the_list->last = NULL; + + assert(list_is_valid(the_list)); +} + + +/********************************************************************* + * + * Function : list_to_text + * + * Description : "Flatten" a string list into 1 long \r\n delimited string, + * adding an empty line at the end. NULL entries are ignored. + * This function does not change the_list. + * + * Parameters : + * 1 : the_list = pointer to list + * + * Returns : NULL on malloc error, else new long string. + * Caller must free() it. + * + *********************************************************************/ +char *list_to_text(const struct list *the_list) +{ + struct list_entry *cur_entry; + char *ret = NULL; + char *s; + size_t size = 2; + + assert(the_list); + assert(list_is_valid(the_list)); + + for (cur_entry = the_list->first; cur_entry ; cur_entry = cur_entry->next) + { + if (cur_entry->str) + { + size += strlen(cur_entry->str) + 2; + } + } + + if ((ret = (char *)malloc(size + 1)) == NULL) + { + return NULL; + } + + ret[size] = '\0'; + + s = ret; + + for (cur_entry = the_list->first; cur_entry ; cur_entry = cur_entry->next) + { + if (cur_entry->str) + { + strcpy(s, cur_entry->str); + s += strlen(s); + *s++ = '\r'; *s++ = '\n'; + } + } + *s++ = '\r'; *s++ = '\n'; + + return ret; +} + + +/********************************************************************* + * + * Function : list_remove_item + * + * Description : Remove a string from a specified string list. + * + * Parameters : + * 1 : the_list = pointer to list + * 2 : str = string to remove from the list - non-NULL + * + * Returns : Number of times it was removed. + * + *********************************************************************/ +int list_remove_item(struct list *the_list, const char *str) +{ + struct list_entry *prev = NULL; + struct list_entry *cur; + struct list_entry *next; + int count = 0; + + assert(the_list); + assert(list_is_valid(the_list)); + assert(str); + + cur = the_list->first; + + while (cur != NULL) + { + next = cur->next; + + if ((cur->str != NULL) && (0 == strcmp(str, cur->str))) + { + count++; + + if (prev != NULL) + { + prev->next = next; + } + else + { + the_list->first = next; + } + free((char *)cur->str); + free(cur); + } + else + { + prev = cur; + } + cur = next; + } + + the_list->last = prev; + + assert(list_is_valid(the_list)); + + return count; +} + + +/********************************************************************* + * + * Function : list_remove_list + * + * Description : Remove all strings in one list from another list. + * This is currently a brute-force algorithm + * (it compares every pair of strings). + * + * Parameters : + * 1 : dest = list to change + * 2 : src = list of strings to remove + * + * Returns : Total number of strings removed. + * + *********************************************************************/ +int list_remove_list(struct list *dest, const struct list *src) +{ + struct list_entry *cur; + int count = 0; + + assert(src); + assert(dest); + assert(list_is_valid(src)); + assert(list_is_valid(dest)); + + for (cur = src->first; cur != NULL; cur = cur->next) + { + if (cur->str != NULL) + { + count += list_remove_item(dest, cur->str); + } + } + + assert(list_is_valid(src)); + assert(list_is_valid(dest)); + + return count; +} + + +/********************************************************************* + * + * Function : list_duplicate + * + * Description : Copy a string list + * + * Parameters : + * 1 : dest = Destination list. Must be a valid list. + * All existing entries will be removed. + * 1 : src = pointer to source list for copy. + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory error. + * On error, dest will be empty. + * + *********************************************************************/ +jb_err list_duplicate(struct list *dest, const struct list *src) +{ + struct list_entry * cur_src; + struct list_entry * cur_dest; + + assert(src); + assert(dest); + assert(list_is_valid(src)); + assert(list_is_valid(dest)); + + list_remove_all(dest); + + /* Need to process first entry specially so we can set dest->first */ + cur_src = src->first; + if (cur_src) + { + cur_dest = dest->first = (struct list_entry *)zalloc(sizeof(*cur_dest)); + if (cur_dest == NULL) + { + destroy_list(dest); + + assert(list_is_valid(src)); + assert(list_is_valid(dest)); + + return JB_ERR_MEMORY; + } + + if (cur_src->str) + { + cur_dest->str = strdup(cur_src->str); + if (cur_dest->str == NULL) + { + destroy_list(dest); + + assert(list_is_valid(src)); + assert(list_is_valid(dest)); + + return JB_ERR_MEMORY; + } + } + /* else { cur_dest->str = NULL; } - implied by zalloc */ + + /* Now process the rest */ + for (cur_src = cur_src->next; cur_src; cur_src = cur_src->next) + { + cur_dest = cur_dest->next = (struct list_entry *)zalloc(sizeof(*cur_dest)); + if (cur_dest == NULL) + { + destroy_list(dest); + + assert(list_is_valid(src)); + assert(list_is_valid(dest)); + + return JB_ERR_MEMORY; + } + if (cur_src->str) + { + cur_dest->str = strdup(cur_src->str); + if (cur_dest->str == NULL) + { + destroy_list(dest); + + assert(list_is_valid(src)); + assert(list_is_valid(dest)); + + return JB_ERR_MEMORY; + } + } + /* else { cur_dest->str = NULL; } - implied by zalloc */ + } + + dest->last = cur_dest; + } + + assert(list_is_valid(src)); + assert(list_is_valid(dest)); + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : list_append_list_unique + * + * Description : Append a string list to another list. + * Duplicate items are not added. + * + * Parameters : + * 1 : dest = pointer to destination list for merge. + * 2 : src = pointer to source for merge. + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory error. + * On error, some (but not all) of src might have + * been copied into dest. + * + *********************************************************************/ +jb_err list_append_list_unique(struct list *dest, const struct list *src) +{ + struct list_entry * cur; + + assert(src); + assert(dest); + assert(list_is_valid(src)); + assert(list_is_valid(dest)); + + for (cur = src->first; cur; cur = cur->next) + { + if (cur->str) + { + if (enlist_unique(dest, cur->str, 0)) + { + assert(list_is_valid(src)); + assert(list_is_valid(dest)); + + return JB_ERR_MEMORY; + } + } + } + + assert(list_is_valid(src)); + assert(list_is_valid(dest)); + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : list_is_empty + * + * Description : Test whether a list is empty. Does not change the list. + * + * Parameters : + * 1 : the_list = pointer to list to test. + * + * Returns : Nonzero iff the list contains no entries. + * + *********************************************************************/ +int list_is_empty(const struct list *the_list) +{ + assert(the_list); + assert(list_is_valid(the_list)); + + return (the_list->first == NULL); +} + + +/********************************************************************* + * + * Function : new_map + * + * Description : Create a new, empty map. + * + * Parameters : N/A + * + * Returns : A new, empty map, or NULL if out of memory. + * + *********************************************************************/ +struct map *new_map(void) +{ + return (struct map *) zalloc(sizeof(struct map)); +} + + +/********************************************************************* + * + * Function : free_map + * + * Description : Free the memory occupied by a map and its + * depandant strings + * + * Parameters : + * 1 : the_map = map to be freed. May be NULL. + * + * Returns : N/A + * + *********************************************************************/ +void free_map(struct map *the_map) +{ + struct map_entry *cur_entry; + struct map_entry *next_entry; + + if (the_map == NULL) + { + return; + } + + for (cur_entry = the_map->first; cur_entry != NULL; cur_entry = next_entry) + { + freez(cur_entry->name); + freez(cur_entry->value); + + next_entry = cur_entry->next; + free(cur_entry); + } + + the_map->first = the_map->last = NULL; + + free(the_map); +} + + +/********************************************************************* + * + * Function : map + * + * Description : Add a mapping from given name to given value to a + * given map. + * + * Note: Since all strings will be free()d in free_map() + * later, set the copy flags for constants or + * strings that will be independantly free()d. + * + * Note2: This function allows NULL parameters - it + * returns JB_ERR_MEMORY in that case. + * + * Note3: If this function returns JB_ERR_MEMORY, + * it will free(name) unless you specify + * name_needs_copying, and similarly it will + * free(value) unless you specify + * value_needs_copying. + * + * Due to Note2 and Note3 above, the following code + * is legal, and will never crash or leak memory even + * if the system runs out of memory: + * + * err = map(mymap, "xyz", 1, html_encode(somestring), 0); + * + * err will be set to JB_ERR_MEMORY if either call runs + * out-of-memory. Without these features, you would + * need to check the return value of html_encode in the + * above example for NULL, which (at least) doubles the + * amount of error-checking code needed. + * + * Parameters : + * 1 : the_map = map to add to + * 2 : name = name to add + * 3 : name_needs_copying = flag set if a copy of name should be used + * 4 : value = value to add + * 5 : value_needs_copying = flag set if a copy of value should be used + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err map(struct map *the_map, + const char *name, int name_needs_copying, + const char *value, int value_needs_copying) +{ + struct map_entry *new_entry; + + assert(the_map); + + if ( (NULL == value) + || (NULL == name) + || (NULL == (new_entry = zalloc(sizeof(*new_entry)))) ) + { + if ((name != NULL) && (!name_needs_copying)) + { + free((char *)name); + } + if ((value != NULL) && (!value_needs_copying)) + { + free((char *)value); + } + return JB_ERR_MEMORY; + } + + if (name_needs_copying) + { + if (NULL == (name = strdup(name))) + { + free(new_entry); + if (!value_needs_copying) + { + free((char *)value); + } + return JB_ERR_MEMORY; + } + } + + if (value_needs_copying) + { + if (NULL == (value = strdup(value))) + { + free((char *)name); + free(new_entry); + return JB_ERR_MEMORY; + } + } + + new_entry->name = name; + new_entry->value = value; + /* new_entry->next = NULL; - implied by zalloc */ + + if (the_map->last) + { + the_map->last->next = new_entry; + the_map->last = new_entry; + } + else + { + the_map->first = new_entry; + the_map->last = new_entry; + } + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : lookup + * + * Description : Look up an item with a given name in a map, and + * return its value + * + * Parameters : + * 1 : the_map = map to look in + * 2 : name = name parameter to look for + * + * Returns : the value if found, else the empty string. + * Return value is alloced as part of the map, so + * it is freed when the map is destroyed. Caller + * must not free or modify it. + * + *********************************************************************/ +const char *lookup(const struct map *the_map, const char *name) +{ + const struct map_entry *cur_entry; + + assert(the_map); + assert(name); + + for (cur_entry = the_map->first; cur_entry != NULL; cur_entry = cur_entry->next) + { + if (!strcmp(name, cur_entry->name)) + { + return cur_entry->value; + } + } + return ""; +} + + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/list.h b/src/list.h new file mode 100644 index 00000000..8dd18356 --- /dev/null +++ b/src/list.h @@ -0,0 +1,167 @@ +#ifndef LIST_H_INCLUDED +#define LIST_H_INCLUDED +#define LIST_H_VERSION "$Id: list.h,v 1.12 2002/03/26 22:29:55 swa Exp $" +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/list.h,v $ + * + * Purpose : Declares functions to handle lists. + * Functions declared include: + * `destroy_list', `enlist' and `list_to_text' + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: list.h,v $ + * Revision 1.12 2002/03/26 22:29:55 swa + * we have a new homepage! + * + * Revision 1.11 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.10 2002/03/07 03:46:17 oes + * Fixed compiler warnings + * + * Revision 1.9 2001/10/23 21:21:03 jongfoster + * New error handling - error codes are now jb_errs, not ints. + * Changed the way map() handles out-of-memory, to dramatically + * reduce the amount of error-checking clutter needed. + * + * Revision 1.8 2001/09/16 17:30:24 jongfoster + * Fixing a compiler warning. + * + * Revision 1.7 2001/09/16 13:20:29 jongfoster + * Rewrite of list library. Now has seperate header and list_entry + * structures. Also added a large sprinking of assert()s to the list + * code. + * + * Revision 1.6 2001/08/05 16:06:20 jongfoster + * Modifiying "struct map" so that there are now separate header and + * "map_entry" structures. This means that functions which modify a + * map no longer need to return a pointer to the modified map. + * Also, it no longer reverses the order of the entries (which may be + * important with some advanced template substitutions). + * + * Revision 1.5 2001/07/29 18:43:08 jongfoster + * Changing #ifdef _FILENAME_H to FILENAME_H_INCLUDED, to conform to + * ANSI C rules. + * + * Revision 1.4 2001/06/29 13:30:37 oes + * - Introduced enlist_unique_header() + * - Removed logentry from cancelled commit + * + * Revision 1.3 2001/06/03 11:03:48 oes + * introduced functions for new list type "map": map(), lookup(), + * free_map(), and extended enlist_unique + * + * Revision 1.2 2001/06/01 18:49:17 jongfoster + * Replaced "list_share" with "list" - the tiny memory gain was not + * worth the extra complexity. + * + * Revision 1.1 2001/05/31 21:11:53 jongfoster + * - Moved linked list support to new "list.c" file. + * Structure definitions are still in project.h, + * function prototypes are now in "list.h". + * - Added support for "struct list_share", which is identical + * to "struct list" except it saves memory by not duplicating + * the strings. Obviously, this only works if there is some + * other way of managing the memory used by the strings. + * (These list_share lists are used for lists which last + * for only 1 request, and where all the list entries are + * just coming directly from entries in the actionsfile.) + * Note that you still need to destroy list_share lists + * properly to free the nodes - it's only the strings + * which are shared. + * + * + *********************************************************************/ + + +#include "project.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * struct list + * + * A linked list class. + */ + +extern void init_list (struct list *the_list); +extern void destroy_list (struct list *the_list); + +extern jb_err enlist (struct list *the_list, const char *str); +extern jb_err enlist_unique (struct list *the_list, const char *str, size_t num_significant_chars); +extern jb_err enlist_unique_header (struct list *the_list, const char *name, const char *value); +extern jb_err enlist_first (struct list *the_list, const char *str); +extern jb_err list_append_list_unique(struct list *dest, const struct list *src); +extern jb_err list_duplicate (struct list *dest, const struct list *src); + +extern int list_remove_item(struct list *the_list, const char *str); +extern int list_remove_list(struct list *dest, const struct list *src); +extern void list_remove_all (struct list *the_list); + +extern int list_is_empty(const struct list *the_list); + +extern char * list_to_text(const struct list *the_list); + + +/* + * struct map + * + * A class which maps names to values. + * + * Note: You must allocate this through new_map() and free it + * through free_map(). + */ + +extern struct map * new_map (void); +extern void free_map (struct map * the_map); + +extern int map (struct map * the_map, + const char * name, int name_needs_copying, + const char * value, int value_needs_copying); +extern const char * lookup (const struct map * the_map, const char * name); + + +/* Revision control strings from this header and associated .c file */ +extern const char list_rcs[]; +extern const char list_h_rcs[]; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ndef LIST_H_INCLUDED */ + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/loadcfg.c b/src/loadcfg.c new file mode 100644 index 00000000..30ca4674 --- /dev/null +++ b/src/loadcfg.c @@ -0,0 +1,1598 @@ +const char loadcfg_rcs[] = "$Id: loadcfg.c,v 1.48 2002/05/14 21:30:38 oes Exp $"; +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/loadcfg.c,v $ + * + * Purpose : Loads settings from the configuration file into + * global variables. This file contains both the + * routine to load the configuration and the global + * variables it writes to. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: loadcfg.c,v $ + * Revision 1.48 2002/05/14 21:30:38 oes + * savearg now uses own linking code instead of (now special-cased) add_help_link + * + * Revision 1.47 2002/05/12 21:36:29 jongfoster + * Correcting function comments + * + * Revision 1.46 2002/04/26 12:55:14 oes + * - New option "user-manual", defaults to our site + * via project.h #define + * - savearg now embeds option names in help links + * + * Revision 1.45 2002/04/24 02:11:54 oes + * Jon's multiple AF patch: Allow up to MAX_ACTION_FILES actionsfile options + * + * Revision 1.44 2002/04/08 20:37:13 swa + * fixed JB spelling + * + * Revision 1.43 2002/04/08 20:36:50 swa + * fixed JB spelling + * + * Revision 1.42 2002/04/05 15:50:15 oes + * fix for invalid HTML proxy_args + * + * Revision 1.41 2002/03/31 17:19:00 jongfoster + * Win32 only: Enabling STRICT to fix a VC++ compile warning. + * + * Revision 1.40 2002/03/26 22:29:55 swa + * we have a new homepage! + * + * Revision 1.39 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.38 2002/03/24 13:05:48 jongfoster + * Renaming re_filterfile to filterfile + * + * Revision 1.37 2002/03/16 23:54:06 jongfoster + * Adding graceful termination feature, to help look for memory leaks. + * If you enable this (which, by design, has to be done by hand + * editing config.h) and then go to http://i.j.b/die, then the program + * will exit cleanly after the *next* request. It should free all the + * memory that was used. + * + * Revision 1.36 2002/03/13 00:27:05 jongfoster + * Killing warnings + * + * Revision 1.35 2002/03/07 03:52:44 oes + * Set logging to tty for --no-daemon mode + * + * Revision 1.34 2002/03/06 23:14:35 jongfoster + * Trivial cosmetic changes to make function comments easier to find. + * + * Revision 1.33 2002/03/05 04:52:42 oes + * Deleted non-errlog debugging code + * + * Revision 1.32 2002/03/04 18:24:53 oes + * Re-enabled output of unknown config directive hash + * + * Revision 1.31 2002/03/03 15:07:20 oes + * Re-enabled automatic config reloading + * + * Revision 1.30 2002/01/22 23:31:43 jongfoster + * Replacing strsav() with string_append() + * + * Revision 1.29 2002/01/17 21:02:30 jongfoster + * Moving all our URL and URL pattern parsing code to urlmatch.c. + * + * Renaming free_url to free_url_spec, since it frees a struct url_spec. + * + * Revision 1.28 2001/12/30 14:07:32 steudten + * - Add signal handling (unix) + * - Add SIGHUP handler (unix) + * - Add creation of pidfile (unix) + * - Add action 'top' in rc file (RH) + * - Add entry 'SIGNALS' to manpage + * - Add exit message to logfile (unix) + * + * Revision 1.27 2001/11/07 00:02:13 steudten + * Add line number in error output for lineparsing for + * actionsfile and configfile. + * Special handling for CLF added. + * + * Revision 1.26 2001/11/05 21:41:43 steudten + * Add changes to be a real daemon just for unix os. + * (change cwd to /, detach from controlling tty, set + * process group and session leader to the own process. + * Add DBG() Macro. + * Add some fatal-error log message for failed malloc(). + * Add '-d' if compiled with 'configure --with-debug' to + * enable debug output. + * + * Revision 1.25 2001/10/25 03:40:48 david__schmidt + * Change in porting tactics: OS/2's EMX porting layer doesn't allow multiple + * threads to call select() simultaneously. So, it's time to do a real, live, + * native OS/2 port. See defines for __EMX__ (the porting layer) vs. __OS2__ + * (native). Both versions will work, but using __OS2__ offers multi-threading. + * + * Revision 1.24 2001/10/23 21:40:30 jongfoster + * Added support for enable-edit-actions and enable-remote-toggle config + * file options. + * + * Revision 1.23 2001/10/07 15:36:00 oes + * Introduced new config option "buffer-limit" + * + * Revision 1.22 2001/09/22 16:36:59 jongfoster + * Removing unused parameter fs from read_config_line() + * + * Revision 1.21 2001/09/16 17:10:43 jongfoster + * Moving function savearg() here, since it was the only thing left in + * showargs.c. + * + * Revision 1.20 2001/07/30 22:08:36 jongfoster + * Tidying up #defines: + * - All feature #defines are now of the form FEATURE_xxx + * - Permanently turned off WIN_GUI_EDIT + * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS + * + * Revision 1.19 2001/07/15 17:45:16 jongfoster + * Removing some unused #includes + * + * Revision 1.18 2001/07/13 14:01:14 oes + * - Removed all #ifdef PCRS + * - Removed vim-settings + * + * Revision 1.17 2001/06/29 13:31:03 oes + * - Improved comments + * - Fixed (actionsfile) and sorted hashes + * - Introduced admin_address and proxy-info-url + * as config parameters + * - Renamed config->proxy_args_invocation (which didn't have + * the invocation but the options!) to config->proxy_args + * - Various adaptions + * - Removed logentry from cancelled commit + * + * Revision 1.16 2001/06/09 10:55:28 jongfoster + * Changing BUFSIZ ==> BUFFER_SIZE + * + * Revision 1.15 2001/06/07 23:13:40 jongfoster + * Merging ACL and forward files into config file. + * Cosmetic: Sorting config file options alphabetically. + * Cosmetic: Adding brief syntax comments to config file options. + * + * Revision 1.14 2001/06/07 14:46:25 joergs + * Missing make_path() added for re_filterfile. + * + * Revision 1.13 2001/06/05 22:33:54 jongfoster + * + * Fixed minor memory leak. + * Also now uses make_path to prepend the pathnames. + * + * Revision 1.12 2001/06/05 20:04:09 jongfoster + * Now uses _snprintf() in place of snprintf() under Win32. + * + * Revision 1.11 2001/06/04 18:31:58 swa + * files are now prefixed with either `confdir' or `logdir'. + * `make redhat-dist' replaces both entries confdir and logdir + * with redhat values + * + * Revision 1.10 2001/06/03 19:11:54 oes + * introduced confdir option + * + * Revision 1.9 2001/06/01 20:06:24 jongfoster + * Removed support for "tinygif" option - moved to actions file. + * + * Revision 1.8 2001/05/31 21:27:13 jongfoster + * Removed many options from the config file and into the + * "actions" file: add_forwarded, suppress_vanilla_wafer, + * wafer, add_header, user_agent, referer, from + * Also globally replaced "permission" with "action". + * + * Revision 1.7 2001/05/29 09:50:24 jongfoster + * Unified blocklist/imagelist/permissionslist. + * File format is still under discussion, but the internal changes + * are (mostly) done. + * + * Also modified interceptor behaviour: + * - We now intercept all URLs beginning with one of the following + * prefixes (and *only* these prefixes): + * * http://i.j.b/ + * * http://ijbswa.sf.net/config/ + * * http://ijbswa.sourceforge.net/config/ + * - New interceptors "home page" - go to http://i.j.b/ to see it. + * - Internal changes so that intercepted and fast redirect pages + * are not replaced with an image. + * - Interceptors now have the option to send a binary page direct + * to the client. (i.e. ijb-send-banner uses this) + * - Implemented show-url-info interceptor. (Which is why I needed + * the above interceptors changes - a typical URL is + * "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif". + * The previous mechanism would not have intercepted that, and + * if it had been intercepted then it then it would have replaced + * it with an image.) + * + * Revision 1.6 2001/05/26 00:28:36 jongfoster + * Automatic reloading of config file. + * Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32). + * Most of the global variables have been moved to a new + * struct configuration_spec, accessed through csp->config->globalname + * Most of the globals remaining are used by the Win32 GUI. + * + * Revision 1.5 2001/05/25 22:34:30 jongfoster + * Hard tabs->Spaces + * + * Revision 1.4 2001/05/22 18:46:04 oes + * + * - Enabled filtering banners by size rather than URL + * by adding patterns that replace all standard banner + * sizes with the "Junkbuster" gif to the re_filterfile + * + * - Enabled filtering WebBugs by providing a pattern + * which kills all 1x1 images + * + * - Added support for PCRE_UNGREEDY behaviour to pcrs, + * which is selected by the (nonstandard and therefore + * capital) letter 'U' in the option string. + * It causes the quantifiers to be ungreedy by default. + * Appending a ? turns back to greedy (!). + * + * - Added a new interceptor ijb-send-banner, which + * sends back the "Junkbuster" gif. Without imagelist or + * MSIE detection support, or if tinygif = 1, or the + * URL isn't recognized as an imageurl, a lame HTML + * explanation is sent instead. + * + * - Added new feature, which permits blocking remote + * script redirects and firing back a local redirect + * to the browser. + * The feature is conditionally compiled, i.e. it + * can be disabled with --disable-fast-redirects, + * plus it must be activated by a "fast-redirects" + * line in the config file, has its own log level + * and of course wants to be displayed by show-proxy-args + * Note: Boy, all the #ifdefs in 1001 locations and + * all the fumbling with configure.in and acconfig.h + * were *way* more work than the feature itself :-( + * + * - Because a generic redirect template was needed for + * this, tinygif = 3 now uses the same. + * + * - Moved GIFs, and other static HTTP response templates + * to project.h + * + * - Some minor fixes + * + * - Removed some >400 CRs again (Jon, you really worked + * a lot! ;-) + * + * Revision 1.3 2001/05/20 01:21:20 jongfoster + * Version 2.9.4 checkin. + * - Merged popupfile and cookiefile, and added control over PCRS + * filtering, in new "permissionsfile". + * - Implemented LOG_LEVEL_FATAL, so that if there is a configuration + * file error you now get a message box (in the Win32 GUI) rather + * than the program exiting with no explanation. + * - Made killpopup use the PCRS MIME-type checking and HTTP-header + * skipping. + * - Removed tabs from "config" + * - Moved duplicated url parsing code in "loaders.c" to a new funcition. + * - Bumped up version number. + * + * Revision 1.2 2001/05/17 23:01:01 oes + * - Cleaned CRLF's from the sources and related files + * + * Revision 1.1.1.1 2001/05/15 13:58:58 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 + +# ifndef STRICT +# define STRICT +# endif +# include + +# include "win32.h" +# ifndef _WIN_CONSOLE +# include "w32log.h" +# endif /* ndef _WIN_CONSOLE */ + +/* VC++ has "_snprintf", not "snprintf" */ +#define snprintf _snprintf + +#else /* ifndef _WIN32 */ + +#ifndef __OS2__ +# include +# include +#endif +# include +# include +# include + +#endif + +#include "loadcfg.h" +#include "list.h" +#include "jcc.h" +#include "filters.h" +#include "loaders.h" +#include "miscutil.h" +#include "errlog.h" +#include "ssplit.h" +#include "encode.h" +#include "urlmatch.h" +#include "cgi.h" + +const char loadcfg_h_rcs[] = LOADCFG_H_VERSION; + +/* + * Fix a problem with Solaris. There should be no effect on other + * platforms. + * Solaris's isspace() is a macro which uses it's argument directly + * as an array index. Therefore we need to make sure that high-bit + * characters generate +ve values, and ideally we also want to make + * the argument match the declared parameter type of "int". + */ +#define ijb_isupper(__X) isupper((int)(unsigned char)(__X)) +#define ijb_tolower(__X) tolower((int)(unsigned char)(__X)) + +#ifdef FEATURE_TOGGLE +/* by haroon - indicates if ijb is enabled */ +int g_bToggleIJB = 1; /* Privoxy is enabled by default. */ +#endif /* def FEATURE_TOGGLE */ + +/* The filename of the configfile */ +const char *configfile = NULL; + +/* + * CGI functions will later need access to the invocation args, + * so we will make argc and argv global. + */ +int Argc = 0; +const char **Argv = NULL; + +static struct file_list *current_configfile = NULL; + + +/* + * This takes the "cryptic" hash of each keyword and aliases them to + * something a little more readable. This also makes changing the + * hash values easier if they should change or the hash algorthm changes. + * Use the included "hash" program to find out what the hash will be + * for any string supplied on the command line. (Or just put it in the + * config file and read the number from the error message in the log). + * + * Please keep this list sorted alphabetically (but with the Windows + * console and GUI specific options last). + */ + +#define hash_actions_file 1196306641ul /* "actionsfile" */ +#define hash_admin_address 4112573064ul /* "admin-address" */ +#define hash_buffer_limit 1881726070ul /* "buffer-limit */ +#define hash_confdir 1978389ul /* "confdir" */ +#define hash_debug 78263ul /* "debug" */ +#define hash_deny_access 1227333715ul /* "deny-access" */ +#define hash_enable_edit_actions 2517097536ul /* "enable-edit-actions" */ +#define hash_enable_remote_toggle 2979744683ul /* "enable-remote-toggle" */ +#define hash_filterfile 250887266ul /* "filterfile" */ +#define hash_forward 2029845ul /* "forward" */ +#define hash_forward_socks4 3963965521ul /* "forward-socks4" */ +#define hash_forward_socks4a 2639958518ul /* "forward-socks4a" */ +#define hash_jarfile 2046641ul /* "jarfile" */ +#define hash_listen_address 1255650842ul /* "listen-address" */ +#define hash_logdir 422889ul /* "logdir" */ +#define hash_logfile 2114766ul /* "logfile" */ +#define hash_permit_access 3587953268ul /* "permit-access" */ +#define hash_proxy_info_url 3903079059ul /* "proxy-info-url" */ +#define hash_single_threaded 4250084780ul /* "single-threaded" */ +#define hash_suppress_blocklists 1948693308ul /* "suppress-blocklists" */ +#define hash_toggle 447966ul /* "toggle" */ +#define hash_trust_info_url 430331967ul /* "trust-info-url" */ +#define hash_trustfile 56494766ul /* "trustfile" */ +#define hash_usermanual 1416668518ul /* "user-manual" */ +#define hash_activity_animation 1817904738ul /* "activity-animation" */ +#define hash_close_button_minimizes 3651284693ul /* "close-button-minimizes" */ +#define hash_hide_console 2048809870ul /* "hide-console" */ +#define hash_log_buffer_size 2918070425ul /* "log-buffer-size" */ +#define hash_log_font_name 2866730124ul /* "log-font-name" */ +#define hash_log_font_size 2866731014ul /* "log-font-size" */ +#define hash_log_highlight_messages 4032101240ul /* "log-highlight-messages" */ +#define hash_log_max_lines 2868344173ul /* "log-max-lines" */ +#define hash_log_messages 2291744899ul /* "log-messages" */ +#define hash_show_on_task_bar 215410365ul /* "show-on-task-bar" */ + + +static void savearg(char *command, char *argument, struct configuration_spec * config); + +/********************************************************************* + * + * Function : unload_configfile + * + * Description : Free the config structure and all components. + * + * Parameters : + * 1 : data: struct configuration_spec to unload + * + * Returns : N/A + * + *********************************************************************/ +void unload_configfile (void * data) +{ + struct configuration_spec * config = (struct configuration_spec *)data; + struct forward_spec *cur_fwd = config->forward; +#ifdef FEATURE_ACL + struct access_control_list *cur_acl = config->acl; + int i; + + while (cur_acl != NULL) + { + struct access_control_list * next_acl = cur_acl->next; + free(cur_acl); + cur_acl = next_acl; + } + config->acl = NULL; +#endif /* def FEATURE_ACL */ + + while (cur_fwd != NULL) + { + struct forward_spec * next_fwd = cur_fwd->next; + free_url_spec(cur_fwd->url); + + freez(cur_fwd->gateway_host); + freez(cur_fwd->forward_host); + free(cur_fwd); + cur_fwd = next_fwd; + } + config->forward = NULL; + +#ifdef FEATURE_COOKIE_JAR + if ( NULL != config->jar ) + { + fclose( config->jar ); + config->jar = NULL; + } +#endif /* def FEATURE_COOKIE_JAR */ + + freez(config->confdir); + freez(config->logdir); + + freez(config->haddr); + freez(config->logfile); + + for (i = 0; i < MAX_ACTION_FILES; i++) + { + freez(config->actions_file_short[i]); + freez(config->actions_file[i]); + } + + freez(config->admin_address); + freez(config->proxy_info_url); + freez(config->proxy_args); + freez(config->usermanual); + +#ifdef FEATURE_COOKIE_JAR + freez(config->jarfile); +#endif /* def FEATURE_COOKIE_JAR */ + + freez(config->re_filterfile); + +} + + +#ifdef FEATURE_GRACEFUL_TERMINATION +/********************************************************************* + * + * Function : unload_current_config_file + * + * Description : Unloads current config file - reset to state at + * beginning of program. + * + * Parameters : None + * + * Returns : N/A + * + *********************************************************************/ +void unload_current_config_file(void) +{ + if (current_configfile) + { + current_configfile->unloader = unload_configfile; + current_configfile = NULL; + } +} +#endif + + +/********************************************************************* + * + * Function : load_config + * + * Description : Load the config file and all parameters. + * + * Parameters : None + * + * Returns : The configuration_spec, or NULL on error. + * + *********************************************************************/ +struct configuration_spec * load_config(void) +{ + char buf[BUFFER_SIZE]; + char *p, *q; + FILE *configfp = NULL; + struct configuration_spec * config = NULL; + struct client_state * fake_csp; + struct file_list *fs; + unsigned long linenum = 0; + int i; + + if ( !check_file_changed(current_configfile, configfile, &fs)) + { + /* No need to load */ + return ((struct configuration_spec *)current_configfile->f); + } + if (!fs) + { + log_error(LOG_LEVEL_FATAL, "can't check configuration file '%s': %E", + configfile); + } + + log_error(LOG_LEVEL_INFO, "loading configuration file '%s':", configfile); + +#ifdef FEATURE_TOGGLE + g_bToggleIJB = 1; +#endif /* def FEATURE_TOGGLE */ + + fs->f = config = (struct configuration_spec *)zalloc(sizeof(*config)); + + if (config==NULL) + { + freez(fs->filename); + freez(fs); + log_error(LOG_LEVEL_FATAL, "can't allocate memory for configuration"); + /* Never get here - LOG_LEVEL_FATAL causes program exit */ + } + + /* + * This is backwards from how it's usually done. + * Following the usual pattern, "fs" would be stored in a member + * variable in "csp", and then we'd access "config" from "fs->f", + * using a cast. However, "config" is used so often that a + * cast each time would be very ugly, and the extra indirection + * would waste CPU cycles. Therefore we store "config" in + * "csp->config", and "fs" in "csp->config->config_file_list". + */ + config->config_file_list = fs; + + /* + * Set to defaults + */ + config->multi_threaded = 1; + config->hport = HADDR_PORT; + config->buffer_limit = 4096 * 1024; + config->usermanual = strdup(USER_MANUAL_URL); + config->proxy_args = strdup(""); + + if ((configfp = fopen(configfile, "r")) == NULL) + { + log_error(LOG_LEVEL_FATAL, "can't open configuration file '%s': %E", + configfile); + /* Never get here - LOG_LEVEL_FATAL causes program exit */ + } + + while (read_config_line(buf, sizeof(buf), configfp, &linenum) != NULL) + { + char cmd[BUFFER_SIZE]; + char arg[BUFFER_SIZE]; + char tmp[BUFFER_SIZE]; +#ifdef FEATURE_ACL + struct access_control_list *cur_acl; +#endif /* def FEATURE_ACL */ + struct forward_spec *cur_fwd; + int vec_count; + char *vec[3]; + + strcpy(tmp, buf); + + /* Copy command (i.e. up to space or tab) into cmd */ + p = buf; + q = cmd; + while (*p && (*p != ' ') && (*p != '\t')) + { + *q++ = *p++; + } + *q = '\0'; + + /* Skip over the whitespace in buf */ + while (*p && ((*p == ' ') || (*p == '\t'))) + { + p++; + } + + /* Copy the argument into arg */ + strcpy(arg, p); + + /* Should never happen, but check this anyway */ + if (*cmd == '\0') + { + continue; + } + + /* Make sure the command field is lower case */ + for (p=cmd; *p; p++) + { + if (ijb_isupper(*p)) + { + *p = ijb_tolower(*p); + } + } + + /* Save the argument for show-proxy-args */ + savearg(cmd, arg, config); + + + switch( hash_string( cmd ) ) + { +/* ************************************************************************* + * actionsfile actions-file-name + * In confdir by default + * *************************************************************************/ + case hash_actions_file : + i = 0; + while ((i < MAX_ACTION_FILES) && (NULL != config->actions_file[i])) + { + i++; + } + + if (i >= MAX_ACTION_FILES) + { + log_error(LOG_LEVEL_FATAL, "Too many 'actionsfile' directives in config file - limit is %d.\n" + "(You can increase this limit by changing MAX_ACTION_FILES in project.h and recompiling).", + MAX_ACTION_FILES); + } + config->actions_file_short[i] = strdup(arg); + p = malloc(strlen(arg) + sizeof(".action")); + if (p == NULL) + { + log_error(LOG_LEVEL_FATAL, "Out of memory"); + } + strcpy(p, arg); + strcat(p, ".action"); + config->actions_file[i] = make_path(config->confdir, p); + free(p); + continue; + +/* ************************************************************************* + * admin-address email-address + * *************************************************************************/ + case hash_admin_address : + freez(config->admin_address); + config->admin_address = strdup(arg); + continue; + +/* ************************************************************************* + * buffer-limit n + * *************************************************************************/ + case hash_buffer_limit : + config->buffer_limit = (size_t) 1024 * atoi(arg); + continue; + +/* ************************************************************************* + * confdir directory-name + * *************************************************************************/ + case hash_confdir : + freez(config->confdir); + config->confdir = make_path( NULL, arg); + continue; + +/* ************************************************************************* + * debug n + * Specifies debug level, multiple values are ORed together. + * *************************************************************************/ + case hash_debug : + config->debug |= atoi(arg); + continue; + +/* ************************************************************************* + * deny-access source-ip[/significant-bits] [dest-ip[/significant-bits]] + * *************************************************************************/ +#ifdef FEATURE_ACL + case hash_deny_access: + vec_count = ssplit(arg, " \t", vec, SZ(vec), 1, 1); + + if ((vec_count != 1) && (vec_count != 2)) + { + log_error(LOG_LEVEL_ERROR, "Wrong number of parameters for " + "deny-access directive in configuration file."); + string_append(&config->proxy_args, + "
    \nWARNING: Wrong number of parameters for " + "deny-access directive in configuration file.

    \n"); + continue; + } + + /* allocate a new node */ + cur_acl = (struct access_control_list *) zalloc(sizeof(*cur_acl)); + + if (cur_acl == NULL) + { + log_error(LOG_LEVEL_FATAL, "can't allocate memory for configuration"); + /* Never get here - LOG_LEVEL_FATAL causes program exit */ + continue; + } + cur_acl->action = ACL_DENY; + + if (acl_addr(vec[0], cur_acl->src) < 0) + { + log_error(LOG_LEVEL_ERROR, "Invalid source IP for deny-access " + "directive in configuration file: \"%s\"", vec[0]); + string_append(&config->proxy_args, + "
    \nWARNING: Invalid source IP for deny-access directive" + " in configuration file: \""); + string_append(&config->proxy_args, + vec[0]); + string_append(&config->proxy_args, + "\"

    \n"); + freez(cur_acl); + continue; + } + if (vec_count == 2) + { + if (acl_addr(vec[1], cur_acl->dst) < 0) + { + log_error(LOG_LEVEL_ERROR, "Invalid destination IP for deny-access " + "directive in configuration file: \"%s\"", vec[0]); + string_append(&config->proxy_args, + "
    \nWARNING: Invalid destination IP for deny-access directive" + " in configuration file: \""); + string_append(&config->proxy_args, + vec[0]); + string_append(&config->proxy_args, + "\"

    \n"); + freez(cur_acl); + continue; + } + } + + /* + * Add it to the list. Note we reverse the list to get the + * behaviour the user expects. With both the ACL and + * actions file, the last match wins. However, the internal + * implementations are different: The actions file is stored + * in the same order as the file, and scanned completely. + * With the ACL, we reverse the order as we load it, then + * when we scan it we stop as soon as we get a match. + */ + cur_acl->next = config->acl; + config->acl = cur_acl; + + continue; +#endif /* def FEATURE_ACL */ + +/* ************************************************************************* + * enable-edit-actions 0|1 + * *************************************************************************/ +#ifdef FEATURE_CGI_EDIT_ACTIONS + case hash_enable_edit_actions: + if ((*arg != '\0') && (0 != atoi(arg))) + { + config->feature_flags |= RUNTIME_FEATURE_CGI_EDIT_ACTIONS; + } + else + { + config->feature_flags &= ~RUNTIME_FEATURE_CGI_EDIT_ACTIONS; + } + continue; +#endif /* def FEATURE_CGI_EDIT_ACTIONS */ + +/* ************************************************************************* + * enable-remote-toggle 0|1 + * *************************************************************************/ +#ifdef FEATURE_CGI_EDIT_ACTIONS + case hash_enable_remote_toggle: + if ((*arg != '\0') && (0 != atoi(arg))) + { + config->feature_flags |= RUNTIME_FEATURE_CGI_TOGGLE; + } + else + { + config->feature_flags &= ~RUNTIME_FEATURE_CGI_TOGGLE; + } + continue; +#endif /* def FEATURE_CGI_EDIT_ACTIONS */ + +/* ************************************************************************* + * forward url-pattern (.|http-proxy-host[:port]) + * *************************************************************************/ + case hash_forward: + vec_count = ssplit(arg, " \t", vec, SZ(vec), 1, 1); + + if (vec_count != 2) + { + log_error(LOG_LEVEL_ERROR, "Wrong number of parameters for forward " + "directive in configuration file."); + string_append(&config->proxy_args, + "
    \nWARNING: Wrong number of parameters for " + "forward directive in configuration file."); + continue; + } + + /* allocate a new node */ + cur_fwd = zalloc(sizeof(*cur_fwd)); + if (cur_fwd == NULL) + { + log_error(LOG_LEVEL_FATAL, "can't allocate memory for configuration"); + /* Never get here - LOG_LEVEL_FATAL causes program exit */ + continue; + } + + cur_fwd->type = SOCKS_NONE; + + /* Save the URL pattern */ + if (create_url_spec(cur_fwd->url, vec[0])) + { + log_error(LOG_LEVEL_ERROR, "Bad URL specifier for forward " + "directive in configuration file."); + string_append(&config->proxy_args, + "
    \nWARNING: Bad URL specifier for " + "forward directive in configuration file."); + continue; + } + + /* Parse the parent HTTP proxy host:port */ + p = vec[1]; + + if (strcmp(p, ".") != 0) + { + cur_fwd->forward_host = strdup(p); + + if (NULL != (p = strchr(cur_fwd->forward_host, ':'))) + { + *p++ = '\0'; + cur_fwd->forward_port = atoi(p); + } + + if (cur_fwd->forward_port <= 0) + { + cur_fwd->forward_port = 8000; + } + } + + /* Add to list. */ + cur_fwd->next = config->forward; + config->forward = cur_fwd; + + continue; + +/* ************************************************************************* + * forward-socks4 url-pattern socks-proxy[:port] (.|http-proxy[:port]) + * *************************************************************************/ + case hash_forward_socks4: + vec_count = ssplit(arg, " \t", vec, SZ(vec), 1, 1); + + if (vec_count != 3) + { + log_error(LOG_LEVEL_ERROR, "Wrong number of parameters for " + "forward-socks4 directive in configuration file."); + string_append(&config->proxy_args, + "
    \nWARNING: Wrong number of parameters for " + "forward-socks4 directive in configuration file."); + continue; + } + + /* allocate a new node */ + cur_fwd = zalloc(sizeof(*cur_fwd)); + if (cur_fwd == NULL) + { + log_error(LOG_LEVEL_FATAL, "can't allocate memory for configuration"); + /* Never get here - LOG_LEVEL_FATAL causes program exit */ + continue; + } + + cur_fwd->type = SOCKS_4; + + /* Save the URL pattern */ + if (create_url_spec(cur_fwd->url, vec[0])) + { + log_error(LOG_LEVEL_ERROR, "Bad URL specifier for forward-socks4 " + "directive in configuration file."); + string_append(&config->proxy_args, + "
    \nWARNING: Bad URL specifier for " + "forward-socks4 directive in configuration file."); + continue; + } + + /* Parse the SOCKS proxy host[:port] */ + p = vec[1]; + + if (strcmp(p, ".") != 0) + { + cur_fwd->gateway_host = strdup(p); + + if (NULL != (p = strchr(cur_fwd->gateway_host, ':'))) + { + *p++ = '\0'; + cur_fwd->gateway_port = atoi(p); + } + if (cur_fwd->gateway_port <= 0) + { + cur_fwd->gateway_port = 1080; + } + } + + /* Parse the parent HTTP proxy host[:port] */ + p = vec[2]; + + if (strcmp(p, ".") != 0) + { + cur_fwd->forward_host = strdup(p); + + if (NULL != (p = strchr(cur_fwd->forward_host, ':'))) + { + *p++ = '\0'; + cur_fwd->forward_port = atoi(p); + } + + if (cur_fwd->forward_port <= 0) + { + cur_fwd->forward_port = 8000; + } + } + + /* Add to list. */ + cur_fwd->next = config->forward; + config->forward = cur_fwd; + + continue; + +/* ************************************************************************* + * forward-socks4a url-pattern socks-proxy[:port] (.|http-proxy[:port]) + * *************************************************************************/ + case hash_forward_socks4a: + vec_count = ssplit(arg, " \t", vec, SZ(vec), 1, 1); + + if (vec_count != 3) + { + log_error(LOG_LEVEL_ERROR, "Wrong number of parameters for " + "forward-socks4a directive in configuration file."); + string_append(&config->proxy_args, + "
    \nWARNING: Wrong number of parameters for " + "forward-socks4a directive in configuration file."); + continue; + } + + /* allocate a new node */ + cur_fwd = zalloc(sizeof(*cur_fwd)); + if (cur_fwd == NULL) + { + log_error(LOG_LEVEL_FATAL, "can't allocate memory for configuration"); + /* Never get here - LOG_LEVEL_FATAL causes program exit */ + continue; + } + + cur_fwd->type = SOCKS_4A; + + /* Save the URL pattern */ + if (create_url_spec(cur_fwd->url, vec[0])) + { + log_error(LOG_LEVEL_ERROR, "Bad URL specifier for forward-socks4a " + "directive in configuration file."); + string_append(&config->proxy_args, + "
    \nWARNING: Bad URL specifier for " + "forward-socks4a directive in configuration file."); + continue; + } + + /* Parse the SOCKS proxy host[:port] */ + p = vec[1]; + + cur_fwd->gateway_host = strdup(p); + + if (NULL != (p = strchr(cur_fwd->gateway_host, ':'))) + { + *p++ = '\0'; + cur_fwd->gateway_port = atoi(p); + } + if (cur_fwd->gateway_port <= 0) + { + cur_fwd->gateway_port = 1080; + } + + /* Parse the parent HTTP proxy host[:port] */ + p = vec[2]; + + if (strcmp(p, ".") != 0) + { + cur_fwd->forward_host = strdup(p); + + if (NULL != (p = strchr(cur_fwd->forward_host, ':'))) + { + *p++ = '\0'; + cur_fwd->forward_port = atoi(p); + } + + if (cur_fwd->forward_port <= 0) + { + cur_fwd->forward_port = 8000; + } + } + + /* Add to list. */ + cur_fwd->next = config->forward; + config->forward = cur_fwd; + + continue; + +/* ************************************************************************* + * jarfile jar-file-name + * In logdir by default + * *************************************************************************/ +#ifdef FEATURE_COOKIE_JAR + case hash_jarfile : + freez(config->jarfile); + config->jarfile = make_path(config->logdir, arg); + continue; +#endif /* def FEATURE_COOKIE_JAR */ + +/* ************************************************************************* + * listen-address [ip][:port] + * *************************************************************************/ + case hash_listen_address : + freez(config->haddr); + config->haddr = strdup(arg); + continue; + +/* ************************************************************************* + * logdir directory-name + * *************************************************************************/ + case hash_logdir : + freez(config->logdir); + config->logdir = make_path(NULL, arg); + continue; + +/* ************************************************************************* + * logfile log-file-name + * In logdir by default + * *************************************************************************/ + case hash_logfile : + freez(config->logfile); + config->logfile = no_daemon ? NULL : make_path(config->logdir, arg); + continue; + +/* ************************************************************************* + * permit-access source-ip[/significant-bits] [dest-ip[/significant-bits]] + * *************************************************************************/ +#ifdef FEATURE_ACL + case hash_permit_access: + vec_count = ssplit(arg, " \t", vec, SZ(vec), 1, 1); + + if ((vec_count != 1) && (vec_count != 2)) + { + log_error(LOG_LEVEL_ERROR, "Wrong number of parameters for " + "permit-access directive in configuration file."); + string_append(&config->proxy_args, + "
    \nWARNING: Wrong number of parameters for " + "permit-access directive in configuration file.

    \n"); + + continue; + } + + /* allocate a new node */ + cur_acl = (struct access_control_list *) zalloc(sizeof(*cur_acl)); + + if (cur_acl == NULL) + { + log_error(LOG_LEVEL_FATAL, "can't allocate memory for configuration"); + /* Never get here - LOG_LEVEL_FATAL causes program exit */ + continue; + } + cur_acl->action = ACL_PERMIT; + + if (acl_addr(vec[0], cur_acl->src) < 0) + { + log_error(LOG_LEVEL_ERROR, "Invalid source IP for permit-access " + "directive in configuration file: \"%s\"", vec[0]); + string_append(&config->proxy_args, + "
    \nWARNING: Invalid source IP for permit-access directive" + " in configuration file: \""); + string_append(&config->proxy_args, + vec[0]); + string_append(&config->proxy_args, + "\"

    \n"); + freez(cur_acl); + continue; + } + if (vec_count == 2) + { + if (acl_addr(vec[1], cur_acl->dst) < 0) + { + log_error(LOG_LEVEL_ERROR, "Invalid destination IP for " + "permit-access directive in configuration file: \"%s\"", + vec[0]); + string_append(&config->proxy_args, + "
    \nWARNING: Invalid destination IP for permit-access directive" + " in configuration file: \""); + string_append(&config->proxy_args, + vec[0]); + string_append(&config->proxy_args, + "\"

    \n"); + freez(cur_acl); + continue; + } + } + + /* + * Add it to the list. Note we reverse the list to get the + * behaviour the user expects. With both the ACL and + * actions file, the last match wins. However, the internal + * implementations are different: The actions file is stored + * in the same order as the file, and scanned completely. + * With the ACL, we reverse the order as we load it, then + * when we scan it we stop as soon as we get a match. + */ + cur_acl->next = config->acl; + config->acl = cur_acl; + + continue; +#endif /* def FEATURE_ACL */ + +/* ************************************************************************* + * proxy-info-url url + * *************************************************************************/ + case hash_proxy_info_url : + freez(config->proxy_info_url); + config->proxy_info_url = strdup(arg); + continue; + +/* ************************************************************************* + * re_filterfile file-name + * In confdir by default. + * *************************************************************************/ + case hash_filterfile : + freez(config->re_filterfile); + config->re_filterfile = make_path(config->confdir, arg); + continue; + +/* ************************************************************************* + * single-threaded + * *************************************************************************/ + case hash_single_threaded : + config->multi_threaded = 0; + continue; + +/* ************************************************************************* + * toggle (0|1) + * *************************************************************************/ +#ifdef FEATURE_TOGGLE + case hash_toggle : + g_bToggleIJB = atoi(arg); + continue; +#endif /* def FEATURE_TOGGLE */ + +/* ************************************************************************* + * trust-info-url url + * *************************************************************************/ +#ifdef FEATURE_TRUST + case hash_trust_info_url : + enlist(config->trust_info, arg); + continue; +#endif /* def FEATURE_TRUST */ + +/* ************************************************************************* + * trustfile filename + * (In confdir by default.) + * *************************************************************************/ +#ifdef FEATURE_TRUST + case hash_trustfile : + freez(config->trustfile); + config->trustfile = make_path(config->confdir, arg); + continue; +#endif /* def FEATURE_TRUST */ + +/* ************************************************************************* + * usermanual url + * *************************************************************************/ + case hash_usermanual : + freez(config->usermanual); + config->usermanual = strdup(arg); + continue; + +/* ************************************************************************* + * Win32 Console options: + * *************************************************************************/ + +/* ************************************************************************* + * hide-console + * *************************************************************************/ +#ifdef _WIN_CONSOLE + case hash_hide_console : + hideConsole = 1; + continue; +#endif /*def _WIN_CONSOLE*/ + + +/* ************************************************************************* + * Win32 GUI options: + * *************************************************************************/ + +#if defined(_WIN32) && ! defined(_WIN_CONSOLE) +/* ************************************************************************* + * activity-animation (0|1) + * *************************************************************************/ + case hash_activity_animation : + g_bShowActivityAnimation = atoi(arg); + continue; + +/* ************************************************************************* + * close-button-minimizes (0|1) + * *************************************************************************/ + case hash_close_button_minimizes : + g_bCloseHidesWindow = atoi(arg); + continue; + +/* ************************************************************************* + * log-buffer-size (0|1) + * *************************************************************************/ + case hash_log_buffer_size : + g_bLimitBufferSize = atoi(arg); + continue; + +/* ************************************************************************* + * log-font-name fontnane + * *************************************************************************/ + case hash_log_font_name : + strcpy( g_szFontFaceName, arg ); + continue; + +/* ************************************************************************* + * log-font-size n + * *************************************************************************/ + case hash_log_font_size : + g_nFontSize = atoi(arg); + continue; + +/* ************************************************************************* + * log-highlight-messages (0|1) + * *************************************************************************/ + case hash_log_highlight_messages : + g_bHighlightMessages = atoi(arg); + continue; + +/* ************************************************************************* + * log-max-lines n + * *************************************************************************/ + case hash_log_max_lines : + g_nMaxBufferLines = atoi(arg); + continue; + +/* ************************************************************************* + * log-messages (0|1) + * *************************************************************************/ + case hash_log_messages : + g_bLogMessages = atoi(arg); + continue; + +/* ************************************************************************* + * show-on-task-bar (0|1) + * *************************************************************************/ + case hash_show_on_task_bar : + g_bShowOnTaskBar = atoi(arg); + continue; + +#endif /* defined(_WIN32) && ! defined(_WIN_CONSOLE) */ + + +/* ************************************************************************* + * Warnings about unsupported features + * *************************************************************************/ +#ifndef FEATURE_ACL + case hash_deny_access: +#endif /* ndef FEATURE_ACL */ +#ifndef FEATURE_CGI_EDIT_ACTIONS + case hash_enable_edit_actions: + case hash_enable_remote_toggle: +#endif /* def FEATURE_CGI_EDIT_ACTIONS */ +#ifndef FEATURE_COOKIE_JAR + case hash_jarfile : +#endif /* ndef FEATURE_COOKIE_JAR */ +#ifndef FEATURE_ACL + case hash_permit_access: +#endif /* ndef FEATURE_ACL */ +#ifndef FEATURE_TOGGLE + case hash_toggle : +#endif /* ndef FEATURE_TOGGLE */ +#ifndef FEATURE_TRUST + case hash_trustfile : + case hash_trust_info_url : +#endif /* ndef FEATURE_TRUST */ + +#ifndef _WIN_CONSOLE + case hash_hide_console : +#endif /* ndef _WIN_CONSOLE */ + +#if defined(_WIN_CONSOLE) || ! defined(_WIN32) + case hash_activity_animation : + case hash_close_button_minimizes : + case hash_log_buffer_size : + case hash_log_font_name : + case hash_log_font_size : + case hash_log_highlight_messages : + case hash_log_max_lines : + case hash_log_messages : + case hash_show_on_task_bar : +#endif /* defined(_WIN_CONSOLE) || ! defined(_WIN32) */ + /* These warnings are annoying - so hide them. -- Jon */ + /* log_error(LOG_LEVEL_INFO, "Unsupported directive \"%s\" ignored.", cmd); */ + continue; + +/* *************************************************************************/ + default : +/* *************************************************************************/ + /* + * I decided that I liked this better as a warning than an + * error. To change back to an error, just change log level + * to LOG_LEVEL_FATAL. + */ + log_error(LOG_LEVEL_ERROR, "Unrecognized directive '%s' (%luul) in line %lu in " + "configuration file (%s).", buf, hash_string(cmd), linenum, configfile); + string_append(&config->proxy_args, "
    \nWARNING: unrecognized directive : "); + string_append(&config->proxy_args, buf); + string_append(&config->proxy_args, "

    \n"); + continue; + +/* *************************************************************************/ + } /* end switch( hash_string(cmd) ) */ + } /* end while ( read_config_line(...) ) */ + + fclose(configfp); + + if (NULL == config->proxy_args) + { + log_error(LOG_LEVEL_FATAL, "Out of memory loading config - insufficient memory for config->proxy_args"); + } + + init_error_log(Argv[0], config->logfile, config->debug); + + if (config->actions_file[0]) + { + add_loader(load_actions_file, config); + } + + if (config->re_filterfile) + { + add_loader(load_re_filterfile, config); + } + +#ifdef FEATURE_TRUST + if (config->trustfile) + { + add_loader(load_trustfile, config); + } +#endif /* def FEATURE_TRUST */ + +#ifdef FEATURE_COOKIE_JAR + if ( NULL != config->jarfile ) + { + if ( NULL == (config->jar = fopen(config->jarfile, "a")) ) + { + log_error(LOG_LEVEL_FATAL, "can't open jarfile '%s': %E", config->jarfile); + /* Never get here - LOG_LEVEL_FATAL causes program exit */ + } + setbuf(config->jar, NULL); + } +#endif /* def FEATURE_COOKIE_JAR */ + + if ( NULL == config->haddr ) + { + config->haddr = strdup( HADDR_DEFAULT ); + } + + if ( NULL != config->haddr ) + { + if (NULL != (p = strchr(config->haddr, ':'))) + { + *p++ = '\0'; + if (*p) + { + config->hport = atoi(p); + } + } + + if (config->hport <= 0) + { + *--p = ':'; + log_error(LOG_LEVEL_FATAL, "invalid bind port spec %s", config->haddr); + /* Never get here - LOG_LEVEL_FATAL causes program exit */ + } + if (*config->haddr == '\0') + { + config->haddr = NULL; + } + } + + /* + * Want to run all the loaders once now. + * + * Need to set up a fake csp, so they can get to the config. + */ + fake_csp = (struct client_state *) zalloc (sizeof(*fake_csp)); + fake_csp->config = config; + + if (run_loader(fake_csp)) + { + freez(fake_csp); + log_error(LOG_LEVEL_FATAL, "A loader failed while loading config file. Exiting."); + /* Never get here - LOG_LEVEL_FATAL causes program exit */ + } + freez(fake_csp); + +/* FIXME: this is a kludge for win32 */ +#if defined(_WIN32) && !defined (_WIN_CONSOLE) + + g_actions_file = config->actions_file[0]; /* FIXME only works for first action file */ + g_re_filterfile = config->re_filterfile; + +#ifdef FEATURE_TRUST + g_trustfile = config->trustfile; +#endif /* def FEATURE_TRUST */ + + +#endif /* defined(_WIN32) && !defined (_WIN_CONSOLE) */ +/* FIXME: end kludge */ + + + config->need_bind = 1; + + if (current_configfile) + { + struct configuration_spec * oldcfg = (struct configuration_spec *) + current_configfile->f; + /* + * Check if config->haddr,hport == oldcfg->haddr,hport + * + * The following could be written more compactly as a single, + * (unreadably long) if statement. + */ + config->need_bind = 0; + if (config->hport != oldcfg->hport) + { + config->need_bind = 1; + } + else if (config->haddr == NULL) + { + if (oldcfg->haddr != NULL) + { + config->need_bind = 1; + } + } + else if (oldcfg->haddr == NULL) + { + config->need_bind = 1; + } + else if (0 != strcmp(config->haddr, oldcfg->haddr)) + { + config->need_bind = 1; + } + + current_configfile->unloader = unload_configfile; + } + + fs->next = files->next; + files->next = fs; + + current_configfile = fs; + + return (config); +} + + +/********************************************************************* + * + * Function : savearg + * + * Description : Called from `load_config'. It saves each non-empty + * and non-comment line from config into + * config->proxy_args. This is used to create the + * show-proxy-args page. On error, frees + * config->proxy_args and sets it to NULL + * + * Parameters : + * 1 : command = config setting that was found + * 2 : argument = the setting's argument (if any) + * 3 : config = Configuration to save into. + * + * Returns : N/A + * + *********************************************************************/ +static void savearg(char *command, char *argument, struct configuration_spec * config) +{ + char * buf; + char * s; + + assert(command); + assert(*command); + assert(argument); + + /* + * Add config option name embedded in + * link to it's section in the user-manual + */ + buf = strdup("usermanual); + string_append(&buf, CONFIG_HELP_PREFIX); + string_join (&buf, string_toupper(command)); + string_append(&buf, "\">"); + string_append(&buf, command); + string_append(&buf, " "); + + if (NULL == buf) + { + freez(config->proxy_args); + return; + } + + if ( (NULL != argument) && ('\0' != *argument) ) + { + s = html_encode(argument); + if (NULL == s) + { + freez(buf); + freez(config->proxy_args); + return; + } + + if (strncmpic(argument, "http://", 7) == 0) + { + string_append(&buf, ""); + string_join (&buf, s); + string_append(&buf, ""); + } + else + { + string_join (&buf, s); + } + } + + string_append(&buf, "
    \n"); + + string_join(&config->proxy_args, buf); +} + + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/loadcfg.h b/src/loadcfg.h new file mode 100644 index 00000000..6216a90e --- /dev/null +++ b/src/loadcfg.h @@ -0,0 +1,192 @@ +#ifndef LOADCFG_H_INCLUDED +#define LOADCFG_H_INCLUDED +#define LOADCFG_H_VERSION "$Id: loadcfg.h,v 1.11 2002/03/26 22:29:55 swa Exp $" +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/loadcfg.h,v $ + * + * Purpose : Loads settings from the configuration file into + * global variables. This file contains both the + * routine to load the configuration and the global + * variables it writes to. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: loadcfg.h,v $ + * Revision 1.11 2002/03/26 22:29:55 swa + * we have a new homepage! + * + * Revision 1.10 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.9 2002/03/16 23:54:06 jongfoster + * Adding graceful termination feature, to help look for memory leaks. + * If you enable this (which, by design, has to be done by hand + * editing config.h) and then go to http://i.j.b/die, then the program + * will exit cleanly after the *next* request. It should free all the + * memory that was used. + * + * Revision 1.8 2001/12/30 14:07:32 steudten + * - Add signal handling (unix) + * - Add SIGHUP handler (unix) + * - Add creation of pidfile (unix) + * - Add action 'top' in rc file (RH) + * - Add entry 'SIGNALS' to manpage + * - Add exit message to logfile (unix) + * + * Revision 1.7 2001/07/30 22:08:36 jongfoster + * Tidying up #defines: + * - All feature #defines are now of the form FEATURE_xxx + * - Permanently turned off WIN_GUI_EDIT + * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS + * + * Revision 1.6 2001/07/29 18:58:15 jongfoster + * Removing nested #includes, adding forward declarations for needed + * structures, and changing the #define _FILENAME_H to FILENAME_H_INCLUDED. + * + * Revision 1.5 2001/05/26 00:28:36 jongfoster + * Automatic reloading of config file. + * Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32). + * Most of the global variables have been moved to a new + * struct configuration_spec, accessed through csp->config->globalname + * Most of the globals remaining are used by the Win32 GUI. + * + * Revision 1.4 2001/05/22 18:46:04 oes + * + * - Enabled filtering banners by size rather than URL + * by adding patterns that replace all standard banner + * sizes with the "Junkbuster" gif to the re_filterfile + * + * - Enabled filtering WebBugs by providing a pattern + * which kills all 1x1 images + * + * - Added support for PCRE_UNGREEDY behaviour to pcrs, + * which is selected by the (nonstandard and therefore + * capital) letter 'U' in the option string. + * It causes the quantifiers to be ungreedy by default. + * Appending a ? turns back to greedy (!). + * + * - Added a new interceptor ijb-send-banner, which + * sends back the "Junkbuster" gif. Without imagelist or + * MSIE detection support, or if tinygif = 1, or the + * URL isn't recognized as an imageurl, a lame HTML + * explanation is sent instead. + * + * - Added new feature, which permits blocking remote + * script redirects and firing back a local redirect + * to the browser. + * The feature is conditionally compiled, i.e. it + * can be disabled with --disable-fast-redirects, + * plus it must be activated by a "fast-redirects" + * line in the config file, has its own log level + * and of course wants to be displayed by show-proxy-args + * Note: Boy, all the #ifdefs in 1001 locations and + * all the fumbling with configure.in and acconfig.h + * were *way* more work than the feature itself :-( + * + * - Because a generic redirect template was needed for + * this, tinygif = 3 now uses the same. + * + * - Moved GIFs, and other static HTTP response templates + * to project.h + * + * - Some minor fixes + * + * - Removed some >400 CRs again (Jon, you really worked + * a lot! ;-) + * + * Revision 1.3 2001/05/20 01:21:20 jongfoster + * Version 2.9.4 checkin. + * - Merged popupfile and cookiefile, and added control over PCRS + * filtering, in new "permissionsfile". + * - Implemented LOG_LEVEL_FATAL, so that if there is a configuration + * file error you now get a message box (in the Win32 GUI) rather + * than the program exiting with no explanation. + * - Made killpopup use the PCRS MIME-type checking and HTTP-header + * skipping. + * - Removed tabs from "config" + * - Moved duplicated url parsing code in "loaders.c" to a new funcition. + * - Bumped up version number. + * + * Revision 1.2 2001/05/17 23:01:01 oes + * - Cleaned CRLF's from the sources and related files + * + * Revision 1.1.1.1 2001/05/15 13:58:58 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Don't need project.h, only this: */ +struct configuration_spec; + +/* Global variables */ + +#ifdef FEATURE_TOGGLE +/* indicates if ijb is enabled */ +extern int g_bToggleIJB; +#endif /* def FEATURE_TOGGLE */ + +extern const char *configfile; + + +/* The load_config function is now going to call: + * init_proxy_args, so it will need argc and argv. + * Since load_config will also be a signal handler, + * we need to have these globally available. + */ +extern int Argc; +extern const char **Argv; +extern short int MustReload; + + +extern struct configuration_spec * load_config(void); + +#ifdef FEATURE_GRACEFUL_TERMINATION +void unload_current_config_file(void); +#endif + +/* Revision control strings from this header and associated .c file */ +extern const char loadcfg_rcs[]; +extern const char loadcfg_h_rcs[]; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ndef LOADCFG_H_INCLUDED */ + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/loaders.c b/src/loaders.c new file mode 100644 index 00000000..3d7ca633 --- /dev/null +++ b/src/loaders.c @@ -0,0 +1,1449 @@ +const char loaders_rcs[] = "$Id: loaders.c,v 1.50 2002/04/24 02:12:16 oes Exp $"; +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/loaders.c,v $ + * + * Purpose : Functions to load and unload the various + * configuration files. Also contains code to manage + * the list of active loaders, and to automatically + * unload files that are no longer in use. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: loaders.c,v $ + * Revision 1.50 2002/04/24 02:12:16 oes + * Jon's multiple AF patch: Sweep now takes care of all AFs + * + * Revision 1.49 2002/04/19 16:53:25 jongfoster + * Optimize away a function call by using an equivalent macro + * + * Revision 1.48 2002/04/05 00:56:09 gliptak + * Correcting typo to clean up on realloc failure + * + * Revision 1.47 2002/03/26 22:29:55 swa + * we have a new homepage! + * + * Revision 1.46 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.45 2002/03/16 23:54:06 jongfoster + * Adding graceful termination feature, to help look for memory leaks. + * If you enable this (which, by design, has to be done by hand + * editing config.h) and then go to http://i.j.b/die, then the program + * will exit cleanly after the *next* request. It should free all the + * memory that was used. + * + * Revision 1.44 2002/03/16 21:51:00 jongfoster + * Fixing free(NULL). + * + * Revision 1.43 2002/03/16 20:28:34 oes + * Added descriptions to the filters so users will know what they select in the cgi editor + * + * Revision 1.42 2002/03/13 00:27:05 jongfoster + * Killing warnings + * + * Revision 1.41 2002/03/12 01:42:50 oes + * Introduced modular filters + * + * Revision 1.40 2002/03/08 17:46:04 jongfoster + * Fixing int/size_t warnings + * + * Revision 1.39 2002/03/07 03:46:17 oes + * Fixed compiler warnings + * + * Revision 1.38 2002/03/06 22:54:35 jongfoster + * Automated function-comment nitpicking. + * + * Revision 1.37 2002/03/03 15:07:49 oes + * Re-enabled automatic config reloading + * + * Revision 1.36 2002/01/22 23:46:18 jongfoster + * Moving edit_read_line() and simple_read_line() to loaders.c, and + * extending them to support reading MS-DOS, Mac and UNIX style files + * on all platforms. + * + * Modifying read_config_line() (without changing it's prototype) to + * be a trivial wrapper for edit_read_line(). This means that we have + * one function to read a line and handle comments, which is common + * between the initialization code and the edit interface. + * + * Revision 1.35 2002/01/17 21:03:08 jongfoster + * Moving all our URL and URL pattern parsing code to urlmatch.c. + * + * Renaming free_url to free_url_spec, since it frees a struct url_spec. + * + * Revision 1.34 2001/12/30 14:07:32 steudten + * - Add signal handling (unix) + * - Add SIGHUP handler (unix) + * - Add creation of pidfile (unix) + * - Add action 'top' in rc file (RH) + * - Add entry 'SIGNALS' to manpage + * - Add exit message to logfile (unix) + * + * Revision 1.33 2001/11/13 00:16:38 jongfoster + * Replacing references to malloc.h with the standard stdlib.h + * (See ANSI or K&R 2nd Ed) + * + * Revision 1.32 2001/11/07 00:02:13 steudten + * Add line number in error output for lineparsing for + * actionsfile and configfile. + * Special handling for CLF added. + * + * Revision 1.31 2001/10/26 17:39:01 oes + * Removed csp->referrer + * Moved ijb_isspace and ijb_tolower to project.h + * + * Revision 1.30 2001/10/25 03:40:48 david__schmidt + * Change in porting tactics: OS/2's EMX porting layer doesn't allow multiple + * threads to call select() simultaneously. So, it's time to do a real, live, + * native OS/2 port. See defines for __EMX__ (the porting layer) vs. __OS2__ + * (native). Both versions will work, but using __OS2__ offers multi-threading. + * + * Revision 1.29 2001/10/23 21:38:53 jongfoster + * Adding error-checking to create_url_spec() + * + * Revision 1.28 2001/10/07 15:40:39 oes + * Replaced 6 boolean members of csp with one bitmap (csp->flags) + * + * Revision 1.27 2001/09/22 16:36:59 jongfoster + * Removing unused parameter fs from read_config_line() + * + * Revision 1.26 2001/09/22 14:05:22 jongfoster + * Bugfix: Multiple escaped "#" characters in a configuration + * file are now permitted. + * Also removing 3 unused headers. + * + * Revision 1.25 2001/09/13 22:44:03 jongfoster + * Adding {} to an if statement + * + * Revision 1.24 2001/07/30 22:08:36 jongfoster + * Tidying up #defines: + * - All feature #defines are now of the form FEATURE_xxx + * - Permanently turned off WIN_GUI_EDIT + * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS + * + * Revision 1.23 2001/07/20 15:51:54 oes + * Fixed indentation of prepocessor commands + * + * Revision 1.22 2001/07/20 15:16:17 haroon + * - per Guy's suggestion, added a while loop in sweep() to catch not just + * the last inactive CSP but all other consecutive inactive CSPs after that + * as well + * + * Revision 1.21 2001/07/18 17:26:24 oes + * Changed to conform to new pcrs interface + * + * Revision 1.20 2001/07/17 13:07:01 oes + * Fixed segv when last line in config files + * lacked a terminating (\r)\n + * + * Revision 1.19 2001/07/13 14:01:54 oes + * Removed all #ifdef PCRS + * + * Revision 1.18 2001/06/29 21:45:41 oes + * Indentation, CRLF->LF, Tab-> Space + * + * Revision 1.17 2001/06/29 13:31:51 oes + * Various adaptions + * + * Revision 1.16 2001/06/09 10:55:28 jongfoster + * Changing BUFSIZ ==> BUFFER_SIZE + * + * Revision 1.15 2001/06/07 23:14:14 jongfoster + * Removing ACL and forward file loaders - these + * files have been merged into the config file. + * Cosmetic: Moving unloader funcs next to their + * respective loader funcs + * + * Revision 1.14 2001/06/01 03:27:04 oes + * Fixed line continuation problem + * + * Revision 1.13 2001/05/31 21:28:49 jongfoster + * Removed all permissionsfile code - it's now called the actions + * file, and (almost) all the code is in actions.c + * + * Revision 1.12 2001/05/31 17:32:31 oes + * + * - Enhanced domain part globbing with infix and prefix asterisk + * matching and optional unanchored operation + * + * Revision 1.11 2001/05/29 23:25:24 oes + * + * - load_config_line() and load_permissions_file() now use chomp() + * + * Revision 1.10 2001/05/29 09:50:24 jongfoster + * Unified blocklist/imagelist/permissionslist. + * File format is still under discussion, but the internal changes + * are (mostly) done. + * + * Also modified interceptor behaviour: + * - We now intercept all URLs beginning with one of the following + * prefixes (and *only* these prefixes): + * * http://i.j.b/ + * * http://ijbswa.sf.net/config/ + * * http://ijbswa.sourceforge.net/config/ + * - New interceptors "home page" - go to http://i.j.b/ to see it. + * - Internal changes so that intercepted and fast redirect pages + * are not replaced with an image. + * - Interceptors now have the option to send a binary page direct + * to the client. (i.e. ijb-send-banner uses this) + * - Implemented show-url-info interceptor. (Which is why I needed + * the above interceptors changes - a typical URL is + * "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif". + * The previous mechanism would not have intercepted that, and + * if it had been intercepted then it then it would have replaced + * it with an image.) + * + * Revision 1.9 2001/05/26 17:12:07 jongfoster + * Fatal errors loading configuration files now give better error messages. + * + * Revision 1.8 2001/05/26 00:55:20 jongfoster + * Removing duplicated code. load_forwardfile() now uses create_url_spec() + * + * Revision 1.7 2001/05/26 00:28:36 jongfoster + * Automatic reloading of config file. + * Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32). + * Most of the global variables have been moved to a new + * struct configuration_spec, accessed through csp->config->globalname + * Most of the globals remaining are used by the Win32 GUI. + * + * Revision 1.6 2001/05/23 12:27:33 oes + * + * Fixed ugly indentation of my last changes + * + * Revision 1.5 2001/05/23 10:39:05 oes + * - Added support for escaping the comment character + * in config files by a backslash + * - Added support for line continuation in config + * files + * - Fixed a buffer overflow bug with long config lines + * + * Revision 1.4 2001/05/22 18:56:28 oes + * CRLF -> LF + * + * Revision 1.3 2001/05/20 01:21:20 jongfoster + * Version 2.9.4 checkin. + * - Merged popupfile and cookiefile, and added control over PCRS + * filtering, in new "permissionsfile". + * - Implemented LOG_LEVEL_FATAL, so that if there is a configuration + * file error you now get a message box (in the Win32 GUI) rather + * than the program exiting with no explanation. + * - Made killpopup use the PCRS MIME-type checking and HTTP-header + * skipping. + * - Removed tabs from "config" + * - Moved duplicated url parsing code in "loaders.c" to a new funcition. + * - Bumped up version number. + * + * Revision 1.2 2001/05/17 23:01:01 oes + * - Cleaned CRLF's from the sources and related files + * + * Revision 1.1.1.1 2001/05/15 13:58:59 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(_WIN32) && !defined(__OS2__) +#include +#endif + +#include "project.h" +#include "list.h" +#include "loaders.h" +#include "filters.h" +#include "parsers.h" +#include "jcc.h" +#include "miscutil.h" +#include "errlog.h" +#include "actions.h" +#include "urlmatch.h" + +const char loaders_h_rcs[] = LOADERS_H_VERSION; + +/* + * Currently active files. + * These are also entered in the main linked list of files. + */ + +#ifdef FEATURE_TRUST +static struct file_list *current_trustfile = NULL; +#endif /* def FEATURE_TRUST */ + +static struct file_list *current_re_filterfile = NULL; + + + +/********************************************************************* + * + * Function : sweep + * + * Description : Basically a mark and sweep garbage collector, it is run + * (by the parent thread) every once in a while to reclaim memory. + * + * It uses a mark and sweep strategy: + * 1) mark all files as inactive + * + * 2) check with each client: + * if it is active, mark its files as active + * if it is inactive, free its resources + * + * 3) free the resources of all of the files that + * are still marked as inactive (and are obsolete). + * + * N.B. files that are not obsolete don't have an unloader defined. + * + * Parameters : None + * + * Returns : N/A + * + *********************************************************************/ +void sweep(void) +{ + struct file_list *fl, *nfl; + struct client_state *csp, *ncsp; + int i; + + /* clear all of the file's active flags */ + for ( fl = files->next; NULL != fl; fl = fl->next ) + { + fl->active = 0; + } + + for (csp = clients; csp && (NULL != (ncsp = csp->next)) ; csp = csp->next) + { + if (ncsp->flags & CSP_FLAG_ACTIVE) + { + /* mark this client's files as active */ + + /* + * Always have a configuration file. + * (Also note the slightly non-standard extra + * indirection here.) + */ + ncsp->config->config_file_list->active = 1; + + for (i = 0; i < MAX_ACTION_FILES; i++) + { + if (ncsp->actions_list[i]) /* actions files */ + { + ncsp->actions_list[i]->active = 1; + } + } + + if (ncsp->rlist) /* pcrsjob files */ + { + ncsp->rlist->active = 1; + } + +#ifdef FEATURE_TRUST + if (ncsp->tlist) /* trust files */ + { + ncsp->tlist->active = 1; + } +#endif /* def FEATURE_TRUST */ + + } + else + /* + * this client is not active, release its resources + * and the ones of all inactive clients that might + * follow it + */ + { + while (!(ncsp->flags & CSP_FLAG_ACTIVE)) + { + csp->next = ncsp->next; + + freez(ncsp->ip_addr_str); + freez(ncsp->my_ip_addr_str); + freez(ncsp->my_hostname); + freez(ncsp->x_forwarded); + freez(ncsp->iob->buf); + + free_http_request(ncsp->http); + + destroy_list(ncsp->headers); + destroy_list(ncsp->cookie_list); + + free_current_action(ncsp->action); + +#ifdef FEATURE_STATISTICS + urls_read++; + if (ncsp->flags & CSP_FLAG_REJECTED) + { + urls_rejected++; + } +#endif /* def FEATURE_STATISTICS */ + + freez(ncsp); + + /* are there any more in sequence after it? */ + if( (ncsp = csp->next) == NULL) + break; + } + } + } + + for (fl = files; fl && ((nfl = fl->next) != NULL) ; fl = fl->next) + { + if ( ( 0 == nfl->active ) && ( NULL != nfl->unloader ) ) + { + fl->next = nfl->next; + + (nfl->unloader)(nfl->f); + + freez(nfl->filename); + + freez(nfl); + } + } + +} + + +/********************************************************************* + * + * Function : check_file_changed + * + * Description : Helper function to check if a file needs reloading. + * If "current" is still current, return it. Otherwise + * allocates a new (zeroed) "struct file_list", fills + * in the disk file name and timestamp, and returns it. + * + * Parameters : + * 1 : current = The file_list currently being used - will + * be checked to see if it is out of date. + * May be NULL (which is treated as out of + * date). + * 2 : filename = Name of file to check. + * 3 : newfl = New file list. [Output only] + * This will be set to NULL, OR a struct + * file_list newly allocated on the + * heap, with the filename and lastmodified + * fields filled, and all others zeroed. + * + * Returns : If file unchanged: 0 (and sets newfl == NULL) + * If file changed: 1 and sets newfl != NULL + * On error: 1 and sets newfl == NULL + * + *********************************************************************/ +int check_file_changed(const struct file_list * current, + const char * filename, + struct file_list ** newfl) +{ + struct file_list *fs; + struct stat statbuf[1]; + + *newfl = NULL; + + if (stat(filename, statbuf) < 0) + { + /* Error, probably file not found. */ + return 1; + } + + if (current + && (current->lastmodified == statbuf->st_mtime) + && (0 == strcmp(current->filename, filename))) + { + return 0; + } + + fs = (struct file_list *)zalloc(sizeof(struct file_list)); + if (fs == NULL) + { + /* Out of memory error */ + return 1; + } + + fs->filename = strdup(filename); + fs->lastmodified = statbuf->st_mtime; + + if (fs->filename == NULL) + { + /* Out of memory error */ + freez (fs); + return 1; + } + *newfl = fs; + return 1; +} + + +/********************************************************************* + * + * Function : simple_read_line + * + * Description : Read a single line from a file and return it. + * This is basically a version of fgets() that malloc()s + * it's own line buffer. Note that the buffer will + * always be a multiple of BUFFER_SIZE bytes long. + * Therefore if you are going to keep the string for + * an extended period of time, you should probably + * strdup() it and free() the original, to save memory. + * + * + * Parameters : + * 1 : dest = destination for newly malloc'd pointer to + * line data. Will be set to NULL on error. + * 2 : fp = File to read from + * 3 : newline = Standard for newlines in the file. + * Will be unchanged if it's value on input is not + * NEWLINE_UNKNOWN. + * On output, may be changed from NEWLINE_UNKNOWN to + * actual convention in file. + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory + * JB_ERR_FILE on EOF. + * + *********************************************************************/ +jb_err simple_read_line(FILE *fp, char **dest, int *newline) +{ + size_t len = 0; + size_t buflen = BUFFER_SIZE; + char * buf; + char * p; + int ch; + int realnewline = NEWLINE_UNKNOWN; + + if (NULL == (buf = malloc(buflen))) + { + return JB_ERR_MEMORY; + } + + p = buf; + +/* + * Character codes. If you have a wierd compiler and the following are + * incorrect, you also need to fix NEWLINE() in loaders.h + */ +#define CHAR_CR '\r' /* ASCII 13 */ +#define CHAR_LF '\n' /* ASCII 10 */ + + for (;;) + { + ch = getc(fp); + if (ch == EOF) + { + if (len > 0) + { + *p = '\0'; + *dest = buf; + return JB_ERR_OK; + } + else + { + free(buf); + *dest = NULL; + return JB_ERR_FILE; + } + } + else if (ch == CHAR_CR) + { + ch = getc(fp); + if (ch == CHAR_LF) + { + if (*newline == NEWLINE_UNKNOWN) + { + *newline = NEWLINE_DOS; + } + } + else + { + if (ch != EOF) + { + ungetc(ch, fp); + } + if (*newline == NEWLINE_UNKNOWN) + { + *newline = NEWLINE_MAC; + } + } + *p = '\0'; + *dest = buf; + if (*newline == NEWLINE_UNKNOWN) + { + *newline = realnewline; + } + return JB_ERR_OK; + } + else if (ch == CHAR_LF) + { + *p = '\0'; + *dest = buf; + if (*newline == NEWLINE_UNKNOWN) + { + *newline = NEWLINE_UNIX; + } + return JB_ERR_OK; + } + else if (ch == 0) + { + *p = '\0'; + *dest = buf; + return JB_ERR_OK; + } + + *p++ = ch; + + if (++len >= buflen) + { + buflen += BUFFER_SIZE; + if (NULL == (p = realloc(buf, buflen))) + { + free(buf); + return JB_ERR_MEMORY; + } + buf = p; + p = buf + len; + } + } +} + + +/********************************************************************* + * + * Function : edit_read_line + * + * Description : Read a single non-empty line from a file and return + * it. Trims comments, leading and trailing whitespace + * and respects escaping of newline and comment char. + * Provides the line in 2 alternative forms: raw and + * preprocessed. + * - raw is the raw data read from the file. If the + * line is not modified, then this should be written + * to the new file. + * - prefix is any comments and blank lines that were + * read from the file. If the line is modified, then + * this should be written out to the file followed + * by the modified data. (If this string is non-empty + * then it will have a newline at the end). + * - data is the actual data that will be parsed + * further by appropriate routines. + * On EOF, the 3 strings will all be set to NULL and + * 0 will be returned. + * + * Parameters : + * 1 : fp = File to read from + * 2 : raw_out = destination for newly malloc'd pointer to + * raw line data. May be NULL if you don't want it. + * 3 : prefix_out = destination for newly malloc'd pointer to + * comments. May be NULL if you don't want it. + * 4 : data_out = destination for newly malloc'd pointer to + * line data with comments and leading/trailing spaces + * removed, and line continuation performed. May be + * NULL if you don't want it. + * 5 : newline = Standard for newlines in the file. + * On input, set to value to use or NEWLINE_UNKNOWN. + * On output, may be changed from NEWLINE_UNKNOWN to + * actual convention in file. May be NULL if you + * don't want it. + * 6 : line_number = Line number in file. In "lines" as + * reported by a text editor, not lines containing data. + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory + * JB_ERR_FILE on EOF. + * + *********************************************************************/ +jb_err edit_read_line(FILE *fp, + char **raw_out, + char **prefix_out, + char **data_out, + int *newline, + unsigned long *line_number) +{ + char *p; /* Temporary pointer */ + char *linebuf; /* Line read from file */ + char *linestart; /* Start of linebuf, usually first non-whitespace char */ + int contflag = 0; /* Nonzero for line continuation - i.e. line ends '\' */ + int is_empty = 1; /* Flag if not got any data yet */ + char *raw = NULL; /* String to be stored in raw_out */ + char *prefix = NULL; /* String to be stored in prefix_out */ + char *data = NULL; /* String to be stored in data_out */ + int scrapnewline; /* Used for (*newline) if newline==NULL */ + jb_err rval = JB_ERR_OK; + + assert(fp); + assert(raw_out || data_out); + assert(newline == NULL + || *newline == NEWLINE_UNKNOWN + || *newline == NEWLINE_UNIX + || *newline == NEWLINE_DOS + || *newline == NEWLINE_MAC); + + if (newline == NULL) + { + scrapnewline = NEWLINE_UNKNOWN; + newline = &scrapnewline; + } + + /* Set output parameters to NULL */ + if (raw_out) + { + *raw_out = NULL; + } + if (prefix_out) + { + *prefix_out = NULL; + } + if (data_out) + { + *data_out = NULL; + } + + /* Set string variables to new, empty strings. */ + + if (raw_out) + { + if ((raw = malloc(1)) == NULL) + { + return JB_ERR_MEMORY; + } + *raw = '\0'; + } + if (prefix_out) + { + if ((prefix = malloc(1)) == NULL) + { + freez(raw); + return JB_ERR_MEMORY; + } + *prefix = '\0'; + } + if (data_out) + { + if ((data = malloc(1)) == NULL) + { + freez(raw); + freez(prefix); + return JB_ERR_MEMORY; + } + *data = '\0'; + } + + /* Main loop. Loop while we need more data & it's not EOF. */ + + while ( (contflag || is_empty) + && (JB_ERR_OK == (rval = simple_read_line(fp, &linebuf, newline)))) + { + if (line_number) + { + (*line_number)++; + } + if (raw) + { + string_append(&raw,linebuf); + if (string_append(&raw,NEWLINE(*newline))) + { + freez(prefix); + freez(data); + free(linebuf); + return JB_ERR_MEMORY; + } + } + + /* Line continuation? Trim escape and set flag. */ + p = linebuf + strlen(linebuf) - 1; + contflag = ((*linebuf != '\0') && (*p == '\\')); + if (contflag) + { + *p = '\0'; + } + + /* Trim leading spaces if we're at the start of the line */ + linestart = linebuf; + if (*data == '\0') + { + /* Trim leading spaces */ + while (*linestart && isspace((int)(unsigned char)*linestart)) + { + linestart++; + } + } + + /* Handle comment characters. */ + p = linestart; + while ((p = strchr(p, '#')) != NULL) + { + /* Found a comment char.. */ + if ((p != linebuf) && (*(p-1) == '\\')) + { + /* ..and it's escaped, left-shift the line over the escape. */ + char *q = p - 1; + while ((*q = *(q + 1)) != '\0') + { + q++; + } + /* Now scan from just after the "#". */ + } + else + { + /* Real comment. Save it... */ + if (p == linestart) + { + /* Special case: Line only contains a comment, so all the + * previous whitespace is considered part of the comment. + * Undo the whitespace skipping, if any. + */ + linestart = linebuf; + p = linestart; + } + if (prefix) + { + string_append(&prefix,p); + if (string_append(&prefix, NEWLINE(*newline))) + { + freez(raw); + freez(data); + free(linebuf); + return JB_ERR_MEMORY; + } + } + + /* ... and chop off the rest of the line */ + *p = '\0'; + } + } /* END while (there's a # character) */ + + /* Write to the buffer */ + if (*linestart) + { + is_empty = 0; + if (data) + { + if (string_append(&data, linestart)) + { + freez(raw); + freez(prefix); + free(linebuf); + return JB_ERR_MEMORY; + } + } + } + + free(linebuf); + } /* END while(we need more data) */ + + /* Handle simple_read_line() errors - ignore EOF */ + if ((rval != JB_ERR_OK) && (rval != JB_ERR_FILE)) + { + freez(raw); + freez(prefix); + freez(data); + return rval; + } + + if (raw ? (*raw == '\0') : is_empty) + { + /* EOF and no data there. (Definition of "data" depends on whether + * the caller cares about "raw" or just "data"). + */ + + freez(raw); + freez(prefix); + freez(data); + + return JB_ERR_FILE; + } + else + { + /* Got at least some data */ + + /* Remove trailing whitespace */ + chomp(data); + + if (raw_out) + { + *raw_out = raw; + } + else + { + freez(raw); + } + if (prefix_out) + { + *prefix_out = prefix; + } + else + { + freez(prefix); + } + if (data_out) + { + *data_out = data; + } + else + { + freez(data); + } + return JB_ERR_OK; + } +} + + +/********************************************************************* + * + * Function : read_config_line + * + * Description : Read a single non-empty line from a file and return + * it. Trims comments, leading and trailing whitespace + * and respects escaping of newline and comment char. + * + * Parameters : + * 1 : buf = Buffer to use. + * 2 : buflen = Size of buffer in bytes. + * 3 : fp = File to read from + * 4 : linenum = linenumber in file + * + * Returns : NULL on EOF or error + * Otherwise, returns buf. + * + *********************************************************************/ +char *read_config_line(char *buf, size_t buflen, FILE *fp, unsigned long *linenum) +{ + jb_err err; + char *buf2 = NULL; + err = edit_read_line(fp, NULL, NULL, &buf2, NULL, linenum); + if (err) + { + if (err == JB_ERR_MEMORY) + { + log_error(LOG_LEVEL_FATAL, "Out of memory loading a config file"); + } + return NULL; + } + else + { + assert(buf2); + assert(strlen(buf2) + 1U < buflen); + strncpy(buf, buf2, buflen - 1); + free(buf2); + buf[buflen - 1] = '\0'; + return buf; + } +} + + +#ifdef FEATURE_TRUST +/********************************************************************* + * + * Function : unload_trustfile + * + * Description : Unloads a trustfile. + * + * Parameters : + * 1 : f = the data structure associated with the trustfile. + * + * Returns : N/A + * + *********************************************************************/ +static void unload_trustfile(void *f) +{ + struct block_spec *cur = (struct block_spec *)f; + struct block_spec *next; + + while (cur != NULL) + { + next = cur->next; + + free_url_spec(cur->url); + free(cur); + + cur = next; + } + +} + + +#ifdef FEATURE_GRACEFUL_TERMINATION +/********************************************************************* + * + * Function : unload_current_trust_file + * + * Description : Unloads current trust file - reset to state at + * beginning of program. + * + * Parameters : None + * + * Returns : N/A + * + *********************************************************************/ +void unload_current_trust_file(void) +{ + if (current_trustfile) + { + current_trustfile->unloader = unload_trustfile; + current_trustfile = NULL; + } +} +#endif /* FEATURE_GRACEFUL_TERMINATION */ + + +/********************************************************************* + * + * Function : load_trustfile + * + * Description : Read and parse a trustfile and add to files list. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : 0 => Ok, everything else is an error. + * + *********************************************************************/ +int load_trustfile(struct client_state *csp) +{ + FILE *fp; + + struct block_spec *b, *bl; + struct url_spec **tl; + + char buf[BUFFER_SIZE], *p, *q; + int reject, trusted; + struct file_list *fs; + unsigned long linenum = 0; + + if (!check_file_changed(current_trustfile, csp->config->trustfile, &fs)) + { + /* No need to load */ + if (csp) + { + csp->tlist = current_trustfile; + } + return(0); + } + if (!fs) + { + goto load_trustfile_error; + } + + fs->f = bl = (struct block_spec *)zalloc(sizeof(*bl)); + if (bl == NULL) + { + goto load_trustfile_error; + } + + if ((fp = fopen(csp->config->trustfile, "r")) == NULL) + { + goto load_trustfile_error; + } + + tl = csp->config->trust_list; + + while (read_config_line(buf, sizeof(buf), fp, &linenum) != NULL) + { + trusted = 0; + reject = 1; + + if (*buf == '+') + { + trusted = 1; + *buf = '~'; + } + + if (*buf == '~') + { + reject = 0; + p = buf; + q = p+1; + while ((*p++ = *q++) != '\0') + { + /* nop */ + } + } + + /* skip blank lines */ + if (*buf == '\0') + { + continue; + } + + /* allocate a new node */ + if ((b = zalloc(sizeof(*b))) == NULL) + { + fclose(fp); + goto load_trustfile_error; + } + + /* add it to the list */ + b->next = bl->next; + bl->next = b; + + b->reject = reject; + + /* Save the URL pattern */ + if (create_url_spec(b->url, buf)) + { + fclose(fp); + goto load_trustfile_error; + } + + /* + * save a pointer to URL's spec in the list of trusted URL's, too + */ + if (trusted) + { + *tl++ = b->url; + /* FIXME BUFFER OVERFLOW if >=64 entries */ + } + } + + *tl = NULL; + + fclose(fp); + + /* the old one is now obsolete */ + if (current_trustfile) + { + current_trustfile->unloader = unload_trustfile; + } + + fs->next = files->next; + files->next = fs; + current_trustfile = fs; + + if (csp) + { + csp->tlist = fs; + } + + return(0); + +load_trustfile_error: + log_error(LOG_LEVEL_FATAL, "can't load trustfile '%s': %E", + csp->config->trustfile); + return(-1); + +} +#endif /* def FEATURE_TRUST */ + + +/********************************************************************* + * + * Function : unload_re_filterfile + * + * Description : Unload the re_filter list by freeing all chained + * re_filterfile specs and their data. + * + * Parameters : + * 1 : f = the data structure associated with the filterfile. + * + * Returns : N/A + * + *********************************************************************/ +static void unload_re_filterfile(void *f) +{ + struct re_filterfile_spec *a, *b = (struct re_filterfile_spec *)f; + + while (b != NULL) + { + a = b->next; + + destroy_list(b->patterns); + pcrs_free_joblist(b->joblist); + freez(b->name); + freez(b->description); + freez(b); + + b = a; + } + + return; +} + + +#ifdef FEATURE_GRACEFUL_TERMINATION +/********************************************************************* + * + * Function : unload_current_re_filterfile + * + * Description : Unloads current re_filter file - reset to state at + * beginning of program. + * + * Parameters : None + * + * Returns : N/A + * + *********************************************************************/ +void unload_current_re_filterfile(void) +{ + if (current_re_filterfile) + { + current_re_filterfile->unloader = unload_re_filterfile; + current_re_filterfile = NULL; + } +} +#endif + + +/********************************************************************* + * + * Function : load_re_filterfile + * + * Description : Load the re_filterfile. + * Generate a chained list of re_filterfile_spec's from + * the "FILTER: " blocks, compiling all their substitutions + * into chained lists of pcrs_job structs. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : 0 => Ok, everything else is an error. + * + *********************************************************************/ +int load_re_filterfile(struct client_state *csp) +{ + FILE *fp; + + struct re_filterfile_spec *new_bl, *bl = NULL; + struct file_list *fs; + + char buf[BUFFER_SIZE]; + int error; + unsigned long linenum = 0; + pcrs_job *dummy; + + /* + * No need to reload if unchanged + */ + if (!check_file_changed(current_re_filterfile, csp->config->re_filterfile, &fs)) + { + if (csp) + { + csp->rlist = current_re_filterfile; + } + return(0); + } + if (!fs) + { + goto load_re_filterfile_error; + } + + /* + * Open the file or fail + */ + if ((fp = fopen(csp->config->re_filterfile, "r")) == NULL) + { + goto load_re_filterfile_error; + } + + /* + * Read line by line + */ + while (read_config_line(buf, sizeof(buf), fp, &linenum) != NULL) + { + /* + * If this is the head of a new filter block, make it a + * re_filterfile spec of its own and chain it to the list: + */ + if (strncmp(buf, "FILTER:", 7) == 0) + { + new_bl = (struct re_filterfile_spec *)zalloc(sizeof(*bl)); + if (new_bl == NULL) + { + goto load_re_filterfile_error; + } + + new_bl->name = chomp(buf + 7); + + if (NULL != (new_bl->description = strchr(new_bl->name, ' '))) + { + *new_bl->description++ = '\0'; + new_bl->description = strdup(chomp(new_bl->description)); + } + else + { + new_bl->description = strdup("No description available for this filter"); + } + + new_bl->name = strdup(chomp(new_bl->name)); + + /* + * If this is the first filter block, chain it + * to the file_list rather than its (nonexistant) + * predecessor + */ + if (fs->f == NULL) + { + fs->f = new_bl; + } + else + { + bl->next = new_bl; + } + bl = new_bl; + + log_error(LOG_LEVEL_RE_FILTER, "Reading in filter \"%s\" (\"%s\")", bl->name, bl->description); + + continue; + } + + /* + * Else, save the expression, make it a pcrs_job + * and chain it into the current filter's joblist + */ + if (bl != NULL) + { + enlist(bl->patterns, buf); + + if ((dummy = pcrs_compile_command(buf, &error)) == NULL) + { + log_error(LOG_LEVEL_RE_FILTER, + "Adding re_filter job %s to filter %s failed with error %d.", buf, bl->name, error); + continue; + } + else + { + dummy->next = bl->joblist; + bl->joblist = dummy; + log_error(LOG_LEVEL_RE_FILTER, "Adding re_filter job %s to filter %s succeeded.", buf, bl->name); + } + } + else + { + log_error(LOG_LEVEL_ERROR, "Ignoring job %s outside filter block in %s, line %d", buf, csp->config->re_filterfile, linenum); + } + } + + fclose(fp); + + /* + * Schedule the now-obsolete old data for unloading + */ + if ( NULL != current_re_filterfile ) + { + current_re_filterfile->unloader = unload_re_filterfile; + } + + /* + * Chain this file into the global list of loaded files + */ + fs->next = files->next; + files->next = fs; + current_re_filterfile = fs; + + if (csp) + { + csp->rlist = fs; + } + + return( 0 ); + +load_re_filterfile_error: + log_error(LOG_LEVEL_FATAL, "can't load re_filterfile '%s': %E", + csp->config->re_filterfile); + return(-1); + +} + + +/********************************************************************* + * + * Function : add_loader + * + * Description : Called from `load_config'. Called once for each input + * file found in config. + * + * Parameters : + * 1 : loader = pointer to a function that can parse and load + * the appropriate config file. + * 2 : config = The configuration_spec to add the loader to. + * + * Returns : N/A + * + *********************************************************************/ +void add_loader(int (*loader)(struct client_state *), + struct configuration_spec * config) +{ + int i; + + for (i=0; i < NLOADERS; i++) + { + if (config->loaders[i] == NULL) + { + config->loaders[i] = loader; + break; + } + } + +} + + +/********************************************************************* + * + * Function : run_loader + * + * Description : Called from `load_config' and `listen_loop'. This + * function keeps the "csp" current with any file mods + * since the last loop. If a file is unchanged, the + * loader functions do NOT reload the file. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * Must be non-null. Reads: "csp->config" + * Writes: various data members. + * + * Returns : 0 => Ok, everything else is an error. + * + *********************************************************************/ +int run_loader(struct client_state *csp) +{ + int ret = 0; + int i; + + for (i=0; i < NLOADERS; i++) + { + if (csp->config->loaders[i] == NULL) + { + break; + } + ret |= (csp->config->loaders[i])(csp); + } + return(ret); + +} + + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/loaders.h b/src/loaders.h new file mode 100644 index 00000000..0b179e97 --- /dev/null +++ b/src/loaders.h @@ -0,0 +1,238 @@ +#ifndef LOADERS_H_INCLUDED +#define LOADERS_H_INCLUDED +#define LOADERS_H_VERSION "$Id: loaders.h,v 1.19 2002/03/26 22:29:55 swa Exp $" +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/loaders.h,v $ + * + * Purpose : Functions to load and unload the various + * configuration files. Also contains code to manage + * the list of active loaders, and to automatically + * unload files that are no longer in use. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: loaders.h,v $ + * Revision 1.19 2002/03/26 22:29:55 swa + * we have a new homepage! + * + * Revision 1.18 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.17 2002/03/16 23:54:06 jongfoster + * Adding graceful termination feature, to help look for memory leaks. + * If you enable this (which, by design, has to be done by hand + * editing config.h) and then go to http://i.j.b/die, then the program + * will exit cleanly after the *next* request. It should free all the + * memory that was used. + * + * Revision 1.16 2002/03/07 03:46:17 oes + * Fixed compiler warnings + * + * Revision 1.15 2002/01/22 23:46:18 jongfoster + * Moving edit_read_line() and simple_read_line() to loaders.c, and + * extending them to support reading MS-DOS, Mac and UNIX style files + * on all platforms. + * + * Modifying read_config_line() (without changing it's prototype) to + * be a trivial wrapper for edit_read_line(). This means that we have + * one function to read a line and handle comments, which is common + * between the initialization code and the edit interface. + * + * Revision 1.14 2002/01/17 21:03:08 jongfoster + * Moving all our URL and URL pattern parsing code to urlmatch.c. + * + * Renaming free_url to free_url_spec, since it frees a struct url_spec. + * + * Revision 1.13 2001/12/30 14:07:32 steudten + * - Add signal handling (unix) + * - Add SIGHUP handler (unix) + * - Add creation of pidfile (unix) + * - Add action 'top' in rc file (RH) + * - Add entry 'SIGNALS' to manpage + * - Add exit message to logfile (unix) + * + * Revision 1.12 2001/11/07 00:02:13 steudten + * Add line number in error output for lineparsing for + * actionsfile and configfile. + * Special handling for CLF added. + * + * Revision 1.11 2001/10/23 21:38:53 jongfoster + * Adding error-checking to create_url_spec() + * + * Revision 1.10 2001/09/22 16:36:59 jongfoster + * Removing unused parameter fs from read_config_line() + * + * Revision 1.9 2001/07/30 22:08:36 jongfoster + * Tidying up #defines: + * - All feature #defines are now of the form FEATURE_xxx + * - Permanently turned off WIN_GUI_EDIT + * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS + * + * Revision 1.8 2001/07/29 18:58:15 jongfoster + * Removing nested #includes, adding forward declarations for needed + * structures, and changing the #define _FILENAME_H to FILENAME_H_INCLUDED. + * + * Revision 1.7 2001/07/13 14:01:54 oes + * Removed all #ifdef PCRS + * + * Revision 1.6 2001/06/07 23:14:38 jongfoster + * Removing ACL and forward file loaders - these files have + * been merged into the config file. + * + * Revision 1.5 2001/05/31 21:28:49 jongfoster + * Removed all permissionsfile code - it's now called the actions + * file, and (almost) all the code is in actions.c + * + * Revision 1.4 2001/05/29 09:50:24 jongfoster + * Unified blocklist/imagelist/permissionslist. + * File format is still under discussion, but the internal changes + * are (mostly) done. + * + * Also modified interceptor behaviour: + * - We now intercept all URLs beginning with one of the following + * prefixes (and *only* these prefixes): + * * http://i.j.b/ + * * http://ijbswa.sf.net/config/ + * * http://ijbswa.sourceforge.net/config/ + * - New interceptors "home page" - go to http://i.j.b/ to see it. + * - Internal changes so that intercepted and fast redirect pages + * are not replaced with an image. + * - Interceptors now have the option to send a binary page direct + * to the client. (i.e. ijb-send-banner uses this) + * - Implemented show-url-info interceptor. (Which is why I needed + * the above interceptors changes - a typical URL is + * "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif". + * The previous mechanism would not have intercepted that, and + * if it had been intercepted then it then it would have replaced + * it with an image.) + * + * Revision 1.3 2001/05/26 00:28:36 jongfoster + * Automatic reloading of config file. + * Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32). + * Most of the global variables have been moved to a new + * struct configuration_spec, accessed through csp->config->globalname + * Most of the globals remaining are used by the Win32 GUI. + * + * Revision 1.2 2001/05/20 01:21:20 jongfoster + * Version 2.9.4 checkin. + * - Merged popupfile and cookiefile, and added control over PCRS + * filtering, in new "permissionsfile". + * - Implemented LOG_LEVEL_FATAL, so that if there is a configuration + * file error you now get a message box (in the Win32 GUI) rather + * than the program exiting with no explanation. + * - Made killpopup use the PCRS MIME-type checking and HTTP-header + * skipping. + * - Removed tabs from "config" + * - Moved duplicated url parsing code in "loaders.c" to a new funcition. + * - Bumped up version number. + * + * Revision 1.1.1.1 2001/05/15 13:59:00 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structures taken from project.h */ +struct client_state; +struct file_list; +struct configuration_spec; +struct url_spec; + +extern void sweep(void); +extern char *read_config_line(char *buf, size_t buflen, FILE *fp, unsigned long *linenum); +extern int check_file_changed(const struct file_list * current, + const char * filename, + struct file_list ** newfl); + +extern jb_err edit_read_line(FILE *fp, + char **raw_out, + char **prefix_out, + char **data_out, + int *newline, + unsigned long *line_number); + +extern jb_err simple_read_line(FILE *fp, char **dest, int *newline); + +/* + * Various types of newlines that a file may contain. + */ +#define NEWLINE_UNKNOWN 0 /* Newline convention in file is unknown */ +#define NEWLINE_UNIX 1 /* Newline convention in file is '\n' (ASCII 10) */ +#define NEWLINE_DOS 2 /* Newline convention in file is '\r\n' (ASCII 13,10) */ +#define NEWLINE_MAC 3 /* Newline convention in file is '\r' (ASCII 13) */ + +/* + * Types of newlines that a file may contain, as strings. If you have an + * extremely wierd compiler that does not have '\r' == CR == ASCII 13 and + * '\n' == LF == ASCII 10), then fix CHAR_CR and CHAR_LF in loaders.c as + * well as these definitions. + */ +#define NEWLINE(style) ((style)==NEWLINE_DOS ? "\r\n" : \ + ((style)==NEWLINE_MAC ? "\r" : "\n")) + + +extern short int MustReload; +extern int load_actions_file(struct client_state *csp); +extern int load_re_filterfile(struct client_state *csp); + +#ifdef FEATURE_TRUST +extern int load_trustfile(struct client_state *csp); +#endif /* def FEATURE_TRUST */ + +#ifdef FEATURE_GRACEFUL_TERMINATION +#ifdef FEATURE_TRUST +void unload_current_trust_file(void); +#endif +void unload_current_re_filterfile(void); +#endif /* FEATURE_GRACEFUL_TERMINATION */ + + +extern void add_loader(int (*loader)(struct client_state *), + struct configuration_spec * config); +extern int run_loader(struct client_state *csp); + +/* Revision control strings from this header and associated .c file */ +extern const char loaders_rcs[]; +extern const char loaders_h_rcs[]; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ndef LOADERS_H_INCLUDED */ + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/miscutil.c b/src/miscutil.c new file mode 100644 index 00000000..53b3b4d7 --- /dev/null +++ b/src/miscutil.c @@ -0,0 +1,1763 @@ +const char miscutil_rcs[] = "$Id: miscutil.c,v 1.37 2002/04/26 18:29:43 jongfoster Exp $"; +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/miscutil.c,v $ + * + * Purpose : zalloc, hash_string, safe_strerror, strcmpic, + * strncmpic, chomp, and MinGW32 strdup + * functions. + * These are each too small to deserve their own file + * but don't really fit in any other file. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: miscutil.c,v $ + * Revision 1.37 2002/04/26 18:29:43 jongfoster + * Fixing this Visual C++ warning: + * miscutil.c(710) : warning C4090: '=' : different 'const' qualifiers + * + * Revision 1.36 2002/04/26 12:55:38 oes + * New function string_toupper + * + * Revision 1.35 2002/03/26 22:29:55 swa + * we have a new homepage! + * + * Revision 1.34 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.33 2002/03/07 03:46:53 oes + * Fixed compiler warnings etc + * + * Revision 1.32 2002/03/06 23:02:57 jongfoster + * Removing tabs + * + * Revision 1.31 2002/03/05 04:52:42 oes + * Deleted non-errlog debugging code + * + * Revision 1.30 2002/03/04 18:27:42 oes + * - Deleted deletePidFile + * - Made write_pid_file use the --pidfile option value + * (or no PID file, if the option was absent) + * - Played styleguide police + * + * Revision 1.29 2002/03/04 02:08:02 david__schmidt + * Enable web editing of actions file on OS/2 (it had been broken all this time!) + * + * Revision 1.28 2002/03/03 09:18:03 joergs + * Made jumbjuster work on AmigaOS again. + * + * Revision 1.27 2002/01/21 00:52:32 jongfoster + * Adding string_join() + * + * Revision 1.26 2001/12/30 14:07:32 steudten + * - Add signal handling (unix) + * - Add SIGHUP handler (unix) + * - Add creation of pidfile (unix) + * - Add action 'top' in rc file (RH) + * - Add entry 'SIGNALS' to manpage + * - Add exit message to logfile (unix) + * + * Revision 1.25 2001/11/13 00:16:38 jongfoster + * Replacing references to malloc.h with the standard stdlib.h + * (See ANSI or K&R 2nd Ed) + * + * Revision 1.24 2001/11/05 21:41:43 steudten + * Add changes to be a real daemon just for unix os. + * (change cwd to /, detach from controlling tty, set + * process group and session leader to the own process. + * Add DBG() Macro. + * Add some fatal-error log message for failed malloc(). + * Add '-d' if compiled with 'configure --with-debug' to + * enable debug output. + * + * Revision 1.23 2001/10/29 03:48:10 david__schmidt + * OS/2 native needed a snprintf() routine. Added one to miscutil, brackedted + * by and __OS2__ ifdef. + * + * Revision 1.22 2001/10/26 17:39:38 oes + * Moved ijb_isspace and ijb_tolower to project.h + * + * Revision 1.21 2001/10/23 21:27:50 jongfoster + * Standardising error codes in string_append + * make_path() no longer adds '\\' if the dir already ends in '\\' (this + * is just copying a UNIX-specific fix to the Windows-specific part) + * + * Revision 1.20 2001/10/22 15:33:56 david__schmidt + * Special-cased OS/2 out of the Netscape-abort-on-404-in-js problem in + * filters.c. Added a FIXME in front of the offending code. I'll gladly + * put in a better/more robust fix for all parties if one is presented... + * It seems that just returning 200 instead of 404 would pretty much fix + * it for everyone, but I don't know all the history of the problem. + * + * Revision 1.19 2001/10/14 22:02:57 jongfoster + * New function string_append() which is like strsav(), but running + * out of memory isn't automatically FATAL. + * + * Revision 1.18 2001/09/20 13:33:43 steudten + * + * change long to int as return value in hash_string(). Remember the wraparound + * for int = long = sizeof(4) - thats maybe not what we want. + * + * Revision 1.17 2001/09/13 20:51:29 jongfoster + * Fixing potential problems with characters >=128 in simplematch() + * This was also a compiler warning. + * + * Revision 1.16 2001/09/10 10:56:59 oes + * Silenced compiler warnings + * + * Revision 1.15 2001/07/13 14:02:24 oes + * Removed vim-settings + * + * Revision 1.14 2001/06/29 21:45:41 oes + * Indentation, CRLF->LF, Tab-> Space + * + * Revision 1.13 2001/06/29 13:32:14 oes + * Removed logentry from cancelled commit + * + * Revision 1.12 2001/06/09 10:55:28 jongfoster + * Changing BUFSIZ ==> BUFFER_SIZE + * + * Revision 1.11 2001/06/07 23:09:19 jongfoster + * Cosmetic indentation changes. + * + * Revision 1.10 2001/06/07 14:51:38 joergs + * make_path() no longer adds '/' if the dir already ends in '/'. + * + * Revision 1.9 2001/06/07 14:43:17 swa + * slight mistake in make_path, unix path style is /. + * + * Revision 1.8 2001/06/05 22:32:01 jongfoster + * New function make_path() to splice directory and file names together. + * + * Revision 1.7 2001/06/03 19:12:30 oes + * introduced bindup() + * + * Revision 1.6 2001/06/01 18:14:49 jongfoster + * Changing the calls to strerr() to check HAVE_STRERR (which is defined + * in config.h if appropriate) rather than the NO_STRERR macro. + * + * Revision 1.5 2001/06/01 10:31:51 oes + * Added character class matching to trivimatch; renamed to simplematch + * + * Revision 1.4 2001/05/31 17:32:31 oes + * + * - Enhanced domain part globbing with infix and prefix asterisk + * matching and optional unanchored operation + * + * Revision 1.3 2001/05/29 23:10:09 oes + * + * + * - Introduced chomp() + * - Moved strsav() from showargs to miscutil + * + * Revision 1.2 2001/05/29 09:50:24 jongfoster + * Unified blocklist/imagelist/permissionslist. + * File format is still under discussion, but the internal changes + * are (mostly) done. + * + * Also modified interceptor behaviour: + * - We now intercept all URLs beginning with one of the following + * prefixes (and *only* these prefixes): + * * http://i.j.b/ + * * http://ijbswa.sf.net/config/ + * * http://ijbswa.sourceforge.net/config/ + * - New interceptors "home page" - go to http://i.j.b/ to see it. + * - Internal changes so that intercepted and fast redirect pages + * are not replaced with an image. + * - Interceptors now have the option to send a binary page direct + * to the client. (i.e. ijb-send-banner uses this) + * - Implemented show-url-info interceptor. (Which is why I needed + * the above interceptors changes - a typical URL is + * "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif". + * The previous mechanism would not have intercepted that, and + * if it had been intercepted then it then it would have replaced + * it with an image.) + * + * Revision 1.1.1.1 2001/05/15 13:59:00 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#include "config.h" + +#include +#include +#include +#if !defined(_WIN32) && !defined(__OS2__) +#include +#endif /* #if !defined(_WIN32) && !defined(__OS2__) */ +#include +#include +#include + +#include "project.h" +#include "miscutil.h" +#include "errlog.h" +#include "jcc.h" + +const char miscutil_h_rcs[] = MISCUTIL_H_VERSION; + +/********************************************************************* + * + * Function : zalloc + * + * Description : Malloc some memory and set it to '\0'. + * The way calloc() ought to be -acjc + * + * Parameters : + * 1 : size = Size of memory chunk to return. + * + * Returns : Pointer to newly malloc'd memory chunk. + * + *********************************************************************/ +void *zalloc(size_t size) +{ + void * ret; + + if ((ret = (void *)malloc(size)) != NULL) + { + memset(ret, 0, size); + } + + return(ret); + +} + + +#if defined(unix) +/********************************************************************* + * + * Function : write_pid_file + * + * Description : Writes a pid file with the pid of the main process + * + * Parameters : None + * + * Returns : N/A + * + *********************************************************************/ +void write_pid_file(void) +{ + FILE *fp; + + /* + * If no --pidfile option was given, + * we can live without one. + */ + if (pidfile == NULL) return; + + if ((fp = fopen(pidfile, "w")) == NULL) + { + log_error(LOG_LEVEL_INFO, "can't open pidfile '%s': %E", pidfile); + } + else + { + fprintf(fp, "%u\n", (unsigned int) getpid()); + fclose (fp); + } + return; + +} +#endif /* def unix */ + + +/********************************************************************* + * + * Function : hash_string + * + * Description : Take a string and compute a (hopefuly) unique numeric + * integer value. This has several uses, but being able + * to "switch" a string the one of my favorites. + * + * Parameters : + * 1 : s : string to be hashed. + * + * Returns : an unsigned long variable with the hashed value. + * + *********************************************************************/ +unsigned int hash_string( const char* s ) +{ + unsigned int h = 0; + + for ( ; *s; ++s ) + { + h = 5 * h + *s; + } + + return (h); + +} + + +#ifdef __MINGW32__ +/********************************************************************* + * + * Function : strdup + * + * Description : For some reason (which is beyond me), gcc and WIN32 + * don't like strdup. When a "free" is executed on a + * strdup'd ptr, it can at times freez up! So I just + * replaced it and problem was solved. + * + * Parameters : + * 1 : s = string to duplicate + * + * Returns : Pointer to newly malloc'ed copy of the string. + * + *********************************************************************/ +char *strdup( const char *s ) +{ + char * result = (char *)malloc( strlen(s)+1 ); + + if (result != NULL) + { + strcpy( result, s ); + } + + return( result ); +} + +#endif /* def __MINGW32__ */ + + + +/********************************************************************* + * + * Function : safe_strerror + * + * Description : Variant of the library routine strerror() which will + * work on systems without the library routine, and + * which should never return NULL. + * + * Parameters : + * 1 : err = the `errno' of the last operation. + * + * Returns : An "English" string of the last `errno'. Allocated + * with strdup(), so caller frees. May be NULL if the + * system is out of memory. + * + *********************************************************************/ +char *safe_strerror(int err) +{ + char *s = NULL; + char buf[BUFFER_SIZE]; + + +#ifdef HAVE_STRERROR + s = strerror(err); +#endif /* HAVE_STRERROR */ + + if (s == NULL) + { + sprintf(buf, "(errno = %d)", err); + s = buf; + } + + return(strdup(s)); + +} + + +/********************************************************************* + * + * Function : strcmpic + * + * Description : Case insensitive string comparison + * + * Parameters : + * 1 : s1 = string 1 to compare + * 2 : s2 = string 2 to compare + * + * Returns : 0 if s1==s2, Negative if s1s2 + * + *********************************************************************/ +int strcmpic(const char *s1, const char *s2) +{ + while (*s1 && *s2) + { + if ( ( *s1 != *s2 ) && ( ijb_tolower(*s1) != ijb_tolower(*s2) ) ) + { + break; + } + s1++, s2++; + } + return(ijb_tolower(*s1) - ijb_tolower(*s2)); + +} + + +/********************************************************************* + * + * Function : strncmpic + * + * Description : Case insensitive string comparison (upto n characters) + * + * Parameters : + * 1 : s1 = string 1 to compare + * 2 : s2 = string 2 to compare + * 3 : n = maximum characters to compare + * + * Returns : 0 if s1==s2, Negative if s1s2 + * + *********************************************************************/ +int strncmpic(const char *s1, const char *s2, size_t n) +{ + if (n <= 0) return(0); + + while (*s1 && *s2) + { + if ( ( *s1 != *s2 ) && ( ijb_tolower(*s1) != ijb_tolower(*s2) ) ) + { + break; + } + + if (--n <= 0) break; + + s1++, s2++; + } + return(ijb_tolower(*s1) - ijb_tolower(*s2)); + +} + + +/********************************************************************* + * + * Function : chomp + * + * Description : In-situ-eliminate all leading and trailing whitespace + * from a string. + * + * Parameters : + * 1 : s : string to be chomped. + * + * Returns : chomped string + * + *********************************************************************/ +char *chomp(char *string) +{ + char *p, *q, *r; + + /* + * strip trailing whitespace + */ + p = string + strlen(string); + while (p > string && ijb_isspace(*(p-1))) + { + p--; + } + *p = '\0'; + + /* + * find end of leading whitespace + */ + q = r = string; + while (*q && ijb_isspace(*q)) + { + q++; + } + + /* + * if there was any, move the rest forwards + */ + if (q != string) + { + while (q <= p) + { + *r++ = *q++; + } + } + + return(string); + +} + + +/********************************************************************* + * + * Function : strsav + * + * Description : Reallocate "old" and append text to it. This makes + * it easier to append to malloc'd strings. + * Running out of memory is a FATAL error. + * + * Parameters : + * 1 : old = Old text that is to be extended. Will be + * free()d by this routine. May be NULL. + * 2 : text_to_append = Text to be appended to old. + * May be NULL. + * + * Returns : Pointer to newly malloc'ed appended string. + * If there is no text to append, return old. Caller + * must free(). + * + *********************************************************************/ +char *strsav(char *old, const char *text_to_append) +{ + size_t old_len, new_len = 0; + char *p; + + if ((text_to_append == NULL) || (*text_to_append == '\0')) + { + return(old); + } + + if (NULL == old) + { + if ((p = strdup(text_to_append)) == NULL) + { + log_error(LOG_LEVEL_FATAL, "strdup() failed!"); + /* Never get here - LOG_LEVEL_FATAL causes program exit */ + } + return p; + } + + old_len = strlen(old); + new_len = old_len + strlen(text_to_append) + 1; + + if ((p = realloc(old, new_len)) == NULL) + { + log_error(LOG_LEVEL_FATAL, "realloc(%d) bytes failed!", new_len); + /* Never get here - LOG_LEVEL_FATAL causes program exit */ + } + + strcpy(p + old_len, text_to_append); + return(p); +} + + +/********************************************************************* + * + * Function : string_append + * + * Description : Reallocate target_string and append text to it. + * This makes it easier to append to malloc'd strings. + * This is similar to the (removed) strsav(), but + * running out of memory isn't catastrophic. + * + * Programming style: + * + * The following style provides sufficient error + * checking for this routine, with minimal clutter + * in the source code. It is recommended if you + * have many calls to this function: + * + * char * s = strdup(...); // don't check for error + * string_append(&s, ...); // don't check for error + * string_append(&s, ...); // don't check for error + * string_append(&s, ...); // don't check for error + * if (NULL == s) { ... handle error ... } + * + * OR, equivalently: + * + * char * s = strdup(...); // don't check for error + * string_append(&s, ...); // don't check for error + * string_append(&s, ...); // don't check for error + * if (string_append(&s, ...)) {... handle error ...} + * + * Parameters : + * 1 : target_string = Pointer to old text that is to be + * extended. *target_string will be free()d by this + * routine. target_string must be non-NULL. + * If *target_string is NULL, this routine will + * do nothing and return with an error - this allows + * you to make many calls to this routine and only + * check for errors after the last one. + * 2 : text_to_append = Text to be appended to old. + * Must not be NULL. + * + * Returns : JB_ERR_OK on success, and sets *target_string + * to newly malloc'ed appended string. Caller + * must free(*target_string). + * JB_ERR_MEMORY on out-of-memory. (And free()s + * *target_string and sets it to NULL). + * JB_ERR_MEMORY if *target_string is NULL. + * + *********************************************************************/ +jb_err string_append(char **target_string, const char *text_to_append) +{ + size_t old_len; + char *new_string; + + assert(target_string); + assert(text_to_append); + + if (*target_string == NULL) + { + return JB_ERR_MEMORY; + } + + if (*text_to_append == '\0') + { + return JB_ERR_OK; + } + + old_len = strlen(*target_string); + + if (NULL == (new_string = realloc(*target_string, + strlen(text_to_append) + old_len + 1))) + { + free(*target_string); + + *target_string = NULL; + return JB_ERR_MEMORY; + } + + strcpy(new_string + old_len, text_to_append); + + *target_string = new_string; + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : string_join + * + * Description : Join two strings together. Frees BOTH the original + * strings. If either or both input strings are NULL, + * fails as if it had run out of memory. + * + * For comparison, string_append requires that the + * second string is non-NULL, and doesn't free it. + * + * Rationale: Too often, we want to do + * string_append(s, html_encode(s2)). That assert()s + * if s2 is NULL or if html_encode() runs out of memory. + * It also leaks memory. Proper checking is cumbersome. + * The solution: string_join(s, html_encode(s2)) is safe, + * and will free the memory allocated by html_encode(). + * + * Parameters : + * 1 : target_string = Pointer to old text that is to be + * extended. *target_string will be free()d by this + * routine. target_string must be non-NULL. + * 2 : text_to_append = Text to be appended to old. + * + * Returns : JB_ERR_OK on success, and sets *target_string + * to newly malloc'ed appended string. Caller + * must free(*target_string). + * JB_ERR_MEMORY on out-of-memory, or if + * *target_string or text_to_append is NULL. (In + * this case, frees *target_string and text_to_append, + * sets *target_string to NULL). + * + *********************************************************************/ +jb_err string_join(char **target_string, char *text_to_append) +{ + jb_err err; + + assert(target_string); + + if (text_to_append == NULL) + { + freez(*target_string); + return JB_ERR_MEMORY; + } + + err = string_append(target_string, text_to_append); + + free(text_to_append); + + return err; +} + + +/********************************************************************* + * + * Function : string_toupper + * + * Description : Produce a copy of string with all convertible + * characters converted to uppercase. + * + * Parameters : + * 1 : string = string to convert + * + * Returns : Uppercase copy of string if possible, + * NULL on out-of-memory or if string was NULL. + * + *********************************************************************/ +char *string_toupper(const char *string) +{ + char *result, *p; + const char *q; + + if (!string || ((result = (char *) zalloc(strlen(string) + 1)) == NULL)) + { + return NULL; + } + + q = string; + p = result; + + while (*q != '\0') + { + *p++ = toupper(*q++); + } + + return result; + +} + + +/********************************************************************* + * + * Function : simplematch + * + * Description : String matching, with a (greedy) '*' wildcard that + * stands for zero or more arbitrary characters and + * character classes in [], which take both enumerations + * and ranges. + * + * Parameters : + * 1 : pattern = pattern for matching + * 2 : text = text to be matched + * + * Returns : 0 if match, else nonzero + * + *********************************************************************/ +int simplematch(char *pattern, char *text) +{ + unsigned char *pat = (unsigned char *) pattern; + unsigned char *txt = (unsigned char *) text; + unsigned char *fallback = pat; + int wildcard = 0; + + unsigned char lastchar = 'a'; + unsigned i; + unsigned char charmap[32]; + + + while (*txt) + { + + /* EOF pattern but !EOF text? */ + if (*pat == '\0') + { + return 1; + } + + /* '*' in the pattern? */ + if (*pat == '*') + { + + /* The pattern ends afterwards? Speed up the return. */ + if (*++pat == '\0') + { + return 0; + } + + /* Else, set wildcard mode and remember position after '*' */ + wildcard = 1; + fallback = pat; + } + + /* Character range specification? */ + if (*pat == '[') + { + memset(charmap, '\0', sizeof(charmap)); + + while (*++pat != ']') + { + if (!*pat) + { + return 1; + } + else if (*pat == '-') + { + if ((*++pat == ']') || *pat == '\0') + { + return(1); + } + for(i = lastchar; i <= *pat; i++) + { + charmap[i / 8] |= (1 << (i % 8)); + } + } + else + { + charmap[*pat / 8] |= (1 << (*pat % 8)); + lastchar = *pat; + } + } + } /* -END- if Character range specification */ + + + /* Compare: Char match, or char range match*/ + if ((*pat == *txt) + || ((*pat == ']') && (charmap[*txt / 8] & (1 << (*txt % 8)))) ) + { + /* Sucess, go ahead */ + pat++; + } + else + { + /* In wildcard mode, just try again after failiure */ + if(wildcard) + { + pat = fallback; + } + + /* Else, bad luck */ + else + { + return 1; + } + } + txt++; + } + + /* Cut off extra '*'s */ + if(*pat == '*') pat++; + + /* If this is the pattern's end, fine! */ + return(*pat); + +} + + +/********************************************************************* + * + * Function : bindup + * + * Description : Duplicate the first n characters of a string that may + * contain '\0' characters. + * + * Parameters : + * 1 : string = string to be duplicated + * 2 : len = number of bytes to duplicate + * + * Returns : pointer to copy, or NULL if failiure + * + *********************************************************************/ +char *bindup(const char *string, size_t len) +{ + char *duplicate; + + if (NULL == (duplicate = (char *)malloc(len))) + { + return NULL; + } + else + { + memcpy(duplicate, string, len); + } + + return duplicate; + +} + + +/********************************************************************* + * + * Function : make_path + * + * Description : Takes a directory name and a file name, returns + * the complete path. Handles windows/unix differences. + * If the file name is already an absolute path, or if + * the directory name is NULL or empty, it returns + * the filename. + * + * Parameters : + * 1 : dir: Name of directory or NULL for none. + * 2 : file: Name of file. Should not be NULL or empty. + * + * Returns : "dir/file" (Or on windows, "dir\file"). + * It allocates the string on the heap. Caller frees. + * Returns NULL in error (i.e. NULL file or out of + * memory) + * + *********************************************************************/ +char * make_path(const char * dir, const char * file) +{ +#ifdef AMIGA + char path[512]; + + if(dir) + { + if(dir[0] == '.') + { + if(dir[1] == '/') + { + strncpy(path,dir+2,512); + } + else + { + strncpy(path,dir+1,512); + } + } + else + { + strncpy(path,dir,512); + } + path[511]=0; + } else { + path[0]=0; + } + if(AddPart(path,file,512)) + { + return strdup(path); + } else { + return NULL; + } +#else /* ndef AMIGA */ + + if ((file == NULL) || (*file == '\0')) + { + return NULL; /* Error */ + } + + if ((dir == NULL) || (*dir == '\0') /* No directory specified */ +#if defined(_WIN32) || defined(__OS2__) + || (*file == '\\') || (file[1] == ':') /* Absolute path (DOS) */ +#else /* ifndef _WIN32 || __OS2__ */ + || (*file == '/') /* Absolute path (U*ix) */ +#endif /* ifndef _WIN32 || __OS2__ */ + ) + { + return strdup(file); + } + else + { + char * path; + +#if defined(unix) + if ( *dir != '/' && basedir && *basedir ) + { + path = malloc( strlen( basedir ) + strlen(dir) + strlen(file) + 3); + if (!path ) log_error(LOG_LEVEL_FATAL, "malloc failed!"); + strcpy(path, basedir); + strcat(path, "/"); + strcat(path, dir); + } + else + { + path = malloc(strlen(dir) + strlen(file) + 2); + if (!path ) log_error(LOG_LEVEL_FATAL, "malloc failed!"); + strcpy(path, dir); + } +#else + + path = malloc(strlen(dir) + strlen(file) + 2); + if (!path ) log_error(LOG_LEVEL_FATAL, "malloc failed!"); + strcpy(path, dir); + +#endif /* defined unix */ + +#if defined(_WIN32) || defined(__OS2__) + if(path[strlen(path)-1] != '\\') + { + strcat(path, "\\"); + } +#else /* ifndef _WIN32 || __OS2__ */ + if(path[strlen(path)-1] != '/') + { + strcat(path, "/"); + } +#endif /* ifndef _WIN32 || __OS2__ */ + strcat(path, file); + + return path; + } +#endif /* ndef AMIGA */ +} + + +/* + * What follows is a portable snprintf routine, written by Mark Martinec. + * See: http://www.ijs.si/software/snprintf/ + * Anyone who needs it can add a define for themselves... so far, only + * OS/2 (native) lacks snprintf. + + snprintf.c + - a portable implementation of snprintf, + including vsnprintf.c, asnprintf, vasnprintf, asprintf, vasprintf + + snprintf is a routine to convert numeric and string arguments to + formatted strings. It is similar to sprintf(3) provided in a system's + C library, yet it requires an additional argument - the buffer size - + and it guarantees never to store anything beyond the given buffer, + regardless of the format or arguments to be formatted. Some newer + operating systems do provide snprintf in their C library, but many do + not or do provide an inadequate (slow or idiosyncratic) version, which + calls for a portable implementation of this routine. + +Author + + Mark Martinec , April 1999, June 2000 + Copyright © 1999, Mark Martinec + + */ + +#ifdef __OS2__ + +#define PORTABLE_SNPRINTF_VERSION_MAJOR 2 +#define PORTABLE_SNPRINTF_VERSION_MINOR 2 + +#if defined(NEED_ASPRINTF) || defined(NEED_ASNPRINTF) || defined(NEED_VASPRINTF) || defined(NEED_VASNPRINTF) +# if defined(NEED_SNPRINTF_ONLY) +# undef NEED_SNPRINTF_ONLY +# endif +# if !defined(PREFER_PORTABLE_SNPRINTF) +# define PREFER_PORTABLE_SNPRINTF +# endif +#endif + +#if defined(SOLARIS_BUG_COMPATIBLE) && !defined(SOLARIS_COMPATIBLE) +#define SOLARIS_COMPATIBLE +#endif + +#if defined(HPUX_BUG_COMPATIBLE) && !defined(HPUX_COMPATIBLE) +#define HPUX_COMPATIBLE +#endif + +#if defined(DIGITAL_UNIX_BUG_COMPATIBLE) && !defined(DIGITAL_UNIX_COMPATIBLE) +#define DIGITAL_UNIX_COMPATIBLE +#endif + +#if defined(PERL_BUG_COMPATIBLE) && !defined(PERL_COMPATIBLE) +#define PERL_COMPATIBLE +#endif + +#if defined(LINUX_BUG_COMPATIBLE) && !defined(LINUX_COMPATIBLE) +#define LINUX_COMPATIBLE +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef isdigit +#undef isdigit +#endif +#define isdigit(c) ((c) >= '0' && (c) <= '9') + +/* For copying strings longer or equal to 'breakeven_point' + * it is more efficient to call memcpy() than to do it inline. + * The value depends mostly on the processor architecture, + * but also on the compiler and its optimization capabilities. + * The value is not critical, some small value greater than zero + * will be just fine if you don't care to squeeze every drop + * of performance out of the code. + * + * Small values favor memcpy, large values favor inline code. + */ +#if defined(__alpha__) || defined(__alpha) +# define breakeven_point 2 /* AXP (DEC Alpha) - gcc or cc or egcs */ +#endif +#if defined(__i386__) || defined(__i386) +# define breakeven_point 12 /* Intel Pentium/Linux - gcc 2.96 */ +#endif +#if defined(__hppa) +# define breakeven_point 10 /* HP-PA - gcc */ +#endif +#if defined(__sparc__) || defined(__sparc) +# define breakeven_point 33 /* Sun Sparc 5 - gcc 2.8.1 */ +#endif + +/* some other values of possible interest: */ +/* #define breakeven_point 8 */ /* VAX 4000 - vaxc */ +/* #define breakeven_point 19 */ /* VAX 4000 - gcc 2.7.0 */ + +#ifndef breakeven_point +# define breakeven_point 6 /* some reasonable one-size-fits-all value */ +#endif + +#define fast_memcpy(d,s,n) \ + { register size_t nn = (size_t)(n); \ + if (nn >= breakeven_point) memcpy((d), (s), nn); \ + else if (nn > 0) { /* proc call overhead is worth only for large strings*/\ + register char *dd; register const char *ss; \ + for (ss=(s), dd=(d); nn>0; nn--) *dd++ = *ss++; } } + +#define fast_memset(d,c,n) \ + { register size_t nn = (size_t)(n); \ + if (nn >= breakeven_point) memset((d), (int)(c), nn); \ + else if (nn > 0) { /* proc call overhead is worth only for large strings*/\ + register char *dd; register const int cc=(int)(c); \ + for (dd=(d); nn>0; nn--) *dd++ = cc; } } + +/* prototypes */ + +#if defined(NEED_ASPRINTF) +int asprintf (char **ptr, const char *fmt, /*args*/ ...); +#endif +#if defined(NEED_VASPRINTF) +int vasprintf (char **ptr, const char *fmt, va_list ap); +#endif +#if defined(NEED_ASNPRINTF) +int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...); +#endif +#if defined(NEED_VASNPRINTF) +int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap); +#endif + +#if defined(HAVE_SNPRINTF) +/* declare our portable snprintf routine under name portable_snprintf */ +/* declare our portable vsnprintf routine under name portable_vsnprintf */ +#else +/* declare our portable routines under names snprintf and vsnprintf */ +#define portable_snprintf snprintf +#if !defined(NEED_SNPRINTF_ONLY) +#define portable_vsnprintf vsnprintf +#endif +#endif + +#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF) +int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...); +#if !defined(NEED_SNPRINTF_ONLY) +int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap); +#endif +#endif + +/* declarations */ + +static char credits[] = "\n\ +@(#)snprintf.c, v2.2: Mark Martinec, \n\ +@(#)snprintf.c, v2.2: Copyright 1999, Mark Martinec. Frontier Artistic License applies.\n\ +@(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n"; + +#if defined(NEED_ASPRINTF) +int asprintf(char **ptr, const char *fmt, /*args*/ ...) { + va_list ap; + size_t str_m; + int str_l; + + *ptr = NULL; + va_start(ap, fmt); /* measure the required size */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap); + va_end(ap); + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + *ptr = (char *) malloc(str_m = (size_t)str_l + 1); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2; + va_start(ap, fmt); + str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + va_end(ap); + assert(str_l2 == str_l); + } + return str_l; +} +#endif + +#if defined(NEED_VASPRINTF) +int vasprintf(char **ptr, const char *fmt, va_list ap) { + size_t str_m; + int str_l; + + *ptr = NULL; + { va_list ap2; + va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/ + va_end(ap2); + } + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + *ptr = (char *) malloc(str_m = (size_t)str_l + 1); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + assert(str_l2 == str_l); + } + return str_l; +} +#endif + +#if defined(NEED_ASNPRINTF) +int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...) { + va_list ap; + int str_l; + + *ptr = NULL; + va_start(ap, fmt); /* measure the required size */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap); + va_end(ap); + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */ + /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */ + if (str_m == 0) { /* not interested in resulting string, just return size */ + } else { + *ptr = (char *) malloc(str_m); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2; + va_start(ap, fmt); + str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + va_end(ap); + assert(str_l2 == str_l); + } + } + return str_l; +} +#endif + +#if defined(NEED_VASNPRINTF) +int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap) { + int str_l; + + *ptr = NULL; + { va_list ap2; + va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/ + va_end(ap2); + } + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */ + /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */ + if (str_m == 0) { /* not interested in resulting string, just return size */ + } else { + *ptr = (char *) malloc(str_m); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + assert(str_l2 == str_l); + } + } + return str_l; +} +#endif + +/* + * If the system does have snprintf and the portable routine is not + * specifically required, this module produces no code for snprintf/vsnprintf. + */ +#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF) + +#if !defined(NEED_SNPRINTF_ONLY) +int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) { + va_list ap; + int str_l; + + va_start(ap, fmt); + str_l = portable_vsnprintf(str, str_m, fmt, ap); + va_end(ap); + return str_l; +} +#endif + +#if defined(NEED_SNPRINTF_ONLY) +int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) { +#else +int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap) { +#endif + +#if defined(NEED_SNPRINTF_ONLY) + va_list ap; +#endif + size_t str_l = 0; + const char *p = fmt; + +/* In contrast with POSIX, the ISO C99 now says + * that str can be NULL and str_m can be 0. + * This is more useful than the old: if (str_m < 1) return -1; */ + +#if defined(NEED_SNPRINTF_ONLY) + va_start(ap, fmt); +#endif + if (!p) p = ""; + while (*p) { + if (*p != '%') { + /* if (str_l < str_m) str[str_l++] = *p++; -- this would be sufficient */ + /* but the following code achieves better performance for cases + * where format string is long and contains few conversions */ + const char *q = strchr(p+1,'%'); + size_t n = !q ? strlen(p) : (q-p); + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memcpy(str+str_l, p, (n>avail?avail:n)); + } + p += n; str_l += n; + } else { + const char *starting_p; + size_t min_field_width = 0, precision = 0; + int zero_padding = 0, precision_specified = 0, justify_left = 0; + int alternate_form = 0, force_sign = 0; + int space_for_positive = 1; /* If both the ' ' and '+' flags appear, + the ' ' flag should be ignored. */ + char length_modifier = '\0'; /* allowed values: \0, h, l, L */ + char tmp[32];/* temporary buffer for simple numeric->string conversion */ + + const char *str_arg; /* string address in case of string argument */ + size_t str_arg_l; /* natural field width of arg without padding + and sign */ + unsigned char uchar_arg; + /* unsigned char argument value - only defined for c conversion. + N.B. standard explicitly states the char argument for + the c conversion is unsigned */ + + size_t number_of_zeros_to_pad = 0; + /* number of zeros to be inserted for numeric conversions + as required by the precision or minimal field width */ + + size_t zero_padding_insertion_ind = 0; + /* index into tmp where zero padding is to be inserted */ + + char fmt_spec = '\0'; + /* current conversion specifier character */ + + str_arg = credits;/* just to make compiler happy (defined but not used)*/ + str_arg = NULL; + starting_p = p; p++; /* skip '%' */ + /* parse flags */ + while (*p == '0' || *p == '-' || *p == '+' || + *p == ' ' || *p == '#' || *p == '\'') { + switch (*p) { + case '0': zero_padding = 1; break; + case '-': justify_left = 1; break; + case '+': force_sign = 1; space_for_positive = 0; break; + case ' ': force_sign = 1; + /* If both the ' ' and '+' flags appear, the ' ' flag should be ignored */ +#ifdef PERL_COMPATIBLE + /* ... but in Perl the last of ' ' and '+' applies */ + space_for_positive = 1; +#endif + break; + case '#': alternate_form = 1; break; + case '\'': break; + } + p++; + } + /* If the '0' and '-' flags both appear, the '0' flag should be ignored. */ + + /* parse field width */ + if (*p == '*') { + int j; + p++; j = va_arg(ap, int); + if (j >= 0) min_field_width = j; + else { min_field_width = -j; justify_left = 1; } + } else if (isdigit((int)(*p))) { + /* size_t could be wider than unsigned int; + make sure we treat argument like common implementations do */ + unsigned int uj = *p++ - '0'; + while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0'); + min_field_width = uj; + } + /* parse precision */ + if (*p == '.') { + p++; precision_specified = 1; + if (*p == '*') { + int j = va_arg(ap, int); + p++; + if (j >= 0) precision = j; + else { + precision_specified = 0; precision = 0; + /* NOTE: + * Solaris 2.6 man page claims that in this case the precision + * should be set to 0. Digital Unix 4.0, HPUX 10 and BSD man page + * claim that this case should be treated as unspecified precision, + * which is what we do here. + */ + } + } else if (isdigit((int)(*p))) { + /* size_t could be wider than unsigned int; + make sure we treat argument like common implementations do */ + unsigned int uj = *p++ - '0'; + while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0'); + precision = uj; + } + } + /* parse 'h', 'l' and 'll' length modifiers */ + if (*p == 'h' || *p == 'l') { + length_modifier = *p; p++; + if (length_modifier == 'l' && *p == 'l') { /* double l = long long */ +#ifdef SNPRINTF_LONGLONG_SUPPORT + length_modifier = '2'; /* double l encoded as '2' */ +#else + length_modifier = 'l'; /* treat it as a single 'l' */ +#endif + p++; + } + } + fmt_spec = *p; + /* common synonyms: */ + switch (fmt_spec) { + case 'i': fmt_spec = 'd'; break; + case 'D': fmt_spec = 'd'; length_modifier = 'l'; break; + case 'U': fmt_spec = 'u'; length_modifier = 'l'; break; + case 'O': fmt_spec = 'o'; length_modifier = 'l'; break; + default: break; + } + /* get parameter value, do initial processing */ + switch (fmt_spec) { + case '%': /* % behaves similar to 's' regarding flags and field widths */ + case 'c': /* c behaves similar to 's' regarding flags and field widths */ + case 's': + length_modifier = '\0'; /* wint_t and wchar_t not supported */ + /* the result of zero padding flag with non-numeric conversion specifier*/ + /* is undefined. Solaris and HPUX 10 does zero padding in this case, */ + /* Digital Unix and Linux does not. */ +#if !defined(SOLARIS_COMPATIBLE) && !defined(HPUX_COMPATIBLE) + zero_padding = 0; /* turn zero padding off for string conversions */ +#endif + str_arg_l = 1; + switch (fmt_spec) { + case '%': + str_arg = p; break; + case 'c': { + int j = va_arg(ap, int); + uchar_arg = (unsigned char) j; /* standard demands unsigned char */ + str_arg = (const char *) &uchar_arg; + break; + } + case 's': + str_arg = va_arg(ap, const char *); + if (!str_arg) str_arg_l = 0; + /* make sure not to address string beyond the specified precision !!! */ + else if (!precision_specified) str_arg_l = strlen(str_arg); + /* truncate string if necessary as requested by precision */ + else if (precision == 0) str_arg_l = 0; + else { + /* memchr on HP does not like n > 2^31 !!! */ + const char *q = memchr(str_arg, '\0', + precision <= 0x7fffffff ? precision : 0x7fffffff); + str_arg_l = !q ? precision : (q-str_arg); + } + break; + default: break; + } + break; + case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': { + /* NOTE: the u, o, x, X and p conversion specifiers imply + the value is unsigned; d implies a signed value */ + + int arg_sign = 0; + /* 0 if numeric argument is zero (or if pointer is NULL for 'p'), + +1 if greater than zero (or nonzero for unsigned arguments), + -1 if negative (unsigned argument is never negative) */ + + int int_arg = 0; unsigned int uint_arg = 0; + /* only defined for length modifier h, or for no length modifiers */ + + long int long_arg = 0; unsigned long int ulong_arg = 0; + /* only defined for length modifier l */ + + void *ptr_arg = NULL; + /* pointer argument value -only defined for p conversion */ + +#ifdef SNPRINTF_LONGLONG_SUPPORT + long long int long_long_arg = 0; + unsigned long long int ulong_long_arg = 0; + /* only defined for length modifier ll */ +#endif + if (fmt_spec == 'p') { + /* HPUX 10: An l, h, ll or L before any other conversion character + * (other than d, i, u, o, x, or X) is ignored. + * Digital Unix: + * not specified, but seems to behave as HPUX does. + * Solaris: If an h, l, or L appears before any other conversion + * specifier (other than d, i, u, o, x, or X), the behavior + * is undefined. (Actually %hp converts only 16-bits of address + * and %llp treats address as 64-bit data which is incompatible + * with (void *) argument on a 32-bit system). + */ +#ifdef SOLARIS_COMPATIBLE +# ifdef SOLARIS_BUG_COMPATIBLE + /* keep length modifiers even if it represents 'll' */ +# else + if (length_modifier == '2') length_modifier = '\0'; +# endif +#else + length_modifier = '\0'; +#endif + ptr_arg = va_arg(ap, void *); + if (ptr_arg != NULL) arg_sign = 1; + } else if (fmt_spec == 'd') { /* signed */ + switch (length_modifier) { + case '\0': + case 'h': + /* It is non-portable to specify a second argument of char or short + * to va_arg, because arguments seen by the called function + * are not char or short. C converts char and short arguments + * to int before passing them to a function. + */ + int_arg = va_arg(ap, int); + if (int_arg > 0) arg_sign = 1; + else if (int_arg < 0) arg_sign = -1; + break; + case 'l': + long_arg = va_arg(ap, long int); + if (long_arg > 0) arg_sign = 1; + else if (long_arg < 0) arg_sign = -1; + break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': + long_long_arg = va_arg(ap, long long int); + if (long_long_arg > 0) arg_sign = 1; + else if (long_long_arg < 0) arg_sign = -1; + break; +#endif + } + } else { /* unsigned */ + switch (length_modifier) { + case '\0': + case 'h': + uint_arg = va_arg(ap, unsigned int); + if (uint_arg) arg_sign = 1; + break; + case 'l': + ulong_arg = va_arg(ap, unsigned long int); + if (ulong_arg) arg_sign = 1; + break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': + ulong_long_arg = va_arg(ap, unsigned long long int); + if (ulong_long_arg) arg_sign = 1; + break; +#endif + } + } + str_arg = tmp; str_arg_l = 0; + /* NOTE: + * For d, i, u, o, x, and X conversions, if precision is specified, + * the '0' flag should be ignored. This is so with Solaris 2.6, + * Digital UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl. + */ +#ifndef PERL_COMPATIBLE + if (precision_specified) zero_padding = 0; +#endif + if (fmt_spec == 'd') { + if (force_sign && arg_sign >= 0) + tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; + /* leave negative numbers for sprintf to handle, + to avoid handling tricky cases like (short int)(-32768) */ +#ifdef LINUX_COMPATIBLE + } else if (fmt_spec == 'p' && force_sign && arg_sign > 0) { + tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; +#endif + } else if (alternate_form) { + if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X') ) + { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = fmt_spec; } + /* alternate form should have no effect for p conversion, but ... */ +#ifdef HPUX_COMPATIBLE + else if (fmt_spec == 'p' + /* HPUX 10: for an alternate form of p conversion, + * a nonzero result is prefixed by 0x. */ +#ifndef HPUX_BUG_COMPATIBLE + /* Actually it uses 0x prefix even for a zero value. */ + && arg_sign != 0 +#endif + ) { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = 'x'; } +#endif + } + zero_padding_insertion_ind = str_arg_l; + if (!precision_specified) precision = 1; /* default precision is 1 */ + if (precision == 0 && arg_sign == 0 +#if defined(HPUX_BUG_COMPATIBLE) || defined(LINUX_COMPATIBLE) + && fmt_spec != 'p' + /* HPUX 10 man page claims: With conversion character p the result of + * converting a zero value with a precision of zero is a null string. + * Actually HP returns all zeroes, and Linux returns "(nil)". */ +#endif + ) { + /* converted to null string */ + /* When zero value is formatted with an explicit precision 0, + the resulting formatted string is empty (d, i, u, o, x, X, p). */ + } else { + char f[5]; int f_l = 0; + f[f_l++] = '%'; /* construct a simple format string for sprintf */ + if (!length_modifier) { } + else if (length_modifier=='2') { f[f_l++] = 'l'; f[f_l++] = 'l'; } + else f[f_l++] = length_modifier; + f[f_l++] = fmt_spec; f[f_l++] = '\0'; + if (fmt_spec == 'p') str_arg_l += sprintf(tmp+str_arg_l, f, ptr_arg); + else if (fmt_spec == 'd') { /* signed */ + switch (length_modifier) { + case '\0': + case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, int_arg); break; + case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, long_arg); break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,long_long_arg); break; +#endif + } + } else { /* unsigned */ + switch (length_modifier) { + case '\0': + case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, uint_arg); break; + case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, ulong_arg); break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,ulong_long_arg);break; +#endif + } + } + /* include the optional minus sign and possible "0x" + in the region before the zero padding insertion point */ + if (zero_padding_insertion_ind < str_arg_l && + tmp[zero_padding_insertion_ind] == '-') { + zero_padding_insertion_ind++; + } + if (zero_padding_insertion_ind+1 < str_arg_l && + tmp[zero_padding_insertion_ind] == '0' && + (tmp[zero_padding_insertion_ind+1] == 'x' || + tmp[zero_padding_insertion_ind+1] == 'X') ) { + zero_padding_insertion_ind += 2; + } + } + { size_t num_of_digits = str_arg_l - zero_padding_insertion_ind; + if (alternate_form && fmt_spec == 'o' +#ifdef HPUX_COMPATIBLE /* ("%#.o",0) -> "" */ + && (str_arg_l > 0) +#endif +#ifdef DIGITAL_UNIX_BUG_COMPATIBLE /* ("%#o",0) -> "00" */ +#else + /* unless zero is already the first character */ + && !(zero_padding_insertion_ind < str_arg_l + && tmp[zero_padding_insertion_ind] == '0') +#endif + ) { /* assure leading zero for alternate-form octal numbers */ + if (!precision_specified || precision < num_of_digits+1) { + /* precision is increased to force the first character to be zero, + except if a zero value is formatted with an explicit precision + of zero */ + precision = num_of_digits+1; precision_specified = 1; + } + } + /* zero padding to specified precision? */ + if (num_of_digits < precision) + number_of_zeros_to_pad = precision - num_of_digits; + } + /* zero padding to specified minimal field width? */ + if (!justify_left && zero_padding) { + int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) number_of_zeros_to_pad += n; + } + break; + } + default: /* unrecognized conversion specifier, keep format string as-is*/ + zero_padding = 0; /* turn zero padding off for non-numeric convers. */ +#ifndef DIGITAL_UNIX_COMPATIBLE + justify_left = 1; min_field_width = 0; /* reset flags */ +#endif +#if defined(PERL_COMPATIBLE) || defined(LINUX_COMPATIBLE) + /* keep the entire format string unchanged */ + str_arg = starting_p; str_arg_l = p - starting_p; + /* well, not exactly so for Linux, which does something inbetween, + * and I don't feel an urge to imitate it: "%+++++hy" -> "%+y" */ +#else + /* discard the unrecognized conversion, just keep * + * the unrecognized conversion character */ + str_arg = p; str_arg_l = 0; +#endif + if (*p) str_arg_l++; /* include invalid conversion specifier unchanged + if not at end-of-string */ + break; + } + if (*p) p++; /* step over the just processed conversion specifier */ + /* insert padding to the left as requested by min_field_width; + this does not include the zero padding in case of numerical conversions*/ + if (!justify_left) { /* left padding with blank or zero */ + int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memset(str+str_l, (zero_padding?'0':' '), (n>avail?avail:n)); + } + str_l += n; + } + } + /* zero padding as requested by the precision or by the minimal field width + * for numeric conversions required? */ + if (number_of_zeros_to_pad <= 0) { + /* will not copy first part of numeric right now, * + * force it to be copied later in its entirety */ + zero_padding_insertion_ind = 0; + } else { + /* insert first part of numerics (sign or '0x') before zero padding */ + int n = zero_padding_insertion_ind; + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memcpy(str+str_l, str_arg, (n>avail?avail:n)); + } + str_l += n; + } + /* insert zero padding as requested by the precision or min field width */ + n = number_of_zeros_to_pad; + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memset(str+str_l, '0', (n>avail?avail:n)); + } + str_l += n; + } + } + /* insert formatted string + * (or as-is conversion specifier for unknown conversions) */ + { int n = str_arg_l - zero_padding_insertion_ind; + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memcpy(str+str_l, str_arg+zero_padding_insertion_ind, + (n>avail?avail:n)); + } + str_l += n; + } + } + /* insert right padding */ + if (justify_left) { /* right blank padding to the field width */ + int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memset(str+str_l, ' ', (n>avail?avail:n)); + } + str_l += n; + } + } + } + } +#if defined(NEED_SNPRINTF_ONLY) + va_end(ap); +#endif + if (str_m > 0) { /* make sure the string is null-terminated + even at the expense of overwriting the last character + (shouldn't happen, but just in case) */ + str[str_l <= str_m-1 ? str_l : str_m-1] = '\0'; + } + /* Return the number of characters formatted (excluding trailing null + * character), that is, the number of characters that would have been + * written to the buffer if it were large enough. + * + * The value of str_l should be returned, but str_l is of unsigned type + * size_t, and snprintf is int, possibly leading to an undetected + * integer overflow, resulting in a negative return value, which is illegal. + * Both XSH5 and ISO C99 (at least the draft) are silent on this issue. + * Should errno be set to EOVERFLOW and EOF returned in this case??? + */ + return (int) str_l; +} +#endif +#endif /* __OS2__ */ +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/miscutil.h b/src/miscutil.h new file mode 100644 index 00000000..77503cd3 --- /dev/null +++ b/src/miscutil.h @@ -0,0 +1,197 @@ +#ifndef MISCUTIL_H_INCLUDED +#define MISCUTIL_H_INCLUDED +#define MISCUTIL_H_VERSION "$Id: miscutil.h,v 1.21 2002/04/26 12:55:38 oes Exp $" +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/miscutil.h,v $ + * + * Purpose : zalloc, hash_string, safe_strerror, strcmpic, + * strncmpic, and MinGW32 strdup functions. These are + * each too small to deserve their own file but don't + * really fit in any other file. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: miscutil.h,v $ + * Revision 1.21 2002/04/26 12:55:38 oes + * New function string_toupper + * + * Revision 1.20 2002/03/26 22:29:55 swa + * we have a new homepage! + * + * Revision 1.19 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.18 2002/03/07 03:46:17 oes + * Fixed compiler warnings + * + * Revision 1.17 2002/03/04 18:28:32 oes + * Deleted deletePidFile, played syleguide police + * + * Revision 1.16 2002/01/21 00:53:36 jongfoster + * Adding string_join() + * + * Revision 1.15 2001/12/30 14:07:32 steudten + * - Add signal handling (unix) + * - Add SIGHUP handler (unix) + * - Add creation of pidfile (unix) + * - Add action 'top' in rc file (RH) + * - Add entry 'SIGNALS' to manpage + * - Add exit message to logfile (unix) + * + * Revision 1.14 2001/11/05 21:43:48 steudten + * Add global var 'basedir' for unix os. + * + * Revision 1.13 2001/10/29 03:48:10 david__schmidt + * OS/2 native needed a snprintf() routine. Added one to miscutil, brackedted + * by and __OS2__ ifdef. + * + * Revision 1.12 2001/10/23 21:27:50 jongfoster + * Standardising error codes in string_append + * make_path() no longer adds '\\' if the dir already ends in '\\' (this + * is just copying a UNIX-specific fix to the Windows-specific part) + * + * Revision 1.11 2001/10/14 22:02:57 jongfoster + * New function string_append() which is like strsav(), but running + * out of memory isn't automatically FATAL. + * + * Revision 1.10 2001/09/20 13:34:09 steudten + * + * change long to int for prototype hash_string() + * + * Revision 1.9 2001/07/29 18:43:08 jongfoster + * Changing #ifdef _FILENAME_H to FILENAME_H_INCLUDED, to conform to + * ANSI C rules. + * + * Revision 1.8 2001/06/29 13:32:14 oes + * Removed logentry from cancelled commit + * + * Revision 1.7 2001/06/05 22:32:01 jongfoster + * New function make_path() to splice directory and file names together. + * + * Revision 1.6 2001/06/03 19:12:30 oes + * introduced bindup() + * + * Revision 1.5 2001/06/01 10:31:51 oes + * Added character class matching to trivimatch; renamed to simplematch + * + * Revision 1.4 2001/05/31 17:32:31 oes + * + * - Enhanced domain part globbing with infix and prefix asterisk + * matching and optional unanchored operation + * + * Revision 1.3 2001/05/29 23:10:09 oes + * + * + * - Introduced chomp() + * - Moved strsav() from showargs to miscutil + * + * Revision 1.2 2001/05/29 09:50:24 jongfoster + * Unified blocklist/imagelist/permissionslist. + * File format is still under discussion, but the internal changes + * are (mostly) done. + * + * Also modified interceptor behaviour: + * - We now intercept all URLs beginning with one of the following + * prefixes (and *only* these prefixes): + * * http://i.j.b/ + * * http://ijbswa.sf.net/config/ + * * http://ijbswa.sourceforge.net/config/ + * - New interceptors "home page" - go to http://i.j.b/ to see it. + * - Internal changes so that intercepted and fast redirect pages + * are not replaced with an image. + * - Interceptors now have the option to send a binary page direct + * to the client. (i.e. ijb-send-banner uses this) + * - Implemented show-url-info interceptor. (Which is why I needed + * the above interceptors changes - a typical URL is + * "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif". + * The previous mechanism would not have intercepted that, and + * if it had been intercepted then it then it would have replaced + * it with an image.) + * + * Revision 1.1.1.1 2001/05/15 13:59:00 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#include "project.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +extern const char *basedir; +extern void *zalloc(size_t size); + +#if defined(unix) +extern void write_pid_file(void); +#endif /* unix */ + +extern unsigned int hash_string(const char* s); + +extern char *safe_strerror(int err); + +extern int strcmpic(const char *s1, const char *s2); +extern int strncmpic(const char *s1, const char *s2, size_t n); + +extern char *strsav(char *old, const char *text_to_append); +extern jb_err string_append(char **target_string, const char *text_to_append); +extern jb_err string_join (char **target_string, char *text_to_append); + +extern char *string_toupper(const char *string); +extern char *chomp(char *string); +extern int simplematch(char *pattern, char *text); + +extern char *bindup(const char *string, size_t len); + +extern char *make_path(const char * dir, const char * file); + +#ifdef __MINGW32__ +extern char *strdup(const char *s); +#endif /* def __MINGW32__ */ + +#ifdef __OS2__ +extern int snprintf(char *, size_t, const char *, /*args*/ ...); +#endif /* def __OS2__ */ + +/* Revision control strings from this header and associated .c file */ +extern const char miscutil_rcs[]; +extern const char miscutil_h_rcs[]; + +#if defined(__cplusplus) +} +#endif + +#endif /* ndef MISCUTIL_H_INCLUDED */ + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/parsers.c b/src/parsers.c new file mode 100644 index 00000000..317d0e38 --- /dev/null +++ b/src/parsers.c @@ -0,0 +1,1794 @@ +const char parsers_rcs[] = "$Id: parsers.c,v 1.56 2002/05/12 15:34:22 jongfoster Exp $"; +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/parsers.c,v $ + * + * Purpose : Declares functions to parse/crunch headers and pages. + * Functions declared include: + * `add_to_iob', `client_cookie_adder', `client_from', + * `client_referrer', `client_send_cookie', `client_ua', + * `client_uagent', `client_x_forwarded', + * `client_x_forwarded_adder', `client_xtra_adder', + * `content_type', `crumble', `destroy_list', `enlist', + * `flush_socket', ``get_header', `sed', + * and `server_set_cookie'. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: parsers.c,v $ + * Revision 1.56 2002/05/12 15:34:22 jongfoster + * Fixing typo in a comment + * + * Revision 1.55 2002/05/08 16:01:07 oes + * Optimized add_to_iob: + * - Use realloc instead of malloc(), memcpy(), free() + * - Expand to powers of two if possible, to get + * O(log n) reallocs instead of O(n). + * - Moved check for buffer limit here from chat + * - Report failure via returncode + * + * Revision 1.54 2002/04/02 15:03:16 oes + * Tiny code cosmetics + * + * Revision 1.53 2002/03/26 22:29:55 swa + * we have a new homepage! + * + * Revision 1.52 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.51 2002/03/13 00:27:05 jongfoster + * Killing warnings + * + * Revision 1.50 2002/03/12 01:45:35 oes + * More verbose logging + * + * Revision 1.49 2002/03/09 20:03:52 jongfoster + * - Making various functions return int rather than size_t. + * (Undoing a recent change). Since size_t is unsigned on + * Windows, functions like read_socket that return -1 on + * error cannot return a size_t. + * + * THIS WAS A MAJOR BUG - it caused frequent, unpredictable + * crashes, and also frequently caused JB to jump to 100% + * CPU and stay there. (Because it thought it had just + * read ((unsigned)-1) == 4Gb of data...) + * + * - The signature of write_socket has changed, it now simply + * returns success=0/failure=nonzero. + * + * - Trying to get rid of a few warnings --with-debug on + * Windows, I've introduced a new type "jb_socket". This is + * used for the socket file descriptors. On Windows, this + * is SOCKET (a typedef for unsigned). Everywhere else, it's + * an int. The error value can't be -1 any more, so it's + * now JB_INVALID_SOCKET (which is -1 on UNIX, and in + * Windows it maps to the #define INVALID_SOCKET.) + * + * - The signature of bind_port has changed. + * + * Revision 1.48 2002/03/07 03:46:53 oes + * Fixed compiler warnings etc + * + * Revision 1.47 2002/02/20 23:15:13 jongfoster + * Parsing functions now handle out-of-memory gracefully by returning + * an error code. + * + * Revision 1.46 2002/01/17 21:03:47 jongfoster + * Moving all our URL and URL pattern parsing code to urlmatch.c. + * + * Revision 1.45 2002/01/09 14:33:03 oes + * Added support for localtime_r. + * + * Revision 1.44 2001/12/14 01:22:54 steudten + * Remove 'user:pass@' from 'proto://user:pass@host' for the + * new added header 'Host: ..'. (See Req ID 491818) + * + * Revision 1.43 2001/11/23 00:26:38 jongfoster + * Fixing two really stupid errors in my previous commit + * + * Revision 1.42 2001/11/22 21:59:30 jongfoster + * Adding code to handle +no-cookies-keep + * + * Revision 1.41 2001/11/05 23:43:05 steudten + * Add time+date to log files. + * + * Revision 1.40 2001/10/26 20:13:09 jongfoster + * ctype.h is needed in Windows, too. + * + * Revision 1.39 2001/10/26 17:40:04 oes + * Introduced get_header_value() + * Removed http->user_agent, csp->referrer and csp->accept_types + * Removed client_accept() + * + * Revision 1.38 2001/10/25 03:40:48 david__schmidt + * Change in porting tactics: OS/2's EMX porting layer doesn't allow multiple + * threads to call select() simultaneously. So, it's time to do a real, live, + * native OS/2 port. See defines for __EMX__ (the porting layer) vs. __OS2__ + * (native). Both versions will work, but using __OS2__ offers multi-threading. + * + * Revision 1.37 2001/10/23 21:36:02 jongfoster + * Documenting sed()'s error behaviou (doc change only) + * + * Revision 1.36 2001/10/13 12:51:51 joergs + * Removed client_host, (was only required for the old 2.0.2-11 http://noijb. + * force-load), instead crumble Host: and add it (again) in client_host_adder + * (in case we get a HTTP/1.0 request without Host: header and forward it to + * a HTTP/1.1 server/proxy). + * + * Revision 1.35 2001/10/09 22:39:21 jongfoster + * assert.h is also required under Win32, so moving out of #ifndef _WIN32 + * block. + * + * Revision 1.34 2001/10/07 18:50:55 oes + * Added server_content_encoding, renamed server_transfer_encoding + * + * Revision 1.33 2001/10/07 18:04:49 oes + * Changed server_http11 to server_http and its pattern to "HTTP". + * Additional functionality: it now saves the HTTP status into + * csp->http->status and sets CT_TABOO for Status 206 (partial range) + * + * Revision 1.32 2001/10/07 15:43:28 oes + * Removed FEATURE_DENY_GZIP and replaced it with client_accept_encoding, + * client_te and client_accept_encoding_adder, triggered by the new + * +no-compression action. For HTTP/1.1 the Accept-Encoding header is + * changed to allow only identity and chunked, and the TE header is + * crunched. For HTTP/1.0, Accept-Encoding is crunched. + * + * parse_http_request no longer does anything than parsing. The rewriting + * of http->cmd and version mangling are gone. It now also recognizes + * the put and delete methods and saves the url in http->url. Removed + * unused variable. + * + * renamed content_type and content_length to have the server_ prefix + * + * server_content_type now only works if csp->content_type != CT_TABOO + * + * added server_transfer_encoding, which + * - Sets CT_TABOO to prohibit filtering if encoding compresses + * - Raises the CSP_FLAG_CHUNKED flag if Encoding is "chunked" + * - Change from "chunked" to "identity" if body was chunked + * but has been de-chunked for filtering. + * + * added server_content_md5 which crunches any Content-MD5 headers + * if the body was modified. + * + * made server_http11 conditional on +downgrade action + * + * Replaced 6 boolean members of csp with one bitmap (csp->flags) + * + * Revision 1.31 2001/10/05 14:25:02 oes + * Crumble Keep-Alive from Server + * + * Revision 1.30 2001/09/29 12:56:03 joergs + * IJB now changes HTTP/1.1 to HTTP/1.0 in requests and answers. + * + * Revision 1.29 2001/09/24 21:09:24 jongfoster + * Fixing 2 memory leaks that Guy spotted, where the paramater to + * enlist() was not being free()d. + * + * Revision 1.28 2001/09/22 16:32:28 jongfoster + * Removing unused #includes. + * + * Revision 1.27 2001/09/20 15:45:25 steudten + * + * add casting from size_t to int for printf() + * remove local variable shadow s2 + * + * Revision 1.26 2001/09/16 17:05:14 jongfoster + * Removing unused #include showarg.h + * + * Revision 1.25 2001/09/16 13:21:27 jongfoster + * Changes to use new list functions. + * + * Revision 1.24 2001/09/13 23:05:50 jongfoster + * Changing the string paramater to the header parsers a "const". + * + * Revision 1.23 2001/09/12 18:08:19 steudten + * + * In parse_http_request() header rewriting miss the host value, so + * from http://www.mydomain.com the result was just " / " not + * http://www.mydomain.com/ in case we forward. + * + * Revision 1.22 2001/09/10 10:58:53 oes + * Silenced compiler warnings + * + * Revision 1.21 2001/07/31 14:46:00 oes + * - Persistant connections now suppressed + * - sed() no longer appends empty header to csp->headers + * + * Revision 1.20 2001/07/30 22:08:36 jongfoster + * Tidying up #defines: + * - All feature #defines are now of the form FEATURE_xxx + * - Permanently turned off WIN_GUI_EDIT + * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS + * + * Revision 1.19 2001/07/25 17:21:54 oes + * client_uagent now saves copy of User-Agent: header value + * + * Revision 1.18 2001/07/13 14:02:46 oes + * - Included fix to repair broken HTTP requests that + * don't contain a path, not even '/'. + * - Removed all #ifdef PCRS + * - content_type now always inspected and classified as + * text, gif or other. + * - formatting / comments + * + * Revision 1.17 2001/06/29 21:45:41 oes + * Indentation, CRLF->LF, Tab-> Space + * + * Revision 1.16 2001/06/29 13:32:42 oes + * - Fixed a comment + * - Adapted free_http_request + * - Removed logentry from cancelled commit + * + * Revision 1.15 2001/06/03 19:12:38 oes + * deleted const struct interceptors + * + * Revision 1.14 2001/06/01 18:49:17 jongfoster + * Replaced "list_share" with "list" - the tiny memory gain was not + * worth the extra complexity. + * + * Revision 1.13 2001/05/31 21:30:33 jongfoster + * Removed list code - it's now in list.[ch] + * Renamed "permission" to "action", and changed many features + * to use the actions file rather than the global config. + * + * Revision 1.12 2001/05/31 17:33:13 oes + * + * CRLF -> LF + * + * Revision 1.11 2001/05/29 20:11:19 joergs + * '/ * inside comment' warning removed. + * + * Revision 1.10 2001/05/29 09:50:24 jongfoster + * Unified blocklist/imagelist/permissionslist. + * File format is still under discussion, but the internal changes + * are (mostly) done. + * + * Also modified interceptor behaviour: + * - We now intercept all URLs beginning with one of the following + * prefixes (and *only* these prefixes): + * * http://i.j.b/ + * * http://ijbswa.sf.net/config/ + * * http://ijbswa.sourceforge.net/config/ + * - New interceptors "home page" - go to http://i.j.b/ to see it. + * - Internal changes so that intercepted and fast redirect pages + * are not replaced with an image. + * - Interceptors now have the option to send a binary page direct + * to the client. (i.e. ijb-send-banner uses this) + * - Implemented show-url-info interceptor. (Which is why I needed + * the above interceptors changes - a typical URL is + * "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif". + * The previous mechanism would not have intercepted that, and + * if it had been intercepted then it then it would have replaced + * it with an image.) + * + * Revision 1.9 2001/05/28 17:26:33 jongfoster + * Fixing segfault if last header was crunched. + * Fixing Windows build (snprintf() is _snprintf() under Win32, but we + * can use the cross-platform sprintf() instead.) + * + * Revision 1.8 2001/05/27 22:17:04 oes + * + * - re_process_buffer no longer writes the modified buffer + * to the client, which was very ugly. It now returns the + * buffer, which it is then written by chat. + * + * - content_length now adjusts the Content-Length: header + * for modified documents rather than crunch()ing it. + * (Length info in csp->content_length, which is 0 for + * unmodified documents) + * + * - For this to work, sed() is called twice when filtering. + * + * Revision 1.7 2001/05/27 13:19:06 oes + * Patched Joergs solution for the content-length in. + * + * Revision 1.6 2001/05/26 13:39:32 jongfoster + * Only crunches Content-Length header if applying RE filtering. + * Without this fix, Microsoft Windows Update wouldn't work. + * + * Revision 1.5 2001/05/26 00:28:36 jongfoster + * Automatic reloading of config file. + * Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32). + * Most of the global variables have been moved to a new + * struct configuration_spec, accessed through csp->config->globalname + * Most of the globals remaining are used by the Win32 GUI. + * + * Revision 1.4 2001/05/22 18:46:04 oes + * + * - Enabled filtering banners by size rather than URL + * by adding patterns that replace all standard banner + * sizes with the "Junkbuster" gif to the re_filterfile + * + * - Enabled filtering WebBugs by providing a pattern + * which kills all 1x1 images + * + * - Added support for PCRE_UNGREEDY behaviour to pcrs, + * which is selected by the (nonstandard and therefore + * capital) letter 'U' in the option string. + * It causes the quantifiers to be ungreedy by default. + * Appending a ? turns back to greedy (!). + * + * - Added a new interceptor ijb-send-banner, which + * sends back the "Junkbuster" gif. Without imagelist or + * MSIE detection support, or if tinygif = 1, or the + * URL isn't recognized as an imageurl, a lame HTML + * explanation is sent instead. + * + * - Added new feature, which permits blocking remote + * script redirects and firing back a local redirect + * to the browser. + * The feature is conditionally compiled, i.e. it + * can be disabled with --disable-fast-redirects, + * plus it must be activated by a "fast-redirects" + * line in the config file, has its own log level + * and of course wants to be displayed by show-proxy-args + * Note: Boy, all the #ifdefs in 1001 locations and + * all the fumbling with configure.in and acconfig.h + * were *way* more work than the feature itself :-( + * + * - Because a generic redirect template was needed for + * this, tinygif = 3 now uses the same. + * + * - Moved GIFs, and other static HTTP response templates + * to project.h + * + * - Some minor fixes + * + * - Removed some >400 CRs again (Jon, you really worked + * a lot! ;-) + * + * Revision 1.3 2001/05/20 01:21:20 jongfoster + * Version 2.9.4 checkin. + * - Merged popupfile and cookiefile, and added control over PCRS + * filtering, in new "permissionsfile". + * - Implemented LOG_LEVEL_FATAL, so that if there is a configuration + * file error you now get a message box (in the Win32 GUI) rather + * than the program exiting with no explanation. + * - Made killpopup use the PCRS MIME-type checking and HTTP-header + * skipping. + * - Removed tabs from "config" + * - Moved duplicated url parsing code in "loaders.c" to a new funcition. + * - Bumped up version number. + * + * Revision 1.2 2001/05/17 23:02:36 oes + * - Made referrer option accept 'L' as a substitute for '§' + * + * Revision 1.1.1.1 2001/05/15 13:59:01 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#include "config.h" + +#ifndef _WIN32 +#include +#include +#endif + +#include +#include +#include +#include + +#if !defined(_WIN32) && !defined(__OS2__) +#include +#endif + +#include "project.h" +#include "list.h" +#include "parsers.h" +#include "encode.h" +#include "ssplit.h" +#include "errlog.h" +#include "jbsockets.h" +#include "miscutil.h" +#include "list.h" + +const char parsers_h_rcs[] = PARSERS_H_VERSION; + +/* Fix a problem with Solaris. There should be no effect on other + * platforms. + * Solaris's isspace() is a macro which uses it's argument directly + * as an array index. Therefore we need to make sure that high-bit + * characters generate +ve values, and ideally we also want to make + * the argument match the declared parameter type of "int". + * + * Why did they write a character function that can't take a simple + * "char" argument? Doh! + */ +#define ijb_isupper(__X) isupper((int)(unsigned char)(__X)) +#define ijb_tolower(__X) tolower((int)(unsigned char)(__X)) + + +const struct parsers client_patterns[] = { + { "referer:", 8, client_referrer }, + { "user-agent:", 11, client_uagent }, + { "ua-", 3, client_ua }, + { "from:", 5, client_from }, + { "cookie:", 7, client_send_cookie }, + { "x-forwarded-for:", 16, client_x_forwarded }, + { "Accept-Encoding:", 16, client_accept_encoding }, + { "TE:", 3, client_te }, + { "Host:", 5, crumble }, +/* { "if-modified-since:", 18, crumble }, */ + { "Keep-Alive:", 11, crumble }, + { "connection:", 11, crumble }, + { "proxy-connection:", 17, crumble }, + { NULL, 0, NULL } +}; + + +const struct parsers server_patterns[] = { + { "HTTP", 4, server_http }, + { "set-cookie:", 11, server_set_cookie }, + { "connection:", 11, crumble }, + { "Content-Type:", 13, server_content_type }, + { "Content-Length:", 15, server_content_length }, + { "Content-MD5:", 12, server_content_md5 }, + { "Content-Encoding:", 17, server_content_encoding }, + { "Transfer-Encoding:", 18, server_transfer_coding }, + { "Keep-Alive:", 11, crumble }, + { NULL, 0, NULL } +}; + + +const add_header_func_ptr add_client_headers[] = { + client_host_adder, + client_cookie_adder, + client_x_forwarded_adder, + client_xtra_adder, + client_accept_encoding_adder, + connection_close_adder, + NULL +}; + + +const add_header_func_ptr add_server_headers[] = { + connection_close_adder, + NULL +}; + + +/********************************************************************* + * + * Function : flush_socket + * + * Description : Write any pending "buffered" content. + * + * Parameters : + * 1 : fd = file descriptor of the socket to read + * 2 : csp = Current client state (buffers, headers, etc...) + * + * Returns : On success, the number of bytes written are returned (zero + * indicates nothing was written). On error, -1 is returned, + * and errno is set appropriately. If count is zero and the + * file descriptor refers to a regular file, 0 will be + * returned without causing any other effect. For a special + * file, the results are not portable. + * + *********************************************************************/ +int flush_socket(jb_socket fd, struct client_state *csp) +{ + struct iob *iob = csp->iob; + int len = iob->eod - iob->cur; + + if (len <= 0) + { + return(0); + } + + if (write_socket(fd, iob->cur, (size_t)len)) + { + return(-1); + } + iob->eod = iob->cur = iob->buf; + return(len); + +} + + +/********************************************************************* + * + * Function : add_to_iob + * + * Description : Add content to the buffered page, expanding the + * buffer if necessary. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : buf = holds the content to be added to the page + * 3 : n = number of bytes to be added + * + * Returns : JB_ERR_OK on success, JB_ERR_MEMORY if out-of-memory + * or buffer limit reached. + * + *********************************************************************/ +jb_err add_to_iob(struct client_state *csp, char *buf, int n) +{ + struct iob *iob = csp->iob; + size_t used, offset, need, want; + char *p; + + if (n <= 0) return JB_ERR_OK; + + used = iob->eod - iob->buf; + offset = iob->cur - iob->buf; + need = used + n + 1; + + /* + * If the buffer can't hold the new data, extend it first. + * Use the next power of two if possible, else use the actual need. + */ + if (need > csp->config->buffer_limit) + { + log_error(LOG_LEVEL_ERROR, "Buffer limit reached while extending the buffer (iob)"); + return JB_ERR_MEMORY; + } + + if (need > iob->size) + { + for (want = csp->iob->size ? csp->iob->size : 512; want <= need;) want *= 2; + + if (want <= csp->config->buffer_limit && NULL != (p = (char *)realloc(iob->buf, want))) + { + iob->size = want; + } + else if (NULL != (p = (char *)realloc(iob->buf, need))) + { + iob->size = need; + } + else + { + log_error(LOG_LEVEL_ERROR, "Extending the buffer (iob) failed: %E"); + return JB_ERR_MEMORY; + } + + /* Update the iob pointers */ + iob->cur = p + offset; + iob->eod = p + used; + iob->buf = p; + } + + /* copy the new data into the iob buffer */ + memcpy(iob->eod, buf, (size_t)n); + + /* point to the end of the data */ + iob->eod += n; + + /* null terminate == cheap insurance */ + *iob->eod = '\0'; + + return JB_ERR_OK; + +} + + +/********************************************************************* + * + * Function : get_header + * + * Description : This (odd) routine will parse the csp->iob + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : Any one of the following: + * + * 1) a pointer to a dynamically allocated string that contains a header line + * 2) NULL indicating that the end of the header was reached + * 3) "" indicating that the end of the iob was reached before finding + * a complete header line. + * + *********************************************************************/ +char *get_header(struct client_state *csp) +{ + struct iob *iob; + char *p, *q, *ret; + iob = csp->iob; + + if ((iob->cur == NULL) + || ((p = strchr(iob->cur, '\n')) == NULL)) + { + return(""); /* couldn't find a complete header */ + } + + *p = '\0'; + + ret = strdup(iob->cur); + if (ret == NULL) + { + /* FIXME No way to handle error properly */ + log_error(LOG_LEVEL_FATAL, "Out of memory in get_header()"); + } + + iob->cur = p+1; + + if ((q = strchr(ret, '\r')) != NULL) *q = '\0'; + + /* is this a blank line (i.e. the end of the header) ? */ + if (*ret == '\0') + { + freez(ret); + return(NULL); + } + + return(ret); + +} + + +/********************************************************************* + * + * Function : get_header_value + * + * Description : Get the value of a given header from a chained list + * of header lines or return NULL if no such header is + * present in the list. + * + * Parameters : + * 1 : header_list = pointer to list + * 2 : header_name = string with name of header to look for. + * Trailing colon required, capitalization + * doesn't matter. + * + * Returns : NULL if not found, else value of header + * + *********************************************************************/ +char *get_header_value(const struct list *header_list, const char *header_name) +{ + struct list_entry *cur_entry; + char *ret = NULL; + size_t length = 0; + + assert(header_list); + assert(header_name); + length = strlen(header_name); + + for (cur_entry = header_list->first; cur_entry ; cur_entry = cur_entry->next) + { + if (cur_entry->str) + { + if (!strncmpic(cur_entry->str, header_name, length)) + { + /* + * Found: return pointer to start of value + */ + ret = (char *) (cur_entry->str + length); + while (*ret && ijb_isspace(*ret)) ret++; + return(ret); + } + } + } + + /* + * Not found + */ + return NULL; + +} + +/********************************************************************* + * + * Function : sed + * + * Description : add, delete or modify lines in the HTTP header streams. + * On entry, it receives a linked list of headers space + * that was allocated dynamically (both the list nodes + * and the header contents). + * + * As a side effect it frees the space used by the original + * header lines. + * + * Parameters : + * 1 : pats = list of patterns to match against headers + * 2 : more_headers = list of functions to add more + * headers (client or server) + * 3 : csp = Current client state (buffers, headers, etc...) + * + * Returns : Single pointer to a fully formed header, or NULL + * on out-of-memory error. + * + *********************************************************************/ +char *sed(const struct parsers pats[], + const add_header_func_ptr more_headers[], + struct client_state *csp) +{ + struct list_entry *p; + const struct parsers *v; + const add_header_func_ptr *f; + jb_err err = JB_ERR_OK; + + for (v = pats; (err == JB_ERR_OK) && (v->str != NULL) ; v++) + { + for (p = csp->headers->first; (err == JB_ERR_OK) && (p != NULL) ; p = p->next) + { + /* Header crunch()ed in previous run? -> ignore */ + if (p->str == NULL) continue; + + if (v == pats) log_error(LOG_LEVEL_HEADER, "scan: %s", p->str); + + if (strncmpic(p->str, v->str, v->len) == 0) + { + err = v->parser(csp, (char **)&(p->str)); + } + } + } + + /* place any additional headers on the csp->headers list */ + for (f = more_headers; (err == JB_ERR_OK) && (*f) ; f++) + { + err = (*f)(csp); + } + + if (err != JB_ERR_OK) + { + return NULL; + } + + return list_to_text(csp->headers); +} + + +/* here begins the family of parser functions that reformat header lines */ + + +/********************************************************************* + * + * Function : crumble + * + * Description : This is called if a header matches a pattern to "crunch" + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : header = On input, pointer to header to modify. + * On output, pointer to the modified header, or NULL + * to remove the header. This function frees the + * original string if necessary. + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err crumble(struct client_state *csp, char **header) +{ + log_error(LOG_LEVEL_HEADER, "crunch!"); + freez(*header); + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : server_content_type + * + * Description : Set the content-type for filterable types (text/.*, + * javascript and image/gif) unless filtering has been + * forbidden (CT_TABOO) while parsing earlier headers. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : header = On input, pointer to header to modify. + * On output, pointer to the modified header, or NULL + * to remove the header. This function frees the + * original string if necessary. + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err server_content_type(struct client_state *csp, char **header) +{ + if (csp->content_type != CT_TABOO) + { + if (strstr(*header, " text/") + || strstr(*header, "application/x-javascript")) + csp->content_type = CT_TEXT; + else if (strstr(*header, " image/gif")) + csp->content_type = CT_GIF; + else + csp->content_type = 0; + } + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : server_transfer_coding + * + * Description : - Prohibit filtering (CT_TABOO) if transfer coding compresses + * - Raise the CSP_FLAG_CHUNKED flag if coding is "chunked" + * - Change from "chunked" to "identity" if body was chunked + * but has been de-chunked for filtering. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : header = On input, pointer to header to modify. + * On output, pointer to the modified header, or NULL + * to remove the header. This function frees the + * original string if necessary. + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err server_transfer_coding(struct client_state *csp, char **header) +{ + /* + * Turn off pcrs and gif filtering if body compressed + */ + if (strstr(*header, "gzip") || strstr(*header, "compress") || strstr(*header, "deflate")) + { + csp->content_type = CT_TABOO; + } + + /* + * Raise flag if body chunked + */ + if (strstr(*header, "chunked")) + { + csp->flags |= CSP_FLAG_CHUNKED; + + /* + * If the body was modified, it has been + * de-chunked first, so adjust the header: + */ + if (csp->flags & CSP_FLAG_MODIFIED) + { + freez(*header); + *header = strdup("Transfer-Encoding: identity"); + return (header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK; + } + } + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : server_content_encoding + * + * Description : Prohibit filtering (CT_TABOO) if content encoding compresses + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : header = On input, pointer to header to modify. + * On output, pointer to the modified header, or NULL + * to remove the header. This function frees the + * original string if necessary. + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err server_content_encoding(struct client_state *csp, char **header) +{ + /* + * Turn off pcrs and gif filtering if body compressed + */ + if (strstr(*header, "gzip") || strstr(*header, "compress") || strstr(*header, "deflate")) + { + csp->content_type = CT_TABOO; + } + + return JB_ERR_OK; + +} + + +/********************************************************************* + * + * Function : server_content_length + * + * Description : Adjust Content-Length header if we modified + * the body. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : header = On input, pointer to header to modify. + * On output, pointer to the modified header, or NULL + * to remove the header. This function frees the + * original string if necessary. + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err server_content_length(struct client_state *csp, char **header) +{ + if (csp->content_length != 0) /* Content length has been modified */ + { + freez(*header); + *header = (char *) zalloc(100); + if (*header == NULL) + { + return JB_ERR_MEMORY; + } + + sprintf(*header, "Content-Length: %d", (int) csp->content_length); + + log_error(LOG_LEVEL_HEADER, "Adjust Content-Length to %d", (int) csp->content_length); + } + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : server_content_md5 + * + * Description : Crumble any Content-MD5 headers if the document was + * modified. FIXME: Should we re-compute instead? + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : header = On input, pointer to header to modify. + * On output, pointer to the modified header, or NULL + * to remove the header. This function frees the + * original string if necessary. + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err server_content_md5(struct client_state *csp, char **header) +{ + if (csp->flags & CSP_FLAG_MODIFIED) + { + log_error(LOG_LEVEL_HEADER, "Crunching Content-MD5"); + freez(*header); + } + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : client_accept_encoding + * + * Description : Rewrite the client's Accept-Encoding header so that + * if doesn't allow compression, if the action applies. + * Note: For HTTP/1.0 the absence of the header is enough. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : header = On input, pointer to header to modify. + * On output, pointer to the modified header, or NULL + * to remove the header. This function frees the + * original string if necessary. + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err client_accept_encoding(struct client_state *csp, char **header) +{ + if ((csp->action->flags & ACTION_NO_COMPRESSION) != 0) + { + log_error(LOG_LEVEL_HEADER, "Supressed offer to compress content"); + + freez(*header); + if (!strcmpic(csp->http->ver, "HTTP/1.1")) + { + *header = strdup("Accept-Encoding: identity;q=1.0, *;q=0"); + if (*header == NULL) + { + return JB_ERR_MEMORY; + } + } + } + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : client_te + * + * Description : Rewrite the client's TE header so that + * if doesn't allow compression, if the action applies. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : header = On input, pointer to header to modify. + * On output, pointer to the modified header, or NULL + * to remove the header. This function frees the + * original string if necessary. + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err client_te(struct client_state *csp, char **header) +{ + if ((csp->action->flags & ACTION_NO_COMPRESSION) != 0) + { + freez(*header); + log_error(LOG_LEVEL_HEADER, "Supressed offer to compress transfer"); + } + + return JB_ERR_OK; +} + +/********************************************************************* + * + * Function : client_referrer + * + * Description : Handle the "referer" config setting properly. + * Called from `sed'. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : header = On input, pointer to header to modify. + * On output, pointer to the modified header, or NULL + * to remove the header. This function frees the + * original string if necessary. + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err client_referrer(struct client_state *csp, char **header) +{ + const char *newval; + +#ifdef FEATURE_FORCE_LOAD + /* Since the referrer can include the prefix even + * even if the request itself is non-forced, we must + * clean it unconditionally + */ + strclean(*header, FORCE_PREFIX); +#endif /* def FEATURE_FORCE_LOAD */ + + /* + * Are we sending referer? + */ + if ((csp->action->flags & ACTION_HIDE_REFERER) == 0) + { + return JB_ERR_OK; + } + + freez(*header); + + newval = csp->action->string[ACTION_STRING_REFERER]; + + if ((newval == NULL) || (0 == strcmpic(newval, "block")) ) + { + /* + * Blocking referer + */ + log_error(LOG_LEVEL_HEADER, "crunch!"); + return JB_ERR_OK; + } + else if (0 == strncmpic(newval, "http://", 7)) + { + /* + * We have a specific (fixed) referer we want to send. + */ + log_error(LOG_LEVEL_HEADER, "modified"); + + *header = strdup("Referer: "); + string_append(header, newval); + + return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK; + } + else + { + /* + * Forge a referer as http://[hostname:port of REQUEST]/ + * to fool stupid checks for in-site links + */ + if (0 != strcmpic(newval, "forge")) + { + /* + * Invalid choice - but forge is probably the best default. + */ + log_error(LOG_LEVEL_ERROR, "Bad parameter: +referer{%s}", newval); + } + + *header = strdup("Referer: http://"); + string_append(header, csp->http->hostport); + string_append(header, "/"); + log_error(LOG_LEVEL_HEADER, "crunch+forge to %s", *header); + + return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK; + } +} + + +/********************************************************************* + * + * Function : client_uagent + * + * Description : Handle the "user-agent" config setting properly + * and remember its original value to enable browser + * bug workarounds. Called from `sed'. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : header = On input, pointer to header to modify. + * On output, pointer to the modified header, or NULL + * to remove the header. This function frees the + * original string if necessary. + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err client_uagent(struct client_state *csp, char **header) +{ + const char *newval; + + if ((csp->action->flags & ACTION_HIDE_USER_AGENT) == 0) + { + return JB_ERR_OK; + } + + newval = csp->action->string[ACTION_STRING_USER_AGENT]; + if (newval == NULL) + { + return JB_ERR_OK; + } + + log_error(LOG_LEVEL_HEADER, "modified"); + + freez(*header); + *header = strdup("User-Agent: "); + string_append(header, newval); + + return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : client_ua + * + * Description : Handle "ua-" headers properly. Called from `sed'. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : header = On input, pointer to header to modify. + * On output, pointer to the modified header, or NULL + * to remove the header. This function frees the + * original string if necessary. + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err client_ua(struct client_state *csp, char **header) +{ + if ((csp->action->flags & ACTION_HIDE_USER_AGENT) != 0) + { + log_error(LOG_LEVEL_HEADER, "crunch!"); + freez(*header); + } + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : client_from + * + * Description : Handle the "from" config setting properly. + * Called from `sed'. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : header = On input, pointer to header to modify. + * On output, pointer to the modified header, or NULL + * to remove the header. This function frees the + * original string if necessary. + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err client_from(struct client_state *csp, char **header) +{ + const char *newval; + + if ((csp->action->flags & ACTION_HIDE_FROM) == 0) + { + return JB_ERR_OK; + } + + freez(*header); + + newval = csp->action->string[ACTION_STRING_FROM]; + + /* + * Are we blocking the e-mail address? + */ + if ((newval == NULL) || (0 == strcmpic(newval, "block")) ) + { + log_error(LOG_LEVEL_HEADER, "crunch!"); + return JB_ERR_OK; + } + + log_error(LOG_LEVEL_HEADER, " modified"); + + *header = strdup("From: "); + string_append(header, newval); + + return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : client_send_cookie + * + * Description : Handle the "cookie" header properly. Called from `sed'. + * If cookie is accepted, add it to the cookie_list, + * else we crunch it. Mmmmmmmmmmm ... cookie ...... + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : header = On input, pointer to header to modify. + * On output, pointer to the modified header, or NULL + * to remove the header. This function frees the + * original string if necessary. + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err client_send_cookie(struct client_state *csp, char **header) +{ + jb_err result = JB_ERR_OK; + + if ((csp->action->flags & ACTION_NO_COOKIE_READ) == 0) + { + /* strlen("cookie: ") == 8 */ + result = enlist(csp->cookie_list, *header + 8); + } + else + { + log_error(LOG_LEVEL_HEADER, " crunch!"); + } + + /* + * Always remove the cookie here. The cookie header + * will be sent at the end of the header. + */ + freez(*header); + + return result; +} + + +/********************************************************************* + * + * Function : client_x_forwarded + * + * Description : Handle the "x-forwarded-for" config setting properly, + * also used in the add_client_headers list. Called from `sed'. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : header = On input, pointer to header to modify. + * On output, pointer to the modified header, or NULL + * to remove the header. This function frees the + * original string if necessary. + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err client_x_forwarded(struct client_state *csp, char **header) +{ + if ((csp->action->flags & ACTION_HIDE_FORWARDED) == 0) + { + /* Save it so we can re-add it later */ + freez(csp->x_forwarded); + csp->x_forwarded = *header; + + /* + * Always set *header = NULL, since this information + * will be sent at the end of the header. + */ + *header = NULL; + } + else + { + freez(*header); + log_error(LOG_LEVEL_HEADER, " crunch!"); + } + + return JB_ERR_OK; +} + +/* the following functions add headers directly to the header list */ + +/********************************************************************* + * + * Function : client_host_adder + * + * Description : (re)adds the host header. Called from `sed'. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err client_host_adder(struct client_state *csp) +{ + char *p; + char *pos; + jb_err err; + + if ( !csp->http->hostport || !*(csp->http->hostport)) + { + return JB_ERR_OK; + } + + p = strdup("Host: "); + /* + ** remove 'user:pass@' from 'proto://user:pass@host' + */ + if ( (pos = strchr( csp->http->hostport, '@')) != NULL ) + { + string_append(&p, pos+1); + } + else + { + string_append(&p, csp->http->hostport); + } + + if (p == NULL) + { + return JB_ERR_MEMORY; + } + + log_error(LOG_LEVEL_HEADER, "addh: %s", p); + + err = enlist(csp->headers, p); + + freez(p); + + return err; +} + + +/********************************************************************* + * + * Function : client_cookie_adder + * + * Description : Used in the add_client_headers list. Called from `sed'. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err client_cookie_adder(struct client_state *csp) +{ + struct list_entry *lst; + char *tmp; + struct list_entry *list1 = csp->cookie_list->first; + struct list_entry *list2 = csp->action->multi[ACTION_MULTI_WAFER]->first; + int first_cookie = 1; + jb_err err; + + if ((list1 == NULL) && (list2 == NULL)) + { + /* Nothing to do */ + return JB_ERR_OK; + } + + tmp = strdup("Cookie: "); + + for (lst = list1; lst ; lst = lst->next) + { + if (first_cookie) + { + first_cookie = 0; + } + else + { + string_append(&tmp, "; "); + } + string_append(&tmp, lst->str); + } + + for (lst = list2; lst ; lst = lst->next) + { + if (first_cookie) + { + first_cookie = 0; + } + else + { + string_append(&tmp, "; "); + } + string_join(&tmp, cookie_encode(lst->str)); + } + + if (tmp == NULL) + { + return JB_ERR_MEMORY; + } + + log_error(LOG_LEVEL_HEADER, "addh: %s", tmp); + err = enlist(csp->headers, tmp); + free(tmp); + return err; +} + + +/********************************************************************* + * + * Function : client_accept_encoding_adder + * + * Description : Add an Accept-Encoding header to the client's request + * that disables compression if the action applies, and + * the header is not already there. Called from `sed'. + * Note: For HTTP/1.0, the absence of the header is enough. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err client_accept_encoding_adder(struct client_state *csp) +{ + if ( ((csp->action->flags & ACTION_NO_COMPRESSION) != 0) + && (!strcmpic(csp->http->ver, "HTTP/1.1")) ) + { + return enlist_unique(csp->headers, "Accept-Encoding: identity;q=1.0, *;q=0", 16); + } + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : client_xtra_adder + * + * Description : Used in the add_client_headers list. Called from `sed'. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err client_xtra_adder(struct client_state *csp) +{ + struct list_entry *lst; + jb_err err; + + for (lst = csp->action->multi[ACTION_MULTI_ADD_HEADER]->first; + lst ; lst = lst->next) + { + log_error(LOG_LEVEL_HEADER, "addh: %s", lst->str); + err = enlist(csp->headers, lst->str); + if (err) + { + return err; + } + + } + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : client_x_forwarded_adder + * + * Description : Used in the add_client_headers list. Called from `sed'. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err client_x_forwarded_adder(struct client_state *csp) +{ + char *p = NULL; + jb_err err; + + if ((csp->action->flags & ACTION_HIDE_FORWARDED) != 0) + { + return JB_ERR_OK; + } + + if (csp->x_forwarded) + { + p = strdup(csp->x_forwarded); + string_append(&p, ", "); + } + else + { + p = strdup("X-Forwarded-For: "); + } + string_append(&p, csp->ip_addr_str); + + if (p == NULL) + { + return JB_ERR_MEMORY; + } + + log_error(LOG_LEVEL_HEADER, "addh: %s", p); + err = enlist(csp->headers, p); + free(p); + + return err; +} + + +/********************************************************************* + * + * Function : connection_close_adder + * + * Description : Adds a "Connection: close" header to csp->headers + * as a temporary fix for the needed but missing HTTP/1.1 + * support. Called from `sed'. + * FIXME: This whole function shouldn't be neccessary! + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err connection_close_adder(struct client_state *csp) +{ + return enlist(csp->headers, "Connection: close"); +} + + +/********************************************************************* + * + * Function : server_http + * + * Description : - Save the HTTP Status into csp->http->status + * - Set CT_TABOO to prevent filtering if the answer + * is a partial range (HTTP status 206) + * - Rewrite HTTP/1.1 answers to HTTP/1.0 if +downgrade + * action applies. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : header = On input, pointer to header to modify. + * On output, pointer to the modified header, or NULL + * to remove the header. This function frees the + * original string if necessary. + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err server_http(struct client_state *csp, char **header) +{ + sscanf(*header, "HTTP/%*d.%*d %d", &(csp->http->status)); + if (csp->http->status == 206) + { + csp->content_type = CT_TABOO; + } + + if ((csp->action->flags & ACTION_DOWNGRADE) != 0) + { + (*header)[7] = '0'; + log_error(LOG_LEVEL_HEADER, "Downgraded answer to HTTP/1.0"); + } + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : server_set_cookie + * + * Description : Handle the server "cookie" header properly. + * Log cookie to the jar file. Then "crunch" it, + * or accept it. Called from `sed'. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : header = On input, pointer to header to modify. + * On output, pointer to the modified header, or NULL + * to remove the header. This function frees the + * original string if necessary. + * + * Returns : JB_ERR_OK on success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err server_set_cookie(struct client_state *csp, char **header) +{ +#ifdef FEATURE_COOKIE_JAR + if (csp->config->jar) + { + /* + * Write timestamp into outbuf. + * + * Complex because not all OSs have tm_gmtoff or + * the %z field in strftime() + */ + char tempbuf[ BUFFER_SIZE ]; + time_t now; + struct tm tm_now; + time (&now); +#ifdef HAVE_LOCALTIME_R + tm_now = *localtime_r(&now, &tm_now); +#else + tm_now = *localtime (&now); +#endif + strftime(tempbuf, BUFFER_SIZE-6, "%b %d %H:%M:%S ", &tm_now); + + /* strlen("set-cookie: ") = 12 */ + fprintf(csp->config->jar, "%s %s\t%s\n", tempbuf, csp->http->host, *header + 12); + } +#endif /* def FEATURE_COOKIE_JAR */ + + if ((csp->action->flags & ACTION_NO_COOKIE_SET) != 0) + { + return crumble(csp, header); + } + else if ((csp->action->flags & ACTION_NO_COOKIE_KEEP) != 0) + { + /* Flag whether or not to log a message */ + int changed = 0; + + /* A variable to store the tag we're working on */ + char *cur_tag; + + /* Skip "Set-Cookie:" (11 characters) in header */ + cur_tag = *header + 11; + + /* skip whitespace between "Set-Cookie:" and value */ + while (*cur_tag && ijb_isspace(*cur_tag)) + { + cur_tag++; + } + + /* Loop through each tag in the cookie */ + while (*cur_tag) + { + /* Find next tag */ + char *next_tag = strchr(cur_tag, ';'); + if (next_tag != NULL) + { + /* Skip the ';' character itself */ + next_tag++; + + /* skip whitespace ";" and start of tag */ + while (*next_tag && ijb_isspace(*next_tag)) + { + next_tag++; + } + } + else + { + /* "Next tag" is the end of the string */ + next_tag = cur_tag + strlen(cur_tag); + } + + /* Is this the "Expires" tag? */ + if (strncmpic(cur_tag, "expires=", 8) == 0) + { + /* Delete the tag by copying the rest of the string over it. + * (Note that we cannot just use "strcpy(cur_tag, next_tag)", + * since the behaviour of strcpy is undefined for overlapping + * strings.) + */ + memmove(cur_tag, next_tag, strlen(next_tag) + 1); + + /* That changed the header, need to issue a log message */ + changed = 1; + + /* Note that the next tag has now been moved to *cur_tag, + * so we do not need to update the cur_tag pointer. + */ + } + else + { + /* Move on to next cookie tag */ + cur_tag = next_tag; + } + } + + if (changed) + { + log_error(LOG_LEVEL_HEADER, "Changed cookie to a temporary one."); + } + } + + return JB_ERR_OK; +} + + +#ifdef FEATURE_FORCE_LOAD +/********************************************************************* + * + * Function : strclean + * + * Description : In-Situ-Eliminate all occurances of substring in + * string + * + * Parameters : + * 1 : string = string to clean + * 2 : substring = substring to eliminate + * + * Returns : Number of eliminations + * + *********************************************************************/ +int strclean(const char *string, const char *substring) +{ + int hits = 0, len = strlen(substring); + char *pos, *p; + + while((pos = strstr(string, substring)) != NULL) + { + p = pos + len; + do + { + *(p - len) = *p; + } + while (*p++ != '\0'); + + hits++; + } + + return(hits); +} +#endif /* def FEATURE_FORCE_LOAD */ + + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/parsers.h b/src/parsers.h new file mode 100644 index 00000000..5d22442f --- /dev/null +++ b/src/parsers.h @@ -0,0 +1,234 @@ +#ifndef PARSERS_H_INCLUDED +#define PARSERS_H_INCLUDED +#define PARSERS_H_VERSION "$Id: parsers.h,v 1.26 2002/05/08 15:59:53 oes Exp $" +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/parsers.h,v $ + * + * Purpose : Declares functions to parse/crunch headers and pages. + * Functions declared include: + * `add_to_iob', `client_cookie_adder', `client_from', + * `client_referrer', `client_send_cookie', `client_ua', + * `client_uagent', `client_x_forwarded', + * `client_x_forwarded_adder', `client_xtra_adder', + * `content_type', `crumble', `destroy_list', `enlist', + * `flush_socket', `free_http_request', `get_header', + * `list_to_text', `parse_http_request', `sed', + * and `server_set_cookie'. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: parsers.h,v $ + * Revision 1.26 2002/05/08 15:59:53 oes + * Changed add_to_iob signature (now returns jb_err) + * + * Revision 1.25 2002/03/26 22:29:55 swa + * we have a new homepage! + * + * Revision 1.24 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.23 2002/03/13 00:27:05 jongfoster + * Killing warnings + * + * Revision 1.22 2002/03/09 20:03:52 jongfoster + * - Making various functions return int rather than size_t. + * (Undoing a recent change). Since size_t is unsigned on + * Windows, functions like read_socket that return -1 on + * error cannot return a size_t. + * + * THIS WAS A MAJOR BUG - it caused frequent, unpredictable + * crashes, and also frequently caused JB to jump to 100% + * CPU and stay there. (Because it thought it had just + * read ((unsigned)-1) == 4Gb of data...) + * + * - The signature of write_socket has changed, it now simply + * returns success=0/failure=nonzero. + * + * - Trying to get rid of a few warnings --with-debug on + * Windows, I've introduced a new type "jb_socket". This is + * used for the socket file descriptors. On Windows, this + * is SOCKET (a typedef for unsigned). Everywhere else, it's + * an int. The error value can't be -1 any more, so it's + * now JB_INVALID_SOCKET (which is -1 on UNIX, and in + * Windows it maps to the #define INVALID_SOCKET.) + * + * - The signature of bind_port has changed. + * + * Revision 1.21 2002/03/07 03:46:17 oes + * Fixed compiler warnings + * + * Revision 1.20 2002/02/20 23:15:13 jongfoster + * Parsing functions now handle out-of-memory gracefully by returning + * an error code. + * + * Revision 1.19 2002/01/17 21:03:47 jongfoster + * Moving all our URL and URL pattern parsing code to urlmatch.c. + * + * Revision 1.18 2001/10/26 17:40:23 oes + * Introduced get_header_value() + * Removed client_accept() + * + * Revision 1.17 2001/10/13 12:47:32 joergs + * Removed client_host, added client_host_adder + * + * Revision 1.16 2001/10/07 18:50:16 oes + * Added server_content_encoding, renamed server_transfer_encoding + * + * Revision 1.15 2001/10/07 18:01:55 oes + * Changed server_http11 to server_http + * + * Revision 1.14 2001/10/07 15:45:48 oes + * added client_accept_encoding, client_te, client_accept_encoding_adder + * + * renamed content_type and content_length + * + * fixed client_host and strclean prototypes + * + * Revision 1.13 2001/09/29 12:56:03 joergs + * IJB now changes HTTP/1.1 to HTTP/1.0 in requests and answers. + * + * Revision 1.12 2001/09/13 23:05:50 jongfoster + * Changing the string paramater to the header parsers a "const". + * + * Revision 1.11 2001/07/31 14:46:53 oes + * Added prototype for connection_close_adder + * + * Revision 1.10 2001/07/30 22:08:36 jongfoster + * Tidying up #defines: + * - All feature #defines are now of the form FEATURE_xxx + * - Permanently turned off WIN_GUI_EDIT + * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS + * + * Revision 1.9 2001/07/29 18:43:08 jongfoster + * Changing #ifdef _FILENAME_H to FILENAME_H_INCLUDED, to conform to + * ANSI C rules. + * + * Revision 1.8 2001/07/13 14:01:54 oes + * Removed all #ifdef PCRS + * + * Revision 1.7 2001/06/29 13:32:14 oes + * Removed logentry from cancelled commit + * + * Revision 1.6 2001/06/03 19:12:38 oes + * deleted const struct interceptors + * + * Revision 1.5 2001/05/31 21:30:33 jongfoster + * Removed list code - it's now in list.[ch] + * Renamed "permission" to "action", and changed many features + * to use the actions file rather than the global config. + * + * Revision 1.4 2001/05/27 13:19:06 oes + * Patched Joergs solution for the content-length in. + * + * Revision 1.3 2001/05/26 13:39:32 jongfoster + * Only crunches Content-Length header if applying RE filtering. + * Without this fix, Microsoft Windows Update wouldn't work. + * + * Revision 1.2 2001/05/20 01:21:20 jongfoster + * Version 2.9.4 checkin. + * - Merged popupfile and cookiefile, and added control over PCRS + * filtering, in new "permissionsfile". + * - Implemented LOG_LEVEL_FATAL, so that if there is a configuration + * file error you now get a message box (in the Win32 GUI) rather + * than the program exiting with no explanation. + * - Made killpopup use the PCRS MIME-type checking and HTTP-header + * skipping. + * - Removed tabs from "config" + * - Moved duplicated url parsing code in "loaders.c" to a new funcition. + * - Bumped up version number. + * + * Revision 1.1.1.1 2001/05/15 13:59:01 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#include "project.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const struct parsers client_patterns[]; +extern const struct parsers server_patterns[]; + +extern const add_header_func_ptr add_client_headers[]; +extern const add_header_func_ptr add_server_headers[]; + +extern int flush_socket(jb_socket fd, struct client_state *csp); +extern jb_err add_to_iob(struct client_state *csp, char *buf, int n); +extern char *get_header(struct client_state *csp); +extern char *get_header_value(const struct list *header_list, const char *header_name); +extern char *sed(const struct parsers pats[], const add_header_func_ptr more_headers[], struct client_state *csp); + +extern jb_err crumble (struct client_state *csp, char **header); +extern jb_err client_referrer (struct client_state *csp, char **header); +extern jb_err client_uagent (struct client_state *csp, char **header); +extern jb_err client_ua (struct client_state *csp, char **header); +extern jb_err client_from (struct client_state *csp, char **header); +extern jb_err client_send_cookie (struct client_state *csp, char **header); +extern jb_err client_x_forwarded (struct client_state *csp, char **header); +extern jb_err client_accept_encoding (struct client_state *csp, char **header); +extern jb_err client_te (struct client_state *csp, char **header); + +extern jb_err client_host_adder (struct client_state *csp); +extern jb_err client_cookie_adder (struct client_state *csp); +extern jb_err client_xtra_adder (struct client_state *csp); +extern jb_err client_accept_encoding_adder(struct client_state *csp); +extern jb_err client_x_forwarded_adder (struct client_state *csp); + +extern jb_err connection_close_adder (struct client_state *csp); + +extern jb_err server_set_cookie (struct client_state *csp, char **header); +extern jb_err server_content_type (struct client_state *csp, char **header); +extern jb_err server_content_length (struct client_state *csp, char **header); +extern jb_err server_content_md5 (struct client_state *csp, char **header); +extern jb_err server_content_encoding(struct client_state *csp, char **header); +extern jb_err server_transfer_coding (struct client_state *csp, char **header); +extern jb_err server_http (struct client_state *csp, char **header); + +#ifdef FEATURE_FORCE_LOAD +extern int strclean(const char *string, const char *substring); +#endif /* def FEATURE_FORCE_LOAD */ + +/* Revision control strings from this header and associated .c file */ +extern const char parsers_rcs[]; +extern const char parsers_h_rcs[]; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ndef PARSERS_H_INCLUDED */ + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/pcrs.c b/src/pcrs.c new file mode 100644 index 00000000..eb3b6771 --- /dev/null +++ b/src/pcrs.c @@ -0,0 +1,942 @@ +const char pcrs_rcs[] = "$Id: pcrs.c,v 1.19 2002/03/08 14:47:48 oes Exp $"; + +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/pcrs.c,v $ + * + * Purpose : pcrs is a supplement to the pcre library by Philip Hazel + * and adds Perl-style substitution. That + * is, it mimics Perl's 's' operator. See pcrs(3) for details. + * + * + * Copyright : Written and Copyright (C) 2000, 2001 by Andreas S. Oesterhelt + * + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser + * General Public License (LGPL), version 2.1, which should + * be included in this distribution (see LICENSE.txt), with + * the exception that the permission to replace that license + * with the GNU General Public License (GPL) given in section + * 3 is restricted to version 2 of the GPL. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the license for more details. + * + * The GNU Lesser General Public License should be included + * with this file. If not, you can view it at + * http://www.gnu.org/licenses/lgpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: pcrs.c,v $ + * Revision 1.19 2002/03/08 14:47:48 oes + * Cosmetics + * + * Revision 1.18 2002/03/08 14:17:14 oes + * Fixing -Wconversion warnings + * + * Revision 1.17 2002/03/08 13:45:48 oes + * Hiding internal functions + * + * Revision 1.16 2001/11/30 21:32:14 jongfoster + * Fixing signed/unsigned comparison (Andreas please check this!) + * One tab->space + * + * Revision 1.15 2001/09/20 16:11:06 steudten + * + * Add casting for some string functions. + * + * Revision 1.14 2001/09/09 21:41:57 oes + * Fixing yet another silly bug + * + * Revision 1.13 2001/09/06 14:05:59 oes + * Fixed silly bug + * + * Revision 1.12 2001/08/18 11:35:00 oes + * - Introduced pcrs_strerror() + * - made some NULL arguments non-fatal + * - added support for \n \r \e \b \t \f \a \0 in substitute + * - made quoting adhere to standard rules + * - added warning for bad backrefs + * - added pcrs_execute_list() + * - fixed comments + * - bugfix & cosmetics + * + * Revision 1.11 2001/08/15 15:32:03 oes + * - Added support for Perl's special variables $+, $' and $` + * - Improved the substitute parser + * - Replaced the hard limit for the maximum number of matches + * by dynamic reallocation + * + * Revision 1.10 2001/08/05 13:13:11 jongfoster + * Making parameters "const" where possible. + * + * Revision 1.9 2001/07/18 17:27:00 oes + * Changed interface; Cosmetics + * + * Revision 1.8 2001/06/29 21:45:41 oes + * Indentation, CRLF->LF, Tab-> Space + * + * Revision 1.7 2001/06/29 13:33:04 oes + * - Cleaned up, renamed and reordered functions, + * improved comments + * - Removed my_strsep + * - Replaced globalflag with a general flags int + * that holds PCRS_GLOBAL, PCRS_SUCCESS, and PCRS_TRIVIAL + * - Introduced trivial option that will prevent pcrs + * from honouring backreferences in the substitute, + * which is useful for large substitutes that are + * red in from somewhere and saves the pain of escaping + * the backrefs + * - Introduced convenience function pcrs_free_joblist() + * - Split pcrs_make_job() into pcrs_compile(), which still + * takes a complete s/// comand as argument and parses it, + * and a new function pcrs_make_job, which takes the + * three separate components. This should make for a + * much friendlier frontend. + * - Removed create_pcrs_job() which was useless + * - Fixed a bug in pcrs_execute + * - Success flag is now handled by pcrs instead of user + * + * Revision 1.6 2001/06/03 19:12:45 oes + * added FIXME + * + * Revision 1.5 2001/05/29 09:50:24 jongfoster + * (Fixed one int -> size_t) + * + * Revision 1.4 2001/05/25 14:12:40 oes + * Fixed bug: Empty substitutes now detected + * + * Revision 1.3 2001/05/25 11:03:55 oes + * Added sanity check for NULL jobs to pcrs_exec_substitution + * + * Revision 1.2 2001/05/22 18:46:04 oes + * + * Added support for PCRE_UNGREEDY behaviour to pcrs, + * which is selected by the (nonstandard and therefore + * capital) letter 'U' in the option string. + * It causes the quantifiers to be ungreedy by default. + * Appending a ? turns back to greedy (!). + * + * Revision 1.1.1.1 2001/05/15 13:59:02 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#include +#include +#include + +#include "pcrs.h" + +const char pcrs_h_rcs[] = PCRS_H_VERSION; + +/* + * Internal prototypes + */ + +static int pcrs_parse_perl_options(const char *optstring, int *flags); +static pcrs_substitute *pcrs_compile_replacement(const char *replacement, int trivialflag, + int capturecount, int *errptr); + +/********************************************************************* + * + * Function : pcrs_strerror + * + * Description : Return a string describing a given error code. + * + * Parameters : + * 1 : error = the error code + * + * Returns : char * to the descriptive string + * + *********************************************************************/ +const char *pcrs_strerror(const int error) +{ + if (error < 0) + { + switch (error) + { + /* Passed-through PCRE error: */ + case PCRE_ERROR_NOMEMORY: return "(pcre:) No memory"; + + /* Shouldn't happen unless PCRE or PCRS bug, or user messed with compiled job: */ + case PCRE_ERROR_NULL: return "(pcre:) NULL code or subject or ovector"; + case PCRE_ERROR_BADOPTION: return "(pcre:) Unrecognized option bit"; + case PCRE_ERROR_BADMAGIC: return "(pcre:) Bad magic number in code"; + case PCRE_ERROR_UNKNOWN_NODE: return "(pcre:) Bad node in pattern"; + + /* Can't happen / not passed: */ + case PCRE_ERROR_NOSUBSTRING: return "(pcre:) Fire in power supply"; + case PCRE_ERROR_NOMATCH: return "(pcre:) Water in power supply"; + + /* PCRS errors: */ + case PCRS_ERR_NOMEM: return "(pcrs:) No memory"; + case PCRS_ERR_CMDSYNTAX: return "(pcrs:) Syntax error while parsing command"; + case PCRS_ERR_STUDY: return "(pcrs:) PCRE error while studying the pattern"; + case PCRS_ERR_BADJOB: return "(pcrs:) Bad job - NULL job, pattern or substitute"; + case PCRS_WARN_BADREF: return "(pcrs:) Backreference out of range"; + + /* What's that? */ + default: return "Unknown error"; + } + } + /* error >= 0: No error */ + return "(pcrs:) Everything's just fine. Thanks for asking."; + +} + + +/********************************************************************* + * + * Function : pcrs_parse_perl_options + * + * Description : This function parses a string containing the options to + * Perl's s/// operator. It returns an integer that is the + * pcre equivalent of the symbolic optstring. + * Since pcre doesn't know about Perl's 'g' (global) or pcrs', + * 'T' (trivial) options but pcrs needs them, the corresponding + * flags are set if 'g'or 'T' is encountered. + * Note: The 'T' and 'U' options do not conform to Perl. + * + * Parameters : + * 1 : optstring = string with options in perl syntax + * 2 : flags = see description + * + * Returns : option integer suitable for pcre + * + *********************************************************************/ +static int pcrs_parse_perl_options(const char *optstring, int *flags) +{ + size_t i; + int rc = 0; + *flags = 0; + + if (NULL == optstring) return 0; + + for (i = 0; i < strlen(optstring); i++) + { + switch(optstring[i]) + { + case 'e': break; /* ToDo ;-) */ + case 'g': *flags |= PCRS_GLOBAL; break; + case 'i': rc |= PCRE_CASELESS; break; + case 'm': rc |= PCRE_MULTILINE; break; + case 'o': break; + case 's': rc |= PCRE_DOTALL; break; + case 'x': rc |= PCRE_EXTENDED; break; + case 'U': rc |= PCRE_UNGREEDY; break; + case 'T': *flags |= PCRS_TRIVIAL; break; + default: break; + } + } + return rc; + +} + + +/********************************************************************* + * + * Function : pcrs_compile_replacement + * + * Description : This function takes a Perl-style replacement (2nd argument + * to the s/// operator and returns a compiled pcrs_substitute, + * or NULL if memory allocation for the substitute structure + * fails. + * + * Parameters : + * 1 : replacement = replacement part of s/// operator + * in perl syntax + * 2 : trivialflag = Flag that causes backreferences to be + * ignored. + * 3 : capturecount = Number of capturing subpatterns in + * the pattern. Needed for $+ handling. + * 4 : errptr = pointer to an integer in which error + * conditions can be returned. + * + * Returns : pcrs_substitute data structure, or NULL if an + * error is encountered. In that case, *errptr has + * the reason. + * + *********************************************************************/ +static pcrs_substitute *pcrs_compile_replacement(const char *replacement, int trivialflag, int capturecount, int *errptr) +{ + int i, k, l, quoted; + size_t length; + char *text; + pcrs_substitute *r; + + i = k = l = quoted = 0; + + /* + * Sanity check + */ + if (NULL == replacement) + { + replacement = ""; + } + + /* + * Get memory or fail + */ + if (NULL == (r = (pcrs_substitute *)malloc(sizeof(pcrs_substitute)))) + { + *errptr = PCRS_ERR_NOMEM; + return NULL; + } + memset(r, '\0', sizeof(pcrs_substitute)); + + length = strlen(replacement); + + if (NULL == (text = (char *)malloc(length + 1))) + { + free(r); + *errptr = PCRS_ERR_NOMEM; + return NULL; + } + memset(text, '\0', length + 1); + + + /* + * In trivial mode, just copy the substitute text + */ + if (trivialflag) + { + text = strncpy(text, replacement, length + 1); + k = length; + } + + /* + * Else, parse, cut out and record all backreferences + */ + else + { + while (i < (int)length) + { + /* Quoting */ + if (replacement[i] == '\\') + { + if (quoted) + { + text[k++] = replacement[i++]; + quoted = 0; + } + else + { + if (replacement[i+1] && strchr("tnrfae0", replacement[i+1])) + { + switch (replacement[++i]) + { + case 't': + text[k++] = '\t'; + break; + case 'n': + text[k++] = '\n'; + break; + case 'r': + text[k++] = '\r'; + break; + case 'f': + text[k++] = '\f'; + break; + case 'a': + text[k++] = 7; + break; + case 'e': + text[k++] = 27; + break; + case '0': + text[k++] = '\0'; + break; + } + i++; + } + else + { + quoted = 1; + i++; + } + } + continue; + } + + /* Backreferences */ + if (replacement[i] == '$' && !quoted && i < (int)(length - 1)) + { + char *symbol, symbols[] = "'`+&"; + r->block_length[l] = k - r->block_offset[l]; + + /* Numerical backreferences */ + if (isdigit((int)replacement[i + 1])) + { + while (i < (int)length && isdigit((int)replacement[++i])) + { + r->backref[l] = r->backref[l] * 10 + replacement[i] - 48; + } + if (r->backref[l] > capturecount) + { + *errptr = PCRS_WARN_BADREF; + } + } + + /* Symbolic backreferences: */ + else if (NULL != (symbol = strchr(symbols, replacement[i + 1]))) + { + + if (symbol - symbols == 2) /* $+ */ + { + r->backref[l] = capturecount; + } + else if (symbol - symbols == 3) /* $& */ + { + r->backref[l] = 0; + } + else /* $' or $` */ + { + r->backref[l] = PCRS_MAX_SUBMATCHES + 1 - (symbol - symbols); + } + i += 2; + } + + /* Invalid backref -> plain '$' */ + else + { + goto plainchar; + } + + /* Valid and in range? -> record */ + if (r->backref[l] < PCRS_MAX_SUBMATCHES + 2) + { + r->backref_count[r->backref[l]] += 1; + r->block_offset[++l] = k; + } + else + { + *errptr = PCRS_WARN_BADREF; + } + continue; + } + +plainchar: + /* Plain chars are copied */ + text[k++] = replacement[i++]; + quoted = 0; + } + } /* -END- if (!trivialflag) */ + + /* + * Finish & return + */ + r->text = text; + r->backrefs = l; + r->block_length[l] = k - r->block_offset[l]; + + return r; + +} + + +/********************************************************************* + * + * Function : pcrs_free_job + * + * Description : Frees the memory used by a pcrs_job struct and its + * dependant structures. + * + * Parameters : + * 1 : job = pointer to the pcrs_job structure to be freed + * + * Returns : a pointer to the next job, if there was any, or + * NULL otherwise. + * + *********************************************************************/ +pcrs_job *pcrs_free_job(pcrs_job *job) +{ + pcrs_job *next; + + if (job == NULL) + { + return NULL; + } + else + { + next = job->next; + if (job->pattern != NULL) free(job->pattern); + if (job->hints != NULL) free(job->hints); + if (job->substitute != NULL) + { + if (job->substitute->text != NULL) free(job->substitute->text); + free(job->substitute); + } + free(job); + } + return next; + +} + + +/********************************************************************* + * + * Function : pcrs_free_joblist + * + * Description : Iterates through a chained list of pcrs_job's and + * frees them using pcrs_free_job. + * + * Parameters : + * 1 : joblist = pointer to the first pcrs_job structure to + * be freed + * + * Returns : N/A + * + *********************************************************************/ +void pcrs_free_joblist(pcrs_job *joblist) +{ + while ( NULL != (joblist = pcrs_free_job(joblist)) ) {}; + + return; + +} + + +/********************************************************************* + * + * Function : pcrs_compile_command + * + * Description : Parses a string with a Perl-style s/// command, + * calls pcrs_compile, and returns a corresponding + * pcrs_job, or NULL if parsing or compiling the job + * fails. + * + * Parameters : + * 1 : command = string with perl-style s/// command + * 2 : errptr = pointer to an integer in which error + * conditions can be returned. + * + * Returns : a corresponding pcrs_job data structure, or NULL + * if an error was encountered. In that case, *errptr + * has the reason. + * + *********************************************************************/ +pcrs_job *pcrs_compile_command(const char *command, int *errptr) +{ + int i, k, l, quoted = FALSE; + size_t limit; + char delimiter; + char *tokens[4]; + pcrs_job *newjob; + + i = k = l = 0; + + /* + * Tokenize the perl command + */ + limit = strlen(command); + if (limit < 4) + { + *errptr = PCRS_ERR_CMDSYNTAX; + return NULL; + } + else + { + delimiter = command[1]; + } + + tokens[l] = (char *) malloc(limit + 1); + + for (i = 0; i <= (int)limit; i++) + { + + if (command[i] == delimiter && !quoted) + { + if (l == 3) + { + l = -1; + break; + } + tokens[0][k++] = '\0'; + tokens[++l] = tokens[0] + k; + continue; + } + + else if (command[i] == '\\' && !quoted) + { + quoted = TRUE; + if (command[i+1] == delimiter) continue; + } + else + { + quoted = FALSE; + } + tokens[0][k++] = command[i]; + } + + /* + * Syntax error ? + */ + if (l != 3) + { + *errptr = PCRS_ERR_CMDSYNTAX; + free(tokens[0]); + return NULL; + } + + newjob = pcrs_compile(tokens[1], tokens[2], tokens[3], errptr); + free(tokens[0]); + return newjob; + +} + + +/********************************************************************* + * + * Function : pcrs_compile + * + * Description : Takes the three arguments to a perl s/// command + * and compiles a pcrs_job structure from them. + * + * Parameters : + * 1 : pattern = string with perl-style pattern + * 2 : substitute = string with perl-style substitute + * 3 : options = string with perl-style options + * 4 : errptr = pointer to an integer in which error + * conditions can be returned. + * + * Returns : a corresponding pcrs_job data structure, or NULL + * if an error was encountered. In that case, *errptr + * has the reason. + * + *********************************************************************/ +pcrs_job *pcrs_compile(const char *pattern, const char *substitute, const char *options, int *errptr) +{ + pcrs_job *newjob; + int flags; + int capturecount; + const char *error; + + *errptr = 0; + + /* + * Handle NULL arguments + */ + if (pattern == NULL) pattern = ""; + if (substitute == NULL) substitute = ""; + + + /* + * Get and init memory + */ + if (NULL == (newjob = (pcrs_job *)malloc(sizeof(pcrs_job)))) + { + *errptr = PCRS_ERR_NOMEM; + return NULL; + } + memset(newjob, '\0', sizeof(pcrs_job)); + + + /* + * Evaluate the options + */ + newjob->options = pcrs_parse_perl_options(options, &flags); + newjob->flags = flags; + + + /* + * Compile the pattern + */ + newjob->pattern = pcre_compile(pattern, newjob->options, &error, errptr, NULL); + if (newjob->pattern == NULL) + { + pcrs_free_job(newjob); + return NULL; + } + + + /* + * Generate hints. This has little overhead, since the + * hints will be NULL for a boring pattern anyway. + */ + newjob->hints = pcre_study(newjob->pattern, 0, &error); + if (error != NULL) + { + *errptr = PCRS_ERR_STUDY; + pcrs_free_job(newjob); + return NULL; + } + + + /* + * Determine the number of capturing subpatterns. + * This is needed for handling $+ in the substitute. + */ + if (0 > (*errptr = pcre_fullinfo(newjob->pattern, newjob->hints, PCRE_INFO_CAPTURECOUNT, &capturecount))) + { + pcrs_free_job(newjob); + return NULL; + } + + + /* + * Compile the substitute + */ + if (NULL == (newjob->substitute = pcrs_compile_replacement(substitute, newjob->flags & PCRS_TRIVIAL, capturecount, errptr))) + { + pcrs_free_job(newjob); + return NULL; + } + + return newjob; + +} + + +/********************************************************************* + * + * Function : pcrs_execute_list + * + * Description : This is a multiple job wrapper for pcrs_execute(). + * Apply the regular substitutions defined by the jobs in + * the joblist to the subject. + * The subject itself is left untouched, memory for the result + * is malloc()ed and it is the caller's responsibility to free + * the result when it's no longer needed. + * + * Parameters : + * 1 : joblist = the chained list of pcrs_jobs to be executed + * 2 : subject = the subject string + * 3 : subject_length = the subject's length + * INCLUDING the terminating zero, if string! + * 4 : result = char** for returning the result + * 5 : result_length = size_t* for returning the result's length + * + * Returns : On success, the number of substitutions that were made. + * May be > 1 if job->flags contained PCRS_GLOBAL + * On failiure, the (negative) pcre error code describing the + * failiure, which may be translated to text using pcrs_strerror(). + * + *********************************************************************/ +int pcrs_execute_list(pcrs_job *joblist, char *subject, size_t subject_length, char **result, size_t *result_length) +{ + pcrs_job *job; + char *old, *new; + int hits, total_hits; + + old = subject; + *result_length = subject_length; + hits = total_hits = 0; + + for (job = joblist; job != NULL; job = job->next) + { + hits = pcrs_execute(job, old, *result_length, &new, result_length); + + if (old != subject) free(old); + + if (hits < 0) + { + return(hits); + } + else + { + total_hits += hits; + old = new; + } + } + + *result = new; + return(total_hits); + +} + + +/********************************************************************* + * + * Function : pcrs_execute + * + * Description : Apply the regular substitution defined by the job to the + * subject. + * The subject itself is left untouched, memory for the result + * is malloc()ed and it is the caller's responsibility to free + * the result when it's no longer needed. + * + * Parameters : + * 1 : job = the pcrs_job to be executed + * 2 : subject = the subject (== original) string + * 3 : subject_length = the subject's length + * INCLUDING the terminating zero, if string! + * 4 : result = char** for returning the result + * 5 : result_length = size_t* for returning the result's length + * + * Returns : On success, the number of substitutions that were made. + * May be > 1 if job->flags contained PCRS_GLOBAL + * On failiure, the (negative) pcre error code describing the + * failiure, which may be translated to text using pcrs_strerror(). + * + *********************************************************************/ +int pcrs_execute(pcrs_job *job, char *subject, size_t subject_length, char **result, size_t *result_length) +{ + int offsets[3 * PCRS_MAX_SUBMATCHES], + offset, + i, k, + matches_found, + submatches, + max_matches = PCRS_MAX_MATCH_INIT; + size_t newsize; + pcrs_match *matches, *dummy; + char *result_offset; + + offset = i = k = 0; + + /* + * Sanity check & memory allocation + */ + if (job == NULL || job->pattern == NULL || job->substitute == NULL) + { + *result = NULL; + return(PCRS_ERR_BADJOB); + } + + if (NULL == (matches = (pcrs_match *)malloc(max_matches * sizeof(pcrs_match)))) + { + *result = NULL; + return(PCRS_ERR_NOMEM); + } + memset(matches, '\0', max_matches * sizeof(pcrs_match)); + + + /* + * Find the pattern and calculate the space + * requirements for the result + */ + newsize = subject_length; + + while ((submatches = pcre_exec(job->pattern, job->hints, subject, (int)subject_length, offset, 0, offsets, 3 * PCRS_MAX_SUBMATCHES)) > 0) + { + job->flags |= PCRS_SUCCESS; + matches[i].submatches = submatches; + + for (k = 0; k < submatches; k++) + { + matches[i].submatch_offset[k] = offsets[2 * k]; + + /* Note: Non-found optional submatches have length -1-(-1)==0 */ + matches[i].submatch_length[k] = offsets[2 * k + 1] - offsets[2 * k]; + + /* reserve mem for each submatch as often as it is ref'd */ + newsize += matches[i].submatch_length[k] * job->substitute->backref_count[k]; + } + /* plus replacement text size minus match text size */ + newsize += strlen(job->substitute->text) - matches[i].submatch_length[0]; + + /* chunk before match */ + matches[i].submatch_offset[PCRS_MAX_SUBMATCHES] = 0; + matches[i].submatch_length[PCRS_MAX_SUBMATCHES] = offsets[0]; + newsize += offsets[0] * job->substitute->backref_count[PCRS_MAX_SUBMATCHES]; + + /* chunk after match */ + matches[i].submatch_offset[PCRS_MAX_SUBMATCHES + 1] = offsets[1]; + matches[i].submatch_length[PCRS_MAX_SUBMATCHES + 1] = subject_length - offsets[1] - 1; + newsize += (subject_length - offsets[1]) * job->substitute->backref_count[PCRS_MAX_SUBMATCHES + 1]; + + /* Storage for matches exhausted? -> Extend! */ + if (++i >= max_matches) + { + max_matches = (int)(max_matches * PCRS_MAX_MATCH_GROW); + if (NULL == (dummy = (pcrs_match *)realloc(matches, max_matches * sizeof(pcrs_match)))) + { + free(matches); + *result = NULL; + return(PCRS_ERR_NOMEM); + } + matches = dummy; + } + + /* Non-global search or limit reached? */ + if (!(job->flags & PCRS_GLOBAL)) break; + + /* Don't loop on empty matches */ + if (offsets[1] == offset) + if ((size_t)offset < subject_length) + offset++; + else + break; + /* Go find the next one */ + else + offset = offsets[1]; + } + /* Pass pcre error through if (bad) failiure */ + if (submatches < PCRE_ERROR_NOMATCH) + { + free(matches); + return submatches; + } + matches_found = i; + + + /* + * Get memory for the result + */ + if ((*result = (char *)malloc(newsize)) == NULL) /* must be free()d by caller */ + { + free(matches); + return PCRS_ERR_NOMEM; + } + + + /* + * Replace + */ + offset = 0; + result_offset = *result; + + for (i = 0; i < matches_found; i++) + { + /* copy the chunk preceding the match */ + memcpy(result_offset, subject + offset, (size_t)matches[i].submatch_offset[0] - offset); + result_offset += matches[i].submatch_offset[0] - offset; + + /* For every segment of the substitute.. */ + for (k = 0; k <= job->substitute->backrefs; k++) + { + /* ...copy its text.. */ + memcpy(result_offset, job->substitute->text + job->substitute->block_offset[k], job->substitute->block_length[k]); + result_offset += job->substitute->block_length[k]; + + /* ..plus, if it's not the last chunk, i.e.: There *is* a backref.. */ + if (k != job->substitute->backrefs + /* ..in legal range.. */ + && job->substitute->backref[k] < PCRS_MAX_SUBMATCHES + 2 + /* ..and referencing a nonempty match.. */ + && matches[i].submatch_length[job->substitute->backref[k]] > 0) + { + /* ..copy the submatch that is ref'd. */ + memcpy( + result_offset, + subject + matches[i].submatch_offset[job->substitute->backref[k]], + matches[i].submatch_length[job->substitute->backref[k]] + ); + result_offset += matches[i].submatch_length[job->substitute->backref[k]]; + } + } + offset = matches[i].submatch_offset[0] + matches[i].submatch_length[0]; + } + + /* Copy the rest. */ + memcpy(result_offset, subject + offset, subject_length - offset); + + *result_length = newsize; + free(matches); + return matches_found; + +} + + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/pcrs.h b/src/pcrs.h new file mode 100644 index 00000000..b7560679 --- /dev/null +++ b/src/pcrs.h @@ -0,0 +1,171 @@ +#ifndef PCRS_H_INCLUDED +#define PCRS_H_INCLUDED + +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/pcrs.h,v $ + * + * Purpose : Header file for pcrs.c + * + * Copyright : see pcrs.c + * + * Revisions : + * $Log: pcrs.h,v $ + * Revision 1.11 2002/03/08 14:18:23 oes + * Fixing -Wconversion warnings + * + * Revision 1.10 2002/03/08 13:44:48 oes + * Hiding internal functions, preventing double inclusion of pcre.h + * + * Revision 1.9 2001/08/18 11:35:29 oes + * - Introduced pcrs_strerror() + * - added pcrs_execute_list() + * + * Revision 1.8 2001/08/15 15:32:50 oes + * Replaced the hard limit for the maximum number of matches + * by dynamic reallocation + * + * Revision 1.7 2001/08/05 13:13:11 jongfoster + * Making parameters "const" where possible. + * + * Revision 1.6 2001/07/29 18:52:06 jongfoster + * Renaming _PCRS_H, and adding "extern C {}" + * + * Revision 1.5 2001/07/18 17:27:00 oes + * Changed interface; Cosmetics + * + * Revision 1.4 2001/06/29 13:33:19 oes + * - Cleaned up, commented and adapted to reflect the + * changes in pcrs.c + * - Introduced the PCRS_* flags + * + * Revision 1.3 2001/06/09 10:58:57 jongfoster + * Removing a single unused #define which referenced BUFSIZ + * + * Revision 1.2 2001/05/25 11:03:55 oes + * Added sanity check for NULL jobs to pcrs_exec_substitution + * + * Revision 1.1.1.1 2001/05/15 13:59:02 oes + * Initial import of version 2.9.3 source tree + * + * Revision 1.4 2001/05/11 01:57:02 rodney + * Added new file header standard w/RCS control tags. + * + * revision 1.3 2001/05/08 02:38:13 rodney + * Changed C++ "//" style comment to C style comments. + * + * revision 1.2 2001/04/30 02:39:24 rodney + * Made this pcrs.h file conditionally included. + * + * revision 1.1 2001/04/16 21:10:38 rodney + * Initial checkin + * + *********************************************************************/ + +#define PCRS_H_VERSION "$Id: pcrs.h,v 1.11 2002/03/08 14:18:23 oes Exp $" + + +#ifndef _PCRE_H +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Constants: + */ + +#define FALSE 0 +#define TRUE 1 + +/* Capacity */ +#define PCRS_MAX_SUBMATCHES 33 /* Maximum number of capturing subpatterns allowed. MUST be <= 99! FIXME: Should be dynamic */ +#define PCRS_MAX_MATCH_INIT 40 /* Initial amount of matches that can be stored in global searches */ +#define PCRS_MAX_MATCH_GROW 1.6 /* Factor by which storage for matches is extended if exhausted */ + +/* Error codes */ +#define PCRS_ERR_NOMEM -10 /* Failed to acquire memory. */ +#define PCRS_ERR_CMDSYNTAX -11 /* Syntax of s///-command */ +#define PCRS_ERR_STUDY -12 /* pcre error while studying the pattern */ +#define PCRS_ERR_BADJOB -13 /* NULL job pointer, pattern or substitute */ +#define PCRS_WARN_BADREF -14 /* Backreference out of range */ + +/* Flags */ +#define PCRS_GLOBAL 1 /* Job should be applied globally, as with perl's g option */ +#define PCRS_TRIVIAL 2 /* Backreferences in the substitute are ignored */ +#define PCRS_SUCCESS 4 /* Job did previously match */ + + +/* + * Data types: + */ + +/* A compiled substitute */ + +typedef struct { + char *text; /* The plaintext part of the substitute, with all backreferences stripped */ + int backrefs; /* The number of backreferences */ + int block_offset[PCRS_MAX_SUBMATCHES]; /* Array with the offsets of all plaintext blocks in text */ + size_t block_length[PCRS_MAX_SUBMATCHES]; /* Array with the lengths of all plaintext blocks in text */ + int backref[PCRS_MAX_SUBMATCHES]; /* Array with the backref number for all plaintext block borders */ + int backref_count[PCRS_MAX_SUBMATCHES + 2]; /* Array with the number of references to each backref index */ +} pcrs_substitute; + + +/* + * A match, including all captured subpatterns (submatches) + * Note: The zeroth is the whole match, the PCRS_MAX_SUBMATCHES + 0th + * is the range before the match, the PCRS_MAX_SUBMATCHES + 1th is the + * range after the match. + */ + +typedef struct { + int submatches; /* Number of captured subpatterns */ + int submatch_offset[PCRS_MAX_SUBMATCHES + 2]; /* Offset for each submatch in the subject */ + size_t submatch_length[PCRS_MAX_SUBMATCHES + 2]; /* Length of each submatch in the subject */ +} pcrs_match; + + +/* A PCRS job */ + +typedef struct PCRS_JOB { + pcre *pattern; /* The compiled pcre pattern */ + pcre_extra *hints; /* The pcre hints for the pattern */ + int options; /* The pcre options (numeric) */ + int flags; /* The pcrs and user flags (see "Flags" above) */ + pcrs_substitute *substitute; /* The compiled pcrs substitute */ + struct PCRS_JOB *next; /* Pointer for chaining jobs to joblists */ +} pcrs_job; + + +/* + * Prototypes: + */ + +/* Main usage */ +extern pcrs_job *pcrs_compile_command(const char *command, int *errptr); +extern pcrs_job *pcrs_compile(const char *pattern, const char *substitute, const char *options, int *errptr); +extern int pcrs_execute(pcrs_job *job, char *subject, size_t subject_length, char **result, size_t *result_length); +extern int pcrs_execute_list(pcrs_job *joblist, char *subject, size_t subject_length, char **result, size_t *result_length); + +/* Freeing jobs */ +extern pcrs_job *pcrs_free_job(pcrs_job *job); +extern void pcrs_free_joblist(pcrs_job *joblist); + +/* Info on errors: */ +extern const char *pcrs_strerror(const int error); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ndef PCRS_H_INCLUDED */ + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/project.h b/src/project.h new file mode 100644 index 00000000..c63c94dc --- /dev/null +++ b/src/project.h @@ -0,0 +1,1437 @@ +#ifndef PROJECT_H_INCLUDED +#define PROJECT_H_INCLUDED +/** Version string. */ +#define PROJECT_H_VERSION "$Id: project.h,v 1.72 2002/05/14 21:35:49 oes Exp $" +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/project.h,v $ + * + * Purpose : Defines data structures which are widely used in the + * project. Does not define any variables or functions + * (though it does declare some macros). + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: project.h,v $ + * Revision 1.72 2002/05/14 21:35:49 oes + * Split HELP_LINK_PREFIX into ACTIONS_HELP_PREFIX and CONFIG_HELP_PREFIX + * because of split in user-manual + * + * Revision 1.71 2002/05/12 21:39:36 jongfoster + * - Adding Doxygen-style comments to structures and #defines. + * + * Revision 1.70 2002/05/12 16:05:50 jongfoster + * Fixing ACTION_MASK_ALL to be unsigned long rather than + * just unsigned int. I don't know if anyone is porting + * Privoxy to 16-bit platforms, but if so, +limit-connect + * wouldn't have worked because of this bug. + * + * Revision 1.69 2002/05/08 16:00:16 oes + * Added size member to struct iob, so it can + * be alloced larger than needed. + * + * Revision 1.68 2002/04/26 12:56:00 oes + * Killed REDIRECT_URL, added USER_MANUAL_URL and HELP_LINK_PREFIX + * + * Revision 1.67 2002/04/24 02:12:43 oes + * - Jon's multiple AF patch: + * - Make csp->actions_list an array + * - #define MAX_ACTION_FILES + * - Moved CGI_PARAM_LEN_MAX (500) here + * + * Revision 1.66 2002/04/15 19:06:43 jongfoster + * Typos + * + * Revision 1.65 2002/04/04 00:36:36 gliptak + * always use pcre for matching + * + * Revision 1.64 2002/04/03 22:28:03 gliptak + * Removed references to gnu_regex + * + * Revision 1.63 2002/03/31 17:19:00 jongfoster + * Win32 only: Enabling STRICT to fix a VC++ compile warning. + * + * Revision 1.62 2002/03/26 22:48:49 swa + * new homepage url + * + * Revision 1.61 2002/03/26 22:29:55 swa + * we have a new homepage! + * + * Revision 1.60 2002/03/24 15:52:17 jongfoster + * Changing CGI URL prefixes for new name + * + * Revision 1.59 2002/03/24 15:23:33 jongfoster + * Name changes + * + * Revision 1.58 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.57 2002/03/16 20:28:34 oes + * Added descriptions to the filters so users will know what they select in the cgi editor + * + * Revision 1.56 2002/03/13 20:27:30 oes + * Fixing bug with CT_TABOO + * + * Revision 1.55 2002/03/12 01:42:50 oes + * Introduced modular filters + * + * Revision 1.54 2002/03/09 20:03:52 jongfoster + * - Making various functions return int rather than size_t. + * (Undoing a recent change). Since size_t is unsigned on + * Windows, functions like read_socket that return -1 on + * error cannot return a size_t. + * + * THIS WAS A MAJOR BUG - it caused frequent, unpredictable + * crashes, and also frequently caused JB to jump to 100% + * CPU and stay there. (Because it thought it had just + * read ((unsigned)-1) == 4Gb of data...) + * + * - The signature of write_socket has changed, it now simply + * returns success=0/failure=nonzero. + * + * - Trying to get rid of a few warnings --with-debug on + * Windows, I've introduced a new type "jb_socket". This is + * used for the socket file descriptors. On Windows, this + * is SOCKET (a typedef for unsigned). Everywhere else, it's + * an int. The error value can't be -1 any more, so it's + * now JB_INVALID_SOCKET (which is -1 on UNIX, and in + * Windows it maps to the #define INVALID_SOCKET.) + * + * - The signature of bind_port has changed. + * + * Revision 1.53 2002/03/08 16:48:55 oes + * Added FEATURE_NO_GIFS and BUILTIN_IMAGE_MIMETYPE + * + * Revision 1.52 2002/03/07 03:46:17 oes + * Fixed compiler warnings + * + * Revision 1.51 2002/03/05 04:52:42 oes + * Deleted non-errlog debugging code + * + * Revision 1.50 2002/03/04 19:32:07 oes + * Changed default port to 8118 + * + * Revision 1.49 2002/03/04 18:28:55 oes + * Deleted PID_FILE_NAME + * + * Revision 1.48 2002/03/03 14:50:40 oes + * Fixed CLF logging: Added ocmd member for client's request to struct http_request + * + * Revision 1.47 2002/02/20 23:15:13 jongfoster + * Parsing functions now handle out-of-memory gracefully by returning + * an error code. + * + * Revision 1.46 2002/01/17 21:06:09 jongfoster + * Now #defining the URLs of the config interface + * + * Minor changes to struct http_request and struct url_spec due to + * standardizing that struct http_request is used to represent a URL, and + * struct url_spec is used to represent a URL pattern. (Before, URLs were + * represented as seperate variables and a partially-filled-in url_spec). + * + * Revision 1.45 2002/01/09 14:33:27 oes + * Added HOSTENT_BUFFER_SIZE + * + * Revision 1.44 2001/12/30 14:07:32 steudten + * - Add signal handling (unix) + * - Add SIGHUP handler (unix) + * - Add creation of pidfile (unix) + * - Add action 'top' in rc file (RH) + * - Add entry 'SIGNALS' to manpage + * - Add exit message to logfile (unix) + * + * Revision 1.43 2001/11/22 21:57:51 jongfoster + * Making action_spec->flags into an unsigned long rather than just an + * unsigned int. + * Adding ACTION_NO_COOKIE_KEEP + * + * Revision 1.42 2001/11/05 21:42:41 steudten + * Include DBG() macro. + * + * Revision 1.41 2001/10/28 19:12:06 jongfoster + * Adding ijb_toupper() + * + * Revision 1.40 2001/10/26 17:40:47 oes + * Moved ijb_isspace and ijb_tolower to project.h + * Removed http->user_agent, csp->referrer and csp->accept_types + * + * Revision 1.39 2001/10/25 03:45:02 david__schmidt + * Adding a (void*) cast to freez() because Visual Age C++ won't expand the + * macro when called with a cast; so moving the cast to the macro def'n + * seems to both eliminate compiler warnings (on darwin and OS/2, anyway) and + * doesn't make macro expansion complain. Hope this works for everyone else + * too... + * + * Revision 1.38 2001/10/23 21:19:04 jongfoster + * New error-handling support: jb_err type and JB_ERR_xxx constants + * CGI functions now return a jb_err, and their parameters map is const. + * Support for RUNTIME_FEATUREs to enable/disable config editor + * Adding a few comments + * + * Revision 1.37 2001/10/14 22:14:01 jongfoster + * Removing name_length field from struct cgi_dispatcher, as this is + * now calculated at runtime from the "name" field. + * + * Revision 1.36 2001/10/10 16:45:15 oes + * Added LIMIT_CONNECT action and string + * Fixed HTTP message line termination + * Added CFORBIDDEN HTTP message + * + * Revision 1.35 2001/10/07 18:06:43 oes + * Added status member to struct http_request + * + * Revision 1.34 2001/10/07 15:45:25 oes + * Added url member to struct http_request and commented all + * members + * + * Added CT_TABOO + * + * Added ACTION_DOWNGRADE and ACTION_NO_COMPRESSION + * + * Replaced struct client_state members rejected, + * force, active and toggled_on with "flags" bitmap. + * + * Added CSP_FLAG_MODIFIED and CSP_FLAG_CHUNKED + * + * Added buffer_limit to struct configuration_spec + * + * Revision 1.33 2001/09/20 13:30:08 steudten + * + * Make freez() more secure in case of: if (exp) { free(z) ; a=*z } + * Last case will set z to NULL in free(z) and thats bad.. + * + * Revision 1.32 2001/09/16 23:02:51 jongfoster + * Fixing warning + * + * Revision 1.31 2001/09/16 13:20:29 jongfoster + * Rewrite of list library. Now has seperate header and list_entry + * structures. Also added a large sprinking of assert()s to the list + * code. + * + * Revision 1.30 2001/09/13 23:52:00 jongfoster + * Support for both static and dynamically generated CGI pages + * + * Revision 1.29 2001/09/13 23:29:43 jongfoster + * Defining FORWARD_SPEC_INITIALIZER + * + * Revision 1.28 2001/09/13 23:05:50 jongfoster + * Changing the string paramater to the header parsers a "const". + * + * Revision 1.27 2001/08/05 16:06:20 jongfoster + * Modifiying "struct map" so that there are now separate header and + * "map_entry" structures. This means that functions which modify a + * map no longer need to return a pointer to the modified map. + * Also, it no longer reverses the order of the entries (which may be + * important with some advanced template substitutions). + * + * Revision 1.26 2001/07/30 22:08:36 jongfoster + * Tidying up #defines: + * - All feature #defines are now of the form FEATURE_xxx + * - Permanently turned off WIN_GUI_EDIT + * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS + * + * Revision 1.25 2001/07/29 18:43:08 jongfoster + * Changing #ifdef _FILENAME_H to FILENAME_H_INCLUDED, to conform to + * ANSI C rules. + * + * Revision 1.24 2001/07/25 17:20:27 oes + * Introduced http->user_agent + * + * Revision 1.23 2001/07/18 12:32:23 oes + * - Added ACTION_STRING_DEANIMATE + * - moved #define freez from jcc.h to project.h + * + * Revision 1.22 2001/07/15 17:51:41 jongfoster + * Renaming #define STATIC to STATIC_PCRE + * + * Revision 1.21 2001/07/13 14:03:19 oes + * - Reorganized regex header inclusion and #defines to + * comply to the scheme in configure.in + * - Added csp->content_type and its CT_* keys + * - Added ACTION_DEANIMATE + * - Removed all #ifdef PCRS + * + * Revision 1.20 2001/06/29 21:45:41 oes + * Indentation, CRLF->LF, Tab-> Space + * + * Revision 1.19 2001/06/29 13:33:36 oes + * - Improved comments + * - Introduced http_request.host_ip_addr_str + * - Introduced http_response.head_length + * - Introduced config.my_ip_addr_str, config.my_hostname, + * config.admin_address and config.proxy_info_url + * - Removed config.proxy_args_header and config.proxy_args_trailer, + * renamed config.proxy_args_invocation to config.proxy_args + * - Removed HTML snipplets and GIFs + * - Removed logentry from cancelled commit + * + * Revision 1.18 2001/06/09 10:57:39 jongfoster + * Adding definition of BUFFER_SIZE. + * Changing struct cgi_dispatcher to use "const" strings. + * + * Revision 1.17 2001/06/07 23:15:09 jongfoster + * Merging ACL and forward files into config file. + * Moving struct gateway members into struct forward_spec + * Removing config->proxy_args_gateways + * Cosmetic: Adding a few comments + * + * Revision 1.16 2001/06/04 18:31:58 swa + * files are now prefixed with either `confdir' or `logdir'. + * `make redhat-dist' replaces both entries confdir and logdir + * with redhat values + * + * Revision 1.15 2001/06/04 11:28:53 swa + * redirect did not work due to missing / + * + * Revision 1.14 2001/06/03 11:03:48 oes + * Added struct map, + * added struct http_response, + * changed struct interceptors to struct cgi_dispatcher, + * moved HTML stuff to cgi.h + * + * Revision 1.13 2001/06/01 20:05:36 jongfoster + * Support for +image-blocker{}: added ACTION_IMAGE_BLOCKER + * constant, and removed csp->tinygif. + * + * Revision 1.12 2001/06/01 18:49:17 jongfoster + * Replaced "list_share" with "list" - the tiny memory gain was not + * worth the extra complexity. + * + * Revision 1.11 2001/06/01 10:32:47 oes + * Added constants for anchoring selection bitmap + * + * Revision 1.10 2001/05/31 21:33:53 jongfoster + * Changes for new actions file, replacing permissionsfile + * and parts of the config file. Also added support for + * list_shared. + * + * Revision 1.9 2001/05/31 17:32:31 oes + * + * - Enhanced domain part globbing with infix and prefix asterisk + * matching and optional unanchored operation + * + * Revision 1.8 2001/05/29 20:09:15 joergs + * HTTP_REDIRECT_TEMPLATE fixed. + * + * Revision 1.7 2001/05/29 09:50:24 jongfoster + * Unified blocklist/imagelist/actionslist. + * File format is still under discussion, but the internal changes + * are (mostly) done. + * + * Also modified interceptor behaviour: + * - We now intercept all URLs beginning with one of the following + * prefixes (and *only* these prefixes): + * * http://i.j.b/ + * * http://ijbswa.sf.net/config/ + * * http://ijbswa.sourceforge.net/config/ + * - New interceptors "home page" - go to http://i.j.b/ to see it. + * - Internal changes so that intercepted and fast redirect pages + * are not replaced with an image. + * - Interceptors now have the option to send a binary page direct + * to the client. (i.e. ijb-send-banner uses this) + * - Implemented show-url-info interceptor. (Which is why I needed + * the above interceptors changes - a typical URL is + * "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif". + * The previous mechanism would not have intercepted that, and + * if it had been intercepted then it then it would have replaced + * it with an image.) + * + * Revision 1.6 2001/05/27 22:17:04 oes + * + * - re_process_buffer no longer writes the modified buffer + * to the client, which was very ugly. It now returns the + * buffer, which it is then written by chat. + * + * - content_length now adjusts the Content-Length: header + * for modified documents rather than crunch()ing it. + * (Length info in csp->content_length, which is 0 for + * unmodified documents) + * + * - For this to work, sed() is called twice when filtering. + * + * Revision 1.5 2001/05/26 00:28:36 jongfoster + * Automatic reloading of config file. + * Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32). + * Most of the global variables have been moved to a new + * struct configuration_spec, accessed through csp->config->globalname + * Most of the globals remaining are used by the Win32 GUI. + * + * Revision 1.4 2001/05/22 18:46:04 oes + * + * - Enabled filtering banners by size rather than URL + * by adding patterns that replace all standard banner + * sizes with the "Junkbuster" gif to the re_filterfile + * + * - Enabled filtering WebBugs by providing a pattern + * which kills all 1x1 images + * + * - Added support for PCRE_UNGREEDY behaviour to pcrs, + * which is selected by the (nonstandard and therefore + * capital) letter 'U' in the option string. + * It causes the quantifiers to be ungreedy by default. + * Appending a ? turns back to greedy (!). + * + * - Added a new interceptor ijb-send-banner, which + * sends back the "Junkbuster" gif. Without imagelist or + * MSIE detection support, or if tinygif = 1, or the + * URL isn't recognized as an imageurl, a lame HTML + * explanation is sent instead. + * + * - Added new feature, which permits blocking remote + * script redirects and firing back a local redirect + * to the browser. + * The feature is conditionally compiled, i.e. it + * can be disabled with --disable-fast-redirects, + * plus it must be activated by a "fast-redirects" + * line in the config file, has its own log level + * and of course wants to be displayed by show-proxy-args + * Note: Boy, all the #ifdefs in 1001 locations and + * all the fumbling with configure.in and acconfig.h + * were *way* more work than the feature itself :-( + * + * - Because a generic redirect template was needed for + * this, tinygif = 3 now uses the same. + * + * - Moved GIFs, and other static HTTP response templates + * to project.h + * + * - Some minor fixes + * + * - Removed some >400 CRs again (Jon, you really worked + * a lot! ;-) + * + * Revision 1.3 2001/05/20 01:21:20 jongfoster + * Version 2.9.4 checkin. + * - Merged popupfile and cookiefile, and added control over PCRS + * filtering, in new "actionsfile". + * - Implemented LOG_LEVEL_FATAL, so that if there is a configuration + * file error you now get a message box (in the Win32 GUI) rather + * than the program exiting with no explanation. + * - Made killpopup use the PCRS MIME-type checking and HTTP-header + * skipping. + * - Removed tabs from "config" + * - Moved duplicated url parsing code in "loaders.c" to a new funcition. + * - Bumped up version number. + * + * Revision 1.2 2001/05/17 23:01:01 oes + * - Cleaned CRLF's from the sources and related files + * + * Revision 1.1.1.1 2001/05/15 13:59:03 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +/* Declare struct FILE for vars and funcs. */ +#include + +/* Need time_t for file_list */ +#include + +/* + * Include appropriate regular expression libraries. + * Note that pcrs and pcre (native) are needed for cgi + * and are included anyway. + */ + +#ifdef STATIC_PCRE +# include "pcre.h" +#else +# include +#endif + +#ifdef STATIC_PCRS +# include "pcrs.h" +#else +# include +#endif + +#ifdef STATIC_PCRE +# include "pcreposix.h" +#else +# include +#endif + +#ifdef AMIGA +#include "amiga.h" +#endif /* def AMIGA */ + +#ifdef _WIN32 +/* + * I don't want to have to #include all this just for the declaration + * of SOCKET. However, it looks like we have to... + */ +#ifndef STRICT +#define STRICT +#endif +#include +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 + +typedef SOCKET jb_socket; + +#define JB_INVALID_SOCKET INVALID_SOCKET + +#else /* ndef _WIN32 */ + +/** + * The type used by sockets. On UNIX it's an int. Microsoft decided to + * make it an unsigned. + */ +typedef int jb_socket; + +/** + * The error value used for variables of type jb_socket. On UNIX this + * is -1, however Microsoft decided to make socket handles unsigned, so + * they use a different value. + */ + +#define JB_INVALID_SOCKET (-1) + +#endif /* ndef _WIN32 */ + + +/** + * A standard error code. This should be JB_ERR_OK or one of the JB_ERR_xxx + * series of errors. + */ +typedef int jb_err; + +#define JB_ERR_OK 0 /**< Success, no error */ +#define JB_ERR_MEMORY 1 /**< Out of memory */ +#define JB_ERR_CGI_PARAMS 2 /**< Missing or corrupt CGI parameters */ +#define JB_ERR_FILE 3 /**< Error opening, reading or writing a file */ +#define JB_ERR_PARSE 4 /**< Error parsing file */ +#define JB_ERR_MODIFIED 5 /**< File has been modified outside of the + CGI actions editor. */ + + +/** + * This macro is used to free a pointer that may be NULL. + * It also sets the variable to NULL after it's been freed. + * The paramater should be a simple variable without side effects. + */ +#define freez(X) { if(X) { free((void*)X); X = NULL ; } } + + +/** + * Fix a problem with Solaris. There should be no effect on other + * platforms. + * + * Solaris's isspace() is a macro which uses it's argument directly + * as an array index. Therefore we need to make sure that high-bit + * characters generate +ve values, and ideally we also want to make + * the argument match the declared parameter type of "int". + * + * Note: Remember to #include if you use these macros. + */ +#define ijb_toupper(__X) toupper((int)(unsigned char)(__X)) +#define ijb_tolower(__X) tolower((int)(unsigned char)(__X)) +#define ijb_isspace(__X) isspace((int)(unsigned char)(__X)) + +/** + * Use for statically allocated buffers if you have no other choice. + * Remember to check the length of what you write into the buffer + * - we don't want any buffer overflows! + */ +#define BUFFER_SIZE 5000 + +/** + * Max length of CGI parameters (arbitrary limit). + */ +#define CGI_PARAM_LEN_MAX 500 + +/** + * Buffer size for capturing struct hostent data in the + * gethostby(name|addr)_r library calls. Since we don't + * loop over gethostbyname_r, the buffer must be sufficient + * to accomodate multiple IN A RRs, as used in DNS round robin + * load balancing. W3C's wwwlib uses 1K, so that should be + * good enough for us, too. + */ +#define HOSTENT_BUFFER_SIZE 1024 + +/** + * Do not use. Originally this was so that you can + * say "while (FOREVER) { ...do something... }". + * However, this gives a warning with some compilers (e.g. VC++). + * Instead, use "for (;;) { ...do something... }". + */ +#define FOREVER 1 + +/** + * Default IP address to listen on, as a string. + * Set to "127.0.0.1". + */ +#define HADDR_DEFAULT "127.0.0.1" + +/** + * Default port to listen on, as a number. + * Set to 8118. + */ +#define HADDR_PORT 8118 + + +/* Forward def for struct client_state */ +struct configuration_spec; + + +/** + * Entry in a linked list of strings. + */ +struct list_entry +{ + /** + * The string. The "const" is only to discourage modification, + * you can actually change it if you *really* want to. + * You can even freez() it and replace it with another + * malloc()d string. If you replace it with NULL, the list + * functions will work, just be careful next time you iterate + * through the list in your own code. + * + * FIXME: Should we remove the "const"? + */ + const char *str; + + /** Next entry in the linked list, or NULL if no more. */ + struct list_entry *next; +}; + +/** + * A header for a linked list of strings. + */ +struct list +{ + /** First entry in the list, or NULL if the list is empty. */ + struct list_entry *first; + + /** Last entry in the list, or NULL if the list is empty. */ + struct list_entry *last; +}; + + +/** + * An entry in a map. This is a name=value pair. + */ +struct map_entry +{ + /** The key for the map. */ + const char *name; + /** The value associated with that key. */ + const char *value; + /** The next map entry, or NULL if none. */ + struct map_entry *next; +}; + +/** + * A map from a string to another string. + * This is used for the paramaters passed in a HTTP GET request, and + * to store the exports when the CGI interface is filling in a template. + */ +struct map +{ + /** The first map entry, or NULL if the map is empty. */ + struct map_entry *first; + /** The last map entry, or NULL if the map is empty. */ + struct map_entry *last; +}; + + +/** + * A HTTP request. This includes the method (GET, POST) and + * the parsed URL. + * + * This is also used whenever we want to match a URL against a + * URL pattern. This always contains the URL to match, and never + * a URL pattern. (See struct url_spec). + */ +struct http_request +{ + char *cmd; /**< Whole command line: method, URL, Version */ + char *ocmd; /**< Backup of original cmd for CLF logging */ + char *gpc; /**< HTTP method: GET, POST, ... */ + char *url; /**< The URL */ + char *ver; /**< Protocol version */ + int status; /**< HTTP Status */ + + char *host; /**< Host part of URL */ + int port; /**< Port of URL or 80 (default) */ + char *path; /**< Path of URL */ + char *hostport; /**< host[:port] */ + int ssl; /**< Flag if protocol is https */ + + char *host_ip_addr_str; /**< String with dotted decimal representation + of host's IP. NULL before connect_to() */ + + char *dbuffer; /**< Buffer with '\0'-delimited domain name. */ + char **dvec; /**< List of pointers to the strings in dbuffer. */ + int dcount; /**< How many parts to this domain? (length of dvec) */ +}; + + +/** + * Response generated by CGI, blocker, or error handler + */ +struct http_response +{ + char *status; /**< HTTP status (string). */ + struct list headers[1]; /**< List of header lines. */ + char *head; /**< Formatted http response head. */ + size_t head_length; /**< Length of http response head. */ + char *body; /**< HTTP document body. */ + size_t content_length; /**< Length of body, REQUIRED if binary body. */ + int is_static; /**< Nonzero if the content will never change and + should be cached by the browser (e.g. images). */ +}; + +/** + * A URL pattern. + */ +struct url_spec +{ + /** The string which was parsed to produce this url_spec. + Used for debugging or display only. */ + char *spec; + + char *dbuffer; /**< Buffer with '\0'-delimited domain name, or NULL to match all hosts. */ + char **dvec; /**< List of pointers to the strings in dbuffer. */ + int dcount; /**< How many parts to this domain? (length of dvec) */ + int unanchored; /**< Bitmap - flags are ANCHOR_LEFT and ANCHOR_RIGHT. */ + + int port; /**< The port number, or 0 to match all ports. */ + + char *path; /**< The source for the regex. */ + int pathlen; /**< ==strlen(path). Needed for prefix matching. FIXME: Now obsolete? */ + regex_t *preg; /**< Regex for matching path part */ +}; + +/** + * If you declare a static url_spec, this is the value to initialize it to zero. + */ +#define URL_SPEC_INITIALIZER { NULL, NULL, NULL, 0, 0, 0, NULL, 0, NULL } + +/** + * Constant for host part matching in URLs. If set, indicates that the start of + * the pattern must match the start of the URL. E.g. this is not set for the + * pattern ".example.com", so that it will match both "example.com" and + * "www.example.com". It is set for the pattern "example.com", which makes it + * match "example.com" only, not "www.example.com". + */ +#define ANCHOR_LEFT 1 + +/** + * Constant for host part matching in URLs. If set, indicates that the end of + * the pattern must match the end of the URL. E.g. this is not set for the + * pattern "ad.", so that it will match any host called "ad", irrespective + * of how many subdomains are in the fully-qualified domain name. + */ +#define ANCHOR_RIGHT 2 + + +/** + * An I/O buffer. Holds a string which can be appended to, and can have data + * removed from the beginning. + */ +struct iob +{ + char *buf; /**< Start of buffer */ + char *cur; /**< Start of relevant data */ + char *eod; /**< End of relevant data */ + size_t size; /**< Size as malloc()ed */ +}; + + +/** + * Return the number of bytes in the I/O buffer associated with the passed + * client_state pointer. + * May be zero. + */ +#define IOB_PEEK(CSP) ((CSP->iob->cur > CSP->iob->eod) ? (CSP->iob->eod - CSP->iob->cur) : 0) + + +/** + * Remove any data in the I/O buffer associated with the passed + * client_state pointer. + */ +#define IOB_RESET(CSP) if(CSP->iob->buf) free(CSP->iob->buf); memset(CSP->iob, '\0', sizeof(CSP->iob)); + +/* Bits for csp->content_type */ +#define CT_TEXT 1 /**< csp->content_type bitmask: + Suitable for pcrs filtering. */ +#define CT_GIF 2 /**< csp->content_type bitmask: + Suitable for GIF filtering. */ +#define CT_TABOO 4 /**< csp->content_type bitmask: + DO NOT filter, irrespective of other flags. */ + +/** + * The mask which includes all actions. + */ +#define ACTION_MASK_ALL (~0UL) + +/** + * The most compatible set of actions - i.e. none. + */ +#define ACTION_MOST_COMPATIBLE 0x00000000UL + +/** Action bitmap: Block the request. */ +#define ACTION_BLOCK 0x00000001UL +/** Action bitmap: Deanimate if it's a GIF. */ +#define ACTION_DEANIMATE 0x00000002UL +/** Action bitmap: Downgrade HTTP/1.1 to 1.0. */ +#define ACTION_DOWNGRADE 0x00000004UL +/** Action bitmap: Fast redirects. */ +#define ACTION_FAST_REDIRECTS 0x00000008UL +/** Action bitmap: Remove existing "Forwarded" header, and do not add another. */ +#define ACTION_HIDE_FORWARDED 0x00000010UL +/** Action bitmap: Hide "From" header. */ +#define ACTION_HIDE_FROM 0x00000020UL +/** Action bitmap: Hide "Referer" header. (sic - follow HTTP, not English). */ +#define ACTION_HIDE_REFERER 0x00000040UL +/** Action bitmap: Hide "User-Agent" and similar headers. */ +#define ACTION_HIDE_USER_AGENT 0x00000080UL +/** Action bitmap: This is an image. */ +#define ACTION_IMAGE 0x00000100UL +/** Action bitmap: Sets the image blocker. */ +#define ACTION_IMAGE_BLOCKER 0x00000200UL +/** Action bitmap: Prevent compression. */ +#define ACTION_NO_COMPRESSION 0x00000400UL +/** Action bitmap: Change cookies to session only cookies. */ +#define ACTION_NO_COOKIE_KEEP 0x00000800UL +/** Action bitmap: Block rending cookies. */ +#define ACTION_NO_COOKIE_READ 0x00001000UL +/** Action bitmap: Block setting cookies. */ +#define ACTION_NO_COOKIE_SET 0x00002000UL +/** Action bitmap: Filter out popups. */ +#define ACTION_NO_POPUPS 0x00004000UL +/** Action bitmap: Send a vanilla wafer. */ +#define ACTION_VANILLA_WAFER 0x00008000UL +/** Action bitmap: Limit CONNECT requests to safe ports. */ +#define ACTION_LIMIT_CONNECT 0x00010000UL + +/** Action string index: How to deanimate GIFs */ +#define ACTION_STRING_DEANIMATE 0 +/** Action string index: Replacement for "From:" header */ +#define ACTION_STRING_FROM 1 +/** Action string index: How to block images */ +#define ACTION_STRING_IMAGE_BLOCKER 2 +/** Action string index: Replacement for "Referer:" header */ +#define ACTION_STRING_REFERER 3 +/** Action string index: Replacement for "User-Agent:" header */ +#define ACTION_STRING_USER_AGENT 4 +/** Action string index: Legal CONNECT ports. */ +#define ACTION_STRING_LIMIT_CONNECT 5 +/** Number of string actions. */ +#define ACTION_STRING_COUNT 6 + +/** Index into current_action_spec::multi[] for headers to add. */ +#define ACTION_MULTI_ADD_HEADER 0 +/** Index into current_action_spec::multi[] for headers to add. */ +#define ACTION_MULTI_WAFER 1 +/** Index into current_action_spec::multi[] for filters to apply. */ +#define ACTION_MULTI_FILTER 2 +/** Number of multi-string actions. */ +#define ACTION_MULTI_COUNT 3 + + +/** + * This structure contains a list of actions to apply to a URL. + * It only contains positive instructions - no "-" options. + * It is not used to store the actions list itself, only for + * url_actions() to return the current values. + */ +struct current_action_spec +{ + /** Actions to apply. A bit set to "1" means perform the action. */ + unsigned long flags; + + /** + * Paramaters for those actions that require them. + * Each entry is valid if & only if the corresponding entry in "flags" is + * set. + */ + char * string[ACTION_STRING_COUNT]; + + /** Lists of strings for multi-string actions. */ + struct list multi[ACTION_MULTI_COUNT][1]; +}; + + +/** + * This structure contains a set of changes to actions. + * It can contain both positive and negative instructions. + * It is used to store an entry in the actions list. + */ +struct action_spec +{ + unsigned long mask; /**< Actions to keep. A bit set to "0" means remove action. */ + unsigned long add; /**< Actions to add. A bit set to "1" means add action. */ + + /** + * Paramaters for those actions that require them. + * Each entry is valid if & only if the corresponding entry in "flags" is + * set. + */ + char * string[ACTION_STRING_COUNT]; + + /** Lists of strings to remove, for multi-string actions. */ + struct list multi_remove[ACTION_MULTI_COUNT][1]; + + /** If nonzero, remove *all* strings from the multi-string action. */ + int multi_remove_all[ACTION_MULTI_COUNT]; + + /** Lists of strings to add, for multi-string actions. */ + struct list multi_add[ACTION_MULTI_COUNT][1]; +}; + + +/** + * This structure is used to store the actions list. + * + * It contains a URL pattern, and the chages to the actions. + * It is a linked list. + */ +struct url_actions +{ + struct url_spec url[1]; /**< URL pattern. */ + + struct action_spec action[1]; /**< Actions. */ + + struct url_actions * next; /**< Next action in file, or NULL. */ +}; + + +/* + * Flags for use in csp->flags + */ + +/** + * Flag for csp->flags: Set if this client is processing data. + * Cleared when the thread associated with this structure dies. + */ +#define CSP_FLAG_ACTIVE 0x01 + +/** + * Flag for csp->flags: Set if the server's reply is in "chunked" + * transfer encoding + */ +#define CSP_FLAG_CHUNKED 0x02 + +/** + * Flag for csp->flags: Set if this request was enforced, although it would + * normally have been blocked. + */ +#define CSP_FLAG_FORCED 0x04 + +/** + * Flag for csp->flags: Set if any modification to the body was done. + */ +#define CSP_FLAG_MODIFIED 0x08 + +/** + * Flag for csp->flags: Set if request was blocked. + */ +#define CSP_FLAG_REJECTED 0x10 + +/** + * Flag for csp->flags: Set if we are toggled on (FEATURE_TOGGLE). + */ +#define CSP_FLAG_TOGGLED_ON 0x20 + + +/** + * Maximum number of actions files. This limit is arbitrary - it's just used + * to size an array. + */ +#define MAX_ACTION_FILES 10 + +/** + * The state of a Privoxy processing thread. + */ +struct client_state +{ + /** The proxy's configuration */ + struct configuration_spec * config; + + /** The actions to perform on the current request */ + struct current_action_spec action[1]; + + /** socket to talk to client (web browser) */ + jb_socket cfd; + + /** socket to talk to server (web server or proxy) */ + jb_socket sfd; + + /** Multi-purpose flag container, see CSP_FLAG_* above */ + unsigned short int flags; + + /** Client PC's IP address, as reported by the accept() function. + As a string. */ + char *ip_addr_str; + /** Client PC's IP address, as reported by the accept() function. + As a number. */ + long ip_addr_long; + + /** Our IP address. I.e. the IP address that the client used to reach us, + as a string. */ + char *my_ip_addr_str; + + /** Our hostname. I.e. the reverse DNS of the IP address that the client + used to reach us, as a string. */ + char *my_hostname; + + /** The URL that was requested */ + struct http_request http[1]; + + /** An I/O buffer used for buffering data read from the network */ + struct iob iob[1]; + + /** List of all headers for this request */ + struct list headers[1]; + + /** List of all cookies for this request */ + struct list cookie_list[1]; + + /** MIME-Type key, see CT_* above */ + unsigned short int content_type; + + /** The "X-Forwarded-For:" header sent by the client */ + char *x_forwarded; + + /** Actions files associated with this client */ + struct file_list *actions_list[MAX_ACTION_FILES]; + + /** pcrs job file. */ + struct file_list *rlist; + + /** Length after content modification. */ + size_t content_length; + +#ifdef FEATURE_TRUST + + /** Trust file. */ + struct file_list *tlist; + +#endif /* def FEATURE_TRUST */ + + /** Next thread in linked list. Only read or modify from the main thread! */ + struct client_state *next; +}; + + +/** + * A function to add a header + */ +typedef jb_err (*add_header_func_ptr)(struct client_state *); + +/** + * A function to process a header + */ +typedef jb_err (*parser_func_ptr )(struct client_state *, char **); + + +/** + * List of functions to run on a list of headers + */ +struct parsers +{ + /** The header prefix to match */ + char *str; + + /** The length of the prefix to match */ + size_t len; + + /** The function to apply to this line */ + parser_func_ptr parser; +}; + + +/** + * List of available CGI functions. + */ +struct cgi_dispatcher +{ + /** The URL of the CGI, relative to the CGI root. */ + const char * const name; + + /** The handler function for the CGI */ + jb_err (* const handler)(struct client_state *csp, struct http_response *rsp, const struct map *parameters); + + /** The description of the CGI, to appear on the main menu, or NULL to hide it. */ + const char * const description; +}; + + +/** + * A data file used by Privoxy. Kept in a linked list. + */ +struct file_list +{ + /** + * This is a pointer to the data structures associated with the file. + * Read-only once the structure has been created. + */ + void *f; + + /** + * The unloader function. + * Normally NULL. When we are finished with file (i.e. when we have + * loaded a new one), set to a pointer to an unloader function. + * Unloader will be called by sweep() (called from main loop) when + * all clients using this file are done. This prevents threading + * problems. + */ + void (*unloader)(void *); + + /** + * Used internally by sweep(). Do not access from elsewhere. + */ + int active; + + /** + * File last-modified time, so we can check if file has been changed. + * Read-only once the structure has been created. + */ + time_t lastmodified; + + /** + * The full filename. + */ + char * filename; + + /** + * Pointer to next entry in the linked list of all "file_list"s. + * This linked list is so that sweep() can navigate it. + * Since sweep() can remove items from the list, we must be careful + * to only access this value from main thread (when we know sweep + * won't be running). + */ + struct file_list *next; +}; + + +#ifdef FEATURE_TRUST + +/** + * The format of a trust file when loaded into memory. + */ +struct block_spec +{ + struct url_spec url[1]; /**< The URL pattern */ + int reject; /**< FIXME: Please document this! */ + struct block_spec *next; /**< Next entry in linked list */ +}; + +#endif /* def FEATURE_TRUST */ + + +#define SOCKS_NONE 0 /**< Don't use a SOCKS server */ +#define SOCKS_4 40 /**< original SOCKS 4 protocol */ +#define SOCKS_4A 41 /**< as modified for hosts w/o external DNS */ + + +/** + * How to forward a connection to a parent proxy. + */ +struct forward_spec +{ + /** URL pattern that this forward_spec is for. */ + struct url_spec url[1]; + + /** Connection type. Must be SOCKS_NONE, SOCKS_4, or SOCKS_4A. */ + int type; + + /** SOCKS server hostname. Only valid if "type" is SOCKS_4 or SOCKS_4A. */ + char *gateway_host; + + /** SOCKS server port. */ + int gateway_port; + + /** Parent HTTP proxy hostname, or NULL for none. */ + char *forward_host; + + /** Parent HTTP proxy port. */ + int forward_port; + + /** Next entry in the linked list. */ + struct forward_spec *next; +}; + + +/** + * Initializer for a static struct forward_spec. + */ +#define FORWARD_SPEC_INITIALIZER { { URL_SPEC_INITIALIZER }, 0, NULL, 0, NULL, 0, NULL } + + +/** + * This struct represents one filter (one block) from + * the re_filterfile. If there is more than one filter + * in the file, the file will be represented by a + * chained list of re_filterfile specs. + */ +struct re_filterfile_spec +{ + char *name; /**< Name from FILTER: statement in re_filterfile. */ + char *description; /**< Description from FILTER: statement in re_filterfile. */ + struct list patterns[1]; /**< The patterns from the re_filterfile. */ + pcrs_job *joblist; /**< The resulting compiled pcrs_jobs. */ + struct re_filterfile_spec *next; /**< The pointer for chaining. */ +}; + + +#ifdef FEATURE_ACL + +#define ACL_PERMIT 1 /**< Accept connection request */ +#define ACL_DENY 2 /**< Reject connection request */ + +/** + * An IP address pattern. Used to specify networks in the ACL. + */ +struct access_control_addr +{ + unsigned long addr; /**< The IP address as an integer. */ + unsigned long mask; /**< The network mask as an integer. */ + unsigned long port; /**< The port number. */ +}; + +/** + * An access control list (ACL) entry. + * + * This is a linked list. + */ +struct access_control_list +{ + struct access_control_addr src[1]; /**< Client IP address */ + struct access_control_addr dst[1]; /**< Website or parent proxy IP address */ + + short action; /**< ACL_PERMIT or ACL_DENY */ + struct access_control_list *next; /**< The next entry in the ACL. */ +}; + +#endif /* def FEATURE_ACL */ + + +/** Maximum number of loaders (actions, re_filter, ...) */ +#define NLOADERS 8 + + +/** configuration_spec::feature_flags: CGI actions editor. */ +#define RUNTIME_FEATURE_CGI_EDIT_ACTIONS 1 + +/** configuration_spec::feature_flags: Web-based toggle. */ +#define RUNTIME_FEATURE_CGI_TOGGLE 2 + + +/** + * Data loaded from the configuration file. + * + * (Anomaly: toggle is still handled through a global, not this structure) + */ +struct configuration_spec +{ + /** What to log */ + int debug; + + /** Nonzero to enable multithreading. */ + int multi_threaded; + + /** + * Bitmask of features that can be enabled/disabled through the config + * file. Currently defined bits: + * + * - RUNTIME_FEATURE_CGI_EDIT_ACTIONS + * - RUNTIME_FEATURE_CGI_TOGGLE + */ + unsigned feature_flags; + + /** The log file name. */ + const char *logfile; + + /** The config file directory. */ + const char *confdir; + + /** The log file directory. */ + const char *logdir; + + /** The full paths to the actions files. */ + const char *actions_file[MAX_ACTION_FILES]; + + /** The short names of the actions files. */ + const char *actions_file_short[MAX_ACTION_FILES]; + + /** The administrator's email address */ + char *admin_address; + + /** A URL with info on this proxy */ + char *proxy_info_url; + + /** URL to the user manual (on our website or local copy) */ + char *usermanual; + + /** The file name of the pcre filter file */ + const char *re_filterfile; + +#ifdef FEATURE_COOKIE_JAR + + /** The file name of the cookie jar file */ + const char * jarfile; + + /** The handle to the cookie jar file */ + FILE * jar; + +#endif /* def FEATURE_COOKIE_JAR */ + + /** IP address to bind to. Defaults to HADDR_DEFAULT == 127.0.0.1. */ + const char *haddr; + + /** Port to bind to. Defaults to HADDR_PORT == 8118. */ + int hport; + + /** Size limit for IOB */ + size_t buffer_limit; + +#ifdef FEATURE_TRUST + + /** The file name of the trust file. */ + const char * trustfile; + + /** FIXME: DOCME: Document this. */ + struct list trust_info[1]; + + /** FIXME: DOCME: Document this. */ + struct url_spec *trust_list[64]; + +#endif /* def FEATURE_TRUST */ + +#ifdef FEATURE_ACL + + /** The access control list (ACL). */ + struct access_control_list *acl; + +#endif /* def FEATURE_ACL */ + + /** Information about parent proxies (forwarding). */ + struct forward_spec *forward; + + /** All options from the config file, HTML-formatted. */ + char *proxy_args; + + /** The configuration file object. */ + struct file_list *config_file_list; + + /** List of loaders */ + int (*loaders[NLOADERS])(struct client_state *); + + /** Nonzero if we need to bind() to the new port. */ + int need_bind; +}; + +/** Calculates the number of elements in an array, using sizeof. */ +#define SZ(X) (sizeof(X) / sizeof(*X)) + +#ifdef FEATURE_FORCE_LOAD +/** The force load URL prefix. */ +#define FORCE_PREFIX "/PRIVOXY-FORCE" +#endif /* def FEATURE_FORCE_LOAD */ + +#ifdef FEATURE_NO_GIFS +/** The MIME type for images ("image/png" or "image/gif"). */ +#define BUILTIN_IMAGE_MIMETYPE "image/png" +#else +#define BUILTIN_IMAGE_MIMETYPE "image/gif" +#endif /* def FEATURE_NO_GIFS */ + + +/* + * Hardwired URLs + */ + +/** URL for the Privoxy home page. */ +#define HOME_PAGE_URL "http://www.privoxy.org/" + +/** URL for the Privoxy user manual. */ +#define USER_MANUAL_URL HOME_PAGE_URL "/" VERSION "/user-manual/" + +/** Prefix for actions help links (append to USER_MANUAL_URL). */ +#define ACTIONS_HELP_PREFIX "actions-file.html#" + +/** Prefix for config option help links (append to USER_MANUAL_URL). */ +#define CONFIG_HELP_PREFIX "config.html#" + +/* + * The "hosts" to intercept and display CGI pages. + * First one is a hostname only, second one can specify host and path. + * + * Notes: + * 1) Do not specify the http: prefix + * 2) CGI_SITE_2_PATH must not end with /, one will be added automatically. + * 3) CGI_SITE_2_PATH must start with /, unless it is the empty string. + */ +#define CGI_SITE_1_HOST "p.p" +#define CGI_SITE_2_HOST "config.privoxy.org" +#define CGI_SITE_2_PATH "" + +/** + * The prefix for CGI pages. Written out in generated HTML. + * INCLUDES the trailing slash. + */ +#define CGI_PREFIX "http://" CGI_SITE_2_HOST CGI_SITE_2_PATH "/" + + +/* HTTP snipplets. + * + * FIXME: This is very inefficient. There could be one copy of these strings + * for each .c file!! They should be "extern", not "static". + */ +static const char CSUCCEED[] = + "HTTP/1.0 200 Connection established\n" + "Proxy-Agent: Privoxy/" VERSION "\r\n\r\n"; + +static const char CHEADER[] = + "HTTP/1.0 400 Invalid header received from browser\r\n\r\n"; + +static const char CFORBIDDEN[] = + "HTTP/1.0 403 Connection not allowable\r\nX-Hint: If you read this message interactively, then you know why this happens ,-)\r\n\r\n"; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ndef PROJECT_H_INCLUDED */ + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/ssplit.c b/src/ssplit.c new file mode 100644 index 00000000..1c3a2f5c --- /dev/null +++ b/src/ssplit.c @@ -0,0 +1,211 @@ +const char ssplit_rcs[] = "$Id: ssplit.c,v 1.6 2002/03/26 22:29:55 swa Exp $"; +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/ssplit.c,v $ + * + * Purpose : A function to split a string at specified deliminters. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: ssplit.c,v $ + * Revision 1.6 2002/03/26 22:29:55 swa + * we have a new homepage! + * + * Revision 1.5 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.4 2001/11/13 00:16:38 jongfoster + * Replacing references to malloc.h with the standard stdlib.h + * (See ANSI or K&R 2nd Ed) + * + * Revision 1.3 2001/05/29 08:54:25 jongfoster + * Rewrote the innards of ssplit() to be easier to understand, + * faster, and to use less memory. Didn't change the interface + * except to give the parameters meaningful names. + * + * Revision 1.2 2001/05/17 23:01:01 oes + * - Cleaned CRLF's from the sources and related files + * + * Revision 1.1.1.1 2001/05/15 13:59:04 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#include "config.h" + +#include +#include + +#include "ssplit.h" +#include "miscutil.h" + +const char ssplit_h_rcs[] = SSPLIT_H_VERSION; + +/* Define this for lots of debugging information to stdout */ +#undef SSPLIT_VERBOSE +/* #define SSPLIT_VERBOSE 1 */ + + +/********************************************************************* + * + * Function : ssplit + * + * Description : Split a string using delimiters in `delim'. Results + * go into `vec'. + * + * Parameters : + * 1 : str = string to split. Will be split in place + * (i.e. do not free until you've finished with vec, + * previous contents will be trashed by the call). + * 2 : delim = array of delimiters (if NULL, uses " \t"). + * 3 : vec[] = results vector (aka. array) [out] + * 4 : vec_len = number of usable slots in the vector (aka. array size) + * 5 : dont_save_empty_fields = zero if consecutive delimiters + * give a null output field(s), nonzero if they are just + * to be considered as single delimeter + * 6 : ignore_leading = nonzero to ignore leading field + * separators. + * + * Returns : -1 => Error: vec_len is too small to hold all the + * data, or str == NULL. + * >=0 => the number of fields put in `vec'. + * On error, vec and str may still have been overwritten. + * + *********************************************************************/ +int ssplit(char *str, const char *delim, char *vec[], int vec_len, + int dont_save_empty_fields, int ignore_leading) +{ + unsigned char is_delim[256]; + unsigned char char_type; + int vec_count = 0; + + if (!str) + { + return(-1); + } + + + /* Build is_delim array */ + + memset(is_delim, '\0', sizeof(is_delim)); + + if (!delim) + { + delim = " \t"; /* default field separators */ + } + + while (*delim) + { + is_delim[(unsigned)(unsigned char)*delim++] = 1; /* separator */ + } + + is_delim[(unsigned)(unsigned char)'\0'] = 2; /* terminator */ + is_delim[(unsigned)(unsigned char)'\n'] = 2; /* terminator */ + + + /* Parse string */ + + if (ignore_leading) + { + /* skip leading separators */ + while (is_delim[(unsigned)(unsigned char)*str] == 1) + { + str++; + } + } + + /* first pointer is the beginning of string */ + /* Check if we want to save this field */ + if ( (!dont_save_empty_fields) + || (is_delim[(unsigned)(unsigned char)*str] == 0) ) + { + /* + * We want empty fields, or the first character in this + * field is not a delimiter or the end of string. + * So save it. + */ + if (vec_count >= vec_len) + { + return(-1); /* overflow */ + } + vec[vec_count++] = (char *) str; + } + + while ((char_type = is_delim[(unsigned)(unsigned char)*str]) != 2) + { + if (char_type == 1) + { + /* the char is a separator */ + + /* null terminate the substring */ + *str++ = '\0'; + + /* Check if we want to save this field */ + if ( (!dont_save_empty_fields) + || (is_delim[(unsigned)(unsigned char)*str] == 0) ) + { + /* + * We want empty fields, or the first character in this + * field is not a delimiter or the end of string. + * So save it. + */ + if (vec_count >= vec_len) + { + return(-1); /* overflow */ + } + vec[vec_count++] = (char *) str; + } + } + else + { + str++; + } + } + *str = '\0'; /* null terminate the substring */ + +#ifdef SSPLIT_VERBOSE + { + int i; + printf("dump %d strings\n", vec_count); + for (i = 0; i < vec_count; i++) + { + printf("%d '%s'\n", i, vec[i]); + } + } +#endif /* def SSPLIT_VERBOSE */ + + return(vec_count); +} + + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/ssplit.h b/src/ssplit.h new file mode 100644 index 00000000..9e1e8ed1 --- /dev/null +++ b/src/ssplit.h @@ -0,0 +1,80 @@ +#ifndef SSPLIT_H_INCLUDED +#define SSPLIT_H_INCLUDED +#define SSPLIT_H_VERSION "$Id: ssplit.h,v 1.5 2002/03/26 22:29:55 swa Exp $" +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/ssplit.h,v $ + * + * Purpose : A function to split a string at specified deliminters. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: ssplit.h,v $ + * Revision 1.5 2002/03/26 22:29:55 swa + * we have a new homepage! + * + * Revision 1.4 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.3 2001/07/29 18:43:08 jongfoster + * Changing #ifdef _FILENAME_H to FILENAME_H_INCLUDED, to conform to + * ANSI C rules. + * + * Revision 1.2 2001/05/29 08:54:25 jongfoster + * Rewrote the innards of ssplit() to be easier to understand, + * faster, and to use less memory. Didn't change the interface + * except to give the parameters meaningful names. + * + * Revision 1.1.1.1 2001/05/15 13:59:04 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#ifdef __cplusplus +extern "C" { +#endif + +extern int ssplit(char *str, const char *delim, char *vec[], int vec_len, + int dont_save_empty_fields, int ignore_leading); + +/* Revision control strings from this header and associated .c file */ +extern const char ssplit_rcs[]; +extern const char ssplit_h_rcs[]; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ndef SSPLIT_H_INCLUDED */ + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/urlmatch.c b/src/urlmatch.c new file mode 100644 index 00000000..4bad343a --- /dev/null +++ b/src/urlmatch.c @@ -0,0 +1,812 @@ +const char urlmatch_rcs[] = "$Id: urlmatch.c,v 1.10 2002/05/12 21:40:37 jongfoster Exp $"; +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/urlmatch.c,v $ + * + * Purpose : Declares functions to match URLs against URL + * patterns. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: urlmatch.c,v $ + * Revision 1.10 2002/05/12 21:40:37 jongfoster + * - Removing some unused code + * + * Revision 1.9 2002/04/04 00:36:36 gliptak + * always use pcre for matching + * + * Revision 1.8 2002/04/03 23:32:47 jongfoster + * Fixing memory leak on error + * + * Revision 1.7 2002/03/26 22:29:55 swa + * we have a new homepage! + * + * Revision 1.6 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.5 2002/03/13 00:27:05 jongfoster + * Killing warnings + * + * Revision 1.4 2002/03/07 03:46:17 oes + * Fixed compiler warnings + * + * Revision 1.3 2002/03/03 14:51:11 oes + * Fixed CLF logging: Added ocmd member for client's request to struct http_request + * + * Revision 1.2 2002/01/21 00:14:09 jongfoster + * Correcting comment style + * Fixing an uninitialized memory bug in create_url_spec() + * + * Revision 1.1 2002/01/17 20:53:46 jongfoster + * Moving all our URL and URL pattern parsing code to the same file - it + * was scattered around in filters.c, loaders.c and parsers.c. + * + * Providing a single, simple url_match(pattern,url) function - rather than + * the 3-line match routine which was repeated all over the place. + * + * Renaming free_url to free_url_spec, since it frees a struct url_spec. + * + * Providing parse_http_url() so that URLs can be parsed without faking a + * HTTP request line for parse_http_request() or repeating the parsing + * code (both of which were techniques that were actually in use). + * + * Standardizing that struct http_request is used to represent a URL, and + * struct url_spec is used to represent a URL pattern. (Before, URLs were + * represented as seperate variables and a partially-filled-in url_spec). + * + * + *********************************************************************/ + + +#include "config.h" + +#ifndef _WIN32 +#include +#include +#endif + +#include +#include +#include +#include + +#if !defined(_WIN32) && !defined(__OS2__) +#include +#endif + +#include "project.h" +#include "urlmatch.h" +#include "ssplit.h" +#include "miscutil.h" +#include "errlog.h" + +const char urlmatch_h_rcs[] = URLMATCH_H_VERSION; + + +/********************************************************************* + * + * Function : free_http_request + * + * Description : Freez a http_request structure + * + * Parameters : + * 1 : http = points to a http_request structure to free + * + * Returns : N/A + * + *********************************************************************/ +void free_http_request(struct http_request *http) +{ + assert(http); + + freez(http->cmd); + freez(http->ocmd); + freez(http->gpc); + freez(http->host); + freez(http->url); + freez(http->hostport); + freez(http->path); + freez(http->ver); + freez(http->host_ip_addr_str); + freez(http->dbuffer); + freez(http->dvec); + http->dcount = 0; +} + + +/********************************************************************* + * + * Function : parse_http_url + * + * Description : Parse out the host and port from the URL. Find the + * hostname & path, port (if ':'), and/or password (if '@') + * + * Parameters : + * 1 : url = URL (or is it URI?) to break down + * 2 : http = pointer to the http structure to hold elements. + * Will be zeroed before use. Note that this + * function sets the http->gpc and http->ver + * members to NULL. + * 3 : csp = Current client state (buffers, headers, etc...) + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out of memory + * JB_ERR_CGI_PARAMS on malformed command/URL + * or >100 domains deep. + * + *********************************************************************/ +jb_err parse_http_url(const char * url, + struct http_request *http, + struct client_state *csp) +{ + /* + * Zero out the results structure + */ + memset(http, '\0', sizeof(*http)); + + + /* + * Save our initial URL + */ + http->url = strdup(url); + if (http->url == NULL) + { + return JB_ERR_MEMORY; + } + + + /* + * Split URL into protocol,hostport,path. + */ + { + char *buf; + char *url_noproto; + char *url_path; + + buf = strdup(url); + if (buf == NULL) + { + return JB_ERR_MEMORY; + } + + /* Find the start of the URL in our scratch space */ + url_noproto = buf; + if (strncmpic(url_noproto, "http://", 7) == 0) + { + url_noproto += 7; + http->ssl = 0; + } + else if (strncmpic(url_noproto, "https://", 8) == 0) + { + url_noproto += 8; + http->ssl = 1; + } + else + { + http->ssl = 0; + } + + url_path = strchr(url_noproto, '/'); + if (url_path != NULL) + { + /* + * Got a path. + * + * NOTE: The following line ignores the path for HTTPS URLS. + * This means that you get consistent behaviour if you type a + * https URL in and it's parsed by the function. (When the + * URL is actually retrieved, SSL hides the path part). + */ + http->path = strdup(http->ssl ? "/" : url_path); + *url_path = '\0'; + http->hostport = strdup(url_noproto); + } + else + { + /* + * Repair broken HTTP requests that don't contain a path, + * or CONNECT requests + */ + http->path = strdup("/"); + http->hostport = strdup(url_noproto); + } + + free(buf); + + if ( (http->path == NULL) + || (http->hostport == NULL)) + { + free(buf); + free_http_request(http); + return JB_ERR_MEMORY; + } + } + + + /* + * Split hostport into user/password (ignored), host, port. + */ + { + char *buf; + char *host; + char *port; + + buf = strdup(http->hostport); + if (buf == NULL) + { + free_http_request(http); + return JB_ERR_MEMORY; + } + + /* check if url contains username and/or password */ + host = strchr(buf, '@'); + if (host != NULL) + { + /* Contains username/password, skip it and the @ sign. */ + host++; + } + else + { + /* No username or password. */ + host = buf; + } + + /* check if url contains port */ + port = strchr(host, ':'); + if (port != NULL) + { + /* Contains port */ + /* Terminate hostname and point to start of port string */ + *port++ = '\0'; + http->port = atoi(port); + } + else + { + /* No port specified. */ + http->port = (http->ssl ? 143 : 80); + } + + http->host = strdup(host); + + free(buf); + + if (http->host == NULL) + { + free_http_request(http); + return JB_ERR_MEMORY; + } + } + + + /* + * Split domain name so we can compare it against wildcards + */ + { + char *vec[BUFFER_SIZE]; + size_t size; + char *p; + + http->dbuffer = strdup(http->host); + if (NULL == http->dbuffer) + { + free_http_request(http); + return JB_ERR_MEMORY; + } + + /* map to lower case */ + for (p = http->dbuffer; *p ; p++) + { + *p = tolower((int)(unsigned char)*p); + } + + /* split the domain name into components */ + http->dcount = ssplit(http->dbuffer, ".", vec, SZ(vec), 1, 1); + + if (http->dcount <= 0) + { + /* + * Error: More than SZ(vec) components in domain + * or: no components in domain + */ + free_http_request(http); + return JB_ERR_PARSE; + } + + /* save a copy of the pointers in dvec */ + size = http->dcount * sizeof(*http->dvec); + + http->dvec = (char **)malloc(size); + if (NULL == http->dvec) + { + free_http_request(http); + return JB_ERR_MEMORY; + } + + memcpy(http->dvec, vec, size); + } + + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : parse_http_request + * + * Description : Parse out the host and port from the URL. Find the + * hostname & path, port (if ':'), and/or password (if '@') + * + * Parameters : + * 1 : req = HTTP request line to break down + * 2 : http = pointer to the http structure to hold elements + * 3 : csp = Current client state (buffers, headers, etc...) + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out of memory + * JB_ERR_CGI_PARAMS on malformed command/URL + * or >100 domains deep. + * + *********************************************************************/ +jb_err parse_http_request(const char *req, + struct http_request *http, + struct client_state *csp) +{ + char *buf; + char *v[10]; + int n; + jb_err err; + int is_connect = 0; + + memset(http, '\0', sizeof(*http)); + + buf = strdup(req); + if (buf == NULL) + { + return JB_ERR_MEMORY; + } + + n = ssplit(buf, " \r\n", v, SZ(v), 1, 1); + if (n != 3) + { + free(buf); + return JB_ERR_PARSE; + } + + /* this could be a CONNECT request */ + if (strcmpic(v[0], "connect") == 0) + { + /* Secure */ + is_connect = 1; + } + /* or it could be any other basic HTTP request type */ + else if ((0 == strcmpic(v[0], "get")) + || (0 == strcmpic(v[0], "head")) + || (0 == strcmpic(v[0], "post")) + || (0 == strcmpic(v[0], "put")) + || (0 == strcmpic(v[0], "delete")) + + /* or a webDAV extension (RFC2518) */ + || (0 == strcmpic(v[0], "propfind")) + || (0 == strcmpic(v[0], "proppatch")) + || (0 == strcmpic(v[0], "move")) + || (0 == strcmpic(v[0], "copy")) + || (0 == strcmpic(v[0], "mkcol")) + || (0 == strcmpic(v[0], "lock")) + || (0 == strcmpic(v[0], "unlock")) + ) + { + /* Normal */ + is_connect = 0; + } + else + { + /* Unknown HTTP method */ + free(buf); + return JB_ERR_PARSE; + } + + err = parse_http_url(v[1], http, csp); + if (err) + { + free(buf); + return err; + } + + /* + * Copy the details into the structure + */ + http->ssl = is_connect; + http->cmd = strdup(req); + http->gpc = strdup(v[0]); + http->ver = strdup(v[2]); + + if ( (http->cmd == NULL) + || (http->gpc == NULL) + || (http->ver == NULL) ) + { + free(buf); + free_http_request(http); + return JB_ERR_MEMORY; + } + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : simple_domaincmp + * + * Description : Domain-wise Compare fqdn's. The comparison is + * both left- and right-anchored. The individual + * domain names are compared with simplematch(). + * This is only used by domain_match. + * + * Parameters : + * 1 : pv = array of patterns to compare + * 2 : fv = array of domain components to compare + * 3 : len = length of the arrays (both arrays are the + * same length - if they weren't, it couldn't + * possibly be a match). + * + * Returns : 0 => domains are equivalent, else no match. + * + *********************************************************************/ +static int simple_domaincmp(char **pv, char **fv, int len) +{ + int n; + + for (n = 0; n < len; n++) + { + if (simplematch(pv[n], fv[n])) + { + return 1; + } + } + + return 0; + +} + + +/********************************************************************* + * + * Function : domain_match + * + * Description : Domain-wise Compare fqdn's. Governed by the bimap in + * pattern->unachored, the comparison is un-, left-, + * right-anchored, or both. + * The individual domain names are compared with + * simplematch(). + * + * Parameters : + * 1 : pattern = a domain that may contain a '*' as a wildcard. + * 2 : fqdn = domain name against which the patterns are compared. + * + * Returns : 0 => domains are equivalent, else no match. + * + *********************************************************************/ +static int domain_match(const struct url_spec *pattern, const struct http_request *fqdn) +{ + char **pv, **fv; /* vectors */ + int plen, flen; + int unanchored = pattern->unanchored & (ANCHOR_RIGHT | ANCHOR_LEFT); + + plen = pattern->dcount; + flen = fqdn->dcount; + + if (flen < plen) + { + /* fqdn is too short to match this pattern */ + return 1; + } + + pv = pattern->dvec; + fv = fqdn->dvec; + + if (unanchored == ANCHOR_LEFT) + { + /* + * Right anchored. + * + * Convert this into a fully anchored pattern with + * the fqdn and pattern the same length + */ + fv += (flen - plen); /* flen - plen >= 0 due to check above */ + return simple_domaincmp(pv, fv, plen); + } + else if (unanchored == 0) + { + /* Fully anchored, check length */ + if (flen != plen) + { + return 1; + } + return simple_domaincmp(pv, fv, plen); + } + else if (unanchored == ANCHOR_RIGHT) + { + /* Left anchored, ignore all extra in fqdn */ + return simple_domaincmp(pv, fv, plen); + } + else + { + /* Unanchored */ + int n; + int maxn = flen - plen; + for (n = 0; n <= maxn; n++) + { + if (!simple_domaincmp(pv, fv, plen)) + { + return 0; + } + /* + * Doesn't match from start of fqdn + * Try skipping first part of fqdn + */ + fv++; + } + return 1; + } + +} + + +/********************************************************************* + * + * Function : create_url_spec + * + * Description : Creates a "url_spec" structure from a string. + * When finished, free with unload_url(). + * + * Parameters : + * 1 : url = Target url_spec to be filled in. Will be + * zeroed before use. + * 2 : buf = Source pattern, null terminated. NOTE: The + * contents of this buffer are destroyed by this + * function. If this function succeeds, the + * buffer is copied to url->spec. If this + * function fails, the contents of the buffer + * are lost forever. + * + * Returns : JB_ERR_OK - Success + * JB_ERR_MEMORY - Out of memory + * JB_ERR_PARSE - Cannot parse regex (Detailed message + * written to system log) + * + *********************************************************************/ +jb_err create_url_spec(struct url_spec * url, const char * buf) +{ + char *p; + + assert(url); + assert(buf); + + /* Zero memory */ + memset(url, '\0', sizeof(*url)); + + /* save a copy of the orignal specification */ + if ((url->spec = strdup(buf)) == NULL) + { + return JB_ERR_MEMORY; + } + + if ((p = strchr(buf, '/')) != NULL) + { + if (NULL == (url->path = strdup(p))) + { + freez(url->spec); + return JB_ERR_MEMORY; + } + url->pathlen = strlen(url->path); + *p = '\0'; + } + else + { + url->path = NULL; + url->pathlen = 0; + } + if (url->path) + { + int errcode; + char rebuf[BUFFER_SIZE]; + + if (NULL == (url->preg = zalloc(sizeof(*url->preg)))) + { + freez(url->spec); + freez(url->path); + return JB_ERR_MEMORY; + } + + sprintf(rebuf, "^(%s)", url->path); + + errcode = regcomp(url->preg, rebuf, + (REG_EXTENDED|REG_NOSUB|REG_ICASE)); + if (errcode) + { + size_t errlen = regerror(errcode, + url->preg, rebuf, sizeof(rebuf)); + + if (errlen > (sizeof(rebuf) - (size_t)1)) + { + errlen = sizeof(rebuf) - (size_t)1; + } + rebuf[errlen] = '\0'; + + log_error(LOG_LEVEL_ERROR, "error compiling %s: %s", + url->spec, rebuf); + + freez(url->spec); + freez(url->path); + regfree(url->preg); + freez(url->preg); + + return JB_ERR_PARSE; + } + } + if ((p = strchr(buf, ':')) == NULL) + { + url->port = 0; + } + else + { + *p++ = '\0'; + url->port = atoi(p); + } + + if (buf[0] != '\0') + { + char *v[150]; + size_t size; + + /* Parse domain part */ + if (buf[strlen(buf) - 1] == '.') + { + url->unanchored |= ANCHOR_RIGHT; + } + if (buf[0] == '.') + { + url->unanchored |= ANCHOR_LEFT; + } + + /* split domain into components */ + + url->dbuffer = strdup(buf); + if (NULL == url->dbuffer) + { + freez(url->spec); + freez(url->path); + regfree(url->preg); + freez(url->preg); + return JB_ERR_MEMORY; + } + + /* map to lower case */ + for (p = url->dbuffer; *p ; p++) + { + *p = tolower((int)(unsigned char)*p); + } + + /* split the domain name into components */ + url->dcount = ssplit(url->dbuffer, ".", v, SZ(v), 1, 1); + + if (url->dcount < 0) + { + freez(url->spec); + freez(url->path); + regfree(url->preg); + freez(url->preg); + freez(url->dbuffer); + url->dcount = 0; + return JB_ERR_MEMORY; + } + else if (url->dcount != 0) + { + + /* save a copy of the pointers in dvec */ + size = url->dcount * sizeof(*url->dvec); + + url->dvec = (char **)malloc(size); + if (NULL == url->dvec) + { + freez(url->spec); + freez(url->path); + regfree(url->preg); + freez(url->preg); + freez(url->dbuffer); + url->dcount = 0; + return JB_ERR_MEMORY; + } + + memcpy(url->dvec, v, size); + } + } + + return JB_ERR_OK; + +} + + +/********************************************************************* + * + * Function : free_url_spec + * + * Description : Called from the "unloaders". Freez the url + * structure elements. + * + * Parameters : + * 1 : url = pointer to a url_spec structure. + * + * Returns : N/A + * + *********************************************************************/ +void free_url_spec(struct url_spec *url) +{ + if (url == NULL) return; + + freez(url->spec); + freez(url->dbuffer); + freez(url->dvec); + freez(url->path); + if (url->preg) + { + regfree(url->preg); + freez(url->preg); + } +} + + +/********************************************************************* + * + * Function : url_match + * + * Description : Compare a URL against a URL pattern. + * + * Parameters : + * 1 : pattern = a URL pattern + * 2 : url = URL to match + * + * Returns : 0 iff the URL matches the pattern, else nonzero. + * + *********************************************************************/ +int url_match(const struct url_spec *pattern, + const struct http_request *url) +{ + return ((pattern->port == 0) || (pattern->port == url->port)) + && ((pattern->dbuffer == NULL) || (domain_match(pattern, url) == 0)) + && ((pattern->path == NULL) || + (regexec(pattern->preg, url->path, 0, NULL, 0) == 0) + ); +} + + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/urlmatch.h b/src/urlmatch.h new file mode 100644 index 00000000..fb4b4517 --- /dev/null +++ b/src/urlmatch.h @@ -0,0 +1,100 @@ +#ifndef URLMATCH_H_INCLUDED +#define URLMATCH_H_INCLUDED +#define URLMATCH_H_VERSION "$Id: urlmatch.h,v 1.3 2002/03/26 22:29:55 swa Exp $" +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/urlmatch.h,v $ + * + * Purpose : Declares functions to match URLs against URL + * patterns. + * + * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: urlmatch.h,v $ + * Revision 1.3 2002/03/26 22:29:55 swa + * we have a new homepage! + * + * Revision 1.2 2002/03/24 13:25:43 swa + * name change related issues + * + * Revision 1.1 2002/01/17 20:53:46 jongfoster + * Moving all our URL and URL pattern parsing code to the same file - it + * was scattered around in filters.c, loaders.c and parsers.c. + * + * Providing a single, simple url_match(pattern,url) function - rather than + * the 3-line match routine which was repeated all over the place. + * + * Renaming free_url to free_url_spec, since it frees a struct url_spec. + * + * Providing parse_http_url() so that URLs can be parsed without faking a + * HTTP request line for parse_http_request() or repeating the parsing + * code (both of which were techniques that were actually in use). + * + * Standardizing that struct http_request is used to represent a URL, and + * struct url_spec is used to represent a URL pattern. (Before, URLs were + * represented as seperate variables and a partially-filled-in url_spec). + * + * + *********************************************************************/ + + +#include "project.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern void free_http_request(struct http_request *http); +extern jb_err parse_http_request(const char *req, + struct http_request *http, + struct client_state *csp); +extern jb_err parse_http_url(const char * url, + struct http_request *http, + struct client_state *csp); + +extern int url_match(const struct url_spec *pattern, + const struct http_request *url); + +extern jb_err create_url_spec(struct url_spec * url, const char * buf); +extern void free_url_spec(struct url_spec *url); + + +/* Revision control strings from this header and associated .c file */ +extern const char urlmatch_rcs[]; +extern const char urlmatch_h_rcs[]; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ndef URLMATCH_H_INCLUDED */ + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/w32log.c b/src/w32log.c new file mode 100644 index 00000000..5559a38c --- /dev/null +++ b/src/w32log.c @@ -0,0 +1,1347 @@ +const char w32log_rcs[] = "$Id: w32log.c,v 1.25 2002/04/04 00:36:36 gliptak Exp $"; +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/w32log.c,v $ + * + * Purpose : Functions for creating and destroying the log window, + * ouputting strings, processing messages and so on. + * + * Copyright : Written by and Copyright (C) 2001-2002 members of + * the Privoxy team. http://www.privoxy.org/ + * + * Written by and Copyright (C) 1999 Adam Lock + * + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: w32log.c,v $ + * Revision 1.25 2002/04/04 00:36:36 gliptak + * always use pcre for matching + * + * Revision 1.24 2002/03/31 17:19:00 jongfoster + * Win32 only: Enabling STRICT to fix a VC++ compile warning. + * + * Revision 1.23 2002/03/26 22:57:10 jongfoster + * Web server name should begin www. + * + * Revision 1.22 2002/03/24 12:48:23 jongfoster + * Fixing doc links + * + * Revision 1.21 2002/03/24 12:07:35 jongfoster + * Consistern name for filters file + * + * Revision 1.20 2002/03/24 12:03:47 jongfoster + * Name change + * + * Revision 1.19 2002/01/17 21:04:17 jongfoster + * Replacing hard references to the URL of the config interface + * with #defines from project.h + * + * Revision 1.18 2001/11/30 23:37:24 jongfoster + * Renaming the Win32 config file to config.txt - this is almost the + * same as the corresponding UNIX name "config" + * + * Revision 1.17 2001/11/16 00:46:31 jongfoster + * Fixing compiler warnings + * + * Revision 1.16 2001/08/01 19:58:12 jongfoster + * Fixing documentation filenames in help menu, and making status + * option work without needing the "Junkbuster Status.URL" file. + * + * Revision 1.15 2001/07/30 22:08:36 jongfoster + * Tidying up #defines: + * - All feature #defines are now of the form FEATURE_xxx + * - Permanently turned off WIN_GUI_EDIT + * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS + * + * Revision 1.14 2001/07/29 18:47:05 jongfoster + * Adding missing #include "loadcfg.h" + * + * Revision 1.13 2001/07/19 19:15:14 haroon + * - Added a FIXME for EditFile but didn't fix :-) + * + * Revision 1.12 2001/07/13 14:04:59 oes + * Removed all #ifdef PCRS + * + * Revision 1.11 2001/06/07 23:08:12 jongfoster + * Forward and ACL edit options removed. + * + * Revision 1.10 2001/05/31 21:37:11 jongfoster + * GUI changes to rename "permissions file" to "actions file". + * + * Revision 1.9 2001/05/31 17:33:13 oes + * + * CRLF -> LF + * + * Revision 1.8 2001/05/29 09:50:24 jongfoster + * Unified blocklist/imagelist/permissionslist. + * File format is still under discussion, but the internal changes + * are (mostly) done. + * + * Also modified interceptor behaviour: + * - We now intercept all URLs beginning with one of the following + * prefixes (and *only* these prefixes): + * * http://i.j.b/ + * * http://ijbswa.sf.net/config/ + * * http://ijbswa.sourceforge.net/config/ + * - New interceptors "home page" - go to http://i.j.b/ to see it. + * - Internal changes so that intercepted and fast redirect pages + * are not replaced with an image. + * - Interceptors now have the option to send a binary page direct + * to the client. (i.e. ijb-send-banner uses this) + * - Implemented show-url-info interceptor. (Which is why I needed + * the above interceptors changes - a typical URL is + * "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif". + * The previous mechanism would not have intercepted that, and + * if it had been intercepted then it then it would have replaced + * it with an image.) + * + * Revision 1.7 2001/05/26 01:26:34 jongfoster + * New #define, WIN_GUI_EDIT, enables the (embryonic) Win32 GUI editor. + * This #define cannot be set from ./configure - there's no point, it + * doesn't work yet. See feature request # 425722 + * + * Revision 1.6 2001/05/26 00:31:30 jongfoster + * Fixing compiler warning about comparing signed/unsigned. + * + * Revision 1.5 2001/05/26 00:28:36 jongfoster + * Automatic reloading of config file. + * Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32). + * Most of the global variables have been moved to a new + * struct configuration_spec, accessed through csp->config->globalname + * Most of the globals remaining are used by the Win32 GUI. + * + * Revision 1.4 2001/05/22 18:56:28 oes + * CRLF -> LF + * + * Revision 1.3 2001/05/20 15:07:54 jongfoster + * File is now ignored if _WIN_CONSOLE is defined. + * + * Revision 1.2 2001/05/20 01:21:20 jongfoster + * Version 2.9.4 checkin. + * - Merged popupfile and cookiefile, and added control over PCRS + * filtering, in new "permissionsfile". + * - Implemented LOG_LEVEL_FATAL, so that if there is a configuration + * file error you now get a message box (in the Win32 GUI) rather + * than the program exiting with no explanation. + * - Made killpopup use the PCRS MIME-type checking and HTTP-header + * skipping. + * - Removed tabs from "config" + * - Moved duplicated url parsing code in "loaders.c" to a new funcition. + * - Bumped up version number. + * + * Revision 1.1.1.1 2001/05/15 13:59:07 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#include "config.h" + +#include +#include + +#ifndef STRICT +#define STRICT +#endif +#include +#include + +#include "project.h" +#include "w32log.h" +#include "w32taskbar.h" +#include "win32.h" +#include "w32res.h" +#include "jcc.h" +#include "miscutil.h" +#include "errlog.h" +#include "loadcfg.h" + +const char w32res_h_rcs[] = W32RES_H_VERSION; + +#ifdef __MINGW32__ +#include "cygwin.h" +const char cygwin_h_rcs[] = CYGWIN_H_VERSION; +#endif + +const char w32log_h_rcs[] = W32LOG_H_VERSION; + +#ifndef _WIN_CONSOLE /* entire file */ + +/* + * Timers and the various durations + */ +#define TIMER_ANIM_ID 1 +#define TIMER_ANIM_TIME 100 +#define TIMER_ANIMSTOP_ID 2 +#define TIMER_ANIMSTOP_TIME 1000 +#define TIMER_CLIPBUFFER_ID 3 +#define TIMER_CLIPBUFFER_TIME 1000 +#define TIMER_CLIPBUFFER_FORCE_ID 4 +#define TIMER_CLIPBUFFER_FORCE_TIME 5000 + +/* + * Styles of text that can be output + */ +#define STYLE_NONE 0 +#define STYLE_HIGHLIGHT 1 +#define STYLE_LINK 2 +#define STYLE_HEADER 3 + +/* + * Number of frames of animation in tray activity sequence + */ +#define ANIM_FRAMES 8 + +#define DEFAULT_MAX_BUFFER_LINES 200 +#define DEFAULT_LOG_FONT_NAME "MS Sans Serif" +#define DEFAULT_LOG_FONT_SIZE 8 + +/* + * These values affect the way the log window behaves, they should be read + * from a file but for the moment, they are hardcoded here. Some options are + * configurable through the UI. + */ + +/* Indicates whether task bar shows activity animation */ +BOOL g_bShowActivityAnimation = 1; + +/* Indicates if the log window appears on the task bar */ +BOOL g_bShowOnTaskBar = 0; + +/* Indicates whether closing the log window really just hides it */ +BOOL g_bCloseHidesWindow = 1; + +/* Indicates if messages are logged at all */ +BOOL g_bLogMessages = 1; + +/* Indicates whether log messages are highlighted */ +BOOL g_bHighlightMessages = 1; + +/* Indicates if buffer is limited in size */ +BOOL g_bLimitBufferSize = 1; + +/* Maximum number of lines allowed in buffer when limited */ +int g_nMaxBufferLines = DEFAULT_MAX_BUFFER_LINES; + +/* Font to use */ +char g_szFontFaceName[255] = DEFAULT_LOG_FONT_NAME; + +/* Size of font to use */ +int g_nFontSize = DEFAULT_LOG_FONT_SIZE; + + +/* FIXME: this is a kludge */ + +const char * g_actions_file = NULL; +const char * g_re_filterfile = NULL; +#ifdef FEATURE_TRUST +const char * g_trustfile = NULL; +#endif /* def FEATURE_TRUST */ + +/* FIXME: end kludge */ + +/* Regular expression for detected URLs */ +#define RE_URL "http:[^ \n\r]*" + +/* + * Regular expressions that are used to perform highlight in the log window + */ +static struct _Pattern +{ + const char *str; + int style; + regex_t buffer; +} patterns_to_highlight[] = +{ + /* url headers */ + { RE_URL, STYLE_LINK }, +/* { "[a-zA-Z0-9]+\\.[a-zA-Z0-9]+\\.[a-zA-Z0-9]+\\.[^ \n\r]*", STYLE_LINK }, */ + /* interesting text to highlight */ + { "crunch!", STYLE_HIGHLIGHT }, + /* http headers */ + { "referer:", STYLE_HEADER }, + { "proxy-connection:", STYLE_HEADER }, + { "proxy-agent:", STYLE_HEADER }, + { "user-agent:", STYLE_HEADER }, + { "host:", STYLE_HEADER }, + { "accept:", STYLE_HEADER }, + { "accept-encoding:", STYLE_HEADER }, + { "accept-language:", STYLE_HEADER }, + { "accept-charset:", STYLE_HEADER }, + { "accept-ranges:", STYLE_HEADER }, + { "date:", STYLE_HEADER }, + { "cache-control:", STYLE_HEADER }, + { "cache-last-checked:", STYLE_HEADER }, + { "connection:", STYLE_HEADER }, + { "content-type", STYLE_HEADER }, + { "content-length", STYLE_HEADER }, + { "cookie", STYLE_HEADER }, + { "last-modified:", STYLE_HEADER }, + { "pragma:", STYLE_HEADER }, + { "server:", STYLE_HEADER }, + { "etag:", STYLE_HEADER }, + { "expires:", STYLE_HEADER }, + { "warning:", STYLE_HEADER }, + /* this is the terminator statement - do not delete! */ + { NULL, STYLE_NONE } +}; + +/* + * Public variables + */ +HWND g_hwndLogFrame; + +/* + * Private variables + */ +static CRITICAL_SECTION g_criticalsection; +static HWND g_hwndTray; +static HWND g_hwndLogBox; +static WNDPROC g_fnLogBox; +static HICON g_hiconAnim[ANIM_FRAMES]; +static HICON g_hiconIdle; +static HICON g_hiconApp; +static int g_nAnimFrame; +static BOOL g_bClipPending = FALSE; +static int g_nRichEditVersion = 0; + +/* + * Private functions + */ +static HWND CreateLogWindow(HINSTANCE hInstance, int nCmdShow); +static HWND CreateHiddenLogOwnerWindow(HINSTANCE hInstance); +static LRESULT CALLBACK LogWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +static LRESULT CALLBACK LogOwnerWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +static LRESULT CALLBACK LogRichEditProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +static BOOL InitRichEdit(void); +static void LogClipBuffer(void); +static void LogCreatePatternMatchingBuffers(void); +static void LogDestroyPatternMatchingBuffers(void); +static int LogPutStringNoMatch(const char *pszText, int style); + + +/********************************************************************* + * + * Function : InitLogWindow + * + * Description : Initialise the log window. + * + * Parameters : None + * + * Returns : Always TRUE (there should be error checking on the resources). + * + *********************************************************************/ +BOOL InitLogWindow(void) +{ + int i; + + /* Load the icons */ + g_hiconIdle = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_IDLE)); + for (i = 0; i < ANIM_FRAMES; i++) + { + g_hiconAnim[i] = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_ANIMATED1 + i)); + } + g_hiconApp = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_MAINICON)); + + /* Create the user interface */ + g_hwndLogFrame = CreateLogWindow(g_hInstance, g_nCmdShow); + g_hwndTray = CreateTrayWindow(g_hInstance); + TrayAddIcon(g_hwndTray, 1, g_hiconApp, "Privoxy"); + + /* Create pattern matching buffers (for highlighting */ + LogCreatePatternMatchingBuffers(); + + /* Create a critical section to protect multi-threaded access to certain things */ + InitializeCriticalSection(&g_criticalsection); + + return TRUE; + +} + + +/********************************************************************* + * + * Function : TermLogWindow + * + * Description : Cleanup the logwindow. + * + * Parameters : None + * + * Returns : N/A + * + *********************************************************************/ +void TermLogWindow(void) +{ + int i; + + LogDestroyPatternMatchingBuffers(); + + TrayDeleteIcon(g_hwndTray, 1); + DeleteObject(g_hiconApp); + DeleteObject(g_hiconIdle); + for (i = 0; i < ANIM_FRAMES; i++) + { + DeleteObject(g_hiconAnim[i]); + } + +} + + +/********************************************************************* + * + * Function : LogCreatePatternMatchingBuffers + * + * Description : Compile the pattern matching buffers. + * + * Parameters : None + * + * Returns : N/A + * + *********************************************************************/ +void LogCreatePatternMatchingBuffers(void) +{ + int i; + for (i = 0; patterns_to_highlight[i].str != NULL; i++) + { + regcomp(&patterns_to_highlight[i].buffer, patterns_to_highlight[i].str, REG_ICASE); + } +} + + +/********************************************************************* + * + * Function : LogDestroyPatternMatchingBuffers + * + * Description : Free up the pattern matching buffers. + * + * Parameters : None + * + * Returns : N/A + * + *********************************************************************/ +void LogDestroyPatternMatchingBuffers(void) +{ + int i; + for (i = 0; patterns_to_highlight[i].str != NULL; i++) + { + regfree(&patterns_to_highlight[i].buffer); + } +} + + +/********************************************************************* + * + * Function : LogGetURLUnderCursor + * + * Description : Returns the URL from under the cursor (remember to free it!). + * + * Parameters : None + * + * Returns : NULL or a pointer to an URL string. + * + *********************************************************************/ +char *LogGetURLUnderCursor(void) +{ + char *szResult = NULL; + regex_t re; + POINT ptCursor; + POINTL ptl; + DWORD nPos; + DWORD nWordStart = 0; + DWORD nWordEnd = 0; + + regcomp(&re, RE_URL, REG_ICASE); + + /* Get the position of the cursor over the text window */ + GetCursorPos(&ptCursor); + ScreenToClient(g_hwndLogBox, &ptCursor); + ptl.x = ptCursor.x; + ptl.y = ptCursor.y; + + /* Search backwards and fowards to obtain the word that is highlighted */ + nPos = LOWORD(SendMessage(g_hwndLogBox, EM_CHARFROMPOS, 0, (LPARAM) &ptl)); + nWordStart = SendMessage(g_hwndLogBox, EM_FINDWORDBREAK, WB_LEFT, nPos); + nWordEnd = SendMessage(g_hwndLogBox, EM_FINDWORDBREAK, WB_RIGHTBREAK, nPos); + + /* Compare the string to the pattern */ + if (nWordEnd > nWordStart) + { + TEXTRANGE range; + regmatch_t match; + + range.chrg.cpMin = nWordStart; + range.chrg.cpMax = nWordEnd; + range.lpstrText = (LPSTR)zalloc(nWordEnd - nWordStart + 1); + SendMessage(g_hwndLogBox, EM_GETTEXTRANGE, 0, (LPARAM) &range); + + if (regexec(&re, range.lpstrText, 1, &match, 0) == 0) + { + szResult = range.lpstrText; + } + else + { + free(range.lpstrText); + } + + regfree(&re); + } + return szResult; + +} + + +/********************************************************************* + * + * Function : LogPutString + * + * Description : Inserts text into the logging window. This is really + * a regexp aware wrapper function to `LogPutStringNoMatch'. + * + * Parameters : + * 1 : pszText = pointer to string going to the log window + * + * Returns : 1 => success, else the return code from `LogPutStringNoMatch'. + * FIXME: this is backwards to the rest of IJB and to common + * programming practice. Please use 0 => success instead. + * + *********************************************************************/ +int LogPutString(const char *pszText) +{ + int i; + int result = 0; + + if (pszText == NULL || strlen(pszText) == 0) + { + return 1; + } + + if (!g_bLogMessages) + { + return 1; + } + + /* Critical section stops multiple threads doing nasty interactions that + * foul up the highlighting and output. + */ + EnterCriticalSection(&g_criticalsection); + + if (g_bHighlightMessages) + { + regmatch_t match; + + /* First things first, regexp scan for various things that we would like highlighted */ + for (i = 0; patterns_to_highlight[i].str != NULL; i++) + { + if (regexec(&patterns_to_highlight[i].buffer, pszText, 1, &match, 0) == 0) + { + char *pszBefore = NULL; + char *pszMatch = NULL; + char *pszAfter = NULL; + int nMatchSize; + + /* Split the string up into pieces representing the strings, before + at and after the matching pattern + */ + if (match.rm_so > 0) + { + pszBefore = (char *)malloc((match.rm_so + 1) * sizeof(char)); + memset(pszBefore, 0, (match.rm_so + 1) * sizeof(char)); + strncpy(pszBefore, pszText, match.rm_so); + } + if (match.rm_eo < (regoff_t)strlen(pszText)) + { + pszAfter = strdup(&pszText[match.rm_eo]); + } + nMatchSize = match.rm_eo - match.rm_so; + pszMatch = (char *)malloc(nMatchSize + 1); + strncpy(pszMatch, &pszText[match.rm_so], nMatchSize); + pszMatch[nMatchSize] = '\0'; + + /* Recursively call LogPutString */ + if (pszBefore) + { + LogPutString(pszBefore); + free(pszBefore); + } + if (pszMatch) + { + LogPutStringNoMatch(pszMatch, patterns_to_highlight[i].style); + free(pszMatch); + } + if (pszAfter) + { + LogPutString(pszAfter); + free(pszAfter); + } + + result = 1; + goto end; + } + } + } + + result = LogPutStringNoMatch(pszText, STYLE_NONE); + +end: + LeaveCriticalSection(&g_criticalsection); + + return result; + +} + + +/********************************************************************* + * + * Function : LogPutStringNoMatch + * + * Description : Puts a string into the logging window. + * + * Parameters : + * 1 : pszText = pointer to string going to the log window + * 2 : style = STYLE_NONE, STYLE_HEADER, STYLE_HIGHLIGHT, or STYLE_LINK + * + * Returns : Always 1 => success. + * FIXME: this is backwards to the rest of IJB and to common + * programming practice. Please use 0 => success instead. + * + *********************************************************************/ +int LogPutStringNoMatch(const char *pszText, int style) +{ + CHARRANGE range; + CHARFORMAT format; + int nTextLength; + + assert(g_hwndLogBox); + if (g_hwndLogBox == NULL) + { + return 1; + } + + /* TODO preserve existing selection */ + + /* Go to the end of the text */ + nTextLength = GetWindowTextLength(g_hwndLogBox); + range.cpMin = nTextLength; + range.cpMax = nTextLength; + SendMessage(g_hwndLogBox, EM_EXSETSEL, 0, (LPARAM) &range); + + /* Apply a formatting style */ + memset(&format, 0, sizeof(format)); + format.cbSize = sizeof(format); + format.dwMask = CFM_BOLD | CFM_UNDERLINE | CFM_STRIKEOUT | CFM_ITALIC | CFM_COLOR | CFM_FACE | CFM_SIZE; + format.yHeight = (g_nFontSize * 1440) / 72; + strcpy(format.szFaceName, g_szFontFaceName); + if (style == STYLE_NONE) + { + /* DO NOTHING */ + format.dwEffects |= CFE_AUTOCOLOR; + } + else if (style == STYLE_HEADER) + { + format.dwEffects |= CFE_AUTOCOLOR | CFE_ITALIC; + } + else if (style == STYLE_HIGHLIGHT) + { + format.dwEffects |= CFE_AUTOCOLOR | CFE_BOLD; + } + else if (style == STYLE_LINK) + { + format.dwEffects |= CFE_UNDERLINE; + format.crTextColor = RGB(0, 0, 255); + } + SendMessage(g_hwndLogBox, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &format); + + /* Append text to the end */ + SendMessage(g_hwndLogBox, EM_REPLACESEL, FALSE, (LPARAM) pszText); + + /* TODO Restore the old selection */ + + /* Purge buffer */ + if (strchr(pszText, '\n') != NULL) + { + SetTimer(g_hwndLogFrame, TIMER_CLIPBUFFER_ID, TIMER_CLIPBUFFER_TIME, NULL); + if (!g_bClipPending) + { + /* Set the force clip timer going. This timer ensures clipping is done + intermittently even when there is a sustained burst of logging + */ + SetTimer(g_hwndLogFrame, TIMER_CLIPBUFFER_FORCE_ID, TIMER_CLIPBUFFER_FORCE_TIME, NULL); + } + g_bClipPending = TRUE; + } + + return 1; + +} + + +/********************************************************************* + * + * Function : LogShowActivity + * + * Description : Start the spinner. + * + * Parameters : None + * + * Returns : N/A + * + *********************************************************************/ +void LogShowActivity(void) +{ + /* Start some activity timers */ + if (g_bShowActivityAnimation) + { + SetTimer(g_hwndLogFrame, TIMER_ANIM_ID, TIMER_ANIM_TIME, NULL); + SetTimer(g_hwndLogFrame, TIMER_ANIMSTOP_ID, TIMER_ANIMSTOP_TIME, NULL); + } + +} + + +/********************************************************************* + * + * Function : LogClipBuffer + * + * Description : Prunes old lines from the log. + * + * Parameters : None + * + * Returns : N/A + * + *********************************************************************/ +void LogClipBuffer(void) +{ + int nLines = SendMessage(g_hwndLogBox, EM_GETLINECOUNT, 0, 0); + if (g_bLimitBufferSize && nLines > g_nMaxBufferLines) + { + /* Compute the range representing the lines to be deleted */ + LONG nLastLineToDelete = nLines - g_nMaxBufferLines; + LONG nLastChar = SendMessage(g_hwndLogBox, EM_LINEINDEX, nLastLineToDelete, 0); + CHARRANGE range; + range.cpMin = 0; + range.cpMax = nLastChar; + + /* TODO get current selection */ + + /* TODO adjust and clip old selection against range to be deleted */ + + /* Select range and erase it (turning off autoscroll to prevent + nasty scrolling) */ + SendMessage(g_hwndLogBox, EM_SETOPTIONS, ECOOP_XOR, ECO_AUTOVSCROLL); + SendMessage(g_hwndLogBox, EM_EXSETSEL, 0, (LPARAM) &range); + SendMessage(g_hwndLogBox, EM_REPLACESEL, FALSE, (LPARAM) ""); + SendMessage(g_hwndLogBox, EM_SETOPTIONS, ECOOP_XOR, ECO_AUTOVSCROLL); + + /* Restore old selection */ + } + +} + + +/********************************************************************* + * + * Function : CreateHiddenLogOwnerWindow + * + * Description : Creates a hidden owner window that stops the log + * window appearing in the task bar. + * + * Parameters : + * 1 : hInstance = application's instance handle + * + * Returns : Handle to newly created window. + * + *********************************************************************/ +HWND CreateHiddenLogOwnerWindow(HINSTANCE hInstance) +{ + static const char *szWndName = "PrivoxyLogOwner"; + WNDCLASS wc; + HWND hwnd; + + wc.style = 0; + wc.lpfnWndProc = LogOwnerWindowProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = 0; + wc.hCursor = 0; + wc.hbrBackground = 0; + wc.lpszMenuName = 0; + wc.lpszClassName = szWndName; + + RegisterClass(&wc); + + hwnd = CreateWindow(szWndName, szWndName, + WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, NULL, NULL, hInstance, NULL ); + + return hwnd; + +} + + +/********************************************************************* + * + * Function : LogOwnerWindowProc + * + * Description : Dummy procedure that does nothing special. + * + * Parameters : + * 1 : hwnd = window handle + * 2 : uMsg = message number + * 3 : wParam = first param for this message + * 4 : lParam = next param for this message + * + * Returns : Same as `DefWindowProc'. + * + *********************************************************************/ +LRESULT CALLBACK LogOwnerWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + return DefWindowProc(hwnd, uMsg, wParam, lParam); + +} + + +/********************************************************************* + * + * Function : CreateLogWindow + * + * Description : Create the logging window. + * + * Parameters : + * 1 : hInstance = application's instance handle + * 2 : nCmdShow = window show value (MIN, MAX, NORMAL, etc...) + * + * Returns : Handle to newly created window. + * + *********************************************************************/ +HWND CreateLogWindow(HINSTANCE hInstance, int nCmdShow) +{ + static const char *szWndName = "PrivoxyLogWindow"; + static const char *szWndTitle = "Privoxy"; + + HWND hwnd = NULL; + HWND hwndOwner = (g_bShowOnTaskBar) ? NULL : CreateHiddenLogOwnerWindow(hInstance); + RECT rcClient; + WNDCLASSEX wc; + + memset(&wc, 0, sizeof(wc)); + wc.cbSize = sizeof(wc); + wc.style = CS_DBLCLKS; + wc.lpfnWndProc = LogWindowProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = g_hiconApp; + wc.hCursor = 0; + wc.hbrBackground = 0; + wc.lpszMenuName = MAKEINTRESOURCE(IDR_LOGVIEW); + wc.lpszClassName = szWndName; + wc.hbrBackground = GetStockObject(WHITE_BRUSH); + RegisterClassEx(&wc); + + hwnd = CreateWindowEx(WS_EX_APPWINDOW, szWndName, szWndTitle, + WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, hwndOwner, NULL, hInstance, NULL); + + /* Now create a child list box */ + GetClientRect(hwnd, &rcClient); + + /* Create a rich edit control */ + InitRichEdit(); + g_hwndLogBox = CreateWindowEx(0, (g_nRichEditVersion == 0x0100) ? "RichEdit" : RICHEDIT_CLASS, "", + ES_AUTOVSCROLL | ES_MULTILINE | ES_READONLY | ES_NOHIDESEL | WS_CHILD | WS_VSCROLL | WS_HSCROLL | WS_VISIBLE, + rcClient.left, rcClient.top, rcClient.right, rcClient.bottom, + hwnd, NULL, hInstance, NULL); +/* SendMessage(g_hwndLogBox, EM_SETWORDWRAPMODE, 0, 0); */ + + /* Subclass the control to catch certain messages */ + g_fnLogBox = (WNDPROC) GetWindowLong(g_hwndLogBox, GWL_WNDPROC); + SetWindowLong(g_hwndLogBox, GWL_WNDPROC, (LONG) LogRichEditProc); + + /* Minimizing looks stupid when the log window is not on the task bar, so hide instead */ + if (!g_bShowOnTaskBar && + (nCmdShow == SW_SHOWMINIMIZED || + nCmdShow == SW_MINIMIZE || + nCmdShow == SW_SHOWMINNOACTIVE)) + { + nCmdShow = SW_HIDE; + } + + ShowWindow(hwnd, nCmdShow); + UpdateWindow(hwnd); + + GetClientRect(g_hwndLogFrame, &rcClient); + SetWindowPos(g_hwndLogBox, NULL, rcClient.left, rcClient.top, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, SWP_NOZORDER); + + return hwnd; + +} + + +/********************************************************************* + * + * Function : InitRichEdit + * + * Description : Initialise the rich edit control library. + * + * Parameters : None + * + * Returns : TRUE => success, FALSE => failure. + * FIXME: this is backwards to the rest of IJB and to common + * programming practice. Please use 0 => success instead. + * + *********************************************************************/ +BOOL InitRichEdit(void) +{ + static HINSTANCE hInstRichEdit; + if (hInstRichEdit == NULL) + { + g_nRichEditVersion = 0; + hInstRichEdit = LoadLibraryA("RICHED20.DLL"); + if (hInstRichEdit) + { + g_nRichEditVersion = _RICHEDIT_VER; + } + else + { + hInstRichEdit = LoadLibraryA("RICHED32.DLL"); + if (hInstRichEdit) + { + g_nRichEditVersion = 0x0100; + } + } + } + return (hInstRichEdit != NULL) ? TRUE : FALSE; + +} + + +/********************************************************************* + * + * Function : ShowLogWindow + * + * Description : Shows or hides the log window. We will also raise the + * window on a show command in case it is buried. + * + * Parameters : + * 1 : bShow = TRUE to show, FALSE to mimize/hide + * + * Returns : N/A + * + *********************************************************************/ +void ShowLogWindow(BOOL bShow) +{ + if (bShow) + { + SetForegroundWindow(g_hwndLogFrame); + SetWindowPos(g_hwndLogFrame, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE); + } + else if (g_bShowOnTaskBar) + { + ShowWindow(g_hwndLogFrame, SW_MINIMIZE); + } + else + { + ShowWindow(g_hwndLogFrame, SW_HIDE); + } + +} + + +/********************************************************************* + * + * Function : EditFile + * + * Description : Opens the specified setting file for editing. + * FIXME: What if the file has no associated application. Check for return values +* from ShellExecute?? + * + * Parameters : + * 1 : filename = filename from the config (aka config.txt) file. + * + * Returns : N/A + * + *********************************************************************/ +void EditFile(const char *filename) +{ + if (filename) + { + ShellExecute(g_hwndLogFrame, "open", filename, NULL, NULL, SW_SHOWNORMAL); + } + +} + + +/*--------------------------------------------------------------------------*/ +/* Windows message handlers */ +/*--------------------------------------------------------------------------*/ + + +/********************************************************************* + * + * Function : OnLogRButtonUp + * + * Description : Handler for WM_RBUTTONUP messages. + * + * Parameters : + * 1 : nModifier = wParam from mouse message (unused) + * 2 : x = x coordinate of the mouse event + * 3 : y = y coordinate of the mouse event + * + * Returns : N/A + * + *********************************************************************/ +void OnLogRButtonUp(int nModifier, int x, int y) +{ + HMENU hMenu = LoadMenu(g_hInstance, MAKEINTRESOURCE(IDR_POPUP_SELECTION)); + if (hMenu != NULL) + { + HMENU hMenuPopup = GetSubMenu(hMenu, 0); + + /* Check if there is a selection */ + CHARRANGE range; + SendMessage(g_hwndLogBox, EM_EXGETSEL, 0, (LPARAM) &range); + if (range.cpMin == range.cpMax) + { + EnableMenuItem(hMenuPopup, ID_EDIT_COPY, MF_BYCOMMAND | MF_GRAYED); + } + else + { + EnableMenuItem(hMenuPopup, ID_EDIT_COPY, MF_BYCOMMAND | MF_ENABLED); + } + + /* Display the popup */ + TrackPopupMenu(hMenuPopup, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON, x, y, 0, g_hwndLogFrame, NULL); + DestroyMenu(hMenu); + } + +} + + +/********************************************************************* + * + * Function : OnLogCommand + * + * Description : Handler for WM_COMMAND messages. + * + * Parameters : + * 1 : nCommand = the command portion of the menu selection event + * + * Returns : N/A + * + *********************************************************************/ +void OnLogCommand(int nCommand) +{ + switch (nCommand) + { + case ID_SHOWWINDOW: + ShowLogWindow(TRUE); + break; + + case ID_FILE_EXIT: + PostMessage(g_hwndLogFrame, WM_CLOSE, 0, 0); + break; + + case ID_EDIT_COPY: + SendMessage(g_hwndLogBox, WM_COPY, 0, 0); + break; + + case ID_VIEW_CLEARLOG: + SendMessage(g_hwndLogBox, WM_SETTEXT, 0, (LPARAM) ""); + break; + + case ID_VIEW_LOGMESSAGES: + g_bLogMessages = !g_bLogMessages; + /* SaveLogSettings(); */ + break; + + case ID_VIEW_MESSAGEHIGHLIGHTING: + g_bHighlightMessages = !g_bHighlightMessages; + /* SaveLogSettings(); */ + break; + + case ID_VIEW_LIMITBUFFERSIZE: + g_bLimitBufferSize = !g_bLimitBufferSize; + /* SaveLogSettings(); */ + break; + + case ID_VIEW_ACTIVITYANIMATION: + g_bShowActivityAnimation = !g_bShowActivityAnimation; + /* SaveLogSettings(); */ + break; + +#ifdef FEATURE_TOGGLE + /* by haroon - change toggle to its opposite value */ + case ID_TOGGLE_ENABLED: + g_bToggleIJB = !g_bToggleIJB; + if (g_bToggleIJB) + { + log_error(LOG_LEVEL_INFO, "Now toggled ON."); + } + else + { + log_error(LOG_LEVEL_INFO, "Now toggled OFF."); + } + break; +#endif /* def FEATURE_TOGGLE */ + + case ID_TOOLS_EDITCONFIG: + EditFile(configfile); + break; + + case ID_TOOLS_EDITACTIONS: + EditFile(g_actions_file); + break; + + case ID_TOOLS_EDITFILTERS: + EditFile(g_re_filterfile); + break; + +#ifdef FEATURE_TRUST + case ID_TOOLS_EDITTRUST: + EditFile(g_trustfile); + break; +#endif /* def FEATURE_TRUST */ + + case ID_HELP_GPL: + ShellExecute(g_hwndLogFrame, "open", "LICENSE.txt", NULL, NULL, SW_SHOWNORMAL); + break; + + case ID_HELP_FAQ: + ShellExecute(g_hwndLogFrame, "open", "doc\\faq\\index.html", NULL, NULL, SW_SHOWNORMAL); + break; + + case ID_HELP_MANUAL: + ShellExecute(g_hwndLogFrame, "open", "doc\\user-manual\\index.html", NULL, NULL, SW_SHOWNORMAL); + break; + + case ID_HELP_STATUS: + ShellExecute(g_hwndLogFrame, "open", CGI_PREFIX "show-status", NULL, NULL, SW_SHOWNORMAL); + break; + + case ID_HELP_ABOUT: + MessageBox(g_hwndLogFrame, win32_blurb, "About Privoxy", MB_OK); + break; + + default: + /* DO NOTHING */ + break; + } + +} + + +/********************************************************************* + * + * Function : OnLogInitMenu + * + * Description : Handler for WM_INITMENU messages. Enable, disable, + * check, and/or uncheck menu options as apropos. + * + * Parameters : + * 1 : hmenu = handle to menu to "make current" + * + * Returns : N/A + * + *********************************************************************/ +void OnLogInitMenu(HMENU hmenu) +{ + /* Only enable editors if there is a file to edit */ + EnableMenuItem(hmenu, ID_TOOLS_EDITACTIONS, MF_BYCOMMAND | (g_actions_file ? MF_ENABLED : MF_GRAYED)); + EnableMenuItem(hmenu, ID_TOOLS_EDITFILTERS, MF_BYCOMMAND | (g_re_filterfile ? MF_ENABLED : MF_GRAYED)); +#ifdef FEATURE_TRUST + EnableMenuItem(hmenu, ID_TOOLS_EDITTRUST, MF_BYCOMMAND | (g_trustfile ? MF_ENABLED : MF_GRAYED)); +#endif /* def FEATURE_TRUST */ + + /* Check/uncheck options */ + CheckMenuItem(hmenu, ID_VIEW_LOGMESSAGES, MF_BYCOMMAND | (g_bLogMessages ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(hmenu, ID_VIEW_MESSAGEHIGHLIGHTING, MF_BYCOMMAND | (g_bHighlightMessages ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(hmenu, ID_VIEW_LIMITBUFFERSIZE, MF_BYCOMMAND | (g_bLimitBufferSize ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(hmenu, ID_VIEW_ACTIVITYANIMATION, MF_BYCOMMAND | (g_bShowActivityAnimation ? MF_CHECKED : MF_UNCHECKED)); +#ifdef FEATURE_TOGGLE + /* by haroon - menu item for Enable toggle on/off */ + CheckMenuItem(hmenu, ID_TOGGLE_ENABLED, MF_BYCOMMAND | (g_bToggleIJB ? MF_CHECKED : MF_UNCHECKED)); +#endif /* def FEATURE_TOGGLE */ + +} + + +/********************************************************************* + * + * Function : OnLogTimer + * + * Description : Handler for WM_TIMER messages. + * + * Parameters : + * 1 : nTimer = timer id (animation start/stop or clip buffer) + * + * Returns : N/A + * + *********************************************************************/ +void OnLogTimer(int nTimer) +{ + switch (nTimer) + { + case TIMER_ANIM_ID: + TraySetIcon(g_hwndTray, 1, g_hiconAnim[g_nAnimFrame++ % ANIM_FRAMES]); + break; + + case TIMER_ANIMSTOP_ID: + g_nAnimFrame = 0; + TraySetIcon(g_hwndTray, 1, g_hiconIdle); + KillTimer(g_hwndLogFrame, TIMER_ANIM_ID); + KillTimer(g_hwndLogFrame, TIMER_ANIMSTOP_ID); + break; + + case TIMER_CLIPBUFFER_ID: + case TIMER_CLIPBUFFER_FORCE_ID: + LogClipBuffer(); + g_bClipPending = FALSE; + KillTimer(g_hwndLogFrame, TIMER_CLIPBUFFER_ID); + KillTimer(g_hwndLogFrame, TIMER_CLIPBUFFER_FORCE_ID); + break; + + default: + /* DO NOTHING */ + break; + } + +} + + +/********************************************************************* + * + * Function : LogRichEditProc + * + * Description : Window subclass routine handles some events for the rich edit control. + * + * Parameters : + * 1 : hwnd = window handle of the rich edit control + * 2 : uMsg = message number + * 3 : wParam = first param for this message + * 4 : lParam = next param for this message + * + * Returns : Appropriate M$ window message handler codes. + * + *********************************************************************/ +LRESULT CALLBACK LogRichEditProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_RBUTTONUP: + { + POINT pt; + pt.x = LOWORD(lParam); + pt.y = HIWORD(lParam); + ClientToScreen(hwnd, &pt); + OnLogRButtonUp(wParam, pt.x, pt.y); + } + return 0; + } + return CallWindowProc(g_fnLogBox, hwnd, uMsg, wParam, lParam); + +} + + +/********************************************************************* + * + * Function : LogWindowProc + * + * Description : Windows call back routine handles events on the log window. + * + * Parameters : + * 1 : hwnd = handle of the logging window + * 2 : uMsg = message number + * 3 : wParam = first param for this message + * 4 : lParam = next param for this message + * + * Returns : Appropriate M$ window message handler codes. + * + *********************************************************************/ +LRESULT CALLBACK LogWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_CREATE: + return 0; + + case WM_CLOSE: + /* This is the end - beautiful friend - the end */ + DestroyWindow(g_hwndLogBox); + DestroyWindow(g_hwndLogFrame); + return 0; + + case WM_DESTROY: + PostQuitMessage(0); + return 0; + + case WM_SHOWWINDOW: + case WM_SIZE: + /* Resize the logging window to fit the new frame */ + if (g_hwndLogBox) + { + RECT rc; + GetClientRect(g_hwndLogFrame, &rc); + SetWindowPos(g_hwndLogBox, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER); + } + return 0; + + case WM_INITMENU: + OnLogInitMenu((HMENU) wParam); + return 0; + + case WM_TIMER: + OnLogTimer(wParam); + return 0; + + case WM_COMMAND: + OnLogCommand(LOWORD(wParam)); + return 0; + + case WM_SYSCOMMAND: + switch (wParam) + { + case SC_CLOSE: + if (g_bCloseHidesWindow) + { + ShowLogWindow(FALSE); + return 0; + } + break; + case SC_MINIMIZE: + ShowLogWindow(FALSE); + return 0; + } + break; + } + + return DefWindowProc(hwnd, uMsg, wParam, lParam); + +} + +#endif /* ndef _WIN_CONSOLE - entire file */ + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/w32log.h b/src/w32log.h new file mode 100644 index 00000000..dd7bc412 --- /dev/null +++ b/src/w32log.h @@ -0,0 +1,164 @@ +#ifndef W32LOG_H_INCLUDED +#define W32LOG_H_INCLUDED +#define W32LOG_H_VERSION "$Id: w32log.h,v 1.10 2002/03/26 22:57:10 jongfoster Exp $" +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/w32log.h,v $ + * + * Purpose : Functions for creating and destroying the log window, + * ouputting strings, processing messages and so on. + * + * Copyright : Written by and Copyright (C) 2001-2002 members of + * the Privoxy team. http://www.privoxy.org/ + * + * Written by and Copyright (C) 1999 Adam Lock + * + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: w32log.h,v $ + * Revision 1.10 2002/03/26 22:57:10 jongfoster + * Web server name should begin www. + * + * Revision 1.9 2002/03/24 12:03:47 jongfoster + * Name change + * + * Revision 1.8 2001/07/30 22:08:36 jongfoster + * Tidying up #defines: + * - All feature #defines are now of the form FEATURE_xxx + * - Permanently turned off WIN_GUI_EDIT + * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS + * + * Revision 1.7 2001/07/29 18:43:08 jongfoster + * Changing #ifdef _FILENAME_H to FILENAME_H_INCLUDED, to conform to + * ANSI C rules. + * + * Revision 1.6 2001/07/13 14:04:59 oes + * Removed all #ifdef PCRS + * + * Revision 1.5 2001/06/07 23:08:12 jongfoster + * Forward and ACL edit options removed. + * + * Revision 1.4 2001/05/31 21:37:11 jongfoster + * GUI changes to rename "permissions file" to "actions file". + * + * Revision 1.3 2001/05/29 09:50:24 jongfoster + * Unified blocklist/imagelist/permissionslist. + * File format is still under discussion, but the internal changes + * are (mostly) done. + * + * Also modified interceptor behaviour: + * - We now intercept all URLs beginning with one of the following + * prefixes (and *only* these prefixes): + * * http://i.j.b/ + * * http://ijbswa.sf.net/config/ + * * http://ijbswa.sourceforge.net/config/ + * - New interceptors "home page" - go to http://i.j.b/ to see it. + * - Internal changes so that intercepted and fast redirect pages + * are not replaced with an image. + * - Interceptors now have the option to send a binary page direct + * to the client. (i.e. ijb-send-banner uses this) + * - Implemented show-url-info interceptor. (Which is why I needed + * the above interceptors changes - a typical URL is + * "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif". + * The previous mechanism would not have intercepted that, and + * if it had been intercepted then it then it would have replaced + * it with an image.) + * + * Revision 1.2 2001/05/26 00:28:36 jongfoster + * Automatic reloading of config file. + * Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32). + * Most of the global variables have been moved to a new + * struct configuration_spec, accessed through csp->config->globalname + * Most of the globals remaining are used by the Win32 GUI. + * + * Revision 1.1.1.1 2001/05/15 13:59:07 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#ifdef __cplusplus +extern "C" { +#endif + +extern HWND g_hwndLogFrame; + +/* Indicates whether task bar shows activity animation */ +extern BOOL g_bShowActivityAnimation; + +/* Indicates if the log window appears on the task bar */ +extern BOOL g_bShowOnTaskBar; + +/* Indicates whether closing the log window really just hides it */ +extern BOOL g_bCloseHidesWindow; + +/* Indicates if messages are logged at all */ +extern BOOL g_bLogMessages; + +/* Indicates whether log messages are highlighted */ +extern BOOL g_bHighlightMessages; + +/* Indicates if buffer is limited in size */ +extern BOOL g_bLimitBufferSize; + +/* Maximum number of lines allowed in buffer when limited */ +extern int g_nMaxBufferLines; + +/* Font to use */ +extern char g_szFontFaceName[255]; + +/* Size of font to use */ +extern int g_nFontSize; + + +/* FIXME: this is a kludge */ + +extern const char * g_actions_file; +extern const char * g_re_filterfile; +#ifdef FEATURE_TRUST +extern const char * g_trustfile; +#endif /* def FEATURE_TRUST */ + +/* FIXME: end kludge */ + +extern int LogPutString(const char *pszText); +extern BOOL InitLogWindow(void); +extern void TermLogWindow(void); +extern void ShowLogWindow(BOOL bShow); +extern void LogShowActivity(void); + +/* Revision control strings from this header and associated .c file */ +extern const char w32log_rcs[]; +extern const char w32log_h_rcs[]; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ndef W32LOG_H_INCLUDED */ + + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/w32res.h b/src/w32res.h new file mode 100644 index 00000000..5302580e --- /dev/null +++ b/src/w32res.h @@ -0,0 +1,177 @@ +#ifndef W32RES_H_INCLUDED +#define W32RES_H_INCLUDED +#define W32RES_H_VERSION "$Id: w32res.h,v 1.13 2002/03/26 22:57:10 jongfoster Exp $" +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/w32res.h,v $ + * + * Purpose : Identifiers for Windows GUI resources. + * + * Copyright : Written by and Copyright (C) 2001-2002 members of + * the Privoxy team. http://www.privoxy.org/ + * + * Based on the Internet Junkbuster originally written + * by and Copyright (C) 1997 Anonymous Coders and + * Junkbusters Corporation. http://www.junkbusters.com + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: w32res.h,v $ + * Revision 1.13 2002/03/26 22:57:10 jongfoster + * Web server name should begin www. + * + * Revision 1.12 2002/03/24 12:07:36 jongfoster + * Consistern name for filters file + * + * Revision 1.11 2002/03/24 12:03:47 jongfoster + * Name change + * + * Revision 1.10 2001/07/30 22:08:36 jongfoster + * Tidying up #defines: + * - All feature #defines are now of the form FEATURE_xxx + * - Permanently turned off WIN_GUI_EDIT + * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS + * + * Revision 1.9 2001/07/29 18:43:08 jongfoster + * Changing #ifdef _FILENAME_H to FILENAME_H_INCLUDED, to conform to + * ANSI C rules. + * + * Revision 1.8 2001/07/13 14:04:59 oes + * Removed all #ifdef PCRS + * + * Revision 1.7 2001/06/07 23:08:12 jongfoster + * Forward and ACL edit options removed. + * + * Revision 1.6 2001/05/31 21:37:11 jongfoster + * GUI changes to rename "permissions file" to "actions file". + * + * Revision 1.5 2001/05/29 09:50:24 jongfoster + * Unified blocklist/imagelist/permissionslist. + * File format is still under discussion, but the internal changes + * are (mostly) done. + * + * Also modified interceptor behaviour: + * - We now intercept all URLs beginning with one of the following + * prefixes (and *only* these prefixes): + * * http://i.j.b/ + * * http://ijbswa.sf.net/config/ + * * http://ijbswa.sourceforge.net/config/ + * - New interceptors "home page" - go to http://i.j.b/ to see it. + * - Internal changes so that intercepted and fast redirect pages + * are not replaced with an image. + * - Interceptors now have the option to send a binary page direct + * to the client. (i.e. ijb-send-banner uses this) + * - Implemented show-url-info interceptor. (Which is why I needed + * the above interceptors changes - a typical URL is + * "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif". + * The previous mechanism would not have intercepted that, and + * if it had been intercepted then it then it would have replaced + * it with an image.) + * + * Revision 1.4 2001/05/26 01:26:34 jongfoster + * New #define, WIN_GUI_EDIT, enables the (embryonic) Win32 GUI editor. + * This #define cannot be set from ./configure - there's no point, it + * doesn't work yet. See feature request # 425722 + * + * Revision 1.3 2001/05/26 00:28:36 jongfoster + * Automatic reloading of config file. + * Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32). + * Most of the global variables have been moved to a new + * struct configuration_spec, accessed through csp->config->globalname + * Most of the globals remaining are used by the Win32 GUI. + * + * Revision 1.2 2001/05/20 01:21:20 jongfoster + * Version 2.9.4 checkin. + * - Merged popupfile and cookiefile, and added control over PCRS + * filtering, in new "permissionsfile". + * - Implemented LOG_LEVEL_FATAL, so that if there is a configuration + * file error you now get a message box (in the Win32 GUI) rather + * than the program exiting with no explanation. + * - Made killpopup use the PCRS MIME-type checking and HTTP-header + * skipping. + * - Removed tabs from "config" + * - Moved duplicated url parsing code in "loaders.c" to a new funcition. + * - Bumped up version number. + * + * Revision 1.1.1.1 2001/05/15 13:59:08 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + +#define IDR_TRAYMENU 101 +#define IDI_IDLE 102 +#define IDR_LOGVIEW 103 +#define IDR_ACCELERATOR 104 +#define IDR_POPUP_SELECTION 105 + + +#define IDI_MAINICON 200 +#define IDI_ANIMATED1 201 +#define IDI_ANIMATED2 202 +#define IDI_ANIMATED3 203 +#define IDI_ANIMATED4 204 +#define IDI_ANIMATED5 205 +#define IDI_ANIMATED6 206 +#define IDI_ANIMATED7 207 +#define IDI_ANIMATED8 208 + +#define ID_SHOWWINDOW 4000 +#define ID_HELP_ABOUT 4001 +#define ID_FILE_EXIT 4002 +#define ID_VIEW_CLEARLOG 4003 +#define ID_VIEW_LOGMESSAGES 4004 +#define ID_VIEW_MESSAGEHIGHLIGHTING 4005 +#define ID_VIEW_LIMITBUFFERSIZE 4006 +#define ID_VIEW_ACTIVITYANIMATION 4007 +#define ID_HELP_FAQ 4008 +#define ID_HELP_MANUAL 4009 +#define ID_HELP_GPL 4010 +#define ID_HELP_STATUS 4011 +#ifdef FEATURE_TOGGLE +#define ID_TOGGLE_ENABLED 4012 +#endif /* def FEATURE_TOGGLE */ + +/* Break these out so they are easier to extend, but keep consecutive */ +#define ID_TOOLS_EDITCONFIG 5000 +#define ID_TOOLS_EDITACTIONS 5001 +#define ID_TOOLS_EDITFILTERS 5002 + +#ifdef FEATURE_TRUST +#define ID_TOOLS_EDITTRUST 5003 +#endif /* def FEATURE_TRUST */ + +/* + * The following symbols are declared in in VC++. + * However, mingw32 doesn't have that header. Let's + * always declare them here, for consistency. + * These are the VC++ values. + */ +#define IDC_STATIC (-1) +#define ID_EDIT_COPY 30000 + + +#endif /* ndef W32RES_H_INCLUDED */ + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/w32taskbar.c b/src/w32taskbar.c new file mode 100644 index 00000000..2d45209c --- /dev/null +++ b/src/w32taskbar.c @@ -0,0 +1,291 @@ +const char w32taskbar_rcs[] = "$Id: w32taskbar.c,v 1.7 2002/03/31 17:19:00 jongfoster Exp $"; +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/w32taskbar.c,v $ + * + * Purpose : Functions for creating, setting and destroying the + * workspace tray icon + * + * Copyright : Written by and Copyright (C) 2001-2002 members of + * the Privoxy team. http://www.privoxy.org/ + * + * Written by and Copyright (C) 1999 Adam Lock + * + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: w32taskbar.c,v $ + * Revision 1.7 2002/03/31 17:19:00 jongfoster + * Win32 only: Enabling STRICT to fix a VC++ compile warning. + * + * Revision 1.6 2002/03/26 22:57:10 jongfoster + * Web server name should begin www. + * + * Revision 1.5 2002/03/24 12:03:47 jongfoster + * Name change + * + * Revision 1.4 2001/11/16 00:46:31 jongfoster + * Fixing compiler warnings + * + * Revision 1.3 2001/05/22 18:56:28 oes + * CRLF -> LF + * + * Revision 1.2 2001/05/20 15:07:54 jongfoster + * File is now ignored if _WIN_CONSOLE is defined. + * + * Revision 1.1.1.1 2001/05/15 13:59:08 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#include "config.h" + +#include + +#ifndef STRICT +#define STRICT +#endif +#include + +#include "w32taskbar.h" +#include "w32res.h" +#include "w32log.h" + +const char w32taskbar_h_rcs[] = W32TASKBAR_H_VERSION; + +#ifndef _WIN_CONSOLE /* entire file */ + +#define WM_TRAYMSG WM_USER+1 + +static HMENU g_hmenuTray; +static HWND g_hwndTrayX; + +static LRESULT CALLBACK TrayProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); + + +/********************************************************************* + * + * Function : CreateTrayWindow + * + * Description : Creates and returns the invisible window responsible + * for processing tray messages. + * + * Parameters : + * 1 : hInstance = instance handle of this application + * + * Returns : Handle of the systray window. + * + *********************************************************************/ +HWND CreateTrayWindow(HINSTANCE hInstance) +{ + WNDCLASS wc; + static const char *szWndName = "PrivoxyTrayWindow"; + + wc.style = 0; + wc.lpfnWndProc = TrayProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = 0; + wc.hCursor = 0; + wc.hbrBackground = 0; + wc.lpszMenuName = 0; + wc.lpszClassName = szWndName; + + RegisterClass(&wc); + + g_hwndTrayX = CreateWindow(szWndName, szWndName, + WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, NULL, NULL, hInstance, NULL ); + + ShowWindow(g_hwndTrayX, SW_HIDE); + UpdateWindow(g_hwndTrayX); + + g_hmenuTray = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_TRAYMENU)); + + return g_hwndTrayX; + +} + + +/********************************************************************* + * + * Function : TraySetIcon + * + * Description : Sets the tray icon to the specified shape. + * + * Parameters : + * 1 : hwnd = handle of the systray window + * 2 : uID = user message number to notify systray window + * 3 : hicon = set the current icon to this handle + * + * Returns : Same value as `Shell_NotifyIcon'. + * + *********************************************************************/ +BOOL TraySetIcon(HWND hwnd, UINT uID, HICON hicon) +{ + NOTIFYICONDATA nid; + + memset(&nid, 0, sizeof(nid)); + + nid.cbSize = sizeof(nid); + nid.hWnd = hwnd; + nid.uID = uID; + nid.uFlags = NIF_ICON; + nid.uCallbackMessage = 0; + nid.hIcon = hicon; + + return( Shell_NotifyIcon(NIM_MODIFY, &nid) ); + +} + + +/********************************************************************* + * + * Function : TrayAddIcon + * + * Description : Adds a tray icon. + * + * Parameters : + * 1 : hwnd = handle of the systray window + * 2 : uID = user message number to notify systray window + * 3 : hicon = handle of icon to add to systray window + * 4 : pszToolTip = tool tip when mouse hovers over systray window + * + * Returns : Same as `Shell_NotifyIcon'. + * + *********************************************************************/ +BOOL TrayAddIcon(HWND hwnd, UINT uID, HICON hicon, const char *pszToolTip) +{ + NOTIFYICONDATA nid; + + memset(&nid, 0, sizeof(nid)); + + nid.cbSize = sizeof(nid); + nid.hWnd = hwnd; + nid.uID = uID; + nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; + nid.uCallbackMessage = WM_TRAYMSG; + nid.hIcon = hicon; + + if (pszToolTip) + { + strcpy(nid.szTip, pszToolTip); + } + + return( Shell_NotifyIcon(NIM_ADD, &nid) ); + +} + + +/********************************************************************* + * + * Function : TrayDeleteIcon + * + * Description : Deletes a tray icon. + * + * Parameters : + * 1 : hwnd = handle of the systray window + * 2 : uID = user message number to notify systray window + * + * Returns : Same as `Shell_NotifyIcon'. + * + *********************************************************************/ +BOOL TrayDeleteIcon(HWND hwnd, UINT uID) +{ + NOTIFYICONDATA nid; + + memset(&nid, 0, sizeof(nid)); + + nid.cbSize = sizeof(nid); + nid.hWnd = hwnd; + nid.uID = uID; + + return( Shell_NotifyIcon(NIM_DELETE, &nid) ); + +} + + +/********************************************************************* + * + * Function : TrayProc + * + * Description : Call back procedure processes tray messages. + * + * Parameters : + * 1 : hwnd = handle of the systray window + * 2 : msg = message number + * 3 : wParam = first param for this message + * 4 : lParam = next param for this message + * + * Returns : Appropriate M$ window message handler codes. + * + *********************************************************************/ +LRESULT CALLBACK TrayProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_CREATE: + return 0; + + case WM_CLOSE: + PostQuitMessage(0); + return 0; + + case WM_TRAYMSG: + { + /* UINT uID = (UINT) wParam; */ + UINT uMouseMsg = (UINT) lParam; + + if (uMouseMsg == WM_RBUTTONDOWN) + { + POINT pt; + HMENU hmenu = GetSubMenu(g_hmenuTray,0); + GetCursorPos(&pt); + SetForegroundWindow(g_hwndLogFrame); + TrackPopupMenu(hmenu, TPM_LEFTALIGN | TPM_TOPALIGN, pt.x, pt.y, 0, g_hwndLogFrame, NULL); + PostMessage(g_hwndLogFrame, WM_NULL, 0, 0 ) ; + } + else if (uMouseMsg == WM_LBUTTONDBLCLK) + { + ShowLogWindow(TRUE); + } + } + return 0; + + default: + /* DO NOTHING */ + break; + } + + return DefWindowProc(hwnd, msg, wParam, lParam); + +} + + +#endif /* ndef _WIN_CONSOLE - entire file */ + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/w32taskbar.h b/src/w32taskbar.h new file mode 100644 index 00000000..1ccc75c8 --- /dev/null +++ b/src/w32taskbar.h @@ -0,0 +1,78 @@ +#ifndef W32TASKBAR_H_INCLUDED +#define W32TASKBAR_H_INCLUDED +#define W32TASKBAR_H_VERSION "$Id: w32taskbar.h,v 1.4 2002/03/26 22:57:10 jongfoster Exp $" +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/w32taskbar.h,v $ + * + * Purpose : Functions for creating, setting and destroying the + * workspace tray icon + * + * Copyright : Written by and Copyright (C) 2001-2002 members of + * the Privoxy team. http://www.privoxy.org/ + * + * Written by and Copyright (C) 1999 Adam Lock + * + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: w32taskbar.h,v $ + * Revision 1.4 2002/03/26 22:57:10 jongfoster + * Web server name should begin www. + * + * Revision 1.3 2002/03/24 12:03:47 jongfoster + * Name change + * + * Revision 1.2 2001/07/29 18:43:08 jongfoster + * Changing #ifdef _FILENAME_H to FILENAME_H_INCLUDED, to conform to + * ANSI C rules. + * + * Revision 1.1.1.1 2001/05/15 13:59:08 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#ifdef __cplusplus +extern "C" { +#endif + +extern HWND CreateTrayWindow(HINSTANCE hInstance); +extern BOOL TrayAddIcon(HWND hwnd, UINT uID, HICON hicon, const char *pszToolTip); +extern BOOL TraySetIcon(HWND hwnd, UINT uID, HICON hicon); +extern BOOL TrayDeleteIcon(HWND hwnd, UINT uID); + +/* Revision control strings from this header and associated .c file */ +extern const char w32taskbar_rcs[]; +extern const char w32taskbar_h_rcs[]; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ndef W32TASKBAR_H_INCLUDED */ + + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/win32.c b/src/win32.c new file mode 100644 index 00000000..41dea70b --- /dev/null +++ b/src/win32.c @@ -0,0 +1,314 @@ +const char win32_rcs[] = "$Id: win32.c,v 1.9 2002/03/31 17:19:00 jongfoster Exp $"; +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/win32.c,v $ + * + * Purpose : Win32 User Interface initialization and message loop + * + * Copyright : Written by and Copyright (C) 2001-2002 members of + * the Privoxy team. http://www.privoxy.org/ + * + * Written by and Copyright (C) 1999 Adam Lock + * + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: win32.c,v $ + * Revision 1.9 2002/03/31 17:19:00 jongfoster + * Win32 only: Enabling STRICT to fix a VC++ compile warning. + * + * Revision 1.8 2002/03/26 22:57:10 jongfoster + * Web server name should begin www. + * + * Revision 1.7 2002/03/24 12:03:47 jongfoster + * Name change + * + * Revision 1.6 2002/03/16 21:53:28 jongfoster + * VC++ Heap debug option + * + * Revision 1.5 2002/03/04 23:47:30 jongfoster + * - Rewritten, simpler command-line pre-parser + * - not using raise(SIGINT) any more + * + * Revision 1.4 2001/11/30 21:29:33 jongfoster + * Fixing a warning + * + * Revision 1.3 2001/11/16 00:46:31 jongfoster + * Fixing compiler warnings + * + * Revision 1.2 2001/07/29 19:32:00 jongfoster + * Renaming _main() [mingw32 only] to real_main(), for ANSI compliance. + * + * Revision 1.1.1.1 2001/05/15 13:59:08 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#include "config.h" + +#ifdef _WIN32 + +#include + +#include "project.h" +#include "jcc.h" +#include "miscutil.h" + +/* Uncomment this if you want to build Win32 as a console app */ +/* #define _WIN_CONSOLE */ + +#ifndef STRICT +#define STRICT +#endif +#include + +#include +#include + +#if defined(_WIN32) && defined(_MSC_VER) && defined(_DEBUG) +/* Visual C++ Heap debugging */ +#include +#endif /* defined(_WIN32) && defined(_MSC_VER) && defined(_DEBUG) */ + +#include "win32.h" + +const char win32_h_rcs[] = WIN32_H_VERSION; + +const char win32_blurb[] = +"Privoxy version " VERSION " for Windows\n" +"Copyright (C) 2000-2002 by members of the Privoxy Team\n" +"Copyright (C) 1997-8 by Junkbusters Corp.\n" +"This is free software; it may be used and copied under the\n" +"GNU General Public License: http://www.gnu.org/copyleft/gpl.html .\n" +"This program comes with ABSOLUTELY NO WARRANTY OF ANY KIND.\n" +"\n" +"For information about how to to configure the proxy and your browser, see\n" +" " HOME_PAGE_URL "\n" +"\n"; + +#ifdef _WIN_CONSOLE + +int hideConsole = 0; + +#else + +HINSTANCE g_hInstance; +int g_nCmdShow; + +static void __cdecl UserInterfaceThread(void *); + +#endif + + +/********************************************************************* + * + * Function : WinMain + * + * Description : M$ Windows "main" routine: + * parse the `lpCmdLine' param into main's argc and argv variables, + * start the user interface thread (for the systray window), and + * call main (i.e. patch execution into normal startup). + * + * Parameters : + * 1 : hInstance = instance handle of this execution + * 2 : hPrevInstance = instance handle of previous execution + * 3 : lpCmdLine = command line string which started us + * 4 : nCmdShow = window show value (MIN, MAX, NORMAL, etc...) + * + * Returns : `main' never returns, so WinMain will also never return. + * + *********************************************************************/ +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + int i; + int res; + int argc = 1; + const char *argv[3]; + char szModule[MAX_PATH+1]; +#ifndef _WIN_CONSOLE + HANDLE hInitCompleteEvent = NULL; +#endif + + +#if defined(_WIN32) && defined(_MSC_VER) && defined(_DEBUG) +#if 0 + /* Visual C++ Heap debugging */ + + /* Get current flag*/ + int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG ); + + /* Turn on leak-checking bit */ + tmpFlag |= _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF; + + /* Turn off CRT block checking bit */ + tmpFlag &= ~(_CRTDBG_CHECK_CRT_DF | _CRTDBG_DELAY_FREE_MEM_DF); + + /* Set flag to the new value */ + _CrtSetDbgFlag( tmpFlag ); +#endif +#endif /* defined(_WIN32) && defined(_MSC_VER) && defined(_DEBUG) */ + + /* + * Cheat in parsing the command line. We only ever have at most one + * paramater, which may optionally be specified inside double quotes. + */ + + if (lpCmdLine != NULL) + { + /* Make writable copy */ + lpCmdLine = strdup(lpCmdLine); + } + if (lpCmdLine != NULL) + { + chomp(lpCmdLine); + i = strlen(lpCmdLine); + if ((i >= 2) && (lpCmdLine[0] == '\"') && (lpCmdLine[i - 1] == '\"')) + { + lpCmdLine[i - 1] = '\0'; + lpCmdLine++; + } + if (lpCmdLine[0] == '\0') + { + lpCmdLine = NULL; + } + } + + GetModuleFileName(hInstance, szModule, MAX_PATH); + argv[0] = szModule; + argv[1] = lpCmdLine; + argv[2] = NULL; + argc = ((lpCmdLine != NULL) ? 2 : 1); + +#ifndef _WIN_CONSOLE + /* Create a user-interface thread and wait for it to initialise */ + hInitCompleteEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + g_hInstance = hInstance; + g_nCmdShow = nCmdShow; + _beginthread(UserInterfaceThread, 0, &hInitCompleteEvent); + WaitForSingleObject(hInitCompleteEvent, INFINITE); + DeleteObject(hInitCompleteEvent); +#endif + +#ifdef __MINGW32__ + res = real_main( argc, argv ); +#else + res = main( argc, argv ); +#endif + + return res; + +} + +#endif + +/********************************************************************* + * + * Function : InitWin32 + * + * Description : Initialise windows, setting up the console or windows as appropriate. + * + * Parameters : None + * + * Returns : N/A + * + *********************************************************************/ +void InitWin32(void) +{ + WORD wVersionRequested; + WSADATA wsaData; + +#ifdef _WIN_CONSOLE + SetProcessShutdownParameters(0x100, SHUTDOWN_NORETRY); + if (hideConsole) + { + FreeConsole(); + } +#endif + wVersionRequested = MAKEWORD(2, 0); + if (WSAStartup(wVersionRequested, &wsaData) != 0) + { +#ifndef _WIN_CONSOLE + MessageBox(NULL, "Cannot initialize WinSock library", "Privoxy Error", + MB_OK | MB_ICONERROR | MB_TASKMODAL | MB_SETFOREGROUND | MB_TOPMOST); +#endif + exit(1); + } + +} + + +#ifndef _WIN_CONSOLE +#include +#include + +#include "win32.h" +#include "w32log.h" + + +/********************************************************************* + * + * Function : UserInterfaceThread + * + * Description : User interface thread. WinMain will wait for us to set + * the hInitCompleteEvent before patching over to `main'. + * This ensures the systray window is active before beginning + * operations. + * + * Parameters : + * 1 : pData = pointer to `hInitCompleteEvent'. + * + * Returns : N/A + * + *********************************************************************/ +static void __cdecl UserInterfaceThread(void *pData) +{ + MSG msg; + HANDLE hInitCompleteEvent = *((HANDLE *) pData); + + /* Initialise */ + InitLogWindow(); + SetEvent(hInitCompleteEvent); + + /* Enter a message processing loop */ + while (GetMessage(&msg, (HWND) NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + /* Cleanup */ + TermLogWindow(); + + /* Time to die... */ + exit(0); + +} + + +#endif + + +/* + Local Variables: + tab-width: 3 + end: +*/ diff --git a/src/win32.h b/src/win32.h new file mode 100644 index 00000000..7e82c811 --- /dev/null +++ b/src/win32.h @@ -0,0 +1,90 @@ +#ifndef WIN32_H_INCLUDED +#define WIN32_H_INCLUDED +#define WIN32_H_VERSION "$Id: win32.h,v 1.5 2002/03/26 22:57:10 jongfoster Exp $" +/********************************************************************* + * + * File : $Source: /cvsroot/ijbswa/current/win32.h,v $ + * + * Purpose : Win32 User Interface initialization and message loop + * + * Copyright : Written by and Copyright (C) 2001-2002 members of + * the Privoxy team. http://www.privoxy.org/ + * + * Written by and Copyright (C) 1999 Adam Lock + * + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * The GNU General Public License should be included with + * this file. If not, you can view it at + * http://www.gnu.org/copyleft/gpl.html + * or write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Revisions : + * $Log: win32.h,v $ + * Revision 1.5 2002/03/26 22:57:10 jongfoster + * Web server name should begin www. + * + * Revision 1.4 2002/03/24 12:03:47 jongfoster + * Name change + * + * Revision 1.3 2001/07/30 22:08:36 jongfoster + * Tidying up #defines: + * - All feature #defines are now of the form FEATURE_xxx + * - Permanently turned off WIN_GUI_EDIT + * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS + * + * Revision 1.2 2001/07/29 18:43:08 jongfoster + * Changing #ifdef _FILENAME_H to FILENAME_H_INCLUDED, to conform to + * ANSI C rules. + * + * Revision 1.1.1.1 2001/05/15 13:59:08 oes + * Initial import of version 2.9.3 source tree + * + * + *********************************************************************/ + + +#ifdef __cplusplus +extern "C" { +#endif + +extern const char win32_blurb[]; + +extern void InitWin32(void); + +#ifdef _WIN_CONSOLE +extern int hideConsole; +#endif /*def _WIN_CONSOLE */ + +extern HINSTANCE g_hInstance; +extern int g_nCmdShow; + +extern int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow); + +/* Revision control strings from this header and associated .c file */ +extern const char win32_rcs[]; +extern const char win32_h_rcs[]; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ndef WIN32_H_INCLUDED */ + +/* + Local Variables: + tab-width: 3 + end: +*/ -- 2.39.2