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