/** * store.c * * Copyright (C) 2003 - 2006 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. * */ #include "private.h" /* * Application and session API functions */ static apr_status_t xmlctx_clean(void *data){ xmlFreeParserCtxt((xmlParserCtxt *)data); return APR_SUCCESS; } static apr_status_t xmldoc_clean(void *data){ xmlFreeDoc((xmlDoc *)data); return APR_SUCCESS; } static void config_parse(rxv_spin_ctx_t *ctx){ xmlParserCtxt *ctxt; xmlDoc *doc; if(!(ctxt=xmlNewParserCtxt())) return; apr_pool_cleanup_register(ctx->pool,ctxt,xmlctx_clean,apr_pool_cleanup_null); if((doc=xmlCtxtReadFile(ctxt,ctx->cfgfn,NULL,XML_PARSE_DTDVALID))){ xmlNode *root,*node,*text; xmlAttr *attr; apr_pool_cleanup_register(ctx->pool,doc,xmldoc_clean,apr_pool_cleanup_null); root=xmlDocGetRootElement(doc); for(node=root->children;node;node=node->next){ if(xmlStrEqual(node->name,(xmlChar*)"prop") && (text=node->children) && xmlStrEqual(text->name,(xmlChar*)"text") && text->content && (attr=node->properties) && xmlStrEqual(attr->name,(xmlChar*)"name") && attr->children){ apr_hash_set(ctx->app,attr->children->content,APR_HASH_KEY_STRING, rxv_spin_single(ctx->pool,(char *)text->content)); } } } } #define SIZE_DIGITS RXV_SPIN_UNS_DIGITS(apr_size_t) static void data_parse(apr_pool_t *pool,char *data,apr_hash_t *h){ apr_size_t len,i,b; char *p,*e,*k,*v,*end=data+strlen(data); for(p=data;pend) return; /* length of the key */ for(e=p+SIZE_DIGITS-1,len=0,b=1,i=0;iend) return; /* copy the key */ k=apr_pstrmemdup(pool,p,len); /* advance over the key to the beginning of the value length */ p+=len; /* check bounds */ if(p+SIZE_DIGITS>end) return; /* length of the value */ for(e=p+SIZE_DIGITS-1,len=0,b=1,i=0;iend) return; /* copy the value */ v=apr_pstrmemdup(pool,p,len); /* advance over the value to the beginning of the next key length */ p+=len; apr_hash_set(h,k,APR_HASH_KEY_STRING,rxv_spin_single(pool,v)); } } static char *data_store(apr_pool_t *pool,apr_hash_t *h){ apr_size_t tlen=0,slen,i,l; char *p,*e,*k,*v,*data; apr_hash_index_t *hi; const void *key; void *val; apr_ssize_t len; for(hi=apr_hash_first(pool,h);hi;hi=apr_hash_next(hi)){ apr_hash_this(hi,&key,&len,&val); k=(char *)key; v=((rxv_spin_data_t *)val)->data; tlen+=SIZE_DIGITS+strlen(k)+SIZE_DIGITS+strlen(v); } data=apr_palloc(pool,tlen+1); for(p=data,hi=apr_hash_first(pool,h);hi;hi=apr_hash_next(hi)){ apr_hash_this(hi,&key,&len,&val); k=(char *)key; v=((rxv_spin_data_t *)val)->data; /* encode key length */ slen=strlen(k); for(e=p+SIZE_DIGITS-1,l=slen,i=0;istore->driver,ctx->pool, ctx->store->handle,&txn)){ if(txn) apr_dbd_transaction_end(ctx->store->driver,ctx->pool,txn); apr_sleep(TXN_TIMEOUT); continue; } /* only update if application data modified */ if(ctx->adirty){ if(!astamp){ astamp=apr_psprintf(ctx->pool,"%" APR_TIME_T_FMT,ctx->ptime); adata=data_store(ctx->pool,ctx->app); } if(!ctx->ustore) ctx->ustore=apr_psprintf(ctx->pool,update,ctx->sttbl); switch(rxv_spin_db_pquery(ctx->pool,ctx->store,ctx->ustore, astamp,adata,"__application",NULL)){ case -1: apr_dbd_transaction_end(ctx->store->driver,ctx->pool,txn); apr_sleep(TXN_TIMEOUT); continue; case 0: if(!ctx->istore) ctx->istore=apr_psprintf(ctx->pool,insert,ctx->sttbl); if(rxv_spin_db_pquery(ctx->pool,ctx->store,ctx->istore, "__application",astamp,adata,NULL)<=0){ apr_dbd_transaction_end(ctx->store->driver,ctx->pool,txn); apr_sleep(TXN_TIMEOUT); continue; } break; default: break; } } /* only update if session valid */ if(ctx->sesid && ctx->valid){ if(!sstamp) sstamp=apr_psprintf(ctx->pool,"%" APR_TIME_T_FMT,ctx->atime); /* only update access time if session isn't dirty */ if(ctx->sdirty){ if(!sdata) sdata=data_store(ctx->pool,ctx->ses); if(!ctx->ustore) ctx->ustore=apr_psprintf(ctx->pool,update,ctx->sttbl); switch(rxv_spin_db_pquery(ctx->pool,ctx->store, ctx->ustore,sstamp,sdata,ctx->sesid,NULL)){ case -1: apr_dbd_transaction_end(ctx->store->driver,ctx->pool,txn); apr_sleep(TXN_TIMEOUT); continue; case 0: if(!ctx->istore) ctx->istore=apr_psprintf(ctx->pool,insert,ctx->sttbl); if(rxv_spin_db_pquery(ctx->pool,ctx->store, ctx->istore,ctx->sesid,sstamp,sdata,NULL)<=0){ apr_dbd_transaction_end(ctx->store->driver,ctx->pool,txn); apr_sleep(TXN_TIMEOUT); continue; } break; default: break; } } else{ if(!ctx->ustamp) ctx->ustamp=apr_psprintf(ctx->pool,ustamp,ctx->sttbl); switch(rxv_spin_db_pquery(ctx->pool,ctx->store, ctx->ustamp,sstamp,ctx->sesid,NULL)){ case -1: apr_dbd_transaction_end(ctx->store->driver,ctx->pool,txn); apr_sleep(TXN_TIMEOUT); continue; case 0: if(!ctx->istore) ctx->istore=apr_psprintf(ctx->pool,insert,ctx->sttbl); if(!sdata) sdata=data_store(ctx->pool,ctx->ses); if(rxv_spin_db_pquery(ctx->pool,ctx->store, ctx->istore,ctx->sesid,sstamp,sdata,NULL)<=0){ apr_dbd_transaction_end(ctx->store->driver,ctx->pool,txn); apr_sleep(TXN_TIMEOUT); continue; } break; default: break; } } } if(apr_dbd_transaction_end(ctx->store->driver,ctx->pool,txn)) return APR_EGENERAL; return APR_SUCCESS; } if(txn) apr_dbd_transaction_end(ctx->store->driver,ctx->pool,txn); return APR_EGENERAL; } static void store_open(rxv_spin_ctx_t *ctx){ rxv_spin_db_t *store; rxv_spin_data_t *res; apr_finfo_t *fi; apr_dbd_transaction_t *txn=NULL; const char *select="SELECT tstamp,data FROM %s WHERE id=%%s"; /* first mark that we've been here before */ ctx->close=store_close; if(!(store=rxv_spin_db_connect(ctx,ctx->cinfo))) return; fi=apr_pcalloc(ctx->pool,sizeof(*fi)); if(apr_stat(fi,ctx->cfgfn,APR_FINFO_MTIME,ctx->pool)!=APR_SUCCESS) return; if(apr_dbd_transaction_start(store->driver,ctx->pool,store->handle,&txn)) return; if(!ctx->sstore) ctx->sstore=apr_psprintf(ctx->pool,select,ctx->sttbl); /* pick up previous change time and application store data */ res=rxv_spin_db_pselect(ctx->pool,store,ctx->sstore,"__application",NULL); if(res && res->size>0){ rxv_spin_data_t *s; s=apr_hash_get(res->cols,"tstamp",APR_HASH_KEY_STRING); ctx->ptime=apr_atoi64(s->data); s=apr_hash_get(res->cols,"data",APR_HASH_KEY_STRING); data_parse(ctx->pool,s->data,ctx->app); } if(ctx->sesid && ctx->valid){ /* pick up session store data */ res=rxv_spin_db_pselect(ctx->pool,store,ctx->sstore,ctx->sesid,NULL); if(res && res->size>0){ apr_time_t atime; rxv_spin_data_t *s; s=apr_hash_get(res->cols,"tstamp",APR_HASH_KEY_STRING); atime=apr_atoi64(s->data); /* pick up the session data only if session didn't time out */ if(!ctx->tmout || atime+ctx->tmout>=ctx->atime){ s=apr_hash_get(res->cols,"data",APR_HASH_KEY_STRING); data_parse(ctx->pool,s->data,ctx->ses); } } } if(apr_dbd_transaction_end(store->driver,ctx->pool,txn)) return; /* parse if application configuration file changed since last parse */ if(fi->mtime>=ctx->ptime){ ctx->adirty=RXV_SPIN_TRUE; ctx->ptime=ctx->atime; config_parse(ctx); } /* announce that we have the store */ ctx->store=store; } /* application get/set/del */ rxv_spin_data_t *rxv_spin_app_get(rxv_spin_ctx_t *ctx,const char *key){ if(ctx && key){ if(!ctx->close) store_open(ctx); if(ctx->store) return apr_hash_get(ctx->app,key,APR_HASH_KEY_STRING); } return NULL; } rxv_spin_data_t *rxv_spin_app_set(rxv_spin_ctx_t *ctx, const char *key,rxv_spin_data_t *val){ if(ctx && key && val && val->type==RXV_SPIN_DATA_SGL){ if(!ctx->close) store_open(ctx); if(ctx->store){ apr_hash_set(ctx->app,key,APR_HASH_KEY_STRING,val); ctx->adirty=RXV_SPIN_TRUE; return val; } } return NULL; } apr_status_t rxv_spin_app_del(rxv_spin_ctx_t *ctx,const char *key){ if(ctx && key){ if(!ctx->close) store_open(ctx); if(ctx->store){ apr_hash_set(ctx->app,key,APR_HASH_KEY_STRING,NULL); ctx->adirty=RXV_SPIN_TRUE; return APR_SUCCESS; } } return APR_EGENERAL; } /* session get/set/del */ rxv_spin_data_t *rxv_spin_ses_get(rxv_spin_ctx_t *ctx,const char *key){ if(ctx && ctx->valid && ctx->sesid && key){ if(!ctx->close) store_open(ctx); if(ctx->store) return apr_hash_get(ctx->ses,key,APR_HASH_KEY_STRING); } return NULL; } rxv_spin_data_t *rxv_spin_ses_set(rxv_spin_ctx_t *ctx, const char *key,rxv_spin_data_t *val){ if(ctx && ctx->valid && ctx->sesid && key && val && val->type==RXV_SPIN_DATA_SGL){ if(!ctx->close) store_open(ctx); if(ctx->store){ apr_hash_set(ctx->ses,key,APR_HASH_KEY_STRING,val); ctx->sdirty=RXV_SPIN_TRUE; return val; } } return NULL; } apr_status_t rxv_spin_ses_del(rxv_spin_ctx_t *ctx,const char *key){ if(ctx && ctx->valid && ctx->sesid && key){ if(!ctx->close) store_open(ctx); if(ctx->store){ apr_hash_set(ctx->ses,key,APR_HASH_KEY_STRING,NULL); ctx->sdirty=RXV_SPIN_TRUE; return APR_SUCCESS; } } return APR_EGENERAL; } /* session id and validity */ char *rxv_spin_ses_id(rxv_spin_ctx_t *ctx){ return ctx?ctx->sesid:NULL; } int rxv_spin_ses_valid(rxv_spin_ctx_t *ctx){ return ctx?(int)ctx->valid:0; } /* access time for application/session */ apr_time_t rxv_spin_ses_atime(rxv_spin_ctx_t *ctx){ if((ctx && ctx->valid && ctx->sesid)) return ctx->atime; return 0; }