Several changes from Debian package 3.0.12 to 3.0.19-1.
[privoxy.git] / parsers.c
index 1184560..bdf36cb 100644 (file)
--- a/parsers.c
+++ b/parsers.c
@@ -1,4 +1,4 @@
-const char parsers_rcs[] = "$Id: parsers.c,v 1.222 2011/04/19 13:00:47 fabiankeil Exp $";
+const char parsers_rcs[] = "$Id: parsers.c,v 1.236 2011/10/23 11:23:55 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/parsers.c,v $
@@ -187,10 +187,10 @@ struct parsers
 {
    /** The header prefix to match */
    const char *str;
-   
+
    /** The length of the prefix to match */
    const size_t len;
-   
+
    /** The function to apply to this line */
    const parser_func_ptr parser;
 };
@@ -252,7 +252,6 @@ static const add_header_func_ptr add_client_headers[] = {
    client_host_adder,
    client_x_forwarded_for_adder,
    client_xtra_adder,
-   /* Temporarily disabled:    client_accept_encoding_adder, */
    client_connection_header_adder,
    NULL
 };
@@ -350,7 +349,7 @@ jb_err add_to_iob(struct client_state *csp, char *buf, long n)
       {
          want *= 2;
       }
-      
+
       if (want <= csp->config->buffer_limit && NULL != (p = (char *)realloc(iob->buf, want)))
       {
          iob->size = want;
@@ -406,7 +405,7 @@ jb_err add_to_iob(struct client_state *csp, char *buf, long n)
 jb_err decompress_iob(struct client_state *csp)
 {
    char  *buf;       /* new, uncompressed buffer */
-   char  *cur;       /* Current iob position (to keep the original 
+   char  *cur;       /* Current iob position (to keep the original
                       * iob->cur unmodified if we return early) */
    size_t bufsize;   /* allocated size of the new buffer */
    size_t old_size;  /* Content size before decompression */
@@ -554,7 +553,7 @@ jb_err decompress_iob(struct client_state *csp)
        *
        * Fortunately, add_to_iob() has thoughtfully null-terminated
        * the buffer; we can just increment the end pointer to include
-       * the dummy byte.  
+       * the dummy byte.
        */
       csp->iob->eod++;
    }
@@ -638,7 +637,7 @@ jb_err decompress_iob(struct client_state *csp)
       {
          bufsize = csp->config->buffer_limit;
       }
-    
+
       /* Try to allocate the new buffer. */
       tmpbuf = realloc(buf, bufsize);
       if (NULL == tmpbuf)
@@ -710,7 +709,7 @@ jb_err decompress_iob(struct client_state *csp)
    csp->iob->cur  = csp->iob->buf + skip_size;
    csp->iob->eod  = (char *)zstr.next_out;
    csp->iob->size = bufsize;
-  
+
    /*
     * Make sure the new uncompressed iob obeys some minimal
     * consistency conditions.
@@ -882,7 +881,7 @@ char *get_header(struct iob *iob)
        * Header spans multiple lines, append the next one.
        */
       char *continued_header;
-      
+
       continued_header = get_header_line(iob);
       if ((continued_header == NULL) || (*continued_header == '\0'))
       {
@@ -1014,7 +1013,7 @@ char *get_header_value(const struct list *header_list, const char *header_name)
       }
    }
 
-   /* 
+   /*
     * Not found
     */
    return NULL;
@@ -1026,7 +1025,7 @@ char *get_header_value(const struct list *header_list, const char *header_name)
  *
  * Function    :  scan_headers
  *
- * Description :  Scans headers, applies tags and updates action bits. 
+ * Description :  Scans headers, applies tags and updates action bits.
  *
  * Parameters  :
  *          1  :  csp = Current client state (buffers, headers, etc...)
@@ -1187,6 +1186,18 @@ jb_err update_server_headers(struct client_state *csp)
    }
 #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
 
+#ifdef FEATURE_COMPRESSION
+   if ((JB_ERR_OK == err)
+      && (csp->flags & CSP_FLAG_BUFFERED_CONTENT_DEFLATED))
+   {
+      err = enlist_unique_header(csp->headers, "Content-Encoding", "deflate");
+      if (JB_ERR_OK == err)
+      {
+         log_error(LOG_LEVEL_HEADER, "Added header: Content-Encoding: deflate");
+      }
+   }
+#endif
+
    return err;
 }
 
@@ -1332,7 +1343,7 @@ static jb_err header_tagger(struct client_state *csp, char *header)
                         b->name);
                      continue;
                   }
+
                   if (!list_contains_item(csp->tags, tag))
                   {
                      if (JB_ERR_OK != enlist(csp->tags, tag))
@@ -1496,7 +1507,7 @@ static jb_err filter_header(struct client_state *csp, char **header)
                   matches = pcrs_execute(job, *header, size, &newheader, &size);
                   if ( 0 < matches )
                   {
-                     current_hits += matches; 
+                     current_hits += matches;
                      log_error(LOG_LEVEL_HEADER, "Transforming \"%s\" to \"%s\"", *header, newheader);
                      freez(*header);
                      *header = newheader;
@@ -1810,7 +1821,7 @@ static jb_err client_save_content_length(struct client_state *csp, char **header
  * Function    :  client_connection
  *
  * Description :  Makes sure a proper "Connection:" header is
- *                set and signals connection_header_adder 
+ *                set and signals connection_header_adder
  *                to do nothing.
  *
  * Parameters  :
@@ -1838,6 +1849,11 @@ static jb_err client_connection(struct client_state *csp, char **header)
              log_error(LOG_LEVEL_HEADER,
                 "Removing \'%s\' to imply keep-alive.", *header);
              freez(*header);
+             /*
+              * While we imply keep-alive to the server,
+              * we have to remember that the client didn't.
+              */
+             csp->flags &= ~CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE;
           }
           else
           {
@@ -1947,7 +1963,7 @@ static jb_err crunch_server_header(struct client_state *csp, char **header)
       /* Is the current header the lucky one? */
       if (strstr(*header, crunch_pattern))
       {
-         log_error(LOG_LEVEL_HEADER, "Crunching server header: %s (contains: %s)", *header, crunch_pattern);  
+         log_error(LOG_LEVEL_HEADER, "Crunching server header: %s (contains: %s)", *header, crunch_pattern);
          freez(*header);
       }
    }
@@ -2028,7 +2044,7 @@ static jb_err server_content_type(struct client_state *csp, char **header)
    {
       /*
        * Make sure the user doesn't accidentally
-       * change the content type of binary documents. 
+       * change the content type of binary documents.
        */
       if ((csp->content_type & CT_TEXT) || (csp->action->flags & ACTION_FORCE_TEXT_MODE))
       {
@@ -2127,7 +2143,7 @@ static jb_err server_transfer_coding(struct client_state *csp, char **header)
  *
  *                If FEATURE_ZLIB is enabled and the compression type
  *                supported, the content is marked for decompression.
- *                
+ *
  *                XXX: Doesn't properly deal with multiple or with
  *                     unsupported but unknown encodings.
  *                     Is case-sensitive but shouldn't be.
@@ -2248,7 +2264,7 @@ static jb_err server_adjust_content_encoding(struct client_state *csp, char **he
       /*
        * We successfully decompressed the content,
        * and have to clean the header now, so the
-       * client no longer expects compressed data..
+       * client no longer expects compressed data.
        *
        * XXX: There is a difference between cleaning
        * and removing it completely.
@@ -2416,7 +2432,7 @@ static jb_err server_content_disposition(struct client_state *csp, char **header
       return JB_ERR_OK;
    }
    else
-   {  
+   {
       /*
        * Replacing Content-Disposition header
        */
@@ -2456,7 +2472,6 @@ static jb_err server_content_disposition(struct client_state *csp, char **header
 static jb_err server_last_modified(struct client_state *csp, char **header)
 {
    const char *newval;
-   char buf[BUFFER_SIZE];
    time_t last_modified;
    char newheader[50];
 
@@ -2481,18 +2496,19 @@ static jb_err server_last_modified(struct client_state *csp, char **header)
       return JB_ERR_OK;
    }
    else if (0 == strcmpic(newval, "reset-to-request-time"))
-   {  
+   {
       /*
        * Setting Last-Modified Header to now.
        */
+      char buf[30];
       get_http_time(0, buf, sizeof(buf));
       freez(*header);
       *header = strdup("Last-Modified: ");
-      string_append(header, buf);   
+      string_append(header, buf);
 
       if (*header == NULL)
       {
-         log_error(LOG_LEVEL_HEADER, "Insufficient memory. Last-Modified header got lost, boohoo.");  
+         log_error(LOG_LEVEL_HEADER, "Insufficient memory. Last-Modified header got lost, boohoo.");
       }
       else
       {
@@ -2527,7 +2543,7 @@ static jb_err server_last_modified(struct client_state *csp, char **header)
 
             if (negative_delta)
             {
-               rtime *= -1; 
+               rtime *= -1;
                log_error(LOG_LEVEL_HEADER, "Server time in the future.");
             }
             rtime = pick_from_range(rtime);
@@ -2562,7 +2578,7 @@ static jb_err server_last_modified(struct client_state *csp, char **header)
             if (*header == NULL)
             {
                log_error(LOG_LEVEL_ERROR, "Insufficient memory, header crunched without replacement.");
-               return JB_ERR_MEMORY;  
+               return JB_ERR_MEMORY;
             }
 
             days    = rtime / (3600 * 24);
@@ -2607,6 +2623,13 @@ static jb_err server_last_modified(struct client_state *csp, char **header)
  *********************************************************************/
 static jb_err client_accept_encoding(struct client_state *csp, char **header)
 {
+#ifdef FEATURE_COMPRESSION
+   if ((csp->config->feature_flags & RUNTIME_FEATURE_COMPRESSION)
+      && strstr(*header, "deflate"))
+   {
+      csp->flags |= CSP_FLAG_CLIENT_SUPPORTS_DEFLATE;
+   }
+#endif
    if ((csp->action->flags & ACTION_NO_COMPRESSION) != 0)
    {
       log_error(LOG_LEVEL_HEADER, "Suppressed offer to compress content");
@@ -2671,7 +2694,7 @@ static jb_err client_referrer(struct client_state *csp, char **header)
    /* booleans for parameters we have to check multiple times */
    int parameter_conditional_block;
    int parameter_conditional_forge;
+
 #ifdef FEATURE_FORCE_LOAD
    /*
     * Since the referrer can include the prefix even
@@ -2769,18 +2792,18 @@ static jb_err client_accept_language(struct client_state *csp, char **header)
       return JB_ERR_OK;
    }
    else
-   {  
+   {
       /*
        * Replacing Accept-Language header
        */
       freez(*header);
       *header = strdup("Accept-Language: ");
-      string_append(header, newval);   
+      string_append(header, newval);
 
       if (*header == NULL)
       {
          log_error(LOG_LEVEL_ERROR,
-            "Insufficient memory. Accept-Language header crunched without replacement.");  
+            "Insufficient memory. Accept-Language header crunched without replacement.");
       }
       else
       {
@@ -2821,7 +2844,7 @@ static jb_err crunch_client_header(struct client_state *csp, char **header)
       /* Is the current header the lucky one? */
       if (strstr(*header, crunch_pattern))
       {
-         log_error(LOG_LEVEL_HEADER, "Crunching client header: %s (contains: %s)", *header, crunch_pattern);  
+         log_error(LOG_LEVEL_HEADER, "Crunching client header: %s (contains: %s)", *header, crunch_pattern);
          freez(*header);
       }
    }
@@ -3126,10 +3149,10 @@ static jb_err client_host(struct client_state *csp, char **header)
       return JB_ERR_OK;
    }
 
-   if (!csp->http->hostport || (*csp->http->hostport == '*') ||  
+   if (!csp->http->hostport || (*csp->http->hostport == '*') ||
        *csp->http->hostport == ' ' || *csp->http->hostport == '\0')
    {
-      
+
       if (NULL == (p = strdup((*header)+6)))
       {
          return JB_ERR_MEMORY;
@@ -3192,13 +3215,13 @@ static jb_err client_if_modified_since(struct client_state *csp, char **header)
    struct tm gmt;
 #endif
    struct tm *timeptr = NULL;
-   time_t tm = 0;                  
+   time_t tm = 0;
    const char *newval;
    char * endptr;
-   
+
    if ( 0 == strcmpic(*header, "If-Modified-Since: Wed, 08 Jun 1955 12:00:00 GMT"))
    {
-      /* 
+      /*
        * The client got an error message because of a temporary problem,
        * the problem is gone and the client now tries to revalidate our
        * error message on the real server. The revalidation would always
@@ -3238,7 +3261,7 @@ static jb_err client_if_modified_since(struct client_state *csp, char **header)
                   *header, rtime, (rtime == 1 || rtime == -1) ? "e": "es");
                if (negative_range)
                {
-                  rtime *= -1; 
+                  rtime *= -1;
                }
                rtime *= 60;
                rtime = pick_from_range(rtime);
@@ -3275,7 +3298,7 @@ static jb_err client_if_modified_since(struct client_state *csp, char **header)
             if (*header == NULL)
             {
                log_error(LOG_LEVEL_HEADER, "Insufficient memory, header crunched without replacement.");
-               return JB_ERR_MEMORY;  
+               return JB_ERR_MEMORY;
             }
 
             hours   = rtime / 3600;
@@ -3315,7 +3338,7 @@ static jb_err client_if_modified_since(struct client_state *csp, char **header)
 static jb_err client_if_none_match(struct client_state *csp, char **header)
 {
    if (csp->action->flags & ACTION_CRUNCH_IF_NONE_MATCH)
-   {  
+   {
       log_error(LOG_LEVEL_HEADER, "Crunching %s", *header);
       freez(*header);
    }
@@ -3357,7 +3380,7 @@ jb_err client_x_filter(struct client_state *csp, char **header)
                "force-text-mode overruled the client's request to fetch without filtering!");
          }
          else
-         {  
+         {
             csp->content_type = CT_TABOO; /* XXX: This hack shouldn't be necessary */
             csp->flags |= CSP_FLAG_NO_FILTERING;
             log_error(LOG_LEVEL_HEADER, "Accepted the client's request to fetch without filtering.");
@@ -3366,7 +3389,7 @@ jb_err client_x_filter(struct client_state *csp, char **header)
          freez(*header);
       }
    }
-   return JB_ERR_OK; 
+   return JB_ERR_OK;
 }
 
 
@@ -3400,7 +3423,7 @@ static jb_err client_range(struct client_state *csp, char **header)
       freez(*header);
    }
 
-   return JB_ERR_OK; 
+   return JB_ERR_OK;
 }
 
 /* the following functions add headers directly to the header list */
@@ -3457,36 +3480,6 @@ static jb_err client_host_adder(struct client_state *csp)
 }
 
 
-#if 0
-/*********************************************************************
- *
- * 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.
- *
- *********************************************************************/
-static 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;
-}
-#endif
-
-
 /*********************************************************************
  *
  * Function    :  client_xtra_adder
@@ -3769,7 +3762,7 @@ static jb_err server_http(struct client_state *csp, char **header)
 static jb_err server_set_cookie(struct client_state *csp, char **header)
 {
    time_t now;
-   time_t cookie_time; 
+   time_t cookie_time;
 
    time(&now);
 
@@ -3979,7 +3972,7 @@ int strclean(char *string, const char *substring)
  *                to get the numerical respresentation.
  *
  * Parameters  :
- *          1  :  header_time = HTTP header time as string. 
+ *          1  :  header_time = HTTP header time as string.
  *          2  :  result = storage for header_time in seconds
  *
  * Returns     :  JB_ERR_OK if the time format was recognized, or
@@ -3989,34 +3982,49 @@ int strclean(char *string, const char *substring)
 static jb_err parse_header_time(const char *header_time, time_t *result)
 {
    struct tm gmt;
-
    /*
-    * Zero out gmt to prevent time zone offsets.
-    *
-    * While this is only necessary on some platforms
-    * (mingw32 for example), I don't know how to
-    * detect these automatically and doing it everywhere
-    * shouldn't hurt.
+    * Checking for two-digit years first in an
+    * attempt to work around GNU libc's strptime()
+    * reporting negative year values when using %Y.
     */
-   memset(&gmt, 0, sizeof(gmt));
-
-                            /* Tue, 02 Jun 2037 20:00:00 */
-   if ((NULL == strptime(header_time, "%a, %d %b %Y %H:%M:%S", &gmt))
-                            /* Tue, 02-Jun-2037 20:00:00 */
-    && (NULL == strptime(header_time, "%a, %d-%b-%Y %H:%M:%S", &gmt))
-                            /* Tue, 02-Jun-37 20:00:00 */
-    && (NULL == strptime(header_time, "%a, %d-%b-%y %H:%M:%S", &gmt))
-                        /* Tuesday, 02-Jun-2037 20:00:00 */
-    && (NULL == strptime(header_time, "%A, %d-%b-%Y %H:%M:%S", &gmt))
-                        /* Tuesday Jun 02 20:00:00 2037 */
-    && (NULL == strptime(header_time, "%A %b %d %H:%M:%S %Y", &gmt)))
+   static const char * const time_formats[] = {
+      /* Tue, 02-Jun-37 20:00:00 */
+      "%a, %d-%b-%y %H:%M:%S",
+      /* Tue, 02 Jun 2037 20:00:00 */
+      "%a, %d %b %Y %H:%M:%S",
+      /* Tue, 02-Jun-2037 20:00:00 */
+      "%a, %d-%b-%Y %H:%M:%S",
+      /* Tuesday, 02-Jun-2037 20:00:00 */
+      "%A, %d-%b-%Y %H:%M:%S",
+      /* Tuesday Jun 02 20:00:00 2037 */
+      "%A %b %d %H:%M:%S %Y"
+   };
+   unsigned int i;
+
+   for (i = 0; i < SZ(time_formats); i++)
    {
-      return JB_ERR_PARSE;
-   }
+      /*
+       * Zero out gmt to prevent time zone offsets.
+       * Documented to be required for GNU libc.
+       */
+      memset(&gmt, 0, sizeof(gmt));
 
-   *result = timegm(&gmt);
+      if (NULL != strptime(header_time, time_formats[i], &gmt))
+      {
+         /* Sanity check for GNU libc. */
+         if (gmt.tm_year < 0)
+         {
+            log_error(LOG_LEVEL_HEADER,
+               "Failed to parse '%s' using '%s'. Moving on.",
+               header_time, time_formats[i]);
+            continue;
+         }
+         *result = timegm(&gmt);
+         return JB_ERR_OK;
+      }
+   }
 
-   return JB_ERR_OK;
+   return JB_ERR_PARSE;
 
 }
 
@@ -4035,7 +4043,7 @@ static jb_err parse_header_time(const char *header_time, time_t *result)
  * Parameters  :
  *          1  :  headers = List of headers (one of them hopefully being
  *                the "Host:" header)
- *          2  :  http = storage for the result (host, port and hostport). 
+ *          2  :  http = storage for the result (host, port and hostport).
  *
  * Returns     :  JB_ERR_MEMORY in case of memory problems,
  *                JB_ERR_PARSE if the host header couldn't be found,
@@ -4110,7 +4118,7 @@ jb_err get_destination_from_headers(const struct list *headers, struct http_requ
  *
  * Description :  Helper for client_referrer to forge a referer as
  *                'http://[hostname:port/' to fool stupid
- *                checks for in-site links 
+ *                checks for in-site links
  *
  * Parameters  :
  *          1  :  header   = Pointer to header pointer