If too many trusted referrers are used,
[privoxy.git] / parsers.c
index 317c8cd..77e6c1a 100644 (file)
--- a/parsers.c
+++ b/parsers.c
@@ -1,4 +1,4 @@
-const char parsers_rcs[] = "$Id: parsers.c,v 1.62 2006/08/14 08:58:42 fabiankeil Exp $";
+const char parsers_rcs[] = "$Id: parsers.c,v 1.68 2006/09/06 10:43:32 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/parsers.c,v $
@@ -40,6 +40,49 @@ const char parsers_rcs[] = "$Id: parsers.c,v 1.62 2006/08/14 08:58:42 fabiankeil
  *
  * Revisions   :
  *    $Log: parsers.c,v $
+ *    Revision 1.68  2006/09/06 10:43:32  fabiankeil
+ *    Added config option enable-remote-http-toggle
+ *    to specify if Privoxy should recognize special
+ *    headers (currently only X-Filter) to change its
+ *    behaviour. Disabled by default.
+ *
+ *    Revision 1.67  2006/09/04 11:01:26  fabiankeil
+ *    After filtering de-chunked instances, remove
+ *    "Transfer-Encoding" header entirely instead of changing
+ *    it to "Transfer-Encoding: identity", which is invalid.
+ *    Thanks Michael Shields <shields@msrl.com>. Fixes PR 1318658.
+ *
+ *    Don't use localtime in parse_header_time. An empty time struct
+ *    is good enough, it gets overwritten by strptime anyway.
+ *
+ *    Revision 1.66  2006/09/03 19:38:28  fabiankeil
+ *    Use gmtime_r if available, fallback to gmtime with mutex
+ *    protection for MacOSX and use vanilla gmtime for the rest.
+ *
+ *    Revision 1.65  2006/08/22 10:55:56  fabiankeil
+ *    Changed client_referrer to use the right type (size_t) for
+ *    hostlenght and to shorten the temporary referrer string with
+ *    '\0' instead of adding a useless line break.
+ *
+ *    Revision 1.64  2006/08/17 17:15:10  fabiankeil
+ *    - Back to timegm() using GnuPG's replacement if necessary.
+ *      Using mktime() and localtime() could add a on hour offset if
+ *      the randomize factor was big enough to lead to a summer/wintertime
+ *      switch.
+ *
+ *    - Removed now-useless Privoxy 3.0.3 compatibility glue.
+ *
+ *    - Moved randomization code into pick_from_range().
+ *
+ *    - Changed parse_header_time definition.
+ *      time_t isn't guaranteed to be signed and
+ *      if it isn't, -1 isn't available as error code.
+ *      Changed some variable types in client_if_modified_since()
+ *      because of the same reason.
+ *
+ *    Revision 1.63  2006/08/14 13:18:08  david__schmidt
+ *    OS/2 compilation compatibility fixups
+ *
  *    Revision 1.62  2006/08/14 08:58:42  fabiankeil
  *    Changed include from strptime.c to strptime.h
  *
@@ -970,20 +1013,13 @@ jb_err filter_header(struct client_state *csp, char **header)
 
    int i, found_filters = 0;
 
-#ifndef  MAX_AF_FILES
-# define MAX_AF_FILES 1
-# define INDEX_OR_NOT
-#else
-# define INDEX_OR_NOT [i] 
-#endif
-
    log_error(LOG_LEVEL_RE_FILTER, "Entered filter_headers");
    /*
     * Need to check the set of re_filterfiles...
     */
    for (i = 0; i < MAX_AF_FILES; i++)
    {
-      fl = csp->rlist INDEX_OR_NOT;
+      fl = csp->rlist[i];
       if (NULL != fl)
       {
          if (NULL != fl->f)
@@ -1002,12 +1038,12 @@ jb_err filter_header(struct client_state *csp, char **header)
 
    for (i = 0; i < MAX_AF_FILES; i++)
    {
-      fl = csp->rlist INDEX_OR_NOT;
+      fl = csp->rlist[i];
       if ((NULL == fl) || (NULL == fl->f))
          break;
       /*
        * For all applying +filter actions, look if a filter by that
-       * name exists and if yes, execute it's pcrs_joblist on the
+       * name exists and if yes, execute its pcrs_joblist on the
        * buffer.
        */
       for (b = fl->f; b; b = b->next)
@@ -1231,8 +1267,8 @@ jb_err server_content_type(struct client_state *csp, char **header)
  *
  * 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.
+ *                - Remove header if body was chunked but has been
+ *                  de-chunked for filtering.
  *
  * Parameters  :
  *          1  :  csp = Current client state (buffers, headers, etc...)
@@ -1263,15 +1299,13 @@ jb_err server_transfer_coding(struct client_state *csp, char **header)
       csp->flags |= CSP_FLAG_CHUNKED;
 
       /*
-       * If the body was modified, it has been
-       * de-chunked first, so adjust the header:
+       * If the body was modified, it has been de-chunked first
+       * and the header must be removed. 
        */
       if (csp->flags & CSP_FLAG_MODIFIED)
       {
+         log_error(LOG_LEVEL_HEADER, "Removing: %s", *header);
          freez(*header);
-         *header = strdup("Transfer-Encoding: identity");
-         log_error(LOG_LEVEL_HEADER, "Set: %s", *header);
-         return (header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
       }
    }
 
@@ -1470,6 +1504,9 @@ jb_err server_last_modified(struct client_state *csp, char **header)
    char buf[BUFFER_SIZE];
 
    char newheader[50];
+#ifdef HAVE_GMTIME_R
+   struct tm gmt;
+#endif
    struct tm *timeptr = NULL;
    time_t now, last_modified;                  
    long int rtime;
@@ -1518,24 +1555,36 @@ jb_err server_last_modified(struct client_state *csp, char **header)
    {
       log_error(LOG_LEVEL_HEADER, "Randomizing: %s", *header);
       now = time(NULL);
+#ifdef HAVE_GMTIME_R
+      timeptr = gmtime_r(&now, &gmt);
+#elif OSX_DARWIN
+      pthread_mutex_lock(&gmtime_mutex);
+      timeptr = gmtime(&now);
+      pthread_mutex_unlock(&gmtime_mutex);
+#else
       timeptr = gmtime(&now);
-      if ( (last_modified = parse_header_time(*header, timeptr)) < 0 )
+#endif
+      if ((timeptr = parse_header_time(*header, &last_modified)) == NULL)
       {
-          log_error(LOG_LEVEL_HEADER, "Couldn't parse: %s (crunching!)", *header);
-          freez(*header);
+         log_error(LOG_LEVEL_HEADER, "Couldn't parse: %s (crunching!)", *header);
+         freez(*header);
       }
       else
       {
          rtime = difftime(now, last_modified);
          if (rtime)
          {
-#if !defined(_WIN32) && !defined(__OS2__)
-            rtime = random() % rtime + 1; 
-#else
-            rtime = rand() % (long int)(rtime + 1);
-#endif /* (ifndef _WIN32 || __OS2__) */
+            rtime = pick_from_range(rtime);
             last_modified += rtime;
-            timeptr = localtime(&last_modified);
+#ifdef HAVE_GMTIME_R
+            timeptr = gmtime_r(&last_modified, &gmt);
+#elif OSX_DARWIN
+            pthread_mutex_lock(&gmtime_mutex);
+            timeptr = gmtime(&last_modified);
+            pthread_mutex_unlock(&gmtime_mutex);
+#else
+            timeptr = gmtime(&last_modified);
+#endif
             strftime(newheader, sizeof(newheader), "%a, %d %b %Y %H:%M:%S GMT", timeptr);
             freez(*header);
             *header = strdup("Last-Modified: ");
@@ -1543,7 +1592,7 @@ jb_err server_last_modified(struct client_state *csp, char **header)
 
             if (*header == NULL)
             {
-               log_error(LOG_LEVEL_ERROR, " Insufficent memory, header crunched without replacement.");
+               log_error(LOG_LEVEL_ERROR, "Insufficent memory, header crunched without replacement.");
                return JB_ERR_MEMORY;  
             }
 
@@ -1667,7 +1716,7 @@ jb_err client_referrer(struct client_state *csp, char **header)
    const char *newval;
    const char *host;
    char *referer;
-   int hostlenght;
+   size_t hostlenght;
  
 #ifdef FEATURE_FORCE_LOAD
    /* Since the referrer can include the prefix even
@@ -1725,7 +1774,7 @@ jb_err client_referrer(struct client_state *csp, char **header)
           *if www.example.org/www.example.com-shall-see-the-referer/
           *links to www.example.com/
           */
-         referer[hostlenght+17] = '\n';
+         referer[hostlenght+17] = '\0';
       }
       if ( 0 == strstr(referer, host)) /*Host has changed*/
       {
@@ -2216,11 +2265,14 @@ jb_err client_host(struct client_state *csp, char **header)
 jb_err client_if_modified_since(struct client_state *csp, char **header)
 {
    char newheader[50];
+#ifdef HAVE_GMTIME_R
+   struct tm gmt;
+#endif
    struct tm *timeptr = NULL;
    time_t tm = 0;                  
    const char *newval;
-   time_t rtime;
-   time_t hours, minutes, seconds;
+   long int rtime;
+   long int hours, minutes, seconds;
    int negative = 0;
    char * endptr;
    
@@ -2247,28 +2299,25 @@ jb_err client_if_modified_since(struct client_state *csp, char **header)
       }
       else /* add random value */
       {
-         if ( (tm = parse_header_time(*header, timeptr)) < 0 )
+         if ((timeptr = parse_header_time(*header, &tm)) == NULL)
          {
             log_error(LOG_LEVEL_HEADER, "Couldn't parse: %s (crunching!)", *header);
             freez(*header);
          }
          else
          {
-            rtime = (time_t) strtol(newval, &endptr, 0);
+            rtime = strtol(newval, &endptr, 0);
+            if(rtime < 0)
+            {
+                rtime *= -1; 
+                negative = 1;
+            }
             if(rtime)
             {
                log_error(LOG_LEVEL_HEADER, "Randomizing: %s (random range: %d hou%s)",
                   *header, rtime, (rtime == 1 || rtime == -1) ? "r": "rs");
                rtime *= 3600;
-#if !defined(_WIN32) && !defined(__OS2__)
-               rtime = random() % rtime + 1; 
-#else
-               rtime = rand() % (long int)(rtime + 1);
-#endif /* (_WIN32 || __OS2__) */
-               if(newval[0] == '-')
-               {
-                  rtime *= -1;      
-               }
+               rtime = pick_from_range(rtime);
             }
             else
             {
@@ -2276,7 +2325,15 @@ jb_err client_if_modified_since(struct client_state *csp, char **header)
                   *header);
             }
             tm += rtime;
-            timeptr = localtime(&tm);
+#ifdef HAVE_GMTIME_R
+            timeptr = gmtime_r(&tm, &gmt);
+#elif OSX_DARWIN
+            pthread_mutex_lock(&gmtime_mutex);
+            timeptr = gmtime(&tm);
+            pthread_mutex_unlock(&gmtime_mutex);
+#else
+            timeptr = gmtime(&tm);
+#endif
             strftime(newheader, sizeof(newheader), "%a, %d %b %Y %H:%M:%S GMT", timeptr);
 
             freez(*header);
@@ -2285,20 +2342,15 @@ jb_err client_if_modified_since(struct client_state *csp, char **header)
 
             if (*header == NULL)
             {
-               log_error(LOG_LEVEL_HEADER, " Insufficent memory, header crunched without replacement.");
+               log_error(LOG_LEVEL_HEADER, "Insufficent memory, header crunched without replacement.");
                return JB_ERR_MEMORY;  
             }
 
             if(LOG_LEVEL_HEADER & debug) /* Save cycles if the user isn't interested. */
             {
-               if(rtime < 0)
-               {
-                  rtime *= -1; 
-                  negative = 1;
-               }
-               hours   = (int)rtime / 3600 % 24;
-               minutes = (int)rtime / 60 % 60;
-               seconds = (int)rtime % 60;            
+               hours   = rtime / 3600;
+               minutes = rtime / 60 % 60;
+               seconds = rtime % 60;            
 
                log_error(LOG_LEVEL_HEADER, "Randomized:  %s (%s %d hou%s %d minut%s %d second%s",
                   *header, (negative) ? "subtracted" : "added", hours, (hours == 1) ? "r" : "rs",
@@ -2360,17 +2412,24 @@ jb_err client_x_filter(struct client_state *csp, char **header)
 {
    if ( 0 == strcmpic(*header, "X-Filter: No"))
    {
-      if (csp->action->flags & ACTION_FORCE_TEXT_MODE)
+      if (!(csp->config->feature_flags & RUNTIME_FEATURE_HTTP_TOGGLE))
       {
-         log_error(LOG_LEVEL_HEADER, "force-text-mode overruled the client's request to disable filtering!");
+         log_error(LOG_LEVEL_INFO, "Ignored the client's request to fetch without filtering.");
       }
-      else
-      {  
-         csp->content_type = CT_TABOO;
-         log_error(LOG_LEVEL_HEADER, "Disabled filter mode on behalf of the client.");
+               else
+      {
+         if (csp->action->flags & ACTION_FORCE_TEXT_MODE)
+         {
+            log_error(LOG_LEVEL_HEADER, "force-text-mode overruled the client's request to fetch without filtering!");
+         }
+         else
+         {  
+            csp->content_type = CT_TABOO;
+            log_error(LOG_LEVEL_HEADER, "Accepted the client's request to fetch without filtering.");
+         }
+         log_error(LOG_LEVEL_HEADER, "Crunching %s", *header);
+         freez(*header);
       }
-      log_error(LOG_LEVEL_HEADER, "Crunching %s", *header);
-      freez(*header);
    }
    return JB_ERR_OK; 
 }
@@ -2832,26 +2891,30 @@ int strclean(const char *string, const char *substring)
  *
  * Parameters  :
  *          1  :  header = header to parse
- *          2  :  timeptr = storage for the resulting time structure 
+ *          2  :  tm = storage for the resulting time in seconds 
  *
- * Returns     :  Time in seconds since Unix epoch or -1 for failure.
+ * Returns     :  Time struct containing the header time, or
+ *                NULL in case of a parsing problem.
  *
  *********************************************************************/
-time_t parse_header_time(char *header, struct tm *timeptr) {
+struct tm *parse_header_time(char *header, time_t *tm) {
 
    char * timestring;
-   time_t tm;
+   struct tm gmt;
+   struct tm * timeptr;
 
-   tm = time(NULL);
-   timeptr = localtime(&tm);
    /* Skipping header name */
    timestring = strstr(header, ": ");
-   if (strptime(timestring, ": %a, %d %b %Y %H:%M:%S", timeptr) == NULL)
+   if (strptime(timestring, ": %a, %d %b %Y %H:%M:%S", &gmt) == NULL)
    {
-      return(-1);
+      timeptr = NULL;
+   }
+   else
+   {
+      *tm = timegm(&gmt);
+      timeptr=&gmt;
    }
-   tm = mktime(timeptr);
-   return(tm);
+   return(timeptr);
 }