Add a digit missed in the previous commit
[privoxy.git] / parsers.c
index e29c451..db9527e 100644 (file)
--- a/parsers.c
+++ b/parsers.c
@@ -1,4 +1,4 @@
-const char parsers_rcs[] = "$Id: parsers.c,v 1.245 2012/04/06 15:17:10 fabiankeil Exp $";
+const char parsers_rcs[] = "$Id: parsers.c,v 1.257 2012/10/21 12:39:27 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/parsers.c,v $
@@ -280,21 +280,21 @@ long flush_socket(jb_socket fd, struct iob *iob)
  *
  * Function    :  add_to_iob
  *
- * Description :  Add content to the buffered page, expanding the
+ * Description :  Add content to the buffer, 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
+ *          1  :  iob = Destination buffer.
+ *          2  :  buffer_limit = Limit to which the destination may grow
+ *          3  :  src = holds the content to be added
+ *          4  :  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, long n)
+jb_err add_to_iob(struct iob *iob, const size_t buffer_limit, char *src, long n)
 {
-   struct iob *iob = csp->iob;
    size_t used, offset, need;
    char *p;
 
@@ -308,24 +308,24 @@ jb_err add_to_iob(struct client_state *csp, char *buf, long n)
     * 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)
+   if (need > buffer_limit)
    {
       log_error(LOG_LEVEL_INFO,
          "Buffer limit reached while extending the buffer (iob). Needed: %d. Limit: %d",
-         need, csp->config->buffer_limit);
+         need, buffer_limit);
       return JB_ERR_MEMORY;
    }
 
    if (need > iob->size)
    {
-      size_t want = csp->iob->size ? csp->iob->size : 512;
+      size_t want = iob->size ? iob->size : 512;
 
       while (want <= need)
       {
          want *= 2;
       }
 
-      if (want <= csp->config->buffer_limit && NULL != (p = (char *)realloc(iob->buf, want)))
+      if (want <= buffer_limit && NULL != (p = (char *)realloc(iob->buf, want)))
       {
          iob->size = want;
       }
@@ -346,7 +346,7 @@ jb_err add_to_iob(struct client_state *csp, char *buf, long n)
    }
 
    /* copy the new data into the iob buffer */
-   memcpy(iob->eod, buf, (size_t)n);
+   memcpy(iob->eod, src, (size_t)n);
 
    /* point to the end of the data */
    iob->eod += n;
@@ -359,6 +359,27 @@ jb_err add_to_iob(struct client_state *csp, char *buf, long n)
 }
 
 
+/*********************************************************************
+ *
+ * Function    :  clear_iob
+ *
+ * Description :  Frees the memory allocated for an I/O buffer and
+ *                resets the structure.
+ *
+ * Parameters  :
+ *          1  :  iob = I/O buffer to clear.
+ *
+ * Returns     :  JB_ERR_OK on success, JB_ERR_MEMORY if out-of-memory
+ *                or buffer limit reached.
+ *
+ *********************************************************************/
+void clear_iob(struct iob *iob)
+{
+   free(iob->buf);
+   memset(iob, '\0', sizeof(*iob));;
+}
+
+
 #ifdef FEATURE_ZLIB
 /*********************************************************************
  *
@@ -1025,6 +1046,79 @@ static jb_err scan_headers(struct client_state *csp)
 }
 
 
+/*********************************************************************
+ *
+ * Function    :  enforce_header_order
+ *
+ * Description :  Enforces a given header order.
+ *
+ * Parameters  :
+ *          1  :  headers         = List of headers to order.
+ *          2  :  ordered_headers = List of ordered header names.
+ *
+ * Returns     :  N/A
+ *
+ *********************************************************************/
+static void enforce_header_order(struct list *headers, const struct list *ordered_headers)
+{
+   struct list_entry *sorted_header;
+   struct list new_headers[1];
+   struct list_entry *header;
+
+   init_list(new_headers);
+
+   /* The request line is always the first "header" */
+
+   assert(NULL != headers->first->str);
+   enlist(new_headers, headers->first->str);
+   freez(headers->first->str)
+
+   /* Enlist the specified headers in the given order */
+
+   for (sorted_header = ordered_headers->first; sorted_header != NULL;
+        sorted_header = sorted_header->next)
+   {
+      const size_t sorted_header_length = strlen(sorted_header->str);
+      for (header = headers->first; header != NULL; header = header->next)
+      {
+         /* Header enlisted in previous run? -> ignore */
+         if (header->str == NULL) continue;
+
+         if (0 == strncmpic(sorted_header->str, header->str, sorted_header_length)
+            && (header->str[sorted_header_length] == ':'))
+         {
+            log_error(LOG_LEVEL_HEADER, "Enlisting sorted header %s", header->str);
+            if (JB_ERR_OK != enlist(new_headers, header->str))
+            {
+               log_error(LOG_LEVEL_HEADER, "Failed to enlist %s", header->str);
+            }
+            freez(header->str);
+         }
+      }
+   }
+
+   /* Enlist the rest of the headers behind the ordered ones */
+   for (header = headers->first; header != NULL; header = header->next)
+   {
+      /* Header enlisted in previous run? -> ignore */
+      if (header->str == NULL) continue;
+
+      log_error(LOG_LEVEL_HEADER,
+         "Enlisting left-over header %s", header->str);
+      if (JB_ERR_OK != enlist(new_headers, header->str))
+      {
+         log_error(LOG_LEVEL_HEADER, "Failed to enlist %s", header->str);
+      }
+      freez(header->str);
+   }
+
+   list_remove_all(headers);
+   list_duplicate(headers, new_headers);
+   list_remove_all(new_headers);
+
+   return;
+}
+
 /*********************************************************************
  *
  * Function    :  sed
@@ -1091,6 +1185,11 @@ jb_err sed(struct client_state *csp, int filter_server_headers)
       f++;
    }
 
+   if (!filter_server_headers && !list_is_empty(csp->config->ordered_client_headers))
+   {
+      enforce_header_order(csp->headers, csp->config->ordered_client_headers);
+   }
+
    return err;
 }
 
@@ -1722,22 +1821,20 @@ static jb_err client_keep_alive(struct client_state *csp, char **header)
  *                Content-Length header.
  *
  * Parameters  :
- *          1  :  header = The Content-Length header.
+ *          1  :  header_value = The Content-Length header value.
  *          2  :  length = Storage to return the value.
  *
  * Returns     :  JB_ERR_OK on success, or
  *                JB_ERR_PARSE if no value is recognized.
  *
  *********************************************************************/
-static jb_err get_content_length(const char *header, unsigned long long *length)
+static jb_err get_content_length(const char *header_value, unsigned long long *length)
 {
-   assert(header[14] == ':');
-
 #ifdef _WIN32
    assert(sizeof(unsigned long long) > 4);
-   if (1 != sscanf(header+14, ": %I64u", length))
+   if (1 != sscanf(header_value, "%I64u", length))
 #else
-   if (1 != sscanf(header+14, ": %llu", length))
+   if (1 != sscanf(header_value, "%llu", length))
 #endif
    {
       return JB_ERR_PARSE;
@@ -1767,10 +1864,12 @@ static jb_err get_content_length(const char *header, unsigned long long *length)
 static jb_err client_save_content_length(struct client_state *csp, char **header)
 {
    unsigned long long content_length = 0;
+   const char *header_value;
 
    assert(*(*header+14) == ':');
 
-   if (JB_ERR_OK != get_content_length(*header, &content_length))
+   header_value = *header + 15;
+   if (JB_ERR_OK != get_content_length(header_value, &content_length))
    {
       log_error(LOG_LEVEL_ERROR, "Crunching invalid header: %s", *header);
       freez(*header);
@@ -1811,7 +1910,8 @@ static jb_err client_connection(struct client_state *csp, char **header)
    if (!strcmpic(*header, connection_close))
    {
 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
-      if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING))
+      if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING)
+        && !(csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED))
       {
           if (!strcmpic(csp->http->ver, "HTTP/1.1"))
           {
@@ -1843,7 +1943,8 @@ static jb_err client_connection(struct client_state *csp, char **header)
          csp->flags &= ~CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE;
       }
    }
-   else if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE))
+   else if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE)
+        && !(csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED))
    {
       log_error(LOG_LEVEL_HEADER,
          "Keeping the client header '%s' around. "
@@ -1862,7 +1963,7 @@ static jb_err client_connection(struct client_state *csp, char **header)
       freez(old_header);
    }
 
-   /* Signal client_connection_adder() to return early. */
+   /* Signal client_connection_header_adder() to return early. */
    csp->flags |= CSP_FLAG_CLIENT_CONNECTION_HEADER_SET;
 
    return JB_ERR_OK;
@@ -2300,10 +2401,12 @@ static jb_err server_adjust_content_length(struct client_state *csp, char **head
 static jb_err server_save_content_length(struct client_state *csp, char **header)
 {
    unsigned long long content_length = 0;
+   const char *header_value;
 
    assert(*(*header+14) == ':');
 
-   if (JB_ERR_OK != get_content_length(*header, &content_length))
+   header_value = *header + 15;
+   if (JB_ERR_OK != get_content_length(header_value, &content_length))
    {
       log_error(LOG_LEVEL_ERROR, "Crunching invalid header: %s", *header);
       freez(*header);
@@ -2957,7 +3060,7 @@ static jb_err client_from(struct client_state *csp, char **header)
  *********************************************************************/
 static jb_err client_send_cookie(struct client_state *csp, char **header)
 {
-   if (csp->action->flags & ACTION_NO_COOKIE_READ)
+   if (csp->action->flags & ACTION_CRUNCH_OUTGOING_COOKIES)
    {
       log_error(LOG_LEVEL_HEADER, "Crunched outgoing cookie: %s", *header);
       freez(*header);
@@ -3585,8 +3688,7 @@ static jb_err server_connection_adder(struct client_state *csp)
  * Function    :  server_proxy_connection_adder
  *
  * Description :  Adds a "Proxy-Connection: keep-alive" header to
- *                csp->headers if the client asked for keep-alive.
- *                XXX: We should reuse existent ones.
+ *                csp->headers when appropriate.
  *
  * Parameters  :
  *          1  :  csp = Current client state (buffers, headers, etc...)
@@ -3639,6 +3741,7 @@ static jb_err client_connection_header_adder(struct client_state *csp)
 
 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
    if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE)
+      && !(csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED)
       && (csp->http->ssl == 0)
       && !strcmpic(csp->http->ver, "HTTP/1.1"))
    {
@@ -3735,12 +3838,12 @@ static jb_err server_set_cookie(struct client_state *csp, char **header)
 
    time(&now);
 
-   if ((csp->action->flags & ACTION_NO_COOKIE_SET) != 0)
+   if ((csp->action->flags & ACTION_CRUNCH_INCOMING_COOKIES) != 0)
    {
       log_error(LOG_LEVEL_HEADER, "Crunching incoming cookie: %s", *header);
       freez(*header);
    }
-   else if ((csp->action->flags & ACTION_NO_COOKIE_KEEP) != 0)
+   else if ((csp->action->flags & ACTION_SESSION_COOKIES_ONLY) != 0)
    {
       /* Flag whether or not to log a message */
       int changed = 0;
@@ -4225,6 +4328,39 @@ static void create_content_length_header(unsigned long long content_length,
 }
 
 
+/*********************************************************************
+ *
+ * Function    :  get_expected_content_length
+ *
+ * Description :  Figures out the content length from a list of headers.
+ *
+ * Parameters  :
+ *          1  :  headers = List of headers
+ *
+ * Returns     :  Number of bytes to expect
+ *
+ *********************************************************************/
+unsigned long long get_expected_content_length(struct list *headers)
+{
+   const char *content_length_header;
+   unsigned long long content_length = 0;
+
+   content_length_header = get_header_value(headers, "Content-Length:");
+   if (content_length_header != NULL)
+   {
+      if (JB_ERR_OK != get_content_length(content_length_header, &content_length))
+      {
+         log_error(LOG_LEVEL_ERROR,
+            "Failed to get the Content-Length in %s", content_length_header);
+         /* XXX: The header will be removed later on */
+         return 0;
+      }
+   }
+
+   return content_length;
+}
+
+
 /*
   Local Variables:
   tab-width: 3