/* * rxv_spin.h * * Copyright (C) 2003 - 2007 Bojan Smojver, Rexursive * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licence as published by the Free * Software Foundation; either version 2 of the Licence, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licence for * more details. * * You should have received a copy of the GNU General Public Licence along * with this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place, Suite 330, Boston, MA 02111-1307 USA * * In addition, as two special exceptions, Bojan Smojver, Rexursive, gives * permission to: * * 1. Link, both statically and dynamically, the code of this program with * Apache HTTP Server, Apache Portable Runtime and Apache Portable Runtime * Utility Library from Apache Software Foundation (or with modified versions * of the above, that use the same licence - Apache Software Licence 1.1, 2.0 * or any later version), and distribute linked combinations including the * program and the above software from Apache Software Foundation. You must * obey the GNU General Public Licence in all respects for all of the code * used other than Apache HTTP Server, Apache Portable Runtime and Apache * Portable Runtime Utility Library. * * 2. Dynamically link any shared library with this program at run-time, * through the interface of SpinApplication/SpinAppEntry or LoadModule * run-time configuration directives of Apache HTTP Server, as provided by * this program or Apache HTTP Server itself, regardless of licensing terms of * those shared libraries. You must obey the GNU General Public Licence in all * respects for all of the code used other than that of those shared * libraries. * * If you modify this file, you may extend these exceptions to your version of * the file, but you are not obligated to do so. If you do not wish to do so, * delete one or both of these exception statements from your version. * */ #ifndef __RXV_SPIN__ #define __RXV_SPIN__ /** * @file rxv_spin.h * @brief mod_spin data structures and functions */ /** * @mainpage *
Copyright © 2003 - 2007 Bojan Smojver, Rexursive
* * @anchor Introduction *

Introduction

* @par See also: * @ref Installation * @ref News * @ref Licence * * @verbinclude README * * @anchor Installation *

Installation and configuration

* @par See also: * @ref Introduction * @ref News * @ref Licence * * @verbinclude INSTALL * * @anchor News *

News

* @par See also: * @ref Introduction * @ref Installation * @ref Licence * @verbinclude NEWS * * @anchor Licence *

Terms of copying, distribution and modification

* @par See also: * @ref Introduction * @ref News * @ref Installation * @verbinclude COPYING */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* remove some macros, Apache defines them as well */ #ifndef DOXYGEN_SHOULD_SKIP_THIS #undef PACKAGE_BUGREPORT #undef PACKAGE_NAME #undef PACKAGE_STRING #undef PACKAGE_TARNAME #undef PACKAGE_VERSION #endif /* the following also includes httpd.h */ #include /* remove Apache macros, restore good definitions */ #ifndef DOXYGEN_SHOULD_SKIP_THIS #undef PACKAGE_BUGREPORT #undef PACKAGE_NAME #undef PACKAGE_STRING #undef PACKAGE_TARNAME #undef PACKAGE_VERSION #define PACKAGE_BUGREPORT RXV_SPIN_PACKAGE_BUGREPORT #define PACKAGE_NAME RXV_SPIN_PACKAGE_NAME #define PACKAGE_STRING RXV_SPIN_PACKAGE_STRING #define PACKAGE_TARNAME RXV_SPIN_PACKAGE_TARNAME #define PACKAGE_VERSION RXV_SPIN_PACKAGE_VERSION #endif #include #ifndef DOXYGEN_SHOULD_SKIP_THIS #ifdef HAVE_AP_REGEX_H #include #else #define ap_regex_t regex_t #define ap_regmatch_t regmatch_t #define ap_regcomp(a,b,c) regcomp((a),(b),(c)) #define ap_regfree(a) regfree(a) #define AP_REG_ICASE REG_ICASE #define AP_REG_NEWLINE REG_NEWLINE #define AP_REG_NOTBOL REG_NOTBOL #define AP_REG_NOTEOL REG_NOTEOL #define AP_REG_EXTENDED REG_EXTENDED #define AP_REG_NOSUB REG_NOSUB #endif #endif #include #include #ifdef HAVE_LIBPQ #include #endif #ifdef HAVE_LIBMYSQLCLIENT #include #endif /** * Data manipulation functions (mod_spin API) * * @defgroup rxv_spin_data_functions Data manipulation functions * @{ */ #define RXV_SPIN_DATA_SGL 0x01 /**< single data type (size limited string) */ #define RXV_SPIN_DATA_RWS 0x02 /**< rows data type (named columns, numbered rows) */ #define RXV_SPIN_DATA_MTA 0xFF /**< metadata type (not used in rendering) */ #define RXV_SPIN_TRIM_LEFT 0x01 /**< trim whitespace on the left */ #define RXV_SPIN_TRIM_RIGHT 0x02 /**< trim whitespace on the right */ typedef struct rxv_spin_data rxv_spin_data_t; /**< data type */ /** @} */ /** * Context functions (mod_spin API) * * @defgroup rxv_spin_context_functions Context functions * @{ */ typedef struct rxv_spin_context rxv_spin_context_t; /**< context type */ typedef rxv_spin_context_t rxv_spin_ctx_t; /**< shorthand for context type */ /** @} */ /** * Connection functions (mod_spin API) * * @defgroup rxv_spin_connection_functions Connection functions * @{ */ typedef struct rxv_spin_conn rxv_spin_conn_t; /**< connection type */ typedef struct rxv_spin_cpool rxv_spin_cpool_t; /**< connection pool type */ /** @} */ /** * Database functions (mod_spin API) * * @defgroup rxv_spin_database_functions Database functions * @{ */ typedef struct rxv_spin_db_result rxv_spin_db_result_t; /**< database query result type */ /** @} */ /* Data and context structures */ /** * @brief * @ingroup rxv_spin_data_functions * Data structure */ struct rxv_spin_data{ unsigned char type; /**< data type */ size_t size; /**< string size or number of rows */ /** unnamed */ union{ apr_hash_t *cols; /**< columns of the data type rows */ char *data; /**< actual data */ rxv_spin_data_t *meta; /**< metadata (i.e. pointer to an array) */ void *both; /**< when we need to test both cols & data at once */ }; }; /** * @brief * @ingroup rxv_spin_context_functions * Context structure */ struct rxv_spin_context{ request_rec *r; /**< raw request */ apr_pool_t *pool; /**< memory pool used by the context */ rxv_spin_data_t *data; /**< data produced by the application */ apreq_handle_t *req; /**< apreq handle for cookies and parameters */ rxv_spin_cpool_t *cpool; /**< pool of connections */ void *guts; /**< private data used internally by mod_spin */ void *extra; /**< data you may want to place in context */ }; /** * @addtogroup rxv_spin_data_functions * @{ */ /* Data creation and manipulation functions */ /** * Create single data from from a nul terminated string. * String is not copied - use apr_pstrdup() for that. * * @param pool Pool for allocation of data structure * @param str String to create data from * @return rxv_spin_data_t pointer, structure allocated from * the pool, NULL on error * @par Example: * @code * rxv_spin_single(ctx->pool,"some string"); * @endcode */ rxv_spin_data_t *rxv_spin_single(apr_pool_t *pool,const char *str); /** * Get a nul terminated string from the single data. String is not copied, * use apr_pstrdup() for that. * * @param single Single data * @return nul terminated string, NULL on error * @par Example: * @code * rxv_spin_single_get(data); * @endcode * @remarks You can, of course, completely ignore this function if you know * that your data is a single and that it is not NULL. Simply doing * data->data will return the same string. */ char *rxv_spin_single_get(rxv_spin_data_t *single); /** * Replace data (string) in an already existing single. * String is not copied - use apr_pstrdup() for that. * * @param single Already exisiting single * @param str String to replace within single * @return rxv_spin_data_t pointer, NULL on error * @par Example: * @code * rxv_spin_single_set(single,"some new string"); * @endcode * @remarks This function is useful for replacing singles while iterating * through rows of data. In other words, for data postprocessing. */ rxv_spin_data_t *rxv_spin_single_set(rxv_spin_data_t *single,const char *str); /** * Create single data from from a counted string. Although the string is * counted, it has to end with a nul character. This function will check for * it and if it isn't there, it'll refuse to create the single. Keep in mind * that the memory allocated for the counted string will be size + 1, meaning, * if it isn't, the check for that nul character at the end may cause a * segfault. * String is not copied - use apr_pstrmemdup() for that. * * @param pool Pool for allocation of data structure * @param str String to create data from * @param size Size of the string, not counting the ending nul character * @return rxv_spin_data_t pointer, structure allocated from * the pool, NULL on error * @par Example: * @code * rxv_spin_single_mem(ctx->pool,"1234567890",10); * @endcode */ rxv_spin_data_t *rxv_spin_single_mem(apr_pool_t *pool,const char *str, size_t size); /** * Replace data (counted string) in an already existing single. Although the * string is counted, it has to end with a nul character. This function will * check for it and if it isn't there, it'll refuse to create the single. Keep * in mind that the memory allocated for the counted string will be size + 1, * meaning, if it isn't, the check for that nul character at the end may cause * a segfault. * String is not copied - use apr_pstrmemdup() for that. * * @param single Already exisiting single * @param str String to replace within single * @param size Size of the string, not counting the ending nul character * @return rxv_spin_data_t pointer, NULL on error * @par Example: * @code * rxv_spin_single_memset(single,"1234567890",10); * @endcode * @remarks This function is useful for replacing singles while iterating * through rows of data. In other words, for data postprocessing. */ rxv_spin_data_t *rxv_spin_single_memset(rxv_spin_data_t *single,const char *str, size_t size); /** * Convert a single to lowercase. Single pushed into the function is modified, * no copy is made. * * @param single Single data * @return the same single in lowercase, NULL on error * @par Example: * @code * rxv_spin_single_tolower(single); * @endcode */ rxv_spin_data_t *rxv_spin_single_tolower(rxv_spin_data_t *single); /** * Convert a single to uppercase. Single pushed into the function is modified, * no copy is made. * * @param single Single data * @return the same single in uppercase, NULL on error * @par Example: * @code * rxv_spin_single_toupper(single); * @endcode */ rxv_spin_data_t *rxv_spin_single_toupper(rxv_spin_data_t *single); /** * Trim whitespace from a single. No copy is made. * * @param single Single data * @param what Trim left: RXV_SPIN_TRIM_LEFT, trim right: RXV_SPIN_TRIM_RIGHT * @return the same single trimmed, NULL on error * @par Example: * @code * rxv_spin_single_trim(single,RXV_SPIN_TRIM_LEFT|RXV_SPIN_TRIM_RIGHT); * @endcode */ rxv_spin_data_t *rxv_spin_single_trim(rxv_spin_data_t *single, unsigned char what); #define rxv_spin_single_trimboth(s) \ rxv_spin_single_trim((s),(RXV_SPIN_TRIM_LEFT|RXV_SPIN_TRIM_RIGHT)) /**< trim single on both sides */ #define rxv_spin_single_trimleft(s) \ rxv_spin_single_trim((s),(RXV_SPIN_TRIM_LEFT)) /**< trim single on the left side */ #define rxv_spin_single_trimright(s) \ rxv_spin_single_trim((s),(RXV_SPIN_TRIM_RIGHT)) /**< trim single on the right side */ /** * Create metadata from data. * Meta data is an array of rxv_spin_data_t. It is not used to * place data into the context, but simply to facilitate the API. * Data is not copied. * * @param pool Pool for allocation of data structure * @param ... List of rxv_spin_data_t*, ending with NULL * @return rxv_spin_data_t pointer, structures allocated from * the pool, NULL on error * @par Example: * @code * rxv_spin_meta(ctx->pool, * rxv_spin_rows(ctx->pool,"x",x,"y",y,"z",z,NULL), * rxv_spin_rows(ctx->pool,"u",u,"v",v,"w",w,NULL), * NULL); * @endcode */ rxv_spin_data_t *rxv_spin_meta(apr_pool_t *pool,...); /** * Create metadata from from nul terminated strings. * Meta data is an array of rxv_spin_data_t. It is not used to * place data into the context, but simply to facilitate the API. * Strings are not copied - use apr_pstrdup() for that. * * @param pool Pool for allocation of data structure * @param ... List of char*, ending with NULL * @return rxv_spin_data_t pointer, structures allocated from * the pool, NULL on error * @par Example: * @code * rxv_spin_meta_vstr(ctx->pool,"first string","second string",NULL); * @endcode */ rxv_spin_data_t *rxv_spin_meta_vstr(apr_pool_t *pool,...); /** * Create metadata by parsing a string. * Meta data is an array of rxv_spin_data_t. It is not used to * place data into the context, but simply to facilitate the API. * String to parse is NOT copied into pool storage before parsing, therefore, * if you want to parse constant strings, you MUST copy them using * apr_pstrdup(). This function actually chops up the string that is passed * into it, so if the string is supposed to be used somewhere else, you may * get a nasty surprise after calling this function. It is safer to make a * copy. * * @param pool Pool for allocation of data structure * @param str String to parse * @param sep Separators to use when parsing * @return rxv_spin_data_t pointer, structures allocated from * the pool, NULL on error * @par Example: * @code * rxv_spin_meta_parse(ctx->pool, apr_pstrdup(ctx->pool,"first str,second str/third str"), ",/"); * @endcode */ rxv_spin_data_t *rxv_spin_meta_parse(apr_pool_t *pool, char *str,const char *sep); /** * Create an empty column of data. The size of the column is usually picked up * from an existing rows data type. The returned value is metadata and its * size is embedded in it. * * @param pool Pool for allocation of data structures * @param size Size (number of elements) of the column to create * @return rxv_spin_data_t pointer, structures allocated from * the pool, NULL on error * @par Example: * @code * rxv_spin_meta_empty(ctx->pool,rows->size); * @endcode * @remarks This function is useful for creating boilerplate columns, used for * various marker data. */ rxv_spin_data_t *rxv_spin_meta_empty(apr_pool_t *pool,size_t size); /** * Create a hash table of metadata. Keys will be strings from singles, values * will be pointers to the actual singles. Data other than singles will not be * hashed. * * @param pool Pool for all memory allocation * @param data Meta data to hash * @return a hash table of the data, NULL on error * @par Example: * @code * rxv_spin_meta_hash(ctx->pool,data); * @endcode */ apr_hash_t *rxv_spin_meta_hash(apr_pool_t *pool,rxv_spin_data_t *data); /** * Mark an element in the metadata (associate an empty string to it). This is * useful for creating markers for the presentation layer. The data passed * into this function is metadata, meaning, the size is embedded in it. * * @param data Data to mark * @param element The index of the element to mark * @return rxv_spin_data_t pointer to data, NULL on error * @par Example: * @code * rxv_spin_meta_mark(data,rows->size-1); * @endcode */ rxv_spin_data_t *rxv_spin_meta_mark(rxv_spin_data_t *data,size_t element); /** * Mark each n-th element in the metadata (associate an empty string to it). * This is useful for creating markers for the presentation layer. The data * passed into this function metadata, meaning, the size is embedded in it. * * @param data Data to mark * @param off Offset to start from * @param step Each n-th element to mark * @return rxv_spin_data_t pointer to data, NULL on error * @par Example: * @code * rxv_spin_meta_markeach(data,1,2); * @endcode */ rxv_spin_data_t *rxv_spin_meta_markeach(rxv_spin_data_t *data, size_t off,size_t step); /** * Select (mark) data that matches given data set. * Selection data set is either single or metadata. The select operation can * use an optional hash of the data that is being matched. Otherwise, the * search is linear (which is OK for small data sets). * * @param data Data to select * @param select Single or metadata to use for selection * @param hash Optional hash of the data being matched or NULL * @return rxv_spin_data_t pointer to data, NULL on error * @par Example: * @code * rxv_spin_meta_select(data,rxv_spin_str_parse(ctx->pool,"Australia,Japan",","), rxv_spin_meta_hash(ctx->pool,data)); * @endcode */ rxv_spin_data_t *rxv_spin_meta_select(rxv_spin_data_t *data, rxv_spin_data_t *select, apr_hash_t *hash); /** * Create rows of data from metadata. * The list supplied contains alternating column names, which are * strings, and the actual data for the column, in form of metadata. * If metadata contains different array lengths, the smallest size * will be used. * Data is not copied. * * @param pool Pool for allocation of data structure * @param ... List of char* and rxv_spin_data_t*, ending with NULL * @return rxv_spin_data_t pointer, structures allocated from * the pool, NULL on error * @par Example: * @code * rxv_spin_rows(ctx->pool,"def",def,"ghi",ghi,"xyz",xyz,NULL); * @endcode */ rxv_spin_data_t *rxv_spin_rows(apr_pool_t *pool,...); /** * Create a hash table of a column of rows. Keys will be strings from * singles, values will be pointers to the actual singles. Data other than * singles will not be hashed. * * @param pool Pool for all memory allocation * @param rows Rows to hash * @param column Column to hash * @return a hash table of the data, NULL on error * @par Example: * @code * rxv_spin_rows_hash(ctx->pool,rows,"secondcolumn"); * @endcode */ apr_hash_t *rxv_spin_rows_hash(apr_pool_t *pool,rxv_spin_data_t *rows, const char *column); /** * Mark an element in the column of rows (associate an empty string to it). * This is useful for creating markers for the presentation layer. * * @param rows Rows to mark * @param column Name of the column to mark * @param element The index of the element to mark * @return rxv_spin_data_t pointer to rows, NULL on error * @par Example: * @code * rxv_spin_rows_mark(rows,"lastrow",rows->size-1); * @endcode */ rxv_spin_data_t *rxv_spin_rows_mark(rxv_spin_data_t *rows,const char *column, size_t element); /** * Mark each n-th element in the column of rows (associate an empty string to * it). This is useful for creating markers for the presentation layer. * * @param rows Rows to mark * @param column Name of the column to mark * @param off Offset to start from * @param step Each n-th element to mark * @return rxv_spin_data_t pointer to rows, NULL on error * @par Example: * @code * rxv_spin_rows_markeach(rows,"evenrow",0,2); * @endcode */ rxv_spin_data_t *rxv_spin_rows_markeach(rxv_spin_data_t *rows, const char *column, size_t off,size_t step); /** * Select (mark) rows of data that match given data set. * Selection data set is either single or metadata. The select operation can * use an optional hash of the column that is being matched. Otherwise, the * search is linear (which is OK for small data sets). * * @param rows Rows of data to select * @param select Single or metadata to use for selection * @param column Column used to match the data from select parameter * @param marker Column used to place markers for selected data * @param hash Optional hash of the column being matched or NULL * @return rxv_spin_data_t pointer to rows, NULL on error * @par Example: * @code * rxv_spin_rows_select(rows,rxv_spin_str_parse(ctx->pool,"Australia,Japan",","), "country","selected", rxv_spin_rows_hash(ctx->pool,rows,"country")); * @endcode */ rxv_spin_data_t *rxv_spin_rows_select(rxv_spin_data_t *rows, rxv_spin_data_t *select, const char *column,const char *marker, apr_hash_t *hash); /** * Get a column from rows data type. The column is not copied, rather a * pointer to the first element in the array is returned inside a metadata * type. Being metadata, this column will have its size embedded in it. * * @param pool Pool for allocation of data structure * @param rows Rows data type * @param key Name of the column to get * @return rxv_spin_data_t pointer, NULL on error * @par Example: * @code * column=rxv_spin_column_get(rows,"firstfield"); * for(i=0;isize;i++) * rxv_spin_single_toupper(column+i); * @endcode */ rxv_spin_data_t *rxv_spin_column_get(apr_pool_t *pool, rxv_spin_data_t *rows,const char *key); /** * Set a column in rows data type. The column passed into this function is * metadata. The size of the column has to be equal of greater than the number * of rows. * * @param rows Rows data type * @param key Name of the column to set * @param column Column to set * @return rxv_spin_data_t pointer to the column, NULL on error * @par Example: * @code * rxv_spin_column_set(rows,"newcolumn",rxv_spin_column(rows->size)); * @endcode * @remarks This function is also used for column removal, through a macro. * In such a case, NULL return value is not necessarily a failure. */ rxv_spin_data_t *rxv_spin_column_set(rxv_spin_data_t *rows, const char *key,rxv_spin_data_t *column); #define rxv_spin_column_del(rows,key) rxv_spin_column_set((rows),(key),NULL) /**< delete a column from rows */ /** * Resize rows or metadata. * * @param pool Pool for all memory allocation * @param data Data, rows or metadata type only * @param size New size * @return resized data, NULL on error * @par Example: * @code * rxv_spin_resize(ctx->pool,data,100); * @endcode * @remarks If the requested size is smaller than original size, only the size * field will be adjusted. If the requested size is larger, the * structures containing pointers to data will be copied to the newly * allocated memory space. If you rely on the old data (i.e. before * resizing) in any way, you might create big problems, even security * related ones. */ rxv_spin_data_t *rxv_spin_resize(apr_pool_t *pool,rxv_spin_data_t *data, size_t size); /** * Make a copy of data. The result is a full, deep copy (i.e. there is not a * single byte in common with the original). * * @param pool Pool for all memory allocation * @param data Data, any type * @return copy of the data, NULL on error * @par Example: * @code * rxv_spin_copy(ctx->pool,data); * @endcode * @remarks If you're using this function on rows or metadata, the copying * might take a while, as all memory is going to be allocated from * the pool and all data and structures are going to be copied, down * to the last string. In such a scenario, only use this function if * you are planning on putting the result into context as a new * item, or you need to keep the original data for some other * reason. */ rxv_spin_data_t *rxv_spin_copy(apr_pool_t *pool,rxv_spin_data_t *data); /** * Convert a string to lowercase. String pushed into the function is modified, * no copy is made. * * @param str String to convert, nul terminated * @return the same string in lowercase, NULL on error * @par Example: * @code * rxv_spin_str_tolower(single); * @endcode */ char *rxv_spin_str_tolower(const char *str); /** * Convert a string to uppercase. String pushed into the function is modified, * no copy is made. * * @param str String to convert, nul terminated * @return the same string in uppercase, NULL on error * @par Example: * @code * rxv_spin_str_toupper(single); * @endcode */ char *rxv_spin_str_toupper(const char *str); /** * Trim whitespace from a string. No copy is made. You MUST copy the string * into pool storage using apr_pstrdup() if you're trimming constant strings. * * @param str String * @param what Trim left: RXV_SPIN_TRIM_LEFT, trim right: RXV_SPIN_TRIM_RIGHT * @return the same string trimmed, NULL on error * @par Example: * @code * rxv_spin_str_trim(apr_strdup(ctx->pool," string with whitespace ", RXV_SPIN_TRIM_LEFT|RXV_SPIN_TRIM_RIGHT); * @endcode */ char *rxv_spin_str_trim(char *str,unsigned char what); #define rxv_spin_str_trimboth(s) \ rxv_spin_str_trim((s),(RXV_SPIN_TRIM_LEFT|RXV_SPIN_TRIM_RIGHT)) /**< trim string on both sides */ #define rxv_spin_str_trimleft(s) \ rxv_spin_str_trim((s),(RXV_SPIN_TRIM_LEFT)) /**< trim string on the left side */ #define rxv_spin_str_trimright(s) \ rxv_spin_str_trim((s),(RXV_SPIN_TRIM_RIGHT)) /**< trim string on the right side */ /** @} */ /** * @addtogroup rxv_spin_context_functions * @{ */ /* Context functions and macros */ /** * Retrieve data from the context. * * @param ctx Context * @param key Unique key by which this data is identified * @return pointer to data (no copying is done), NULL if not found * @par Example: * @code * rxv_spin_ctx_get(ctx,"result"); * @endcode */ rxv_spin_data_t *rxv_spin_ctx_get(rxv_spin_context_t *ctx,const char *key); #define rxv_spin_ctx_strget(ctx,key) \ rxv_spin_single_get(rxv_spin_ctx_get((ctx),(key))) /**< get a string from the context by converting from single */ /** * Place data into the context. * * @param ctx Context * @param key Unique key by which this data is identified * @param value The actual data * @return pointer to data (no copying is done), NULL on error * @par Example: * @code * rxv_spin_ctx_set(ctx,"result",result->data); * @endcode * @remarks This function is also used for context data removal, through a * macro. In such a case, NULL return value is not necessarily a * failure. * @par IMPORTANT: * The data set into the context has to have at least the lifetime of the * request. Anything less and the pool used to hold the data may get cleaned * before the bucket holding the value gets created. In most cases, this will * result in a segfault. * @par IMPORTANT: * Do not put data allocated from the heap (with malloc(), calloc() and * friends) into the context unless you set up cleanup functions that will * free the allocated space. Although the context itself and the bucket * brigade passed to Apache will be cleaned automatically (because it is * allocated from the pool and/or has relevant cleanup functions), the heap * storage space has to be freed explicitly. The safest option is to always * use data allocated from the request pool. */ rxv_spin_data_t *rxv_spin_ctx_set(rxv_spin_context_t *ctx, const char *key,rxv_spin_data_t *value); #define rxv_spin_ctx_strset(ctx,key,val) \ rxv_spin_ctx_set((ctx),(key),rxv_spin_single((ctx)->pool,(val))) /**< set a string into context by converting to single */ #define rxv_spin_ctx_del(ctx,key) rxv_spin_ctx_set((ctx),(key),NULL) /**< delete a value from context */ /** @} */ /* Application/session functions */ /** * Application and session functions (mod_spin API) * * @defgroup rxv_spin_as_functions Application and session functions * @{ */ /** * Retrieve a value from the application database. * * @param ctx Context * @param key Unique key by which this value is identified * @return pointer to a copy of the value, NULL on error * @par Example: * @code * rxv_spin_app_get(ctx,"some application key"); * @endcode */ rxv_spin_data_t *rxv_spin_app_get(rxv_spin_context_t *ctx,const char *key); #define rxv_spin_app_strget(ctx,key) \ rxv_spin_single_get(rxv_spin_app_get((ctx),(key))) /**< get a string from application instead of single */ /** * Put value in the application database. * * @param ctx Context * @param key Unique key by which this value is identified * @param val Value to be placed in the database (single) * @return pointer to the value, NULL on error * @par Example: * @code * rxv_spin_app_set(ctx,"some application key", rxv_spin_str_single(ctx->pool,"some application value")); * @endcode */ rxv_spin_data_t *rxv_spin_app_set(rxv_spin_context_t *ctx, const char *key,rxv_spin_data_t *val); #define rxv_spin_app_strset(ctx,key,val) \ rxv_spin_app_set((ctx),(key),rxv_spin_single((ctx)->pool,(val))) /**< set string into application instead of single */ /** * Delete a record in the application database. * * @param ctx Context * @param key Unique key by which record is identified * @return APR_SUCCESS on success, otherwise an error * @par Example: * @code * rxv_spin_app_del(ctx,"some application key"); * @endcode * @remarks It is not an error to delete and non-existent record. */ apr_status_t rxv_spin_app_del(rxv_spin_context_t *ctx,const char *key); /** * Retrieve a value from the session database. * * @param ctx Context * @param key Unique key by which this value is identified * @return pointer to a copy of the value, NULL on error * @par Example: * @code * rxv_spin_app_get(ctx,"some session key"); * @endcode */ rxv_spin_data_t *rxv_spin_ses_get(rxv_spin_context_t *ctx,const char *key); #define rxv_spin_ses_strget(ctx,key) \ rxv_spin_single_get(rxv_spin_ses_get((ctx),(key))) /**< get a string from session instead of single */ /** * Put value in the session database. * * @param ctx Context * @param key Unique key by which this value is identified * @param val Value to be placed in the database (single) * @return pointer to a copy of the value, NULL on error * @par Example: * @code * rxv_spin_app_set(ctx,"some session key", rxv_spin_str_single(ctx->pool,"some session value")); * @endcode */ rxv_spin_data_t *rxv_spin_ses_set(rxv_spin_context_t *ctx, const char *key,rxv_spin_data_t *val); #define rxv_spin_ses_strset(ctx,key,val) \ rxv_spin_ses_set((ctx),(key),rxv_spin_single((ctx)->pool,(val))) /**< set string into application instead of single */ /** * Delete a record in the session database. * * @param ctx Context * @param key Unique key by which record is identified * @return APR_SUCCESS on success, otherwise an error * @par Example: * @code * rxv_spin_ses_del(ctx,"some session key"); * @endcode * @remarks It is not an error to delete and non-existent record. */ apr_status_t rxv_spin_ses_del(rxv_spin_context_t *ctx,const char *key); /** * Get session id * * @param ctx Context * @return Session id or NULL if no session support exists * @par Example: * @code * rxv_spin_ses_idget(ctx); * @endcode * @remarks This function fetches a string representation of the session id, * which draws is origin from mod_unique_id. If mod_unique_id isn't present in * Apache, this function returns NULL. Note that the fact that one can get a * session id does not mean that there is a valid session in existence (that * is to say, that the client accepted the session). Use rxv_spin_ses_valid() * function to find that out. */ char *rxv_spin_ses_idget(rxv_spin_context_t *ctx); /** * Find out if the session is valid. * * @param ctx Context * @return 1 if valid, otherwise 0 * @par Example: * @code * rxv_spin_ses_valid(ctx); * @endcode * @remarks If the client accepted this session, this function returns 1. This * will only be true if the client submitted a valid session id and its * relevant hash to mod_spin. Also, the session will not be considered valid * if there are path tricks in the session id (this is unlikely to happen, due * to hash checking, but nevertheless). */ int rxv_spin_ses_valid(rxv_spin_context_t *ctx); /** @} */ /* Connection macros */ /** * @addtogroup rxv_spin_connection_functions * @{ */ #define RXV_SPIN_CONN_PGSQL 0x01 /**< PostgreSQL connection type */ #define RXV_SPIN_CONN_MYSQL 0x02 /**< MySQL connection type */ #define RXV_SPIN_CONN_MINID 0x01 /**< lowest known connection type */ #define RXV_SPIN_CONN_MAXID 0x3F /* that's six ones in binary */ /**< highest known connection type */ #define RXV_SPIN_CONN_FOREIGN 0x40 /**< foreign connection type */ #define RXV_SPIN_CONN_POOLED 0x80 /**< connection pooled flag */ #define RXV_SPIN_CONN_IS_PGSQL(c) ((((c)->type)&RXV_SPIN_CONN_MAXID)==RXV_SPIN_CONN_PGSQL) /**< check if this connection is of PostgreSQL type */ #define RXV_SPIN_CONN_IS_MYSQL(c) ((((c)->type)&RXV_SPIN_CONN_MAXID)==RXV_SPIN_CONN_MYSQL) /**< check if this connection is of MySQL type */ #define RXV_SPIN_CONN_IS_FOREIGN(c) (((c)->type)&RXV_SPIN_CONN_FOREIGN) /**< check if this connection is of MySQL type */ #define RXV_SPIN_CONN_IS_POOLED(c) (((c)->type)&RXV_SPIN_CONN_POOLED) /**< check if this connection is pooled */ /** @} */ /** * @brief * @ingroup rxv_spin_connection_functions * Connection structure */ struct rxv_spin_conn{ unsigned char type; /**< connection type: PostgreSQL, MySQL (also pooled or not) etc., use the macros to find out */ char *cinfo; /**< full connection identification string: type + connect string */ /** unnamed */ union{ #ifdef HAVE_LIBPQ PGconn *pgconn; /**< PostgreSQL specific connection */ #endif #ifdef HAVE_LIBMYSQLCLIENT MYSQL *myconn; /**< MySQL specific connection */ #endif void *conn; /**< generic or foreign connection */ }; /** unnamed */ union{ rxv_spin_cpool_t *cpool; /**< the pool of connections for this connection */ apr_pool_t *pool; /**< memory pool we registered the cleanup with, if pooled connections aren't used */ }; apr_status_t (*cleanup)(void *data); /**< cleanup function */ }; /** * @brief * @ingroup rxv_spin_connection_functions * Connection pool structure */ struct rxv_spin_cpool{ apr_pool_t *pool; /**< pool used for all allocations */ apr_hash_t *conns; /**< hash table of connections */ }; /* Database access structures and functions NOTE: Everything is designed to be PostgreSQL centric. Sorry :-( */ /** * @brief * @ingroup rxv_spin_database_functions * Database result structure */ struct rxv_spin_db_result{ apr_status_t status; /**< APR_SUCCESS when all is cool */ char *error; /**< descriptive error or NULL */ rxv_spin_data_t *data; /**< results returned or NULL */ }; /** * @addtogroup rxv_spin_database_functions * @{ */ /** * Connect to a database and optionally pool the connection. * * @param pool Memory pool used for memory allocations * @param cpool Connection pool * @param conninfo Connection string * @param type Database type * @return pointer to a database connection, NULL on error * @par Example: * @code * rxv_spin_db_connect(ctx->pool,NULL,"dbname=spintest",RXV_SPIN_DB_PGSQL); * @endcode * @code * rxv_spin_db_connect(ctx->pool,ctx->cpool,"dbname=spintest",RXV_SPIN_DB_MYSQL); * @endcode * @remarks If connection pooling isn't used, the cpool parameter has to be * set to NULL. The pool parameter has to point to a valid APR memory * pool. If pooled connections aren't used, the connection will be * closed on pool (the first argument) cleanup. Meaning, if you want * custom lifetime of the connection, create your own memory pool and * then destroy it when you don't need the database connection any * more. * @par IMPORTANT: * Alywas use relevant macros (such as #RXV_SPIN_CONN_IS_PGSQL(c)) to enquire * about the type of the connection, because the type is an unsigned char that * is a combination of various binary data, including the database type and * connection pooled flag. * @par IMPORTANT: * This function takes connection string in the form defined by PostgreSQL * manual. In fact, the code for the string parsing (for databases other than * PostgreSQL) has been directly lifted from PostgreSQL version 7.4.1. However, * when used with database types other than PostgreSQL, not all keywords are * supported. Namely, for use with MySQL, you can only use "host", "port", * "dbname", "user" and "password". Anything else will cause errors. */ rxv_spin_conn_t *rxv_spin_db_connect(apr_pool_t *pool, rxv_spin_cpool_t *cpool, const char *conninfo, unsigned char type); /** * Execute a database query of any type. * * @param pool Pool used for memory allocation * @param conn Database connection * @param query SQL query to be performed * @return Valid rxv_spin_db_result_t pointer, NULL on error * @par Example: * @code * result=rxv_spin_db_exec(ctx->pool,conn,"select * from spintest"); * @endcode * @remarks Depending on the nature of the query, result->data may be NULL. * Only SELECT and similar queries that return tuples (rows) will * have this member non-NULL. To verify if the query finished * successfully, use result->status. */ rxv_spin_db_result_t *rxv_spin_db_exec(apr_pool_t *pool, rxv_spin_conn_t *conn, const char *query); /** * Get various info about the database connection. * * @param conn Database connection * @param what The type of information to get * @return string with the desired info, NULL on error * @par Example: * @code * rxv_spin_db_info(conn); * @endcode * @remarks You can use either this function or defined macros to get specific * information about this database connection. Note that some * database APIs won't provide all information. In that case, an * empty string might be returned. */ char *rxv_spin_db_info(rxv_spin_conn_t *conn,unsigned char what); /** * Get the status of the connection. * * @param conn Database connection * @return APR_SUCCESS if OK, otherwise an error * @par Example: * @code * rxv_spin_db_status(conn); * @endcode */ apr_status_t rxv_spin_db_status(rxv_spin_conn_t *conn); /** * Escape a string for use within an SQL query. * * @param pool Pool used for memory allocation * @param conn Database connection * @param str string to be escaped * @return Pointer to escaped string, NULL on error * @par Example: * @code * escstr=rxv_spin_db_escape(ctx->pool,conn,"'A string to escape'"); * @endcode * @remarks The connection pointer is used to determine database type and * therefore use the correct, database specific escaping function. * MySQL needs a pointer to the connection in order to determine the * correct character set. Therefore the second argument must be a * valid connection pointer. The underlying database specific * functions have slightly different behaviour, depending on the * database being used. Check in the relevant manual for differences. */ char *rxv_spin_db_escape(apr_pool_t *pool, rxv_spin_conn_t *conn, const char *str); /** * Reset a database connection (i.e. attempt to reconnect). * * @param conn Database connection to reset * @return APR_SUCCESS on success, otherwise an error * @par Example: * @code * rxv_spin_db_reset(conn); * @endcode * @remarks This function is not normally called from the application. * Reset is performed automatically by mod_spin if the connection is * down. However, you may choose to use this function in same rare * cases. */ apr_status_t rxv_spin_db_reset(rxv_spin_conn_t *conn); /** @} */ /** * @addtogroup rxv_spin_connection_functions * @{ */ /** * Create a connection pool. Connection strings used to search for open * connections within the pool are treated as case sensitive and they consist * of the actual connection string preceded by the type (a single character). * * @param pool Memory pool used for allocation * @return pointer to the new connection pool, NULL on error * @par Example: * @code * cpool=rxv_spin_cpool_create(p); * @endcode * @remarks This function is not normally called from the application, even * when connection pools are used. Connection pool creation and * cleanup is performed automatically by mod_spin. If you decide to * manage you own connection pools, you will need this function. * @par IMPORTANT: * The code of mod_spin calls this function to create one connection pool for * each thread of Apache. If you call apr_proc_create() to run a new process * from within any mod_spin applications, registered child cleanup function * will close all connections and remove them from the pool before the new * process is executed inside the forked one. This equally applies to the * connection pools created by you. */ rxv_spin_cpool_t *rxv_spin_cpool_create(apr_pool_t *pool); /** * Get a registered connection of foreign type from the connection pool. * * @param pool Memory pool for temporary allocations * @param cpool Connection pool * @param conninfo Connection string for this connection * @return Pointer to the connection or NULL if not found * @par Example: * @code * rxv_spin_cpool_get(ctx->pool,ctx->cpool, "ldap://ldap.example.com/dc=example,dc=com"); * @endcode */ rxv_spin_conn_t *rxv_spin_cpool_get(apr_pool_t *pool,rxv_spin_cpool_t *cpool, const char *conninfo); /** * Register a connection of foreign type with the connection pool. * * @param cpool Connection pool * @param conninfo Connection string for this connection * @param conn Connection pointer * @param cleanup Cleanup function to call on pool destruction * @return Pointer to the connection, NULL on error * @par Example: * @code * rxv_spin_cpool_set(ctx->cpool,"ldap://ldap.example.com/dc=example,dc=com", ldapconn,ldap_cleanup); * @endcode * @remarks This function will register the connection even if the connection * with the same key already exists. This may lead to multiple * cleanup functions being registered for the same connection and * eventually segfaults. Use rxv_spin_cpool_get() function to verify * if the connection was registered before. * @par IMPORTANT: * The cleanup function MUST remove the connection from the hash table holding * all pooled connections. Failing that, the next time such connection is * requested, an invalid connection structure will be passed to the * application, causing an almost certain segfault. See db_clean() function in * mod_spin source (file db.c) for an example. * @par IMPORTANT: * The lifetime of the connection and all other variables passed into this * function has to be at least the lifetime of the connection pool you are * registering the connection with. Normally, connection pools have thread * lifetime, so if you have shorter lifetime for your connection (e.g. * request, connection), you are setting yourself up for segfaults. Also, if * the lifetime of the connection is longer than the one of the connection * pool, for instance, if it has the lifetime of the process and you are * registering it with the regular thread based connection pool, you may * experience segfaults if you rely on that connection once the thread exited. * If you are managing your own connection pool, same issues apply, except for * the fact that you control the lifetime of the connection pool, rather than * mod_spin. */ rxv_spin_conn_t *rxv_spin_cpool_set(rxv_spin_cpool_t *cpool, const char *conninfo,void *conn, apr_status_t (*cleanup)(void *data)); /** * Close a connection and remove it from the pool. * * @param conn Connection * @return APR_SUCCESS on success, otherwise an error * @par Example: * @code * rxv_spin_conn_close(conn); * @endcode * @remarks This function may be useful for explicit termination of pooled * or non-pooled connections, especially if you're managing your own * connection pools. If pooled connections aren't used, the * connection will be closed anyway during the temporary pool cleanup * (first argument to rxv_spin_db_connect()). */ apr_status_t rxv_spin_conn_close(rxv_spin_conn_t *conn); /** @} */ /* Database info flags */ /** * @addtogroup rxv_spin_database_functions * @{ */ #define RXV_SPIN_DB_INFO_DB 0x01 /**< database name */ #define RXV_SPIN_DB_INFO_USER 0x02 /**< database user */ #define RXV_SPIN_DB_INFO_PASS 0x03 /**< database password */ #define RXV_SPIN_DB_INFO_HOST 0x04 /**< database host */ #define RXV_SPIN_DB_INFO_PORT 0x05 /**< database port */ #define RXV_SPIN_DB_INFO_TTY 0x06 /**< database tty */ #define RXV_SPIN_DB_INFO_OPTIONS 0x07 /**< database options */ #define RXV_SPIN_DB_INFO_ERROR 0x08 /**< database error */ /** @} */ /* Database info macros */ /** * @addtogroup rxv_spin_database_functions * @{ */ #define rxv_spin_db_db(conn) \ rxv_spin_db_info((conn),RXV_SPIN_DB_INFO_DB) /**< get name */ #define rxv_spin_db_user(conn) \ rxv_spin_db_info((conn),RXV_SPIN_DB_INFO_USER) /**< get user */ #define rxv_spin_db_pass(conn) \ rxv_spin_db_info((conn),RXV_SPIN_DB_INFO_PASS) /**< get password */ #define rxv_spin_db_host(conn) \ rxv_spin_db_info((conn),RXV_SPIN_DB_INFO_HOST) /**< get host */ #define rxv_spin_db_port(conn) \ rxv_spin_db_info((conn),RXV_SPIN_DB_INFO_PORT) /**< get port */ #define rxv_spin_db_tty(conn) \ rxv_spin_db_info((conn),RXV_SPIN_DB_INFO_TTY) /**< get tty */ #define rxv_spin_db_options(conn) \ rxv_spin_db_info((conn),RXV_SPIN_DB_INFO_OPTIONS) /**< get options */ #define rxv_spin_db_error(conn) \ rxv_spin_db_info((conn),RXV_SPIN_DB_INFO_ERROR) /**< get error */ /** @} */ /** * Service function (mod_spin API) * * @defgroup rxv_spin_service_function Service function * @{ */ typedef int (*rxv_spin_service_t)(rxv_spin_context_t *context); /**< service function */ /** @} */ /* Legacy macros for backward compatibility - MAY BE REMOVED ANYTIME! */ #ifndef DOXYGEN_SHOULD_SKIP_THIS #define rxv_spin_str_tometa(p,...) rxv_spin_meta_vstr((p),...) #define rxv_spin_column(p,s) rxv_spin_meta_empty((p),(s)) #define rxv_spin_column_mark(c,e) rxv_spin_meta_mark((c),(e)) #define rxv_spin_column_markeach(c,n) rxv_spin_meta_markeach((c),0,(n)) #define rxv_spin_single_tostr(p,s) rxv_spin_single_get(s) #define rxv_spin_ctx_str_set(c,k,v) rxv_spin_ctx_strset((c),(k),(v)) #define rxv_spin_app_str_get(c,k) rxv_spin_app_strget((c),(k)) #define rxv_spin_app_str_set(c,k,v) rxv_spin_app_strset((c),(k),(v)) #define rxv_spin_ses_str_get(c,k) rxv_spin_ses_strget((c),(k)) #define rxv_spin_ses_str_set(c,k,v) rxv_spin_ses_strset((c),(k),(v)) #define rxv_spin_db_conn rxv_spin_conn #define rxv_spin_db_conn_t rxv_spin_conn_t #define rxv_spin_db_pool rxv_spin_cpool #define rxv_spin_db_pool_t rxv_spin_cpool_t #define rxv_spin_db_pool_create(p) rxv_spin_cpool_create(p) #define rxv_spin_db_finish(c) rxv_spin_conn_close(c) #define RXV_SPIN_DB_PGSQL RXV_SPIN_CONN_PGSQL #define RXV_SPIN_DB_MYSQL RXV_SPIN_CONN_MYSQL #define RXV_SPIN_DB_MINID RXV_SPIN_CONN_MINID #define RXV_SPIN_DB_MAXID RXV_SPIN_CONN_MAXID #define RXV_SPIN_DB_POOLED RXV_SPIN_CONN_POOLED #define RXV_SPIN_DB_IS_PGSQL(c) RXV_SPIN_CONN_IS_PGSQL(c) #define RXV_SPIN_DB_IS_MYSQL(c) RXV_SPIN_CONN_IS_MYSQL(c) #define RXV_SPIN_DB_IS_POOLED(c) RXV_SPIN_CONN_IS_POOLED(c) #endif /* End of legacy macros */ #endif