/* * Copyright 2001-2004 Brandon Long * All Rights Reserved. * * ClearSilver Templating System * * This code is made available under the terms of the ClearSilver License. * http://www.clearsilver.net/license.hdf * */ #ifndef __CGI_H_ #define __CGI_H_ 1 #include <stdarg.h> #include "util/neo_err.h" #include "util/neo_hdf.h" #include "cs/cs.h" __BEGIN_DECLS extern NERR_TYPE CGIFinished; extern NERR_TYPE CGIUploadCancelled; extern NERR_TYPE CGIParseNotHandled; /* HACK: Set this value if you want to treat empty CGI Query variables as * non-existant. */ extern int IgnoreEmptyFormVars; typedef struct _cgi CGI; typedef int (*UPLOAD_CB)(CGI *, int nread, int expected); typedef NEOERR* (*CGI_PARSE_CB)(CGI *, char *method, char *ctype, void *rock); struct _cgi_parse_cb { char *method; int any_method; char *ctype; int any_ctype; void *rock; CGI_PARSE_CB parse_cb; struct _cgi_parse_cb *next; }; struct _cgi { /* Only public parts of this structure */ void *data; /* you can store your own information here */ HDF *hdf; /* the HDF dataset associated with this CGI */ BOOL ignore_empty_form_vars; UPLOAD_CB upload_cb; int data_expected; int data_read; struct _cgi_parse_cb *parse_callbacks; /* For line oriented reading of form-data input. Used during cgi_init * only */ char *buf; int buflen; int readlen; BOOL found_nl; BOOL unget; char *last_start; int last_length; int nl; /* this is a list of filepointers pointing at files that were uploaded */ /* Use cgi_filehandle to access these */ ULIST *files; /* By default, cgi_parse unlinks uploaded files as it opens them. */ /* If Config.Upload.Unlink is set to 0, the files are not unlinked */ /* and there names are stored in this list. */ /* Use Query.*.FileName to access these */ ULIST *filenames; /* keep track of the time between cgi_init and cgi_render */ double time_start; double time_end; }; /* * Function: cgi_init - Initialize ClearSilver CGI environment * Description: cgi_init initializes the ClearSilver CGI environment, * including creating the HDF data set. It will then import * the standard CGI environment variables into that dataset, * will parse the QUERY_STRING into the data set, and parse * the HTTP_COOKIE into the data set. Note that if the * var xdisplay is in the form data, cgi_init will attempt * to validate the value and launch the configured debugger * on the CGI program. These variables have to be * specified in the hdf_file pointed to by hdf_file. The * default settings do not allow debugger launching for * security reasons. * Input: cgi - a pointer to a CGI pointer * hdf_file - the path to an HDF data set file that will also be * loaded into the dataset. This will likely have to * a be a full path, as the HDF search paths are not * yet set up. Certain things, like * Output: cgi - an allocated CGI struct, including * Return: NERR_PARSE - parse error in CGI input * NERR_NOMEM - unable to allocate memory * NERR_NOT_FOUND - hdf_file doesn't exist */ NEOERR *cgi_init (CGI **cgi, HDF *hdf); /* * Function: cgi_parse - Parse incoming CGI data * Description: We split cgi_init into two sections, one that parses * just the basics, and the second is cgi_parse. cgi_parse * is responsible for parsing the entity body of the HTTP * request. This payload is typically only sent (expected) * on POST/PUT requests, but generally this is called on * all incoming requests. This function walks the list of * registered parse callbacks (see cgi_register_parse_cb), * and if none of those matches or handles the request, it * falls back to the builtin handlers: * POST w/ application/x-www-form-urlencoded * POST w/ application/form-data * PUT w/ any content type * In general, if there is no Content-Length, then * cgi_parse ignores the payload and doesn't raise an * error. * Input: cgi - a pointer to a CGI pointer * Output: Either data populated into files and cgi->hdf, or whatever * other side effects of your own registered callbacks. * Return: NERR_PARSE - parse error in CGI input * NERR_NOMEM - unable to allocate memory * NERR_NOT_FOUND - hdf_file doesn't exist * NERR_IO - error reading HDF file or reading CGI stdin, or * writing data on multipart/form-data file submission * Anything else you raise. */ NEOERR *cgi_parse (CGI *cgi); /* * Function: cgi_register_parse_cb - Register a parse callback * Description: The ClearSilver CGI Kit has built-in functionality to handle * the following methods: * GET -> doesn't have any data except query string, which * is processed for all methods * POST w/ application/x-www-form-urlencoded * POST w/ multipart/form-data * processed as RFC2388 data into files and HDF (see * cgi_filehandle()) * PUT (any type) * The entire data chunk is stored as a file, with meta * data in HDF (similar to single files in RFC2388). * The data is accessible via cgi_filehandle with NULL * for name. * To handle other methods/content types, you have to * register your own parse function. This isn't necessary * if you aren't expecting any data, and technically HTTP * only allows data on PUT/POST requests (and presumably * user defined methods). In particular, if you want to * implement XML-RPC or SOAP, you'll have to register a * callback here to grab the XML data chunk. Usually * you'll want to register POST w/ application/xml or POST * w/ text/xml (you either need to register both or * register POST w/ * and check the ctype yourself, * remember to nerr_raise(CGIParseNotHandled) if you aren't * handling the POST). * In general, your callback should: * Find out how much data is available: * l = hdf_get_value (cgi->hdf, "CGI.ContentLength", NULL); * len = atoi(l); * And read/handle all of the data using cgiwrap_read. * See the builtin handlers for how this is done. Note * that cgiwrap_read is not guarunteed to return all of * the data you request (just like fread(3)) since it * might be reading of a socket. Sorry. * You should be careful when reading the data to watch * for short reads (ie, end of file) and cases where the * client sends you data ad infinitum. * Input: cgi - a CGI struct * method - the HTTP method you want to handle, or * for all * ctype - the HTTP Content-Type you want to handle, or * for all * rock - opaque data that we'll pass to your call back * Output: None * Return: CGIParseNotHandled if your callback doesn't want to handle * this. This causes cgi_parse to continue walking the list of * callbacks. * */ NEOERR *cgi_register_parse_cb(CGI *cgi, const char *method, const char *ctype, void *rock, CGI_PARSE_CB parse_cb); /* * Function: cgi_destroy - deallocate the data associated with a CGI * Description: cgi_destroy will destroy all the data associated with a * CGI, which mostly means the associated HDF and removal * of any files that were uploaded via multipart/form-data. * (Note that even in the event of a crash, these files * will be deleted, as they were unlinked on creation and * only exist because of the open file pointer) * Input: cgi - a pointer to a pointer to a CGI struct * Output: cgi - NULL on output * Return: None */ void cgi_destroy (CGI **cgi); /* * Function: cgi_cs_init - initialize CS parser with the CGI defaults * Description: cgi_cs_init initializes a CS parser with the CGI HDF * context, and registers the standard CGI filters * Input: cgi - a pointer a CGI struct allocated with cgi_init * cs - a pointer to a CS struct pointer * Output: cs - the allocated/initialized CS struct * Return: NERR_NOMEM - no memory was available to render the template */ NEOERR *cgi_cs_init(CGI *cgi, CSPARSE **cs); /* * Function: cgi_display - render and display the CGI output to the user * Description: cgi_display will render the CS template pointed to by * cs_file using the CGI's HDF data set, and send the * output to the user. Note that the output is actually * rendered into memory first. * Input: cgi - a pointer a CGI struct allocated with cgi_init * cs_file - a ClearSilver template file * Output: None * Return: NERR_IO - an IO error occured during output * NERR_NOMEM - no memory was available to render the template */ NEOERR *cgi_display (CGI *cgi, const char *cs_file); /* * Function: cgi_output - display the CGI output to the user * Description: Normally, this is called by cgi_display, but some * people wanted it external so they could call it * directly. * Input: cgi - a pointer a CGI struct allocated with cgi_init * output - the data to send to output from the CGI * Output: None * Return: NERR_IO - an IO error occured during output * NERR_NOMEM - no memory was available to render the template */ NEOERR *cgi_output (CGI *cgi, STRING *output); /* * Function: cgi_filehandle - return a file pointer to an uploaded file * Description: cgi_filehandle will return the stdio FILE pointer * associated with a file that was uploaded using * multipart/form-data. The FILE pointer is positioned at * the start of the file when first available. * Input: cgi - a pointer to a CGI struct allocated with cgi_init * form_name - the form name that the file was uploaded as * (not the filename) (if NULL, we're asking for the * file handle for the PUT upload) * Output: None * Return: A stdio FILE pointer, or NULL if an error occurs (usually * indicates that the form_name wasn't found, but might indicate * a problem with the HDF dataset) */ FILE *cgi_filehandle (CGI *cgi, const char *form_name); /* * Function: cgi_neo_error - display a NEOERR call backtrace * Description: cgi_neo_error will output a 500 error containing the * NEOERR call backtrace. This function is likely to be * removed from future versions in favor of some sort of * user error mechanism. * Input: cgi - a pointer to a CGI struct * err - a NEOERR (see util/neo_err.h for details) * Output: None * Return: None */ void cgi_neo_error (CGI *cgi, NEOERR *err); /* * Function: cgi_error - display an error string to the user * Description: cgi_error will output a 500 error containing the * specified error message. This function is likely to be * removed from future versions in favor of a user error * mechanism. * Input: cgi - a pointer to a CGI struct * fmt - printf style format string and arguments * Output: None * Return: None */ void cgi_error (CGI *cgi, const char *fmt, ...) ATTRIBUTE_PRINTF(2,3); /* * Function: cgi_debug_init - initialize standalone debugging * Description: cgi_debug_init initializes a CGI program for standalone * debugging. By running a ClearSilver CGI program with a * filename on the command line as the first argument, the * CGI program will load that file of the form K=V as a set * of HTTP/CGI environment variables. This allows you to * run the program under a debugger in a reproducible * environment. * Input: argc/argv - the arguments from main * Output: None * Return: None */ void cgi_debug_init (int argc, char **argv); /* * Function: cgi_url_escape - url escape a string * Description: cgi_url_escape will do URL escaping on the passed in * string, and return a newly allocated string that is escaped. * Characters which are escaped include control characters, * %, ?, +, space, =, &, /, and " * Input: buf - a 0 terminated string * Output: esc - a newly allocated string * Return: NERR_NOMEM - no memory available to allocate the escaped string */ NEOERR *cgi_url_escape (const char *buf, char **esc); /* * Function: cgi_url_escape_more - url escape a string * Description: cgi_url_escape_more will do URL escaping on the passed in * string, and return a newly allocated string that is escaped. * Characters which are escaped include control characters, * %, ?, +, space, =, &, /, and " and any characters in * other * Input: buf - a 0 terminated string * other - a 0 terminated string of characters to escape * Output: esc - a newly allocated string * Return: NERR_NOMEM - no memory available to allocate the escaped string */ NEOERR *cgi_url_escape_more (const char *buf, char **esc, const char *other); /* * Function: cgi_url_validate - validate that url is of an allowed format * Description: cgi_url_validate will check that a URL starts with * one of the accepted safe schemes. * If not, it returns "#" as a safe substitute. * Currently accepted schemes are http, https, ftp and mailto. * It then html escapes the entire URL so that it is safe to * insert in an href attribute. * Input: buf - a 0 terminated string * Output: esc - a newly allocated string * Return: NERR_NOMEM - no memory available to allocate the escaped string */ NEOERR *cgi_url_validate (const char *buf, char **esc); /* * Function: cgi_url_unescape - unescape an url encoded string * Description: cgi_url_unescape will do URL unescaping on the passed in * string. This function modifies the string in place * This function will decode any %XX character, and will * decode + as space * Input: buf - a 0 terminated string * Return: pointer to same buf */ char *cgi_url_unescape (char *buf); /* * Function: cgi_redirect - send an HTTP 302 redirect response * Description: cgi_redirect will redirect the user to another page on * your site. This version takes only the path portion of * the URL. As with all printf style commands, you should * not call this with arbitrary input that may contain % * characters, if you are forwarding something directly, * use a format like cgi_redirect (cgi, "%s", buf) * Input: cgi - cgi struct * fmt - printf style format with args * Output: None * Return: None */ void cgi_redirect (CGI *cgi, const char *fmt, ...) ATTRIBUTE_PRINTF(2,3); /* * Function: cgi_redirect_uri - send an HTTP 302 redirect response * Description: cgi_redirect_uri will redirect the user to another page on * your site. This version takes the full URL, including * protocol/domain/port/path. * As with all printf style commands, you should * not call this with arbitrary input that may contain % * characters, if you are forwarding something directly, * use a format like cgi_redirect (cgi, "%s", buf) * Input: cgi - cgi struct * fmt - printf style format with args * Output: None * Return: None */ void cgi_redirect_uri (CGI *cgi, const char *fmt, ...) ATTRIBUTE_PRINTF(2,3); /* * Function: cgi_vredirect - send an HTTP 302 redirect response * Description: cgi_vredirect is mostly used internally, but can be used * if you need a varargs version of the function. * Input: cgi - cgi struct * uri - whether the URL is full (1) or path only (0) * fmt - printf format string * ap - stdarg va_list * Output: None * Return: None */ void cgi_vredirect (CGI *cgi, int uri, const char *fmt, va_list ap); /* * Function: cgi_cookie_authority - determine the cookie authority for a * domain * Description: cgi_cookie_authority will walk the CookieAuthority * portion of the CGI HDF data set, and return the matching * domain if it exists. The purpose of this is so that you * set domain specific cookies. For instance, you might * have * CookieAuthority.0 = neotonic.com * In which case, any webserver using a hostname ending in * neotonic.com will generate a cookie authority of * neotonic.com. * Input: cgi - a CGI struct * host - optional host to match against. If NULL, the function * will use the HTTP.Host HDF variable. * Output: None * Return: The authority domain, or NULL if none found. */ char *cgi_cookie_authority (CGI *cgi, const char *host); /* * Function: cgi_cookie_set - Set a browser Cookie * Description: cgi_cookie_set will issue a Set-Cookie header that * should cause a browser to return a cookie when required. * Note this function does no escaping of anything, you * have to take care of that first. * Input: cgi - a CGI struct * name - the name of the cookie * value - the value to set the cookie to. * path - optional path for which the cookie is valid. Default * is / * domain - optional domain for which the cookie is valid. You * can use cgi_cookie_authority to determine this * domain. Default is none, which is interpreted by * the browser as the sending domain only. * time_str - expiration time string in the following format * Wdy, DD-Mon-YYYY HH:MM:SS GMT. Only used if * persistent. Default is one year from time of call. * persistent - cookie will be stored by the browser between sessions * secure - cookie will only be sent over secure connections * Output: None * Return: NERR_IO */ NEOERR *cgi_cookie_set (CGI *cgi, const char *name, const char *value, const char *path, const char *domain, const char *time_str, int persistent, int secure); /* * Function: cgi_cookie_clear - clear browser cookie * Description: cgi_cookie_clear will send back a Set-Cookie string that * will attempt to stop a browser from continuing to send * back a cookie. Note that the cookie has to match in * name, domain, and path, and the luck of the Irish has to * be with you for this work all the time, but at the least * it will make the browser send back a cookie with no * value, which the ClearSilver cookie parsing code will * ignore. * Input: cgi - a CGI struct * name - the cookie name to clear * domain - the domain to clear, NULL for none * path - the cookie's path * Output: None * Return: NERR_IO */ NEOERR *cgi_cookie_clear (CGI *cgi, const char *name, const char *domain, const char *path); /* not documented *yet* */ NEOERR *cgi_text_html_strfunc(const char *str, char **ret); NEOERR *cgi_html_strip_strfunc(const char *str, char **ret); NEOERR *cgi_html_escape_strfunc(const char *str, char **ret); NEOERR *cgi_js_escape (const char *buf, char **esc); void cgi_html_ws_strip(STRING *str, int level); NEOERR *cgi_register_strfuncs(CSPARSE *cs); /* internal use only */ NEOERR * parse_rfc2388 (CGI *cgi); NEOERR * open_upload(CGI *cgi, int unlink_files, FILE **fpw); __END_DECLS #endif /* __CGI_H_ */