/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ #if !defined(NET_WANDERINGHORSE_CWAL_S2_CGI_H_INCLUDED) #define NET_WANDERINGHORSE_CWAL_S2_CGI_H_INCLUDED /** This file houses the s2_cgi API, a far-from-complete CGI application framework based on s2. */ #if defined(S2_AMALGAMATION_BUILD) # include "s2_amalgamation.h" #else # include "s2.h" #endif #if defined(__cplusplus) extern "C" { #endif /** State container for the s2 CGI module. It holds all kinds of stuff related to CGI state. */ struct s2_cgi { cwal_engine * e; s2_engine * se; /** Holds info related to the request. */ struct { /** POST data. The library handles JSON and transforms form/url-encoded input to a JSON form here. */ cwal_value * post; /** An Object holding values from the HTTP GET parameters (i.e. from getenv("QUERY_STRING")). */ cwal_value * get; /** An Object holding values from the HTTP cookies (i.e. from getenv("HTTP_COOKIE")). */ cwal_value * cookies; /** An Object holding the raw (currently not unencoded) values from the environ(7) environment (i.e. from getenv(3) and friends). */ cwal_value * env; /** stdin for reading POST data. Currently hard-wired to stdin. */ FILE * input; } request; struct { /** Object of HTTP headers as key/value pairs. */ cwal_value * headers; /** Object of HTTP cookies as key/value pairs. */ cwal_value * cookies; /** Response content type. */ char const * contentType; /** Response HTTP code type. */ int httpCode; /** Response HTTP status message: Status: httpCode httpStatus Owned by this object. */ char * httpStatus; /** Specifies whether or not HTTP headers (including cookies) should be output. 0==no header output >0==output headers <0==determine automatically if headers should be output. */ char headerMode; } response; /** The top-most (global) scope which was active when this object was originally initialized. That scope MUST outlive this object. */ cwal_scope * topScope; /** TODO. Config file (JSON). */ cwal_value * config; /** Scratchpad buffer. */ cwal_buffer tmpBuf; /** Second scratchpad buffer, for when tmpBuf is currently being used. */ cwal_buffer tmpBuf2; /** Holds non-HTTP error information. */ struct { /** Underlying result code. Hopefully one of the CWAL_RC_xxx values. */ int rc; /** _Might_ hold an error string in the first msg.used bytes of msg.mem. */ cwal_buffer msg; } err; }; typedef struct s2_cgi s2_cgi; /** "Empty" s2_cgi object, intended for use in copy-constructing. */ #define s2_cgi_empty_m { \ NULL/*e*/, \ NULL/*se*/, \ {/*request*/ \ NULL/*post*/, \ NULL/*get*/, \ NULL/*cookies*/, \ NULL/*input*/ \ }, \ {/*response*/ \ NULL/*headers*/, \ NULL/*cookies*/, \ NULL/*contentType*/, \ 200/*httpCode*/, \ NULL/*httpStatus*/, \ 1/*headerMode*/ \ }, \ NULL/*topScope*/, \ NULL/*config*/, \ cwal_buffer_empty_m/*tmpBuf*/, \ cwal_buffer_empty_m/*tmpBuf2*/, \ {/*err*/ 0/*rc*/, cwal_buffer_empty_m/*msg*/} \ } /** "Empty" s2_cgi object, intended for use in copy-constructing. */ extern const s2_cgi s2_cgi_empty; /** @def S2_CGI_GETENV_DEFAULT The default environment name(s) to use for cwal_cgi_getenv(). */ #define S2_CGI_GETENV_DEFAULT "gpc" /** Initializes tgt to use ie as its interpreter/engine and sets up tgt's state based on the contents of the CGI-related environment variables. tgt must be a cleanly-initialized s2_cgi instance, and it may be stack allocated (copy s2_cgi_empty over it to cleanly initialize it). Ownership of ie and tgt are not changed and ie must live tgt. Returns 0 on success. On error, if either ie or tgt are NULL then there are no side effects, otherwise tgt might be partially initialized, so the client must eventually pass tgt to s2_cgi_cleanup() before ie is cleaned up. */ int s2_cgi_init( s2_engine * se, s2_cgi * tgt ); /** Cleans up memory owned by the given cgi instance but does not free cgi. The caller is responsible for deallocating cgi in a manner complementary to its allocation mechanism. */ void s2_cgi_cleanup( s2_cgi * cgi ); /** Sets the HTTP response code and message string. The msg bytes are copied by this function, and need not be static. The default response code is 200. If msg is NULL or empty then some built-in string is used, depending on the value of httpCode. */ int s2_cgi_response_status_set(s2_cgi * cx, int httpCode, char const * msg); /** */ cwal_value * s2_cgi_env_get( s2_cgi * cgi, char which, char createIfNeeded ); /** */ cwal_value * s2_cgi_getenv( s2_cgi * cx, char const * fromWhere, char const * key, cwal_size_t keyLen); /** */ char const * s2_cgi_getenv_cstr( s2_cgi * cx, char const * where, char const * key, cwal_size_t keyLen, cwal_size_t* strLen ); /** Sets a variable in one of cx's environment objects. whichEnv must be the conventional character representation (case-insensitive) for on of the following environment objects: - g = GET - p = POST - e = ENV (i.e. getenv(3)) - c = COOKIE - r = REQUEST (==GET, POST, COOKIE) On success 0 is returned and ownership of v is transfered to (or shared with) the appropriate environment object. On error non-zero is returned and ownership of v is not modified. */ int s2_cgi_setenv_v( s2_cgi * cx, char whichEnv, char const * key, cwal_size_t keyLen, cwal_value * v ); /** Convenience form of s2_cgi_setenv_v() which sets the property in the 'e' environment. */ int s2_cgi_setenv( s2_cgi * cx, char const * key, cwal_size_t keyLen, cwal_value * v ); /** */ int s2_cgi_cookie_set( s2_cgi * cx, char const * key, cwal_size_t keyLen, cwal_value * v ); /** */ int s2_cgi_cookie_set2( s2_cgi * cx, char const * key, cwal_size_t keyLen, cwal_value * v, char const * domain, char const * path, unsigned int expires, char secure, char httponly ); /** */ int s2_cgi_response_header_add( s2_cgi * cx, char const * key, cwal_size_t keyLen, cwal_value * v ); /** This outputs cx's HTTP headers, flushes all pending output (buffered via the api.ob API), and cleans up any cleans up any output buffers */ int s2_cgi_response_output_all(s2_cgi * cx); /** URL-decodes a string inline. sLen may be NULL, but if it is not then *sLen must initially be the length of str which should be decoded. On returning, if sLen is not NULL then it will contain the new virtual length of str (which may be shorter than its original length but never longer). str will be NUL-terminated by this function. If sLen is NULL then the equivalent of strlen() is used to determine how many bytes to decode. The only error conditions are if the arguments are invalid, in which case it returns without side effects (if it can determine the arguments are in error) or invokes undefined behaviour (if *sLen is longer than str's valid memory). */ void s2_cgi_urldecode_inline( char * str, cwal_size_t * sLen ); #if 0 /* broken by removal of request.ENV object... */ /** If the environment variable PATH_INFO is found during initialization, it is split into its component dir/file parts as an array. This routine returns the Nth element in that array, or NULL if out of bounds or no PATH_INFO is availble. The array itself is available via s2_cgi_getenv(cx,"e","PATH_INFO_SPLIT", 15). */ cwal_value * s2_cgi_path_part( s2_cgi * cx, unsigned short ndx ); /** Convenience form of s2_cgi_path_part() with fetches the value as a C-string. If strLen is not NULL then it will be set to the length (in bytes) of the returned string pointer. Returns NULL on error or if it can't figure out what to do. */ char const * s2_cgi_path_part_cstr( s2_cgi * cx, unsigned short ndx, cwal_size_t * strLen ); #endif /** URL-encodes src to dest and NUL-terminates it. dest is expanded as needed. Returns 0 on success. On error dest might be partially populated. */ int s2_cgi_urlencode( s2_cgi * cx, char const * src, cwal_size_t srcLen, cwal_buffer * dest ); #if defined(__cplusplus) } /*extern "C"*/ #endif #endif /* NET_WANDERINGHORSE_CWAL_S2_CGI_H_INCLUDED */