/*
* 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_ */