/* * rxv_spin.h * * Copyright (C) 2003 - 2008 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 - 2008 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 */ #define RXV_SPIN_STDC_HEADERS @RXV_SPIN_STDC_HEADERS@ #define RXV_SPIN_HAVE_UNISTD_H @RXV_SPIN_HAVE_UNISTD_H@ #define RXV_SPIN_HAVE_SYS_TYPES_H @RXV_SPIN_HAVE_SYS_TYPES_H@ #define RXV_SPIN_HAVE_APR_STRINGS_H @RXV_SPIN_HAVE_APR_STRINGS_H@ #define RXV_SPIN_HAVE_APR_POOLS_H @RXV_SPIN_HAVE_APR_POOLS_H@ #define RXV_SPIN_HAVE_APR_HASH_H @RXV_SPIN_HAVE_APR_HASH_H@ #define RXV_SPIN_HAVE_APR_TIME_H @RXV_SPIN_HAVE_APR_TIME_H@ #define RXV_SPIN_HAVE_APR_DSO_H @RXV_SPIN_HAVE_APR_DSO_H@ #define RXV_SPIN_HAVE_APR_BUCKETS_H @RXV_SPIN_HAVE_APR_BUCKETS_H@ #define RXV_SPIN_HAVE_APR_DBD_H @RXV_SPIN_HAVE_APR_DBD_H@ #define RXV_SPIN_HAVE_APREQ_PARAM_H @RXV_SPIN_HAVE_APREQ_PARAM_H@ #define RXV_SPIN_HAVE_APREQ_COOKIE_H @RXV_SPIN_HAVE_APREQ_COOKIE_H@ #define RXV_SPIN_HAVE_APREQ_MODULE_H @RXV_SPIN_HAVE_APREQ_MODULE_H@ #define RXV_SPIN_HAVE_HTTPD_H @RXV_SPIN_HAVE_HTTPD_H@ #define RXV_SPIN_HAVE_HTTP_REQUEST_H @RXV_SPIN_HAVE_HTTP_REQUEST_H@ #define RXV_SPIN_HAVE_AP_REGEX_H @RXV_SPIN_HAVE_AP_REGEX_H@ #include #if RXV_SPIN_STDC_HEADERS #include #include #include #include #endif #if RXV_SPIN_HAVE_UNISTD_H #include #endif #if RXV_SPIN_HAVE_SYS_TYPES_H #include #endif #if RXV_SPIN_HAVE_APR_STRINGS_H #include #endif #if RXV_SPIN_HAVE_APR_POOLS_H #include #endif #if RXV_SPIN_HAVE_APR_HASH_H #include #endif #if RXV_SPIN_HAVE_APR_TIME_H #include #endif #if RXV_SPIN_HAVE_APR_DSO_H #include #endif #if RXV_SPIN_HAVE_APR_BUCKETS_H #include #endif #if RXV_SPIN_HAVE_APR_DBD_H #include #endif #if RXV_SPIN_HAVE_APREQ_PARAM_H #include #endif #if RXV_SPIN_HAVE_APREQ_COOKIE_H #include #endif #if RXV_SPIN_HAVE_APREQ_MODULE_H #include #endif /* 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 #if RXV_SPIN_HAVE_HTTPD_H #include #endif /* 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 #if RXV_SPIN_HAVE_HTTP_REQUEST_H #include #endif #if RXV_SPIN_HAVE_AP_REGEX_H #include #endif /** * Data functions (mod_spin API) * * @defgroup rxv_spin_data_functions Data functions * @{ */ 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 */ typedef struct rxv_spin_curs rxv_spin_curs_t; /**< cursor type */ /** @} */ /** * 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. * * @param pool Pool for allocation of data structure * @param str String to create data from * @param data Pointer to existing data or NULL to allocate anew * @return rxv_spin_data_t pointer, structure allocated from * the pool, NULL on error * @par Example: * @code * rxv_spin_datum(pool,"some string",NULL); * @endcode * @remarks Pool can be NULL is data is not NULL. */ rxv_spin_data_t *rxv_spin_datum(apr_pool_t *pool,const char *str, rxv_spin_data_t *data); /** * 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. * * @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 * @param data Pointer to existing data or NULL to allocate anew * @return rxv_spin_data_t pointer, structure allocated from * the pool, NULL on error * @par Example: * @code * rxv_spin_mdatum(pool,"1234567890",10,NULL); * @endcode * @remarks Pool can be NULL is data is not NULL. */ rxv_spin_data_t *rxv_spin_mdatum(apr_pool_t *pool,const char *str, apr_size_t size,rxv_spin_data_t *data); /** * Get a nul terminated string from single data. String is not copied. * * @param single Single data * @return nul terminated string, NULL on error * @par Example: * @code * rxv_spin_string(data); * @endcode */ char *rxv_spin_string(rxv_spin_data_t *single); /** * Get a hash table from rows data. Hash table is not copied. * * @param rows Rows data * @return pointer to hash table NULL on error * @par Example: * @code * rxv_spin_guts(data); * @endcode * @remarks Values in the hash table are apr_array_header_t pointers. The * arrays contain rxv_spin_data_t (meaning: array->elts is * rxv_spin_data_t pointer), so it is possible to use this mechanism * to iterate through individual colums as well. If you do modify * data this way, make sure all arrays contained in the hash have the * same size, or you may get segfaults when you try to access the * data. Keep in mind that although you can find out the size of the * rxv_spin_data_t this way, it is still an opaque type that should * be accessed through mod_spin API. */ apr_hash_t *rxv_spin_guts(rxv_spin_data_t *rows); /** * Get the size of data. This does not include terminating nul byte for * single. Number of rows is returned for rows. * * @param data Data * @return size of single or rows or 0 on error * @par Example: * @code * rxv_spin_size(data); * @endcode */ apr_size_t rxv_spin_size(rxv_spin_data_t *data); #define rxv_spin_multi(d) (!rxv_spin_string(d) && rxv_spin_size(d)>0) /**< check if data is of rows type */ #define rxv_spin_single(d) (!rxv_spin_multi(d)) /**< check if data is of single type */ /** * Create a column of data from other data. The data returned is of type rows. * Data is not copied. * * @param pool Pool for allocation of data structure * @param name Name of the column * @param data Pointer to existing data or NULL to allocate anew * @param ... List of rxv_spin_data_t pointers, ending with NULL * @return rxv_spin_data_t pointer, structures allocated from * the pool, NULL on error * @par Example: * @code * rxv_spin_column(pool,"column1",NULL, * rxv_spin_rows(pool,NULL,x,y,z,NULL), * rxv_spin_rows(pool,NULL,u,v,w,NULL), * rxv_spin_single(pool,"some important data",NULL), * NULL); * @endcode */ rxv_spin_data_t *rxv_spin_column(apr_pool_t *pool,const char *name, rxv_spin_data_t *data,...) #if defined(__GNUC__) && __GNUC__ >= 4 __attribute__((sentinel)) #endif ; /** * Create a column of data by parsing a string. The data returned is of type * rows. * String to parse is NOT copied into pool storage before parsing, therefore, * if you want to parse constant strings, you MUST copy them. 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 name Name of the column * @param str String to parse * @param sep Separators to use when parsing * @param data Pointer to existing data or NULL to allocate anew * @return rxv_spin_data_t pointer, structures allocated from * the pool, NULL on error * @par Example: * @code * rxv_spin_parse(pool,"column1", * apr_pstrdup(pool,"first str,second str/third str"), * ",/",NULL); * @endcode */ rxv_spin_data_t *rxv_spin_parse(apr_pool_t *pool,const char *name, char *str,const char *sep, rxv_spin_data_t *data); /** * Create a column of data from an array of nul terminated strings. The data * returned is of type rows. * Strings are not copied. * * @param pool Pool for allocation of data structure * @param name Name of the column * @param arr Array of pointers to nul terminated strings * @param data Pointer to existing data or NULL to allocate anew * @return rxv_spin_data_t pointer, structures allocated from * the pool, NULL on error * @par Example: * @code * rxv_spin_array(pool,"column1",arr,NULL); * @endcode */ rxv_spin_data_t *rxv_spin_array(apr_pool_t *pool,const char *name, apr_array_header_t *arr,rxv_spin_data_t *data); /** * Create a column of data by reading a bucket brigade. The data returned is * of type rows. * All read data is copied into the storage allocated from the pool. * * @param pool Pool for allocation of data structure * @param name Name of the column * @param bb Bucket brigade to read * @param data Pointer to existing data or NULL to allocate anew * @return rxv_spin_data_t pointer, structures allocated from * the pool, NULL on error * @par Example: * @code * rxv_spin_brigade(pool,"column1",bb,NULL); * @endcode */ rxv_spin_data_t *rxv_spin_brigade(apr_pool_t *pool,const char *name, apr_bucket_brigade *bb, rxv_spin_data_t *data); /** * Create an empty column of data. * * @param pool Pool for allocation of data structures * @param name Name of the column * @param size Size (number of elements) of the column to create * @param data Pointer to existing data or NULL to allocate anew * @return rxv_spin_data_t pointer, structures allocated from * the pool, NULL on error * @par Example: * @code * rxv_spin_null(pool,"column1",rxv_spin_rws_len(r),NULL); * @endcode * @remarks This function is useful for creating boilerplate columns. */ rxv_spin_data_t *rxv_spin_null(apr_pool_t *pool,const char *name, apr_size_t size,rxv_spin_data_t *data); /** * Merge rows of data. * If rows contain different array lengths, the biggest size will be used. * Data is not copied. * * @param pool Pool for allocation of data structure * @param data Pointer to existing data or NULL to allocate anew * @param ... List of rxv_spin_data_t pointers, ending with NULL * @return rxv_spin_data_t pointer, structures allocated from * the pool, NULL on error * @par Example: * @code * rxv_spin_rows(pool,NULL,def,ghi,xyz,NULL); * @endcode * @remarks Rows that appear later in the argument list override columns of * the same name from earlier arguments. If columns need to have * their size adjusted, empty elements will be pushed to the end of * the arrays that columns point to. Note that give that data isn't * copied, this resizing affects original columns too. */ rxv_spin_data_t *rxv_spin_rows(apr_pool_t *pool,rxv_spin_data_t *data,...) #if defined(__GNUC__) && __GNUC__ >= 4 __attribute__((sentinel)) #endif ; /** * Get first row of data. * * @param pool Pool for allocation of data structure * @param rows Data to get the first row from * @param ... List of column names (constant pointer to nul terminated * string) and cursors (pointer to pointer of rxv_spin_curs_t), * ending with NULL * @return 1 if successful, or 0 if no rows available * @par Example: * @code * rxv_spin_curs_t *c1=NULL,*c2=NULL; * * rxv_spin_first(pool,rows,"column1",&c1,"column2",&c2,NULL); * @endcode */ apr_size_t rxv_spin_first(apr_pool_t *pool,rxv_spin_data_t *rows,...) #if defined(__GNUC__) && __GNUC__ >= 4 __attribute__((sentinel)) #endif ; /** * Get next row of data. * * @param curs Pointer to the cursor * @return current row number, counted from 1, or 0 if no more available * @par Example: * @code * for(r=rxv_spin_first(p,rws,"c1",&c1,NULL);r;r=rxv_spin_next(c1)){ * ... * } * @endcode * @remarks Note that it is sufficient to call rxv_spin_next() just once on * any of the cursors that have been initialised through * rxv_spin_first() to get the next row on all of them. */ apr_size_t rxv_spin_next(rxv_spin_curs_t *curs); /** * Get data from the cursor. * * @param curs Pointer to to the cursor * @return rxv_spin_data_t pointer, NULL on error * @par Example: * @code * data=rxv_spin_this(curs); * @endcode */ rxv_spin_data_t *rxv_spin_this(rxv_spin_curs_t *curs); /** * Get an entry from rows of data. * * @param rows Rows data * @param name Name of the column * @param index Row number, starting at 1 * @return rxv_spin_data_t pointer, NULL on error * @par Example: * @code * data=rxv_spin_entry(rows,"thiscolumn",3); * @endcode */ rxv_spin_data_t *rxv_spin_entry(rxv_spin_data_t *rows,const char *name, apr_size_t index); /** * Resize rows. * * @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(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(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 src Data to copy, any type * @param data Pointer to existing data or NULL to allocate anew * @return copy of the data, NULL on error * @par Example: * @code * rxv_spin_copy(pool,data,NULL); * @endcode * @remarks If you're using this function on rows, 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 *src, 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_slower(single); * @endcode */ char *rxv_spin_slower(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_supper(single); * @endcode */ char *rxv_spin_supper(const char *str); /** * Trim whitespace from a string. No copy is made. You MUST copy the string * 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_strim(apr_strdup(pool," string with whitespace ", * RXV_SPIN_TRIM_LEFT|RXV_SPIN_TRIM_RIGHT); * @endcode */ char *rxv_spin_strim(const char *str,rxv_spin_trim_e what); #define rxv_spin_strim2(s) \ rxv_spin_strim((s),(RXV_SPIN_TRIM_LEFT|RXV_SPIN_TRIM_RIGHT)) /**< trim string on both sides */ #define rxv_spin_striml(s) \ rxv_spin_strim((s),(RXV_SPIN_TRIM_LEFT)) /**< trim string on the left side */ #define rxv_spin_strimr(s) \ rxv_spin_strim((s),(RXV_SPIN_TRIM_RIGHT)) /**< trim string on the right side */ #define rxv_spin_lower(s) \ ((s)?(rxv_spin_single(s) \ ?rxv_spin_datum(NULL,rxv_spin_slower(rxv_spin_string(s)),(s)) \ :NULL) \ :NULL) /**< convert single to lowercase */ #define rxv_spin_upper(s) \ ((s)?(rxv_spin_single(s) \ ?rxv_spin_datum(NULL,rxv_spin_supper(rxv_spin_string(s)),(s)) \ :NULL) \ :NULL) /**< convert single to uppercase */ #define rxv_spin_trim(s,w) \ ((s)?(rxv_spin_single(s) \ ?rxv_spin_datum(NULL,rxv_spin_strim(rxv_spin_string(s),(w)),(s)) \ :NULL) \ :NULL) /**< trim single */ #define rxv_spin_trim2(s) \ rxv_spin_trim((s),(RXV_SPIN_TRIM_LEFT|RXV_SPIN_TRIM_RIGHT)) /**< trim single on both sides */ #define rxv_spin_triml(s) \ rxv_spin_trim((s),(RXV_SPIN_TRIM_LEFT)) /**< trim single on the left side */ #define rxv_spin_trimr(s) \ rxv_spin_trim((s),(RXV_SPIN_TRIM_RIGHT)) /**< trim single 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_pool(ctx); * @endcode */ apr_pool_t *rxv_spin_pool(rxv_spin_ctx_t *ctx); /** * Retrieve process specific pool. * * @param ctx Context * @return pointer to process specific pool, NULL on error * @par Example: * @code * rxv_spin_ppool(ctx); * @endcode */ apr_pool_t *rxv_spin_ppool(rxv_spin_ctx_t *ctx); /** * Retrieve data from context. * * @param ctx Context * @return pointer to data, NULL on error * @par Example: * @code * rxv_spin_data(ctx); * @endcode */ rxv_spin_data_t *rxv_spin_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_r(ctx); * @endcode */ request_rec *rxv_spin_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_req(ctx); * @endcode */ apreq_handle_t *rxv_spin_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_xget(ctx); * @endcode */ void *rxv_spin_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_xset(ctx,somedata); * @endcode */ void *rxv_spin_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, NULL if not found * @par Example: * @code * rxv_spin_get(ctx,"result"); * @endcode */ rxv_spin_data_t *rxv_spin_get(rxv_spin_ctx_t *ctx,const char *key); #define rxv_spin_sget(ctx,key) \ rxv_spin_string(rxv_spin_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, NULL on error * @par Example: * @code * rxv_spin_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. * @warning * 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. * @warning * 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_set(rxv_spin_ctx_t *ctx, const char *key,rxv_spin_data_t *value); #define rxv_spin_sset(ctx,key,val) \ rxv_spin_set((ctx),(key),rxv_spin_datum(rxv_spin_pool(ctx),(val),NULL)) /**< set a string into context by converting to single */ #define rxv_spin_del(ctx,key) rxv_spin_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_sget(ctx,key) \ rxv_spin_string(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_datum(pool,"some application value",NULL)); * @endcode * @warning * 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_sset(ctx,key,val) \ rxv_spin_app_set((ctx),(key),rxv_spin_datum(rxv_spin_pool(ctx),(val),NULL)) /**< 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_ses_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_sget(ctx,key) \ rxv_spin_string(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_ses_set(ctx,"some session key", * rxv_spin_datum(pool,"some session value",NULL)); * @endcode * @warning * 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_sset(ctx,key,val) \ rxv_spin_ses_set((ctx),(key),rxv_spin_datum(rxv_spin_pool(ctx),(val),NULL)) /**< 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); /** * Kill the session * * @param ctx Context * @par Example: * @code * rxv_spin_ses_kill(ctx); * @endcode * @remarks Destroys the session by marking session data for removal from the * store. Note that session cookie will still get served. This function would * usually be used to log users out of the system, as any session data would * have to be reestablished once user logs back in. */ void rxv_spin_ses_kill(rxv_spin_ctx_t *ctx); /** * 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 if session has been killed or on error * @par Example: * @code * rxv_spin_ses_atime(ctx); * @endcode */ apr_time_t rxv_spin_ses_atime(rxv_spin_ctx_t *ctx); /** @} */ 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_open(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, * oracle, freetds etc., depending 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. * @remarks Cleanup will be registered with the context pool. */ rxv_spin_db_t *rxv_spin_db_open(rxv_spin_ctx_t *ctx,const char *conninfo); /** * Close a database connection. * * @param ctx Context * @param db Pointer to a database connection * @return APR_SUCCESS on success, otherwise an error * @par Example: * @code * rxv_spin_db_close(ctx,db); * @endcode */ apr_status_t rxv_spin_db_close(rxv_spin_ctx_t *ctx,rxv_spin_db_t *db); /** * Get the status of the connection. * * @param ctx Context * @param db Database connection * @return APR_SUCCESS if OK, otherwise an error * @par Example: * @code * if(rxv_spin_db_status(ctx,db)!=APR_SUCCESS){ * ... * } * @endcode * @remarks If this function returns any code other than success (for valid * ctx and db), the database connection should not be used again. */ apr_status_t rxv_spin_db_status(rxv_spin_ctx_t *ctx,rxv_spin_db_t *db); /** * 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,db,dbdres); * @endcode * @remarks Make sure the pool passed into this function is the same one used * for select or you will have memory allocation problems. */ 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, (constant pointer to nul * terminated string) * @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. * @warning * Make sure you use \%s where the replaced parameter is supposed to go. This * is the official Apache Portable Runtime DBD way :-). * @warning * 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,...) #if defined(__GNUC__) && __GNUC__ >= 4 __attribute__((sentinel)) #endif ; /** * 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, (constant pointer to nul * terminated string) * @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. * @warning * Make sure you use \%s where the replaced parameter is supposed to go. This * is the official Apache Portable Runtime DBD way :-). * @warning * 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,...) #if defined(__GNUC__) && __GNUC__ >= 4 __attribute__((sentinel)) #endif ; /** * 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); /** @} */ /** * 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. * @warning * If you decide to close the connection by hand, you must remove the cleanup * function from the thread specific pool (you can find out what that is by * calling rxv_spin_tpool()). * @warning * 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. */ 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__ */