/*
* 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 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
#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
#if defined (HAVE_LIBPQ) && defined (HAVE_LIBPQ_FE_H)
#include
#endif
#if defined (HAVE_LIBMYSQLCLIENT_R) && defined (HAVE_MYSQL_H)
#include
#endif
#if defined (HAVE_LIBSQLITE) && defined (HAVE_SQLITE_H)
#include
#endif
#if defined (HAVE_LIBSQLITE3) && defined (HAVE_SQLITE3_H)
#include
#endif
/**
* 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 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 polled 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.
*/
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.
*/
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