+/*********************************************************************
+ *
+ * Function : client_if_modified_since
+ *
+ * Description : Remove or modify the If-Modified-Since header.
+ *
+ * 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_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;
+ long int rtime;
+ long int hours, minutes, seconds;
+ int negative = 0;
+ 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
+ * end with the transmission of the whole document and there is
+ * no need to expose the bogus If-Modified-Since header.
+ */
+ log_error(LOG_LEVEL_HEADER, "Crunching useless If-Modified-Since header.");
+ freez(*header);
+ }
+ else if (csp->action->flags & ACTION_HIDE_IF_MODIFIED_SINCE)
+ {
+ newval = csp->action->string[ACTION_STRING_IF_MODIFIED_SINCE];
+
+ if ((0 == strcmpic(newval, "block")))
+ {
+ log_error(LOG_LEVEL_HEADER, "Crunching %s", *header);
+ freez(*header);
+ }
+ else /* add random value */
+ {
+ if ((timeptr = parse_header_time(*header, &tm)) == NULL)
+ {
+ log_error(LOG_LEVEL_HEADER, "Couldn't parse: %s (crunching!)", *header);
+ freez(*header);
+ }
+ else
+ {
+ rtime = strtol(newval, &endptr, 0);
+ if(rtime)
+ {
+ log_error(LOG_LEVEL_HEADER, "Randomizing: %s (random range: %d minut%s)",
+ *header, rtime, (rtime == 1 || rtime == -1) ? "e": "es");
+ if(rtime < 0)
+ {
+ rtime *= -1;
+ negative = 1;
+ }
+ rtime *= 60;
+ rtime = pick_from_range(rtime);
+ }
+ else
+ {
+ log_error(LOG_LEVEL_ERROR, "Random range is 0. Assuming time transformation test.",
+ *header);
+ }
+ tm += rtime * (negative ? -1 : 1);
+#ifdef HAVE_GMTIME_R
+ timeptr = gmtime_r(&tm, &gmt);
+#elif FEATURE_PTHREAD
+ 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);
+ *header = strdup("If-Modified-Since: ");
+ string_append(header, newheader);
+
+ if (*header == NULL)
+ {
+ 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. */
+ {
+ 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",
+ minutes, (minutes == 1) ? "e" : "es", seconds, (seconds == 1) ? ")" : "s)");
+ }
+ }
+ }
+ }
+
+ return JB_ERR_OK;
+}
+
+/*********************************************************************
+ *
+ * Function : client_if_none_match
+ *
+ * Description : Remove the If-None-Match header.
+ *
+ * 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_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);
+ }
+
+ return JB_ERR_OK;
+}
+
+/*********************************************************************
+ *
+ * Function : client_x_filter
+ *
+ * Description : Disables filtering if the client set "X-Filter: No".
+ * 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
+ *
+ *********************************************************************/
+jb_err client_x_filter(struct client_state *csp, char **header)
+{
+ if ( 0 == strcmpic(*header, "X-Filter: No"))
+ {
+ if (!(csp->config->feature_flags & RUNTIME_FEATURE_HTTP_TOGGLE))
+ {
+ log_error(LOG_LEVEL_INFO, "Ignored the client's request to fetch without filtering.");
+ }
+ 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;
+ csp->action->flags &= ~ACTION_FILTER_SERVER_HEADERS;
+ csp->action->flags &= ~ACTION_FILTER_CLIENT_HEADERS;
+ log_error(LOG_LEVEL_HEADER, "Accepted the client's request to fetch without filtering.");
+ }
+ log_error(LOG_LEVEL_HEADER, "Crunching %s", *header);
+ freez(*header);
+ }
+ }
+ return JB_ERR_OK;
+}