+ * 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, "Suppressed 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;
+ const char *host;
+ char *referer;
+ size_t hostlenght;
+
+#ifdef FEATURE_FORCE_LOAD
+ /* Since the referrer can include the prefix 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;
+ }
+
+ newval = csp->action->string[ACTION_STRING_REFERER];
+
+ if ((0 != strcmpic(newval, "conditional-block")))
+ {
+ freez(*header);
+ }
+ if ((newval == NULL) || (0 == strcmpic(newval, "block")) )
+ {
+ /*
+ * Blocking referer
+ */
+ log_error(LOG_LEVEL_HEADER, "Referer crunched!");
+ return JB_ERR_OK;
+ }
+ else if (0 == strcmpic(newval, "conditional-block"))
+ {
+ /*
+ * Block referer if host has changed.
+ */
+
+ if (NULL == (host = strdup(csp->http->hostport)))
+ {
+ freez(*header);
+ log_error(LOG_LEVEL_HEADER, "Referer crunched! Couldn't allocate memory for temporary host copy.");
+ return JB_ERR_MEMORY;
+ }
+ if (NULL == (referer = strdup(*header)))
+ {
+ freez(*header);
+ freez(host);
+ log_error(LOG_LEVEL_HEADER, "Referer crunched! Couldn't allocate memory for temporary referer copy.");
+ return JB_ERR_MEMORY;
+ }
+ hostlenght = strlen(host);
+ if ( hostlenght < (strlen(referer)-17) ) /*referer begins with 'Referer: http[s]://'*/
+ {
+ /*Shorten referer to make sure the referer is blocked
+ *if www.example.org/www.example.com-shall-see-the-referer/
+ *links to www.example.com/
+ */
+ referer[hostlenght+17] = '\0';
+ }
+ if ( 0 == strstr(referer, host)) /*Host has changed*/
+ {
+ log_error(LOG_LEVEL_HEADER, "New host is: %s. Crunching %s!", host, *header);
+ freez(*header);
+ }
+ else
+ {
+ log_error(LOG_LEVEL_HEADER, "%s (not modified, still on %s)", *header, host);
+ }
+ freez(referer);
+ freez(host);
+ return JB_ERR_OK;
+ }
+ else if (0 != strcmpic(newval, "forge"))
+ {
+ /*
+ * We have a specific (fixed) referer we want to send.
+ */
+ if ((0 != strncmpic(newval, "http://", 7)) && (0 != strncmpic(newval, "https://", 8)))
+ {
+ log_error(LOG_LEVEL_HEADER, "Parameter: +referrer{%s} is a bad idea, but I don't care.", newval);
+ }
+ *header = strdup("Referer: ");
+ string_append(header, newval);
+ log_error(LOG_LEVEL_HEADER, "Referer overwritten with: %s", *header);
+
+ 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
+ */
+
+ *header = strdup("Referer: http://");
+ string_append(header, csp->http->hostport);
+ string_append(header, "/");
+ log_error(LOG_LEVEL_HEADER, "Referer forged to: %s", *header);
+
+ return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
+ }
+}
+
+/*********************************************************************
+ *
+ * Function : client_accept_language
+ *
+ * Description : Handle the "Accept-Language" 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_accept_language(struct client_state *csp, char **header)
+{
+ const char *newval;
+
+ /*
+ * Are we messing with the Accept-Language?
+ */
+ if ((csp->action->flags & ACTION_HIDE_ACCEPT_LANGUAGE) == 0)
+ {
+ /*I don't think so*/
+ return JB_ERR_OK;
+ }
+
+ newval = csp->action->string[ACTION_STRING_LANGUAGE];
+
+ if ((newval == NULL) || (0 == strcmpic(newval, "block")) )
+ {
+ /*
+ * Blocking Accept-Language header
+ */
+ log_error(LOG_LEVEL_HEADER, "Crunching Accept-Language!");
+ freez(*header);
+ return JB_ERR_OK;
+ }
+ else
+ {
+ /*
+ * Replacing Accept-Language header
+ */
+ freez(*header);
+ *header = strdup("Accept-Language: ");
+ string_append(header, newval);
+
+ if (*header == NULL)
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "Insufficent memory. Accept-Language header crunched without replacement.");
+ }
+ else
+ {
+ log_error(LOG_LEVEL_HEADER,
+ "Accept-Language header crunched and replaced with: %s", *header);
+ }
+ }
+ return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
+}
+
+/*********************************************************************
+ *
+ * Function : crunch_client_header
+ *
+ * Description : Crunch client header if it matches a string supplied by the
+ * user. 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 and always succeeds
+ *
+ *********************************************************************/
+jb_err crunch_client_header(struct client_state *csp, char **header)
+{
+ const char *crunch_pattern;
+ /*Is there a header to crunch*/
+
+ if ((csp->action->flags & ACTION_CRUNCH_CLIENT_HEADER))
+ {
+ crunch_pattern = csp->action->string[ACTION_STRING_CLIENT_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);
+ freez(*header);
+ }
+ }
+ return 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;
+ }
+
+ freez(*header);
+ *header = strdup("User-Agent: ");
+ string_append(header, newval);
+
+ log_error(LOG_LEVEL_HEADER, "Modified: %s", *header);
+
+ 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, "crunched User-Agent!");
+ 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, "crunched From!");
+ 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, "Crunched outgoing cookie -- yum!");
+ }
+
+ /*
+ * 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, "crunched x-forwarded-for!");
+ }
+
+ return JB_ERR_OK;
+}
+
+
+/*********************************************************************
+ *
+ * Function : client_max_forwards
+ *
+ * Description : If the HTTP method is OPTIONS or TRACE, subtract one
+ * from the value of the Max-Forwards header field.
+ *
+ * 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_max_forwards(struct client_state *csp, char **header)
+{
+ int max_forwards;
+
+ if ((0 == strcmpic(csp->http->gpc, "trace")) ||
+ (0 == strcmpic(csp->http->gpc, "options")))
+ {
+ assert(*(*header+12) == ':');
+ if (1 == sscanf(*header+12, ": %u", &max_forwards))
+ {
+ if (max_forwards > 0)
+ {
+ snprintf(*header, strlen(*header)+1, "Max-Forwards: %u", --max_forwards);
+ log_error(LOG_LEVEL_HEADER, "Max-Forwards value for %s request reduced to %u.",
+ csp->http->gpc, max_forwards);
+ }
+ else if (max_forwards < 0)
+ {
+ log_error(LOG_LEVEL_ERROR, "Crunching invalid header: %s", *header);
+ freez(*header);
+ }
+ else
+ {
+ /*
+ * Not supposed to be reached. direct_response() which
+ * was already called earlier in chat() should have
+ * intercepted the request.
+ */
+ log_error(LOG_LEVEL_ERROR,
+ "Non-intercepted %s request with Max-Forwards zero!", csp->http->gpc);
+ assert(max_forwards != 0);
+ }
+ }
+ else
+ {
+ log_error(LOG_LEVEL_ERROR, "Crunching invalid header: %s", *header);
+ freez(*header);
+ }
+ }
+
+ return JB_ERR_OK;
+}
+
+
+/*********************************************************************
+ *
+ * Function : client_host
+ *
+ * Description : If the request URI did not contain host and
+ * port information, parse and evaluate the Host
+ * header field.
+ *
+ * Also, kill ill-formed HOST: headers as sent by
+ * Apple's iTunes software when used with a proxy.
+ *
+ * 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_host(struct client_state *csp, char **header)
+{
+ char *p, *q;
+
+ /*
+ * If the header field name is all upper-case, chances are that it's
+ * an ill-formed one from iTunes. BTW, killing innocent headers here is
+ * not a problem -- they are regenerated later.
+ */
+ if ((*header)[1] == 'O')
+ {
+ log_error(LOG_LEVEL_HEADER, "Killed all-caps Host header line: %s", *header);
+ freez(*header);
+ return JB_ERR_OK;
+ }
+
+ if (!csp->http->hostport || (*csp->http->hostport == '*') ||
+ *csp->http->hostport == ' ' || *csp->http->hostport == '\0')
+ {
+
+ if (NULL == (p = strdup((*header)+6)))
+ {
+ return JB_ERR_MEMORY;
+ }
+ chomp(p);
+ if (NULL == (q = strdup(p)))
+ {
+ freez(p);
+ return JB_ERR_MEMORY;
+ }
+
+ freez(csp->http->hostport);
+ csp->http->hostport = p;
+ freez(csp->http->host);
+ csp->http->host = q;
+ q = strchr(csp->http->host, ':');
+ if (q != NULL)
+ {
+ /* Terminate hostname and evaluate port string */
+ *q++ = '\0';
+ csp->http->port = atoi(q);
+ }
+ else
+ {
+ csp->http->port = csp->http->ssl ? 443 : 80;
+ }
+
+ log_error(LOG_LEVEL_HEADER, "New host and port from Host field: %s = %s:%d",
+ csp->http->hostport, csp->http->host, csp->http->port);
+ }
+
+ return JB_ERR_OK;
+}
+
+/*********************************************************************
+ *
+ * Function : client_if_modified_since