-const char cgi_rcs[] = "$Id: cgi.c,v 1.18 2001/08/05 16:06:20 jongfoster Exp $";
+const char cgi_rcs[] = "$Id: cgi.c,v 1.24 2001/09/16 11:38:01 jongfoster Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/cgi.c,v $
*
* Revisions :
* $Log: cgi.c,v $
+ * Revision 1.24 2001/09/16 11:38:01 jongfoster
+ * Splitting fill_template() into 2 functions:
+ * template_load() loads the file
+ * template_fill() performs the PCRS regexps.
+ * This is because the CGI edit interface has a "table row"
+ * template which is used many times in the page - this
+ * change means it's only loaded from disk once.
+ *
+ * Revision 1.23 2001/09/16 11:16:05 jongfoster
+ * Better error handling in dispatch_cgi() and parse_cgi_parameters()
+ *
+ * Revision 1.22 2001/09/16 11:00:10 jongfoster
+ * New function alloc_http_response, for symmetry with free_http_response
+ *
+ * Revision 1.21 2001/09/13 23:53:03 jongfoster
+ * Support for both static and dynamically generated CGI pages.
+ * Correctly setting Last-Modified: and Expires: HTTP headers.
+ *
+ * Revision 1.20 2001/09/13 23:40:36 jongfoster
+ * (Cosmetic only) Indentation correction
+ *
+ * Revision 1.19 2001/09/13 23:31:25 jongfoster
+ * Moving image data to cgi.c rather than cgi.h.
+ *
* Revision 1.18 2001/08/05 16:06:20 jongfoster
* Modifiying "struct map" so that there are now separate header and
* "map_entry" structures. This means that functions which modify a
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
+#include <assert.h>
#ifdef _WIN32
#define snprintf _snprintf
const char cgi_h_rcs[] = CGI_H_VERSION;
const struct cgi_dispatcher cgi_dispatcher[] = {
+ { "robots.txt",
+ 10, cgi_robots_txt,
+ "HIDE Sends a robots.txt file to tell robots to go away." },
{ "show-status",
11, cgi_show_status,
"Show information about the current configuration" },
*/
/* Get mem for response or fail*/
- if (NULL == ( rsp = zalloc(sizeof(*rsp))))
+ if (NULL == (rsp = alloc_http_response()))
{
return NULL;
}
{
if (strncmp(argstring, d->name, d->name_length) == 0)
{
- param_list = parse_cgi_parameters(argstring + d->name_length);
+ if (NULL == (param_list =
+ parse_cgi_parameters(argstring + d->name_length)))
+ {
+ free_map(param_list);
+ free_http_response(rsp);
+ return(NULL);
+ }
if ((d->handler)(csp, rsp, param_list))
{
- freez(rsp);
+ free_map(param_list);
+ free_http_response(rsp);
+ return(NULL);
}
free_map(param_list);
}
/* Can't get here, since cgi_default will match all requests */
- freez(rsp);
+ free_http_response(rsp);
return(NULL);
-
}
* Parameters :
* 1 : string = string to be parsed
*
- * Returns : pointer to param list
+ * Returns : pointer to param list, or NULL if out of memory.
*
*********************************************************************/
struct map *parse_cgi_parameters(char *argstring)
char *tmp, *p;
char *vector[BUFFER_SIZE];
int pairs, i;
- struct map *cgi_params = new_map();
+ struct map *cgi_params;
+
+ if (NULL == (cgi_params = new_map()))
+ {
+ return NULL;
+ }
if(*argstring == '?')
{
argstring++;
}
- tmp = strdup(argstring);
+ if (NULL == (tmp = strdup(argstring)))
+ {
+ free_map(cgi_params);
+ return NULL;
+ }
pairs = ssplit(tmp, "&", vector, SZ(vector), 1, 1);
map(exports, "cgi-parameters", 1, "", 1);
}
- rsp->body = fill_template(csp, "default", exports);
+ rsp->body = template_load(csp, "default");
+ template_fill(&rsp->body, exports);
free_map(exports);
return(0);
}
enlist(rsp->headers, "Content-Type: image/gif");
+ rsp->is_static = 1;
return(0);
map(exports, "sourceversions", 1, show_rcs(), 0);
- rsp->body = fill_template(csp, "show-version", exports);
+ rsp->body = template_load(csp, "show-version");
+ template_fill(&rsp->body, exports);
free_map(exports);
return(0);
char * p;
const char * filename = NULL;
char * file_description = NULL;
+#ifdef FEATURE_STATISTICS
+ float perc_rej; /* Percentage of http requests rejected */
+ int local_urls_read;
+ int local_urls_rejected;
+#endif /* ndef FEATURE_STATISTICS */
struct map * exports = default_exports(csp, "show-status");
fclose(fp);
map(exports, "contents", 1, s, 0);
}
- rsp->body = fill_template(csp, "show-status-file", exports);
+ rsp->body = template_load(csp, "show-status-file");
+ template_fill(&rsp->body, exports);
free_map(exports);
return(0);
show_defines(exports);
#ifdef FEATURE_STATISTICS
- add_stats(exports);
+ local_urls_read = urls_read;
+ local_urls_rejected = urls_rejected;
+
+ /*
+ * Need to alter the stats not to include the fetch of this
+ * page.
+ *
+ * Can't do following thread safely! doh!
+ *
+ * urls_read--;
+ * urls_rejected--; * This will be incremented subsequently *
+ */
+
+ if (local_urls_read == 0)
+ {
+ map_block_killer(exports, "have-stats");
+ }
+ else
+ {
+ map_block_killer(exports, "have-no-stats");
+
+ perc_rej = (float)local_urls_rejected * 100.0F /
+ (float)local_urls_read;
+
+ sprintf(buf, "%d", local_urls_read);
+ map(exports, "requests-received", 1, buf, 1);
+
+ sprintf(buf, "%d", local_urls_rejected);
+ map(exports, "requests-blocked", 1, buf, 1);
+
+ sprintf(buf, "%6.2f", perc_rej);
+ map(exports, "percent-blocked", 1, buf, 1);
+ }
+
#else /* ndef FEATURE_STATISTICS */
map_block_killer(exports, "statistics");
#endif /* ndef FEATURE_STATISTICS */
map_block_killer(exports, "trust-support");
#endif /* ndef FEATURE_TRUST */
- rsp->body = fill_template(csp, "show-status", exports);
+ rsp->body = template_load(csp, "show-status");
+ template_fill(&rsp->body, exports);
free_map(exports);
return(0);
}
- /*********************************************************************
+/*********************************************************************
*
* Function : cgi_show_url_info
*
freez(url_param);
free_current_action(action);
- rsp->body = fill_template(csp, "show-url-info", exports);
+ rsp->body = template_load(csp, "show-url-info");
+ template_fill(&rsp->body, exports);
free_map(exports);
return 0;
freez(path);
free_current_action(action);
- rsp->body = fill_template(csp, "show-url-info", exports);
+ rsp->body = template_load(csp, "show-url-info");
+ template_fill(&rsp->body, exports);
free_map(exports);
return 0;
free_current_action(action);
}
- rsp->body = fill_template(csp, "show-url-info", exports);
+ rsp->body = template_load(csp, "show-url-info");
+ template_fill(&rsp->body, exports);
free_map(exports);
return 0;
struct http_response *rsp;
struct map * exports = default_exports(csp, NULL);
- if (NULL == ( rsp = (struct http_response *)zalloc(sizeof(*rsp))))
+ if (NULL == (rsp = alloc_http_response()))
{
return NULL;
- }
+ }
- map(exports, "host-html", 1, html_encode(csp->http->host), 0);
- map(exports, "hostport", 1, csp->http->hostport, 1);
- map(exports, "hostport-html", 1, html_encode(csp->http->hostport), 0);
- map(exports, "path", 1, csp->http->path, 1);
- map(exports, "path-html", 1, html_encode(csp->http->path), 0);
- map(exports, "error", 1, safe_strerror(err), 0);
- map(exports, "host-ip", 1, csp->http->host_ip_addr_str, 1);
+ map(exports, "host-html", 1, html_encode(csp->http->host), 0);
+ map(exports, "hostport", 1, csp->http->hostport, 1);
+ map(exports, "hostport-html", 1, html_encode(csp->http->hostport), 0);
+ map(exports, "path", 1, csp->http->path, 1);
+ map(exports, "path-html", 1, html_encode(csp->http->path), 0);
+ map(exports, "error", 1, safe_strerror(err), 0);
+ map(exports, "host-ip", 1, csp->http->host_ip_addr_str, 1);
- rsp->body = fill_template(csp, templatename, exports);
- free_map(exports);
-
- if (!strcmp(templatename, "no-such-domain"))
- {
- rsp->status = strdup("404 No such domain");
- }
- else if (!strcmp(templatename, "connect-failed"))
- {
- rsp->status = strdup("503 Connect failed");
- }
+ rsp->body = template_load(csp, templatename);
+ template_fill(&rsp->body, exports);
+ free_map(exports);
+
+ if (!strcmp(templatename, "no-such-domain"))
+ {
+ rsp->status = strdup("404 No such domain");
+ }
+ else if (!strcmp(templatename, "connect-failed"))
+ {
+ rsp->status = strdup("503 Connect failed");
+ }
+
+ return(finish_http_response(rsp));
+}
+
+
+/*********************************************************************
+ *
+ * Function : get_http_time
+ *
+ * Description : Get the time in a format suitable for use in a
+ * HTTP header - e.g.:
+ * "Sun, 06 Nov 1994 08:49:37 GMT"
+ *
+ * Parameters :
+ * 1 : time_offset = Time returned will be current time
+ * plus this number of seconds.
+ * 2 : buf = Destination for result. Must be long enough
+ * to hold 29 characters plus a trailing zero.
+ *
+ * Returns : N/A
+ *
+ *********************************************************************/
+static void get_http_time(int time_offset, char * buf)
+{
+ static const char day_names[7][4] =
+ { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+ static const char month_names[12][4] =
+ { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+ struct tm *t;
+ time_t current_time;
+
+ assert(buf);
+
+ time(¤t_time); /* get current time */
+
+/* FIXME: is this needed? time() can't fail on Win32. What about Linux?
+ if(current_time <= 0)
+ {
+ return NULL;
+ }
+*/
- return(finish_http_response(rsp));
+ current_time += time_offset;
+
+ /* get and save the gmt */
+ t = gmtime(¤t_time);
+
+ /* Format: "Sun, 06 Nov 1994 08:49:37 GMT" */
+ snprintf(buf, 30,
+ "%s, %02d %s %4d %02d:%02d:%02d GMT",
+ day_names[t->tm_wday],
+ t->tm_mday,
+ month_names[t->tm_mon],
+ t->tm_year + 1900,
+ t->tm_hour,
+ t->tm_min,
+ t->tm_sec
+ );
+ buf[32] = '\0';
}
enlist(rsp->headers, buf);
/*
- * Fill in the default headers FIXME: Are these correct? sequence OK? check rfc!
- * FIXME: Should have:
- * "JunkBuster" GIF: Last-Modified: any *fixed* date in the past (as now).
- * Expires: 5 minutes after the time when reply sent
- * CGI, "blocked", & all other requests:
- * Last-Modified: Time when reply sent
- * Expires: Time when reply sent
- * "Cache-Control: no-cache"
- *
+ * Fill in the default headers:
+ *
+ * Content-Type: default to text/html if not already specified.
+ * Date: set to current date/time.
+ * Last-Modified: set to date/time the page was last changed.
+ * Expires: set to date/time page next needs reloading.
+ * Cache-Control: set to "no-cache" if applicable.
+ *
* See http://www.w3.org/Protocols/rfc2068/rfc2068
*/
- enlist_unique(rsp->headers, "Last-Modified: Thu Jul 31, 1997 07:42:22 pm GMT", 14);
- enlist_unique(rsp->headers, "Expires: Thu Jul 31, 1997 07:42:22 pm GMT", 8);
enlist_unique(rsp->headers, "Content-Type: text/html", 13);
+ if (rsp->is_static)
+ {
+ /*
+ * Set Expires to about 10 min into the future so it'll get reloaded
+ * occasionally, e.g. if IJB gets upgraded.
+ */
+
+ get_http_time(0, buf);
+ enlist_unique_header(rsp->headers, "Date", buf);
+
+ /* Some date in the past. */
+ enlist_unique_header(rsp->headers, "Last-Modified", "Sat, 17 Jun 2000 12:00:00 GMT");
+
+ get_http_time(10 * 60, buf); /* 10 * 60sec = 10 minutes */
+ enlist_unique_header(rsp->headers, "Expires", buf);
+ }
+ else
+ {
+ /*
+ * Compliant browsers should not cache this due to the "Cache-Control"
+ * setting. However, to be certain, we also set both "Last-Modified"
+ * and "Expires" to the current time.
+ */
+ enlist_unique_header(rsp->headers, "Cache-Control", "no-cache");
+ get_http_time(0, buf);
+ enlist_unique_header(rsp->headers, "Date", buf);
+ enlist_unique_header(rsp->headers, "Last-Modified", buf);
+ enlist_unique_header(rsp->headers, "Expires", buf);
+ }
+
/*
* Write the head
}
+/*********************************************************************
+ *
+ * Function : alloc_http_response
+ *
+ * Description : Allocates a new http_response structure.
+ *
+ * Parameters : N/A
+ *
+ * Returns : pointer to a new http_response, or NULL.
+ *
+ *********************************************************************/
+struct http_response * alloc_http_response(void)
+{
+ return (struct http_response *) zalloc(sizeof(struct http_response));
+}
+
+
/*********************************************************************
*
* Function : free_http_response
*********************************************************************/
void free_http_response(struct http_response *rsp)
{
- if(rsp)
+ if (rsp)
{
freez(rsp->status);
freez(rsp->head);
freez(rsp->body);
destroy_list(rsp->headers);
- freez(rsp);
+ free(rsp);
}
}
* Parameters :
* 1 : csp = Current client state (buffers, headers, etc...)
* 3 : template = name of the HTML template to be used
- * 2 : exports = map with fill in symbol -> name pairs
*
- * Returns : char * with filled out form, or NULL if failiure
+ * Returns : char * with loaded template, or NULL if failure
*
*********************************************************************/
-char *fill_template(struct client_state *csp, const char *templatename, struct map *exports)
+char *template_load(struct client_state *csp, const char *templatename)
{
- struct map_entry *m;
- pcrs_job *job;
char buf[BUFFER_SIZE];
- char *tmp_out_buffer;
char *file_buffer = NULL;
- int size;
- int error;
FILE *fp;
-
/*
* Open template file or fail
*/
while (fgets(buf, BUFFER_SIZE, fp))
{
/* skip lines starting with '#' */
- if(*buf == '#') continue;
+ if(*buf == '#')
+ {
+ continue;
+ }
file_buffer = strsav(file_buffer, buf);
}
fclose(fp);
+ return(file_buffer);
+}
- /*
- * Execute the jobs
- */
+
+/*********************************************************************
+ *
+ * Function : fill_template
+ *
+ * Description : CGI support function that loads a given HTML
+ * template from the confdir, and fills it in
+ * by replacing @name@ with value using pcrs,
+ * for each item in the output map.
+ *
+ * Parameters :
+ * 1 : template_ptr = IN: Template to be filled out.
+ * Will be free()d.
+ * OUT: Filled out template.
+ * Caller must free().
+ * 2 : exports = map with fill in symbol -> name pairs
+ *
+ * Returns : N/A
+ *
+ *********************************************************************/
+void template_fill(char ** template_ptr, struct map *exports)
+{
+ struct map_entry *m;
+ pcrs_job *job;
+ char buf[BUFFER_SIZE];
+ char *tmp_out_buffer;
+ char *file_buffer;
+ int size;
+ int error;
+ const char * flags;
+
+ assert(template_ptr);
+ assert(*template_ptr);
+ assert(exports);
+
+ file_buffer = *template_ptr;
size = strlen(file_buffer) + 1;
/*
*/
for (m = exports->first; m != NULL; m = m->next)
{
- /* Enclose name in @@ */
- snprintf(buf, BUFFER_SIZE, "@%s@", m->name);
+ if (*m->name == '$')
+ {
+ /*
+ * First character of name is '$', so remove this flag
+ * character and allow backreferences ($1 etc) in the
+ * "replace with" text.
+ */
+ snprintf(buf, BUFFER_SIZE, "%s", m->name + 1);
+ flags = "sigU";
+ }
+ else
+ {
+ /*
+ * Treat the "replace with" text as a literal string -
+ * no quoting needed, no backreferences allowed.
+ * ("Trivial" ['T'] flag).
+ */
+ flags = "sigTU";
+
+ /* Enclose name in @@ */
+ snprintf(buf, BUFFER_SIZE, "@%s@", m->name);
+ }
+
+
+ log_error(LOG_LEVEL_CGI, "Substituting: s/%s/%s/%s", buf, m->value, flags);
/* Make and run job. */
- job = pcrs_compile(buf, m->value, "sigTU", &error);
+ job = pcrs_compile(buf, m->value, flags, &error);
if (job == NULL)
{
log_error(LOG_LEVEL_ERROR, "Error compiling template fill job %s: %d", m->name, error);
}
}
-
/*
* Return
*/
- return(file_buffer);
-
+ *template_ptr = file_buffer;
}
}
-#ifdef FEATURE_STATISTICS
/*********************************************************************
*
- * Function : add_stats
+ * Function : cgi_robots_txt
*
- * Description : Add the blocking statistics to a given map.
+ * Description : CGI function to return "/robots.txt".
*
* Parameters :
- * 1 : exports = map to write to.
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ * 2 : rsp = http_response data structure for output
+ * 3 : parameters = map of cgi parameters
*
- * Returns : pointer to extended map
+ * CGI Parameters : None
+ *
+ * Returns : 0
*
*********************************************************************/
-struct map *add_stats(struct map *exports)
+int cgi_robots_txt(struct client_state *csp, struct http_response *rsp,
+ struct map *parameters)
{
- float perc_rej; /* Percentage of http requests rejected */
- char buf[1000];
- int local_urls_read = urls_read;
- int local_urls_rejected = urls_rejected;
-
- /*
- * Need to alter the stats not to include the fetch of this
- * page.
- *
- * Can't do following thread safely! doh!
- *
- * urls_read--;
- * urls_rejected--; * This will be incremented subsequently *
- */
-
- if (local_urls_read == 0)
- {
- map_block_killer(exports, "have-stats");
- }
- else
- {
- map_block_killer(exports, "have-no-stats");
+ char buf[100];
- perc_rej = (float)local_urls_rejected * 100.0F /
- (float)local_urls_read;
-
- sprintf(buf, "%d", local_urls_read);
- map(exports, "requests-received", 1, buf, 1);
+ rsp->body = strdup(
+ "# This is the Internet Junkbuster control interface.\n"
+ "# It isn't very useful to index it, and you're likely to break stuff.\n"
+ "# So go away!\n"
+ "\n"
+ "User-agent: *\n"
+ "Disallow: /\n"
+ "\n");
- sprintf(buf, "%d", local_urls_rejected);
- map(exports, "requests-blocked", 1, buf, 1);
+ enlist_unique(rsp->headers, "Content-Type: text/plain", 13);
- sprintf(buf, "%6.2f", perc_rej);
- map(exports, "percent-blocked", 1, buf, 1);
- }
+ rsp->is_static = 1;
- return(exports);
+ get_http_time(7 * 24 * 60 * 60, buf); /* 7 days into future */
+ enlist_unique_header(rsp->headers, "Expires", buf);
+ return 0;
}
-#endif /* def FEATURE_STATISTICS */
+
/*
Local Variables: