1251876Speter/* Licensed to the Apache Software Foundation (ASF) under one or more 2251876Speter * contributor license agreements. See the NOTICE file distributed with 3251876Speter * this work for additional information regarding copyright ownership. 4251876Speter * The ASF licenses this file to You under the Apache License, Version 2.0 5251876Speter * (the "License"); you may not use this file except in compliance with 6251876Speter * the License. You may obtain a copy of the License at 7251876Speter * 8251876Speter * http://www.apache.org/licenses/LICENSE-2.0 9251876Speter * 10251876Speter * Unless required by applicable law or agreed to in writing, software 11251876Speter * distributed under the License is distributed on an "AS IS" BASIS, 12251876Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13251876Speter * See the License for the specific language governing permissions and 14251876Speter * limitations under the License. 15251876Speter */ 16251876Speter 17251876Speter#include "apu.h" 18251876Speter#include "apu_config.h" 19251876Speter 20251876Speter/* COMPILE_STUBS: compile stubs for unimplemented functions. 21251876Speter * 22251876Speter * This is required to compile in /trunk/, but can be 23251876Speter * undefined to compile a driver for httpd-2.2 and other 24251876Speter * APR-1.2 applications 25251876Speter */ 26251876Speter#define COMPILE_STUBS 27251876Speter 28251876Speter#if APU_HAVE_FREETDS 29251876Speter 30251876Speter#include <ctype.h> 31251876Speter#include <stdlib.h> 32251876Speter 33251876Speter#include "apr_strings.h" 34251876Speter#include "apr_lib.h" 35251876Speter 36251876Speter#include "apr_pools.h" 37251876Speter#include "apr_dbd_internal.h" 38251876Speter 39251876Speter#ifdef HAVE_FREETDS_SYBDB_H 40251876Speter#include <freetds/sybdb.h> 41251876Speter#endif 42251876Speter#ifdef HAVE_SYBDB_H 43251876Speter#include <sybdb.h> 44251876Speter#endif 45251876Speter 46251876Speter#include <stdio.h> 47251876Speter#include <sys/types.h> 48251876Speter#include <regex.h> 49251876Speter 50251876Speter/* This probably needs to change for different applications */ 51251876Speter#define MAX_COL_LEN 256 52251876Speter 53251876Spetertypedef struct freetds_cell_t { 54251876Speter int type; 55251876Speter DBINT len; 56251876Speter BYTE *data; 57251876Speter} freetds_cell_t; 58251876Speter 59251876Speterstruct apr_dbd_transaction_t { 60251876Speter int mode; 61251876Speter int errnum; 62251876Speter apr_dbd_t *handle; 63251876Speter}; 64251876Speter 65251876Speterstruct apr_dbd_t { 66251876Speter DBPROCESS *proc; 67251876Speter apr_dbd_transaction_t *trans; 68251876Speter apr_pool_t *pool; 69251876Speter const char *params; 70251876Speter RETCODE err; 71251876Speter}; 72251876Speter 73251876Speterstruct apr_dbd_results_t { 74251876Speter int random; 75251876Speter size_t ntuples; 76251876Speter size_t sz; 77251876Speter apr_pool_t *pool; 78251876Speter DBPROCESS *proc; 79251876Speter}; 80251876Speter 81251876Speterstruct apr_dbd_row_t { 82251876Speter apr_dbd_results_t *res; 83251876Speter BYTE buf[MAX_COL_LEN]; 84251876Speter}; 85251876Speter 86251876Speterstruct apr_dbd_prepared_t { 87251876Speter int nargs; 88251876Speter regex_t **taint; 89251876Speter int *sz; 90251876Speter char *fmt; 91251876Speter}; 92251876Speter 93251876Speter#define dbd_freetds_is_success(x) (x == SUCCEED) 94251876Speter 95251876Speterstatic int labelnum = 0; /* FIXME */ 96251876Speterstatic regex_t dbd_freetds_find_arg; 97251876Speter 98251876Speter/* execute a query that doesn't return a result set, mop up, 99251876Speter * and return and APR-flavoured status 100251876Speter */ 101251876Speterstatic RETCODE freetds_exec(DBPROCESS *proc, const char *query, 102251876Speter int want_results, int *nrows) 103251876Speter{ 104251876Speter /* TBD */ 105251876Speter RETCODE rv = dbcmd(proc, query); 106251876Speter if (rv != SUCCEED) { 107251876Speter return rv; 108251876Speter } 109251876Speter rv = dbsqlexec(proc); 110251876Speter if (rv != SUCCEED) { 111251876Speter return rv; 112251876Speter } 113251876Speter if (!want_results) { 114251876Speter while (dbresults(proc) != NO_MORE_RESULTS) { 115251876Speter ++*nrows; 116251876Speter } 117251876Speter } 118251876Speter return SUCCEED; 119251876Speter} 120251876Speterstatic apr_status_t clear_result(void *data) 121251876Speter{ 122251876Speter /* clear cursor */ 123251876Speter return (dbcanquery((DBPROCESS*)data) == SUCCEED) 124251876Speter ? APR_SUCCESS 125251876Speter : APR_EGENERAL; 126251876Speter} 127251876Speter 128251876Speterstatic int dbd_freetds_select(apr_pool_t *pool, apr_dbd_t *sql, 129251876Speter apr_dbd_results_t **results, 130251876Speter const char *query, int seek) 131251876Speter{ 132251876Speter apr_dbd_results_t *res; 133251876Speter if (sql->trans && (sql->trans->errnum != SUCCEED)) { 134251876Speter return 1; 135251876Speter } 136251876Speter /* the core of this is 137251876Speter * dbcmd(proc, query); 138251876Speter * dbsqlexec(proc); 139251876Speter * while (dbnextrow(dbproc) != NO_MORE_ROWS) { 140251876Speter * do things 141251876Speter * } 142251876Speter * 143251876Speter * Ignore seek 144251876Speter */ 145251876Speter 146251876Speter sql->err = freetds_exec(sql->proc, query, 1, NULL); 147251876Speter if (!dbd_freetds_is_success(sql->err)) { 148251876Speter if (sql->trans) { 149251876Speter sql->trans->errnum = sql->err; 150251876Speter } 151251876Speter return 1; 152251876Speter } 153251876Speter 154251876Speter sql->err = dbresults(sql->proc); 155251876Speter if (sql->err != SUCCEED) { 156251876Speter if (sql->trans) { 157251876Speter sql->trans->errnum = sql->err; 158251876Speter } 159251876Speter return 1; 160251876Speter } 161251876Speter 162251876Speter if (!*results) { 163251876Speter *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t)); 164251876Speter } 165251876Speter res = *results; 166251876Speter res->proc = sql->proc; 167251876Speter res->random = seek; 168251876Speter res->pool = pool; 169251876Speter res->ntuples = dblastrow(sql->proc); 170251876Speter res->sz = dbnumcols(sql->proc); 171251876Speter apr_pool_cleanup_register(pool, sql->proc, clear_result, 172251876Speter apr_pool_cleanup_null); 173251876Speter 174251876Speter#if 0 175251876Speter /* Now we have a result set. We need to bind to its vars */ 176251876Speter res->vars = apr_palloc(pool, res->sz * sizeof(freetds_cell_t*)); 177251876Speter for (i=1; i <= res->sz; ++i) { 178251876Speter freetds_cell_t *cell = &res->vars[i-1]; 179251876Speter cell->type = dbcoltype(sql->proc, i); 180251876Speter cell->len = dbcollen(sql->proc, i); 181251876Speter cell->data = apr_palloc(pool, cell->len); 182251876Speter sql->err = dbbind(sql->proc, i, /*cell->type */ STRINGBIND, cell->len, cell->data); 183251876Speter if (sql->err != SUCCEED) { 184251876Speter fprintf(stderr, "dbbind error: %d, %d, %d", i, cell->type, cell->len); 185251876Speter } 186251876Speter if ((sql->err != SUCCEED) && (sql->trans != NULL)) { 187251876Speter sql->trans->errnum = sql->err; 188251876Speter } 189251876Speter } 190251876Speter#endif 191251876Speter return (sql->err == SUCCEED) ? 0 : 1; 192251876Speter} 193251876Speterstatic const char *dbd_untaint(apr_pool_t *pool, regex_t *rx, const char *val) 194251876Speter{ 195251876Speter regmatch_t match[1]; 196251876Speter if (rx == NULL) { 197251876Speter /* no untaint expression */ 198251876Speter return val; 199251876Speter } 200251876Speter if (regexec(rx, val, 1, match, 0) == 0) { 201251876Speter return apr_pstrndup(pool, val+match[0].rm_so, 202251876Speter match[0].rm_eo - match[0].rm_so); 203251876Speter } 204251876Speter return ""; 205251876Speter} 206251876Speterstatic const char *dbd_statement(apr_pool_t *pool, 207251876Speter apr_dbd_prepared_t *stmt, 208251876Speter int nargs, const char **args) 209251876Speter{ 210251876Speter int i; 211251876Speter int len; 212251876Speter const char *var; 213251876Speter char *ret; 214251876Speter const char *p_in; 215251876Speter char *p_out; 216251876Speter char *q; 217251876Speter 218251876Speter /* compute upper bound on length (since untaint shrinks) */ 219251876Speter len = strlen(stmt->fmt) +1; 220251876Speter for (i=0; i<nargs; ++i) { 221251876Speter len += strlen(args[i]) - 2; 222251876Speter } 223251876Speter i = 0; 224251876Speter p_in = stmt->fmt; 225251876Speter p_out = ret = apr_palloc(pool, len); 226251876Speter /* FIXME silly bug - this'll catch %%s */ 227251876Speter while (q = strstr(p_in, "%s"), q != NULL) { 228251876Speter len = q-p_in; 229251876Speter strncpy(p_out, p_in, len); 230251876Speter p_in += len; 231251876Speter p_out += len; 232251876Speter var = dbd_untaint(pool, stmt->taint[i], args[i]); 233251876Speter len = strlen(var); 234251876Speter strncpy(p_out, var, len); 235251876Speter p_in += 2; 236251876Speter p_out += len; 237251876Speter ++i; 238251876Speter } 239251876Speter strcpy(p_out, p_in); 240251876Speter return ret; 241251876Speter} 242251876Speterstatic int dbd_freetds_pselect(apr_pool_t *pool, apr_dbd_t *sql, 243251876Speter apr_dbd_results_t **results, 244251876Speter apr_dbd_prepared_t *statement, 245251876Speter int seek, const char **values) 246251876Speter{ 247251876Speter const char *query = dbd_statement(pool, statement, 248251876Speter statement->nargs, values); 249251876Speter return dbd_freetds_select(pool, sql, results, query, seek); 250251876Speter} 251251876Speterstatic int dbd_freetds_pvselect(apr_pool_t *pool, apr_dbd_t *sql, 252251876Speter apr_dbd_results_t **results, 253251876Speter apr_dbd_prepared_t *statement, 254251876Speter int seek, va_list args) 255251876Speter{ 256251876Speter const char **values; 257251876Speter int i; 258251876Speter 259251876Speter if (sql->trans && sql->trans->errnum) { 260251876Speter return sql->trans->errnum; 261251876Speter } 262251876Speter 263251876Speter values = apr_palloc(pool, sizeof(*values) * statement->nargs); 264251876Speter 265251876Speter for (i = 0; i < statement->nargs; i++) { 266251876Speter values[i] = va_arg(args, const char*); 267251876Speter } 268251876Speter 269251876Speter return dbd_freetds_pselect(pool, sql, results, statement, seek, values); 270251876Speter} 271251876Speterstatic int dbd_freetds_query(apr_dbd_t *sql, int *nrows, const char *query); 272251876Speterstatic int dbd_freetds_pquery(apr_pool_t *pool, apr_dbd_t *sql, 273251876Speter int *nrows, apr_dbd_prepared_t *statement, 274251876Speter const char **values) 275251876Speter{ 276251876Speter const char *query = dbd_statement(pool, statement, 277251876Speter statement->nargs, values); 278251876Speter return dbd_freetds_query(sql, nrows, query); 279251876Speter} 280251876Speterstatic int dbd_freetds_pvquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows, 281251876Speter apr_dbd_prepared_t *statement, va_list args) 282251876Speter{ 283251876Speter const char **values; 284251876Speter int i; 285251876Speter 286251876Speter if (sql->trans && sql->trans->errnum) { 287251876Speter return sql->trans->errnum; 288251876Speter } 289251876Speter 290251876Speter values = apr_palloc(pool, sizeof(*values) * statement->nargs); 291251876Speter 292251876Speter for (i = 0; i < statement->nargs; i++) { 293251876Speter values[i] = va_arg(args, const char*); 294251876Speter } 295251876Speter return dbd_freetds_pquery(pool, sql, nrows, statement, values); 296251876Speter} 297251876Speter 298251876Speterstatic int dbd_freetds_get_row(apr_pool_t *pool, apr_dbd_results_t *res, 299251876Speter apr_dbd_row_t **rowp, int rownum) 300251876Speter{ 301251876Speter RETCODE rv = 0; 302251876Speter apr_dbd_row_t *row = *rowp; 303251876Speter int sequential = ((rownum >= 0) && res->random) ? 0 : 1; 304251876Speter 305251876Speter if (row == NULL) { 306251876Speter row = apr_palloc(pool, sizeof(apr_dbd_row_t)); 307251876Speter *rowp = row; 308251876Speter row->res = res; 309251876Speter } 310251876Speter /* 311251876Speter else { 312251876Speter if ( sequential ) { 313251876Speter ++row->n; 314251876Speter } 315251876Speter else { 316251876Speter row->n = rownum; 317251876Speter } 318251876Speter } 319251876Speter */ 320251876Speter if (sequential) { 321251876Speter rv = dbnextrow(res->proc); 322251876Speter } 323251876Speter else { 324251876Speter rv = (rownum >= 0) ? dbgetrow(res->proc, rownum) : NO_MORE_ROWS; 325251876Speter } 326251876Speter switch (rv) { 327251876Speter case SUCCEED: return 0; 328251876Speter case REG_ROW: return 0; 329251876Speter case NO_MORE_ROWS: 330253734Speter apr_pool_cleanup_run(res->pool, res->proc, clear_result); 331251876Speter *rowp = NULL; 332251876Speter return -1; 333251876Speter case FAIL: return 1; 334251876Speter case BUF_FULL: return 2; /* FIXME */ 335251876Speter default: return 3; 336251876Speter } 337251876Speter 338251876Speter return 0; 339251876Speter} 340251876Speter 341251876Speterstatic const char *dbd_freetds_get_entry(const apr_dbd_row_t *row, int n) 342251876Speter{ 343251876Speter /* FIXME: support different data types */ 344251876Speter /* this fails - bind gets some vars but not others 345251876Speter return (const char*)row->res->vars[n].data; 346251876Speter */ 347251876Speter DBPROCESS* proc = row->res->proc; 348251876Speter BYTE *ptr = dbdata(proc, n+1); 349251876Speter int t = dbcoltype(proc, n+1); 350251876Speter int l = dbcollen(proc, n+1); 351251876Speter if (dbwillconvert(t, SYBCHAR)) { 352251876Speter dbconvert(proc, t, ptr, l, SYBCHAR, (BYTE *)row->buf, -1); 353251876Speter return (const char*)row->buf; 354251876Speter } 355251876Speter return (char*)ptr; 356251876Speter} 357251876Speter 358251876Speterstatic const char *dbd_freetds_error(apr_dbd_t *sql, int n) 359251876Speter{ 360251876Speter /* XXX this doesn't seem to exist in the API ??? */ 361251876Speter return apr_psprintf(sql->pool, "Error %d", sql->err); 362251876Speter} 363251876Speter 364251876Speterstatic int dbd_freetds_query(apr_dbd_t *sql, int *nrows, const char *query) 365251876Speter{ 366251876Speter if (sql->trans && sql->trans->errnum) { 367251876Speter return sql->trans->errnum; 368251876Speter } 369251876Speter *nrows = 0; 370251876Speter sql->err = freetds_exec(sql->proc, query, 0, nrows); 371251876Speter 372251876Speter if (sql->err != SUCCEED) { 373251876Speter if (sql->trans) { 374251876Speter sql->trans->errnum = sql->err; 375251876Speter } 376251876Speter return 1; 377251876Speter } 378251876Speter return 0; 379251876Speter} 380251876Speter 381251876Speterstatic const char *dbd_freetds_escape(apr_pool_t *pool, const char *arg, 382251876Speter apr_dbd_t *sql) 383251876Speter{ 384251876Speter return arg; 385251876Speter} 386251876Speter 387251876Speterstatic apr_status_t freetds_regfree(void *rx) 388251876Speter{ 389251876Speter regfree((regex_t*)rx); 390251876Speter return APR_SUCCESS; 391251876Speter} 392251876Speterstatic int recurse_args(apr_pool_t *pool, int n, const char *query, 393251876Speter apr_dbd_prepared_t *stmt, int offs) 394251876Speter{ 395251876Speter 396251876Speter /* we only support %s arguments for now */ 397251876Speter int ret; 398251876Speter char arg[256]; 399251876Speter regmatch_t matches[3]; 400251876Speter if (regexec(&dbd_freetds_find_arg, query, 3, matches, 0) != 0) { 401251876Speter /* No more args */ 402251876Speter stmt->nargs = n; 403251876Speter stmt->taint = apr_palloc(pool, n*sizeof(regex_t*)); 404251876Speter stmt->sz = apr_palloc(pool, n*sizeof(int)); 405251876Speter ret = 0; 406251876Speter } 407251876Speter else { 408251876Speter int i; 409251876Speter int sz = 0; 410251876Speter int len = matches[1].rm_eo - matches[1].rm_so - 2; 411251876Speter if (len > 255) { 412251876Speter return 9999; 413251876Speter } 414251876Speter 415251876Speter ret = recurse_args(pool, n+1, query+matches[0].rm_eo, 416251876Speter stmt, offs+matches[0].rm_eo); 417251876Speter 418251876Speter memmove(stmt->fmt + offs + matches[1].rm_so, 419251876Speter stmt->fmt + offs + matches[0].rm_eo-1, 420251876Speter strlen(stmt->fmt+offs+matches[0].rm_eo)+2); 421251876Speter 422251876Speter /* compile untaint to a regex if found */ 423251876Speter if (matches[1].rm_so == -1) { 424251876Speter stmt->taint[n] = NULL; 425251876Speter } 426251876Speter else { 427251876Speter strncpy(arg, query+matches[1].rm_so+1, 428251876Speter matches[1].rm_eo - matches[1].rm_so - 2); 429251876Speter arg[matches[1].rm_eo - matches[1].rm_so - 2] = '\0'; 430251876Speter stmt->taint[n] = apr_palloc(pool, sizeof(regex_t)); 431251876Speter if (regcomp(stmt->taint[n], arg, REG_ICASE|REG_EXTENDED) != 0) { 432251876Speter ++ret; 433251876Speter } 434251876Speter else { 435251876Speter apr_pool_cleanup_register(pool, stmt->taint[n], freetds_regfree, 436251876Speter apr_pool_cleanup_null); 437251876Speter } 438251876Speter } 439251876Speter 440251876Speter /* record length if specified */ 441251876Speter for (i=matches[2].rm_so; i<matches[2].rm_eo; ++i) { 442251876Speter sz = 10*sz + (query[i]-'\0'); 443251876Speter } 444251876Speter } 445251876Speter return ret; 446251876Speter} 447251876Speter 448251876Speterstatic int dbd_freetds_prepare(apr_pool_t *pool, apr_dbd_t *sql, 449251876Speter const char *query, const char *label, 450251876Speter int nargs, int nvals, apr_dbd_type_e *types, 451251876Speter apr_dbd_prepared_t **statement) 452251876Speter{ 453251876Speter apr_dbd_prepared_t *stmt; 454251876Speter 455251876Speter if (label == NULL) { 456251876Speter label = apr_psprintf(pool, "%d", labelnum++); 457251876Speter } 458251876Speter 459251876Speter if (!*statement) { 460251876Speter *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t)); 461251876Speter } 462251876Speter stmt = *statement; 463251876Speter 464251876Speter#if 0 465251876Speter /* count args */ 466251876Speter stmt->fmt = apr_pstrdup(pool, query); 467251876Speter stmt->fmt = recurse_args(pool, 0, query, stmt, stmt->fmt); 468251876Speter 469251876Speter /* overestimate by a byte or two to simplify */ 470251876Speter len = strlen("CREATE PROC apr.") 471251876Speter + strlen(label) 472251876Speter + stmt->nargs * strlen(" @arg1 varchar(len1),") 473251876Speter + strlen(" AS begin ") 474251876Speter + strlen(stmt->fmt) 475251876Speter + strlen(" end "); /* extra byte for terminator */ 476251876Speter 477251876Speter pquery = apr_pcalloc(pool, len); 478251876Speter sprintf(pquery, "CREATE PROC apr.%s", label); 479251876Speter for (i=0; i<stmt->nargs; ++i) { 480251876Speter sprintf(pquery+strlen(pquery), " @arg%d varchar(%d)", i, stmt->sz[i]); 481251876Speter if (i < stmt->nargs-1) { 482251876Speter pquery[strlen(pquery)] = ','; 483251876Speter } 484251876Speter } 485251876Speter strcat(pquery, " AS BEGIN "); 486251876Speter strcat(pquery, stmt->fmt); 487251876Speter strcat(pquery, " END"); 488251876Speter 489251876Speter return (freetds_exec(sql->proc, pquery, 0, &i) == SUCCEED) ? 0 : 1; 490251876Speter#else 491251876Speter stmt->fmt = apr_pstrdup(pool, query); 492251876Speter return recurse_args(pool, 0, query, stmt, 0); 493251876Speter#endif 494251876Speter 495251876Speter} 496251876Speter 497251876Speterstatic int dbd_freetds_start_transaction(apr_pool_t *pool, apr_dbd_t *handle, 498251876Speter apr_dbd_transaction_t **trans) 499251876Speter{ 500251876Speter int dummy; 501251876Speter 502251876Speter /* XXX handle recursive transactions here */ 503251876Speter 504251876Speter handle->err = freetds_exec(handle->proc, "BEGIN TRANSACTION", 0, &dummy); 505251876Speter 506251876Speter if (dbd_freetds_is_success(handle->err)) { 507251876Speter if (!*trans) { 508251876Speter *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t)); 509251876Speter } 510251876Speter (*trans)->handle = handle; 511251876Speter handle->trans = *trans; 512251876Speter return 0; 513251876Speter } 514251876Speter 515251876Speter return 1; 516251876Speter} 517251876Speter 518251876Speterstatic int dbd_freetds_end_transaction(apr_dbd_transaction_t *trans) 519251876Speter{ 520251876Speter int dummy; 521251876Speter if (trans) { 522251876Speter /* rollback on error or explicit rollback request */ 523251876Speter if (trans->errnum) { 524251876Speter trans->errnum = 0; 525251876Speter trans->handle->err = freetds_exec(trans->handle->proc, 526251876Speter "ROLLBACK", 0, &dummy); 527251876Speter } 528251876Speter else { 529251876Speter trans->handle->err = freetds_exec(trans->handle->proc, 530251876Speter "COMMIT", 0, &dummy); 531251876Speter } 532251876Speter trans->handle->trans = NULL; 533251876Speter } 534251876Speter return (trans->handle->err == SUCCEED) ? 0 : 1; 535251876Speter} 536251876Speter 537251876Speterstatic DBPROCESS *freetds_open(apr_pool_t *pool, const char *params, 538251876Speter const char **error) 539251876Speter{ 540251876Speter char *server = NULL; 541251876Speter DBPROCESS *process; 542251876Speter LOGINREC *login; 543251876Speter static const char *delims = " \r\n\t;|,"; 544251876Speter char *ptr; 545251876Speter char *key; 546251876Speter char *value; 547251876Speter int vlen; 548251876Speter int klen; 549251876Speter char *buf; 550251876Speter char *databaseName = NULL; 551251876Speter 552251876Speter /* FIXME - this uses malloc */ 553251876Speter /* FIXME - pass error message back to the caller in case of failure */ 554251876Speter login = dblogin(); 555251876Speter if (login == NULL) { 556251876Speter return NULL; 557251876Speter } 558251876Speter /* now set login properties */ 559251876Speter for (ptr = strchr(params, '='); ptr; ptr = strchr(ptr, '=')) { 560251876Speter /* don't dereference memory that may not belong to us */ 561251876Speter if (ptr == params) { 562251876Speter ++ptr; 563251876Speter continue; 564251876Speter } 565251876Speter for (key = ptr-1; apr_isspace(*key); --key); 566251876Speter klen = 0; 567251876Speter while (apr_isalpha(*key)) { 568251876Speter --key; 569251876Speter ++klen; 570251876Speter } 571251876Speter ++key; 572251876Speter for (value = ptr+1; apr_isspace(*value); ++value); 573251876Speter 574251876Speter vlen = strcspn(value, delims); 575251876Speter buf = apr_pstrndup(pool, value, vlen); /* NULL-terminated copy */ 576251876Speter 577251876Speter if (!strncasecmp(key, "username", klen)) { 578251876Speter DBSETLUSER(login, buf); 579251876Speter } 580251876Speter else if (!strncasecmp(key, "password", klen)) { 581251876Speter DBSETLPWD(login, buf); 582251876Speter } 583251876Speter else if (!strncasecmp(key, "appname", klen)) { 584251876Speter DBSETLAPP(login, buf); 585251876Speter } 586251876Speter else if (!strncasecmp(key, "dbname", klen)) { 587251876Speter databaseName = buf; 588251876Speter } 589251876Speter else if (!strncasecmp(key, "host", klen)) { 590251876Speter DBSETLHOST(login, buf); 591251876Speter } 592251876Speter else if (!strncasecmp(key, "charset", klen)) { 593251876Speter DBSETLCHARSET(login, buf); 594251876Speter } 595251876Speter else if (!strncasecmp(key, "lang", klen)) { 596251876Speter DBSETLNATLANG(login, buf); 597251876Speter } 598251876Speter else if (!strncasecmp(key, "server", klen)) { 599251876Speter server = buf; 600251876Speter } 601251876Speter else { 602251876Speter /* unknown param */ 603251876Speter } 604251876Speter ptr = value+vlen; 605251876Speter } 606251876Speter 607251876Speter process = dbopen(login, server); 608251876Speter 609251876Speter if (process != NULL && databaseName != NULL) 610251876Speter { 611251876Speter dbuse(process, databaseName); 612251876Speter } 613251876Speter 614251876Speter dbloginfree(login); 615251876Speter if (process == NULL) { 616251876Speter return NULL; 617251876Speter } 618251876Speter 619251876Speter return process; 620251876Speter} 621251876Speterstatic apr_dbd_t *dbd_freetds_open(apr_pool_t *pool, const char *params, 622251876Speter const char **error) 623251876Speter{ 624251876Speter apr_dbd_t *sql; 625251876Speter /* FIXME - pass error message back to the caller in case of failure */ 626251876Speter DBPROCESS *process = freetds_open(pool, params, error); 627251876Speter if (process == NULL) { 628251876Speter return NULL; 629251876Speter } 630253734Speter sql = apr_pcalloc(pool, sizeof (apr_dbd_t)); 631251876Speter sql->pool = pool; 632251876Speter sql->proc = process; 633251876Speter sql->params = params; 634251876Speter return sql; 635251876Speter} 636251876Speter 637251876Speterstatic apr_status_t dbd_freetds_close(apr_dbd_t *handle) 638251876Speter{ 639251876Speter dbclose(handle->proc); 640251876Speter return APR_SUCCESS; 641251876Speter} 642251876Speter 643251876Speterstatic apr_status_t dbd_freetds_check_conn(apr_pool_t *pool, 644251876Speter apr_dbd_t *handle) 645251876Speter{ 646251876Speter if (dbdead(handle->proc)) { 647251876Speter /* try again */ 648251876Speter dbclose(handle->proc); 649251876Speter handle->proc = freetds_open(handle->pool, handle->params, NULL); 650251876Speter if (!handle->proc || dbdead(handle->proc)) { 651251876Speter return APR_EGENERAL; 652251876Speter } 653251876Speter } 654251876Speter /* clear it, in case this is called in error handling */ 655251876Speter dbcancel(handle->proc); 656251876Speter return APR_SUCCESS; 657251876Speter} 658251876Speter 659251876Speterstatic int dbd_freetds_select_db(apr_pool_t *pool, apr_dbd_t *handle, 660251876Speter const char *name) 661251876Speter{ 662251876Speter /* ouch, it's declared int. But we can use APR 0/nonzero */ 663251876Speter return (dbuse(handle->proc, (char*)name) == SUCCEED) ? APR_SUCCESS : APR_EGENERAL; 664251876Speter} 665251876Speter 666251876Speterstatic void *dbd_freetds_native(apr_dbd_t *handle) 667251876Speter{ 668251876Speter return handle->proc; 669251876Speter} 670251876Speter 671251876Speterstatic int dbd_freetds_num_cols(apr_dbd_results_t* res) 672251876Speter{ 673251876Speter return res->sz; 674251876Speter} 675251876Speter 676251876Speterstatic int dbd_freetds_num_tuples(apr_dbd_results_t* res) 677251876Speter{ 678251876Speter if (res->random) { 679251876Speter return res->ntuples; 680251876Speter } 681251876Speter else { 682251876Speter return -1; 683251876Speter } 684251876Speter} 685251876Speter 686251876Speterstatic apr_status_t freetds_term(void *dummy) 687251876Speter{ 688251876Speter dbexit(); 689251876Speter regfree(&dbd_freetds_find_arg); 690251876Speter return APR_SUCCESS; 691251876Speter} 692251876Speterstatic int freetds_err_handler(DBPROCESS *dbproc, int severity, int dberr, 693251876Speter int oserr, char *dberrstr, char *oserrstr) 694251876Speter{ 695251876Speter return INT_CANCEL; /* never exit */ 696251876Speter} 697251876Speterstatic void dbd_freetds_init(apr_pool_t *pool) 698251876Speter{ 699251876Speter int rv = regcomp(&dbd_freetds_find_arg, 700251876Speter "%(\\{[^}]*\\})?([0-9]*)[A-Za-z]", REG_EXTENDED); 701251876Speter if (rv != 0) { 702251876Speter char errmsg[256]; 703251876Speter regerror(rv, &dbd_freetds_find_arg, errmsg, 256); 704251876Speter fprintf(stderr, "regcomp failed: %s\n", errmsg); 705251876Speter } 706251876Speter dbinit(); 707251876Speter dberrhandle(freetds_err_handler); 708251876Speter apr_pool_cleanup_register(pool, NULL, freetds_term, apr_pool_cleanup_null); 709251876Speter} 710251876Speter 711251876Speter#ifdef COMPILE_STUBS 712251876Speter/* get_name is the only one of these that is implemented */ 713251876Speterstatic const char *dbd_freetds_get_name(const apr_dbd_results_t *res, int n) 714251876Speter{ 715251876Speter return (const char*) dbcolname(res->proc, n+1); /* numbering starts at 1 */ 716251876Speter} 717251876Speter 718251876Speter/* These are stubs: transaction modes not implemented here */ 719251876Speter#define DBD_NOTIMPL APR_ENOTIMPL; 720251876Speterstatic int dbd_freetds_transaction_mode_get(apr_dbd_transaction_t *trans) 721251876Speter{ 722251876Speter return trans ? trans->mode : APR_DBD_TRANSACTION_COMMIT; 723251876Speter} 724251876Speter 725251876Speterstatic int dbd_freetds_transaction_mode_set(apr_dbd_transaction_t *trans, 726251876Speter int mode) 727251876Speter{ 728251876Speter if (trans) { 729251876Speter trans->mode = mode & TXN_MODE_BITS; 730251876Speter return trans->mode; 731251876Speter } 732251876Speter return APR_DBD_TRANSACTION_COMMIT; 733251876Speter} 734251876Speterstatic int dbd_freetds_pvbquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows, 735251876Speter apr_dbd_prepared_t *statement, va_list args) 736251876Speter{ 737251876Speter return DBD_NOTIMPL; 738251876Speter} 739251876Speterstatic int dbd_freetds_pbquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows, 740251876Speter apr_dbd_prepared_t * statement, 741251876Speter const void **values) 742251876Speter{ 743251876Speter return DBD_NOTIMPL; 744251876Speter} 745251876Speter 746251876Speterstatic int dbd_freetds_pvbselect(apr_pool_t *pool, apr_dbd_t *sql, 747251876Speter apr_dbd_results_t **results, 748251876Speter apr_dbd_prepared_t *statement, 749251876Speter int seek, va_list args) 750251876Speter{ 751251876Speter return DBD_NOTIMPL; 752251876Speter} 753251876Speterstatic int dbd_freetds_pbselect(apr_pool_t *pool, apr_dbd_t *sql, 754251876Speter apr_dbd_results_t **results, 755251876Speter apr_dbd_prepared_t *statement, 756251876Speter int seek, const void **values) 757251876Speter{ 758251876Speter return DBD_NOTIMPL; 759251876Speter} 760251876Speterstatic apr_status_t dbd_freetds_datum_get(const apr_dbd_row_t *row, int n, 761251876Speter apr_dbd_type_e type, void *data) 762251876Speter{ 763251876Speter return APR_ENOTIMPL; 764251876Speter} 765251876Speter#endif 766251876Speter 767251876SpeterAPU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_freetds_driver = { 768251876Speter "freetds", 769251876Speter dbd_freetds_init, 770251876Speter dbd_freetds_native, 771251876Speter dbd_freetds_open, 772251876Speter dbd_freetds_check_conn, 773251876Speter dbd_freetds_close, 774251876Speter dbd_freetds_select_db, 775251876Speter dbd_freetds_start_transaction, 776251876Speter dbd_freetds_end_transaction, 777251876Speter dbd_freetds_query, 778251876Speter dbd_freetds_select, 779251876Speter dbd_freetds_num_cols, 780251876Speter dbd_freetds_num_tuples, 781251876Speter dbd_freetds_get_row, 782251876Speter dbd_freetds_get_entry, 783251876Speter dbd_freetds_error, 784251876Speter dbd_freetds_escape, 785251876Speter dbd_freetds_prepare, 786251876Speter dbd_freetds_pvquery, 787251876Speter dbd_freetds_pvselect, 788251876Speter dbd_freetds_pquery, 789251876Speter dbd_freetds_pselect, 790251876Speter /* this is only implemented to support httpd/2.2 standard usage, 791251876Speter * as in the original DBD implementation. Everything else is NOTIMPL. 792251876Speter */ 793251876Speter#ifdef COMPILE_STUBS 794251876Speter dbd_freetds_get_name, 795251876Speter dbd_freetds_transaction_mode_get, 796251876Speter dbd_freetds_transaction_mode_set, 797251876Speter "", 798251876Speter dbd_freetds_pvbquery, 799251876Speter dbd_freetds_pvbselect, 800251876Speter dbd_freetds_pbquery, 801251876Speter dbd_freetds_pbselect, 802251876Speter dbd_freetds_datum_get 803251876Speter#endif 804251876Speter}; 805251876Speter#endif 806