/* * 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 Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, 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 Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * */ #ifndef __RXV_SPIN_H__ #define __RXV_SPIN_H__ /** * @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 #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 #include #include #include /** * Data functions (mod_spin API) * * @defgroup rxv_spin_data_functions Data functions * @{ */ typedef enum{ RXV_SPIN_DATA_SGL=1, /**< single data type (size limited string) */ RXV_SPIN_DATA_RWS, /**< rows data type (named columns, numbered rows) */ RXV_SPIN_DATA_MTA /**< metadata type (not used in rendering) */ } rxv_spin_data_e; typedef enum{ RXV_SPIN_TRIM_LEFT=1, /**< trim whitespace on the left */ RXV_SPIN_TRIM_RIGHT /**< trim whitespace on the right */ } rxv_spin_trim_e; typedef struct rxv_spin_data rxv_spin_data_t; /**< data type */ /** * Data structure: representation of all data placed in context, be that * singles (nul terminated, counted strings), rows (hash table of column names * pointing to arrays of data) or metadata (array of data, used to facilitate * the API only. */ struct rxv_spin_data{ rxv_spin_data_e type:2; /**< data type: single, rows or metadata */ apr_size_t size; /**< data size: length of single, number of rows, number of elements in metadata */ union{ char *data; /**< single data */ apr_hash_t *cols; /**< columns of rows type */ rxv_spin_data_t *meta; /**< pointer to array of data */ }; }; /** @} */ /** * Context functions (mod_spin API) * * @defgroup rxv_spin_context_functions Context functions * @{ */ typedef struct rxv_spin_ctx rxv_spin_ctx_t; /**< context type */ /** @} */ /** * @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(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(pool,"1234567890",10); * @endcode */ rxv_spin_data_t *rxv_spin_single_mem(apr_pool_t *pool,const char *str, apr_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, apr_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, rxv_spin_trim_e 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(pool, * rxv_spin_rows(pool,"x",x,"y",y,"z",z,NULL), * rxv_spin_rows(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(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(pool, * apr_pstrdup(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 metadata by reading a bucket brigade. * 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. All read data is copied into * the storage allocated from the pool. * * @param pool Pool for allocation of data structure * @param bb Bucket brigade to read * @return rxv_spin_data_t pointer, structures allocated from * the pool, NULL on error * @par Example: * @code * rxv_spin_meta_brigade(pool,bb); * @endcode */ rxv_spin_data_t *rxv_spin_meta_brigade(apr_pool_t *pool,apr_bucket_brigade *bb); /** * 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(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,apr_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(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,apr_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, apr_size_t off,apr_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(pool,"Australia,Japan",","), * rxv_spin_meta_hash(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(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(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, apr_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, apr_size_t off,apr_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(pool,"Australia,Japan",","), * "country","selected", * rxv_spin_rows_hash(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(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, apr_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(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(pool," string with whitespace ", * RXV_SPIN_TRIM_LEFT|RXV_SPIN_TRIM_RIGHT); * @endcode */ char *rxv_spin_str_trim(char *str,rxv_spin_trim_e 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 context specific pool. * * @param ctx Context * @return pointer to context specific pool, NULL on error * @par Example: * @code * rxv_spin_ctx_pool(ctx); * @endcode */ apr_pool_t *rxv_spin_ctx_pool(rxv_spin_ctx_t *ctx); /** * Retrieve thread specific pool. * * @param ctx Context * @return pointer to thread specific pool, NULL on error * @par Example: * @code * rxv_spin_ctx_tpool(ctx); * @endcode */ apr_pool_t *rxv_spin_ctx_tpool(rxv_spin_ctx_t *ctx); /** * Retrieve data from context. * * @param ctx Context * @return pointer to data, NULL on error * @par Example: * @code * rxv_spin_ctx_data(ctx); * @endcode */ rxv_spin_data_t *rxv_spin_ctx_data(rxv_spin_ctx_t *ctx); /** * Retrieve Apache request from context. * * @param ctx Context * @return pointer to Apache request, NULL on error * @par Example: * @code * rxv_spin_ctx_r(ctx); * @endcode */ request_rec *rxv_spin_ctx_r(rxv_spin_ctx_t *ctx); /** * Retrieve parsed request from context. * * @param ctx Context * @return pointer to parsed request, NULL on error * @par Example: * @code * rxv_spin_ctx_req(ctx); * @endcode */ apreq_handle_t *rxv_spin_ctx_req(rxv_spin_ctx_t *ctx); /** * Retrieve extra data from the context. * * @param ctx Context * @return pointer to extra data, NULL on error * @par Example: * @code * rxv_spin_ctx_xget(ctx); * @endcode */ void *rxv_spin_ctx_xget(rxv_spin_ctx_t *ctx); /** * Set extra data into the context. * * @param ctx Context * @param extra Pointer to extra data to set into the context * @return pointer to extra data, NULL on error * @par Example: * @code * rxv_spin_ctx_xset(ctx,somedata); * @endcode */ void *rxv_spin_ctx_xset(rxv_spin_ctx_t *ctx,void *extra); /** * 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_ctx_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); * @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_ctx_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(rxv_spin_ctx_pool(ctx),(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 store. * * @param ctx Context * @param key Unique key by which this value is identified * @return pointer to 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_ctx_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 store. * * @param ctx Context * @param key Unique key by which this value is identified * @param val Value to be placed in the store (single) * @return pointer to the value, NULL on error * @par Example: * @code * rxv_spin_app_set(ctx,"some application key", * rxv_spin_str_single(pool,"some application value")); * @endcode * @par IMPORTANT: * The data set into the application store 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. */ rxv_spin_data_t *rxv_spin_app_set(rxv_spin_ctx_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(rxv_spin_ctx_pool(ctx),(val))) /**< set string into application instead of single */ /** * Delete a record in the application store. * * @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_ctx_t *ctx,const char *key); /** * Retrieve a value from the session store. * * @param ctx Context * @param key Unique key by which this value is identified * @return pointer to 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_ctx_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 store. * * @param ctx Context * @param key Unique key by which this value is identified * @param val Value to be placed in the store (single) * @return pointer to the value, NULL on error * @par Example: * @code * rxv_spin_app_set(ctx,"some session key", * rxv_spin_str_single(pool,"some session value")); * @endcode * @par IMPORTANT: * The data set into the session store 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. */ rxv_spin_data_t *rxv_spin_ses_set(rxv_spin_ctx_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(rxv_spin_ctx_pool(ctx),(val))) /**< set string into application instead of single */ /** * Delete a record in the session store. * * @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_ctx_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_id(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_id(rxv_spin_ctx_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_ctx_t *ctx); /** * Last access time for the session. * * @param ctx Context * @return Access time, 0 on error * @par Example: * @code * rxv_spin_ses_atime(ctx); * @endcode */ apr_time_t rxv_spin_ses_atime(rxv_spin_ctx_t *ctx); /** @} */ /* Database access structures and functions NOTE: Everything is designed to be PostgreSQL centric. Sorry :-( */ typedef struct rxv_spin_db rxv_spin_db_t; /**< database connection type */ typedef struct rxv_spin_db_txn rxv_spin_db_txn_t; /**< database transaction type */ /** * Database functions (mod_spin API) * * @defgroup rxv_spin_database_functions Database functions * @{ */ /** * Retrieve database specific pool. * * @param db Database connection * @return pointer to database specific pool, NULL on error * @par Example: * @code * rxv_spin_db_pool(db); * @endcode */ apr_pool_t *rxv_spin_db_pool(rxv_spin_db_t *db); /** * Retrieve database connection information. * * @param db Database connection * @return pointer to connection information, NULL on error * @par Example: * @code * rxv_spin_db_cinfo(db); * @endcode */ char *rxv_spin_db_cinfo(rxv_spin_db_t *db); /** * Retrieve database driver. * * @param db Database connection * @return pointer to database driver, NULL on error * @par Example: * @code * rxv_spin_db_driver(db); * @endcode */ const apr_dbd_driver_t *rxv_spin_db_driver(rxv_spin_db_t *db); /** * Retrieve database handle. * * @param db Database connection * @return pointer to database handle, NULL on error * @par Example: * @code * rxv_spin_db_handle(db); * @endcode */ apr_dbd_t *rxv_spin_db_handle(rxv_spin_db_t *db); /** * Retrieve database transaction. * * @param txn Database transaction * @return pointer to database transaction, NULL on error * @par Example: * @code * rxv_spin_db_txn(txn); * @endcode * @remarks This function returns underlying Apache Portable Runtime DBD * transaction. */ apr_dbd_transaction_t *rxv_spin_db_txn(rxv_spin_db_txn_t *txn); /** * Connect to a database and optionally pool the connection. * * @param ctx Context * @param conninfo Connection string * @return pointer to a database connection, NULL on error * @par Example: * @code * rxv_spin_db_connect(ctx,"pgsql:dbname=spintest"); * @endcode * @remarks Whether or not the connection will be pooled, depends on what has * been set as connection pool value in the context. And this depends * on the SpinConnPool configuration parameter (default is on). * Valid database prefixes are pgsql, mysql, sqlite2, sqlite3 and * oracle. However, actual database support depends on the drivers * that have been compiled and linked with Apache Portable Runtime * Utilities Library (APU). * @remarks Connections will be pooled by using connection string as a key * into the hash. So, if a connection string differs in the amount of * white space or case, this will open a new connection. * @par IMPORTANT: * Note that the pooled connection will be closed in the child on * apr_proc_create(). */ rxv_spin_db_t *rxv_spin_db_connect(rxv_spin_ctx_t *ctx,const char *conninfo); /** * Convert APR DBD SQL result set to mod_spin database result. * * @param pool Pool used for memory allocation * @param db Database connection * @param dbdres APR DBD results * @return Valid rxv_spin_data_t pointer, NULL on error * @par Example: * @code * rxv_spin_db_data(pool,conn,&result,dbdres); * @endcode * @remarks If *resultp is NULL, the result will be allocated from the pool. * result->error and result->status won't be set by this function - it * is caller's responsibility to do that. */ rxv_spin_data_t *rxv_spin_db_data(apr_pool_t *pool,rxv_spin_db_t *db, apr_dbd_results_t *dbdres); /** * Execute a database query that returns a result set (i.e. SELECT). * * @param pool Pool used for memory allocation * @param db Database connection * @param query SQL query to be performed * @return Valid rxv_spin_data_t pointer, NULL on error * @par Example: * @code * result=rxv_spin_db_select(pool,db,"select * from spintest"); * @endcode * @remarks If the query finished successfully, returned data will not be * NULL, but a pointer to an empty data structure. */ rxv_spin_data_t *rxv_spin_db_select(apr_pool_t *pool,rxv_spin_db_t *db, const char *query); /** * Execute a database query that doesn't return a result set. * * @param pool Pool used for memory allocation * @param db Database connection * @param query SQL query to be performed * @return Number of rows affected, -1 on error * @par Example: * @code * nrows=rxv_spin_db_query(pool,db,"delete from spintest"); * @endcode */ int rxv_spin_db_query(apr_pool_t *pool,rxv_spin_db_t *db,const char *query); /** * Prepare and execute database that returns a result set (i.e. SELECT) * * @param pool Pool used for memory allocation * @param db Database connection * @param query SQL query to be prepared and executed * @param ... Parameters for prepared statement, (char *) * @return Valid rxv_spin_data_t pointer, NULL on error * @par Example: * @code * result=rxv_spin_db_pselect(pool,db,"select * from names where name = %s", * "Dude",NULL); * @endcode * @remarks Prepared statements done using this function only accept string * parameters. For anything more complicated, use native database API * and then convert the data so it can be used within the context. * Make sure the number of paramters passed is correct, or you may be * in for a nasty surprise when your program segfaults. * @par IMPORTANT: * Make sure you use %s where the replaced parameter is supposed to go. This * is the official Apache Portable Runtime DBD way :-). * @par IMPORTANT: * The last parameter in the list should be NULL and it has to be specified * even when there are no other parameters. */ rxv_spin_data_t *rxv_spin_db_pselect(apr_pool_t *pool,rxv_spin_db_t *db, const char *query,...); /** * Prepare and execute database that doesn't return a result set. * * @param pool Pool used for memory allocation * @param db Database connection * @param query SQL query to be prepared and executed * @param ... Parameters for prepared statement, (char *) * @return Number of rows affected, -1 on error * @par Example: * @code * nrows=rxv_spin_db_pquery(pool,db,"delete from names where name = %s", * "Dude",NULL); * @endcode * @remarks Prepared statements done using this function only accept string * parameters. For anything more complicated, use native database API * and then convert the data so it can be used within the context. * Make sure the number of paramters passed is correct, or you may be * in for a nasty surprise when your program segfaults. * @par IMPORTANT: * Make sure you use %s where the replaced parameter is supposed to go. This * is the official Apache Portable Runtime DBD way :-). * @par IMPORTANT: * The last parameter in the list should be NULL and it has to be specified * even when there are no other parameters. */ int rxv_spin_db_pquery(apr_pool_t *pool,rxv_spin_db_t *db, const char *query,...); /** * Start a transaction. * * @param pool Pool used for memory allocation * @param db Database connection * @return Pointer to a valid transaction, NULL on error * @par Example: * @code * txn=rxv_spin_db_start(pool,db); * @endcode * @remarks The pool is also used to register a cleanup function for the * transaction. It makes little sense to have transactions more * permanent than one request, so this should always be the request * pool, which can be obtained from the context. */ rxv_spin_db_txn_t *rxv_spin_db_start(apr_pool_t *pool,rxv_spin_db_t *db); /** * End a transaction. * * @param txn Database transaction * @return APR_SUCCESS on success, otherwise an error * @par Example: * @code * rxv_spin_db_end(txn); * @endcode */ apr_status_t rxv_spin_db_end(rxv_spin_db_txn_t *txn); /** * Get the status of the connection. * * @param pool Pool used for memory allocation * @param db Database connection * @return APR_SUCCESS if OK, otherwise an error * @par Example: * @code * rxv_spin_db_status(pool,db); * @endcode */ apr_status_t rxv_spin_db_status(apr_pool_t *pool,rxv_spin_db_t *db); /** @} */ /** * Connection functions (mod_spin API) * * @defgroup rxv_spin_connection_functions Connection functions * @{ */ /** * Get a registered connection from the connection pool. * * @param ctx Context * @param conninfo Connection string for this connection * @return Pointer to the connection or NULL if not found * @par Example: * @code * rxv_spin_conn_get(ctx,"openldap:ldap://ldap.example.com/dc=example,dc=com"); * @endcode */ void *rxv_spin_conn_get(rxv_spin_ctx_t *ctx,const char *conninfo); /** * Register a connection with the connection pool. * * @param ctx Context * @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_conn_set(ctx,"openldap:ldap://host.domain/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_conn_get() function to verify * if the connection was registered before. * @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) or the connect string, 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. Note that the connection will be closed in the child on * apr_proc_create(). */ void *rxv_spin_conn_set(rxv_spin_ctx_t *ctx, const char *conninfo,void *conn, apr_status_t (*cleanup)(void *data)); /** @} */ /** * Application entry functions (mod_spin API) * * @defgroup rxv_spin_entry_functions Application entry functions * @{ */ typedef void (*rxv_spin_init_t)(rxv_spin_ctx_t *ctx); /**< init function */ typedef int (*rxv_spin_prepare_t)(rxv_spin_ctx_t *ctx); /**< prepare function */ typedef int (*rxv_spin_service_t)(rxv_spin_ctx_t *ctx); /**< service function */ /** @} */ /* DSO loading functions */ /** * DSO loading functions (mod_spin API) * * @defgroup rxv_spin_dso_functions DSO loading functions * @{ */ /** * Load a DSO into the cache. * * @param ctx Context * @param path Full path of the DSO to load * @param handle DSO handle * @return APR_SUCCESS on successful load, otherwise an error * @par Example: * @code * rxv_spin_dso_load(ctx,"/path/to/dso/object.so",&handle); * @endcode * @remarks To preserve pool sanity, this function copies path into the * memory allocated from the private pool. */ apr_status_t rxv_spin_dso_load(rxv_spin_ctx_t *ctx,const char *path, apr_dso_handle_t **handle); /** @} */ /** * Crypto functions (mod_spin API) * * @defgroup rxv_spin_crypto_functions Crypto functions * @{ */ /** * Create MD5 hash, using ASCII letters only * * @param pool Pool for allocation the hash * @param uniq String to hash * @return MD5 hash of the string, 32 bytes long, encoded [a-p] * @par Example: * @code * rxv_spin_hash(pool,"some string"); * @endcode */ char *rxv_spin_hash(apr_pool_t *pool,const char *uniq); /** * Create MD5 HMAC, using ASCII letters only * * @param pool Pool for allocation the hash * @param uniq String to hash * @param salt Key used for HMAC, must be 64 bytes long * @return MD5 HMAC of the string, 32 bytes long, encoded [a-p] * @par Example: * @code * rxv_spin_hmac(pool,"some string",verysecretkey); * @endcode */ char *rxv_spin_hmac(apr_pool_t *pool,const char *uniq,const char *salt); /** @} */ #endif /* __RXV_SPIN_H__ */