1339230Speter/* Licensed to the Apache Software Foundation (ASF) under one or more 2339230Speter * contributor license agreements. See the NOTICE file distributed with 3339230Speter * this work for additional information regarding copyright ownership. 4339230Speter * The ASF licenses this file to You under the Apache License, Version 2.0 5339230Speter * (the "License"); you may not use this file except in compliance with 6339230Speter * the License. You may obtain a copy of the License at 7339230Speter * 8339230Speter * http://www.apache.org/licenses/LICENSE-2.0 9339230Speter * 10339230Speter * Unless required by applicable law or agreed to in writing, software 11339230Speter * distributed under the License is distributed on an "AS IS" BASIS, 12339230Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13339230Speter * See the License for the specific language governing permissions and 14339230Speter * limitations under the License. 15339230Speter */ 16339230Speter 17339230Speter#ifdef I_CAN_DEAL_WITH_THIS_PARTIAL_DRIVER_AND_UNMAINTAINED_CODE_FOR_FREETDS 18339230Speter 19339230Speter#include "apu.h" 20339230Speter#include "apu_config.h" 21339230Speter 22339230Speter/* COMPILE_STUBS: compile stubs for unimplemented functions. 23339230Speter * 24339230Speter * This is required to compile in /trunk/, but can be 25339230Speter * undefined to compile a driver for httpd-2.2 and other 26339230Speter * APR-1.2 applications 27339230Speter */ 28339230Speter#define COMPILE_STUBS 29339230Speter 30339230Speter#if APU_HAVE_FREETDS 31339230Speter 32339230Speter#include <ctype.h> 33339230Speter#include <stdlib.h> 34339230Speter 35339230Speter#include "apr_strings.h" 36339230Speter#include "apr_lib.h" 37339230Speter 38339230Speter#include "apr_pools.h" 39339230Speter#include "apr_dbd_internal.h" 40339230Speter 41339230Speter#ifdef HAVE_FREETDS_SYBDB_H 42339230Speter#include <freetds/sybdb.h> 43339230Speter#endif 44339230Speter#ifdef HAVE_SYBDB_H 45339230Speter#include <sybdb.h> 46339230Speter#endif 47339230Speter 48339230Speter#include <stdio.h> 49339230Speter#include <sys/types.h> 50339230Speter#include <regex.h> 51339230Speter 52339230Speter/* This probably needs to change for different applications */ 53339230Speter#define MAX_COL_LEN 256 54339230Speter 55339230Spetertypedef struct freetds_cell_t { 56339230Speter int type; 57339230Speter DBINT len; 58339230Speter BYTE *data; 59339230Speter} freetds_cell_t; 60339230Speter 61339230Speterstruct apr_dbd_transaction_t { 62339230Speter int mode; 63339230Speter int errnum; 64339230Speter apr_dbd_t *handle; 65339230Speter}; 66339230Speter 67339230Speterstruct apr_dbd_t { 68339230Speter DBPROCESS *proc; 69339230Speter apr_dbd_transaction_t *trans; 70339230Speter apr_pool_t *pool; 71339230Speter const char *params; 72339230Speter RETCODE err; 73339230Speter}; 74339230Speter 75339230Speterstruct apr_dbd_results_t { 76339230Speter int random; 77339230Speter size_t ntuples; 78339230Speter size_t sz; 79339230Speter apr_pool_t *pool; 80339230Speter DBPROCESS *proc; 81339230Speter}; 82339230Speter 83339230Speterstruct apr_dbd_row_t { 84339230Speter apr_dbd_results_t *res; 85339230Speter BYTE buf[MAX_COL_LEN]; 86339230Speter}; 87339230Speter 88339230Speterstruct apr_dbd_prepared_t { 89339230Speter int nargs; 90339230Speter regex_t **taint; 91339230Speter int *sz; 92339230Speter char *fmt; 93339230Speter}; 94339230Speter 95339230Speter#define dbd_freetds_is_success(x) (x == SUCCEED) 96339230Speter 97339230Speterstatic int labelnum = 0; /* FIXME */ 98339230Speterstatic regex_t dbd_freetds_find_arg; 99339230Speter 100339230Speter/* execute a query that doesn't return a result set, mop up, 101339230Speter * and return and APR-flavoured status 102339230Speter */ 103339230Speterstatic RETCODE freetds_exec(DBPROCESS *proc, const char *query, 104339230Speter int want_results, int *nrows) 105339230Speter{ 106339230Speter /* TBD */ 107339230Speter RETCODE rv = dbcmd(proc, query); 108339230Speter if (rv != SUCCEED) { 109339230Speter return rv; 110339230Speter } 111339230Speter rv = dbsqlexec(proc); 112339230Speter if (rv != SUCCEED) { 113339230Speter return rv; 114339230Speter } 115339230Speter if (!want_results) { 116339230Speter while (dbresults(proc) != NO_MORE_RESULTS) { 117339230Speter ++*nrows; 118339230Speter } 119339230Speter } 120339230Speter return SUCCEED; 121339230Speter} 122339230Speterstatic apr_status_t clear_result(void *data) 123339230Speter{ 124339230Speter /* clear cursor */ 125339230Speter return (dbcanquery((DBPROCESS*)data) == SUCCEED) 126339230Speter ? APR_SUCCESS 127339230Speter : APR_EGENERAL; 128339230Speter} 129339230Speter 130339230Speterstatic int dbd_freetds_select(apr_pool_t *pool, apr_dbd_t *sql, 131339230Speter apr_dbd_results_t **results, 132339230Speter const char *query, int seek) 133339230Speter{ 134339230Speter apr_dbd_results_t *res; 135339230Speter if (sql->trans && (sql->trans->errnum != SUCCEED)) { 136339230Speter return 1; 137339230Speter } 138339230Speter /* the core of this is 139339230Speter * dbcmd(proc, query); 140339230Speter * dbsqlexec(proc); 141339230Speter * while (dbnextrow(dbproc) != NO_MORE_ROWS) { 142339230Speter * do things 143339230Speter * } 144339230Speter * 145339230Speter * Ignore seek 146339230Speter */ 147339230Speter 148339230Speter sql->err = freetds_exec(sql->proc, query, 1, NULL); 149339230Speter if (!dbd_freetds_is_success(sql->err)) { 150339230Speter if (sql->trans) { 151339230Speter sql->trans->errnum = sql->err; 152339230Speter } 153339230Speter return 1; 154339230Speter } 155339230Speter 156339230Speter sql->err = dbresults(sql->proc); 157339230Speter if (sql->err != SUCCEED) { 158339230Speter if (sql->trans) { 159339230Speter sql->trans->errnum = sql->err; 160339230Speter } 161339230Speter return 1; 162339230Speter } 163339230Speter 164339230Speter if (!*results) { 165339230Speter *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t)); 166339230Speter } 167339230Speter res = *results; 168339230Speter res->proc = sql->proc; 169339230Speter res->random = seek; 170339230Speter res->pool = pool; 171339230Speter res->ntuples = dblastrow(sql->proc); 172339230Speter res->sz = dbnumcols(sql->proc); 173339230Speter apr_pool_cleanup_register(pool, sql->proc, clear_result, 174339230Speter apr_pool_cleanup_null); 175339230Speter 176339230Speter#if 0 177339230Speter /* Now we have a result set. We need to bind to its vars */ 178339230Speter res->vars = apr_palloc(pool, res->sz * sizeof(freetds_cell_t*)); 179339230Speter for (i=1; i <= res->sz; ++i) { 180339230Speter freetds_cell_t *cell = &res->vars[i-1]; 181339230Speter cell->type = dbcoltype(sql->proc, i); 182339230Speter cell->len = dbcollen(sql->proc, i); 183339230Speter cell->data = apr_palloc(pool, cell->len); 184339230Speter sql->err = dbbind(sql->proc, i, /*cell->type */ STRINGBIND, cell->len, cell->data); 185339230Speter if (sql->err != SUCCEED) { 186339230Speter fprintf(stderr, "dbbind error: %d, %d, %d", i, cell->type, cell->len); 187339230Speter } 188339230Speter if ((sql->err != SUCCEED) && (sql->trans != NULL)) { 189339230Speter sql->trans->errnum = sql->err; 190339230Speter } 191339230Speter } 192339230Speter#endif 193339230Speter return (sql->err == SUCCEED) ? 0 : 1; 194339230Speter} 195339230Speterstatic const char *dbd_untaint(apr_pool_t *pool, regex_t *rx, const char *val) 196339230Speter{ 197339230Speter regmatch_t match[1]; 198339230Speter if (rx == NULL) { 199339230Speter /* no untaint expression */ 200339230Speter return val; 201339230Speter } 202339230Speter if (regexec(rx, val, 1, match, 0) == 0) { 203339230Speter return apr_pstrndup(pool, val+match[0].rm_so, 204339230Speter match[0].rm_eo - match[0].rm_so); 205339230Speter } 206339230Speter return ""; 207339230Speter} 208339230Speterstatic const char *dbd_statement(apr_pool_t *pool, 209339230Speter apr_dbd_prepared_t *stmt, 210339230Speter int nargs, const char **args) 211339230Speter{ 212339230Speter int i; 213339230Speter int len; 214339230Speter const char *var; 215339230Speter char *ret; 216339230Speter const char *p_in; 217339230Speter char *p_out; 218339230Speter char *q; 219339230Speter 220339230Speter /* compute upper bound on length (since untaint shrinks) */ 221339230Speter len = strlen(stmt->fmt) +1; 222339230Speter for (i=0; i<nargs; ++i) { 223339230Speter len += strlen(args[i]) - 2; 224339230Speter } 225339230Speter i = 0; 226339230Speter p_in = stmt->fmt; 227339230Speter p_out = ret = apr_palloc(pool, len); 228339230Speter /* FIXME silly bug - this'll catch %%s */ 229339230Speter while (q = strstr(p_in, "%s"), q != NULL) { 230339230Speter len = q-p_in; 231339230Speter strncpy(p_out, p_in, len); 232339230Speter p_in += len; 233339230Speter p_out += len; 234339230Speter var = dbd_untaint(pool, stmt->taint[i], args[i]); 235339230Speter len = strlen(var); 236339230Speter strncpy(p_out, var, len); 237339230Speter p_in += 2; 238339230Speter p_out += len; 239339230Speter ++i; 240339230Speter } 241339230Speter strcpy(p_out, p_in); 242339230Speter return ret; 243339230Speter} 244339230Speterstatic int dbd_freetds_pselect(apr_pool_t *pool, apr_dbd_t *sql, 245339230Speter apr_dbd_results_t **results, 246339230Speter apr_dbd_prepared_t *statement, 247339230Speter int seek, const char **values) 248339230Speter{ 249339230Speter const char *query = dbd_statement(pool, statement, 250339230Speter statement->nargs, values); 251339230Speter return dbd_freetds_select(pool, sql, results, query, seek); 252339230Speter} 253339230Speterstatic int dbd_freetds_pvselect(apr_pool_t *pool, apr_dbd_t *sql, 254339230Speter apr_dbd_results_t **results, 255339230Speter apr_dbd_prepared_t *statement, 256339230Speter int seek, va_list args) 257339230Speter{ 258339230Speter const char **values; 259339230Speter int i; 260339230Speter 261339230Speter if (sql->trans && sql->trans->errnum) { 262339230Speter return sql->trans->errnum; 263339230Speter } 264339230Speter 265339230Speter values = apr_palloc(pool, sizeof(*values) * statement->nargs); 266339230Speter 267339230Speter for (i = 0; i < statement->nargs; i++) { 268339230Speter values[i] = va_arg(args, const char*); 269339230Speter } 270339230Speter 271339230Speter return dbd_freetds_pselect(pool, sql, results, statement, seek, values); 272339230Speter} 273339230Speterstatic int dbd_freetds_query(apr_dbd_t *sql, int *nrows, const char *query); 274339230Speterstatic int dbd_freetds_pquery(apr_pool_t *pool, apr_dbd_t *sql, 275339230Speter int *nrows, apr_dbd_prepared_t *statement, 276339230Speter const char **values) 277339230Speter{ 278339230Speter const char *query = dbd_statement(pool, statement, 279339230Speter statement->nargs, values); 280339230Speter return dbd_freetds_query(sql, nrows, query); 281339230Speter} 282339230Speterstatic int dbd_freetds_pvquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows, 283339230Speter apr_dbd_prepared_t *statement, va_list args) 284339230Speter{ 285339230Speter const char **values; 286339230Speter int i; 287339230Speter 288339230Speter if (sql->trans && sql->trans->errnum) { 289339230Speter return sql->trans->errnum; 290339230Speter } 291339230Speter 292339230Speter values = apr_palloc(pool, sizeof(*values) * statement->nargs); 293339230Speter 294339230Speter for (i = 0; i < statement->nargs; i++) { 295339230Speter values[i] = va_arg(args, const char*); 296339230Speter } 297339230Speter return dbd_freetds_pquery(pool, sql, nrows, statement, values); 298339230Speter} 299339230Speter 300339230Speterstatic int dbd_freetds_get_row(apr_pool_t *pool, apr_dbd_results_t *res, 301339230Speter apr_dbd_row_t **rowp, int rownum) 302339230Speter{ 303339230Speter RETCODE rv = 0; 304339230Speter apr_dbd_row_t *row = *rowp; 305339230Speter int sequential = ((rownum >= 0) && res->random) ? 0 : 1; 306339230Speter 307339230Speter if (row == NULL) { 308339230Speter row = apr_palloc(pool, sizeof(apr_dbd_row_t)); 309339230Speter *rowp = row; 310339230Speter row->res = res; 311339230Speter } 312339230Speter /* 313339230Speter else { 314339230Speter if ( sequential ) { 315339230Speter ++row->n; 316339230Speter } 317339230Speter else { 318339230Speter row->n = rownum; 319339230Speter } 320339230Speter } 321339230Speter */ 322339230Speter if (sequential) { 323339230Speter rv = dbnextrow(res->proc); 324339230Speter } 325339230Speter else { 326339230Speter rv = (rownum >= 0) ? dbgetrow(res->proc, rownum) : NO_MORE_ROWS; 327339230Speter } 328339230Speter switch (rv) { 329339230Speter case SUCCEED: return 0; 330339230Speter case REG_ROW: return 0; 331339230Speter case NO_MORE_ROWS: 332339230Speter apr_pool_cleanup_run(res->pool, res->proc, clear_result); 333339230Speter *rowp = NULL; 334339230Speter return -1; 335339230Speter case FAIL: return 1; 336339230Speter case BUF_FULL: return 2; /* FIXME */ 337339230Speter default: return 3; 338339230Speter } 339339230Speter 340339230Speter return 0; 341339230Speter} 342339230Speter 343339230Speterstatic const char *dbd_freetds_get_entry(const apr_dbd_row_t *row, int n) 344339230Speter{ 345339230Speter /* FIXME: support different data types */ 346339230Speter /* this fails - bind gets some vars but not others 347339230Speter return (const char*)row->res->vars[n].data; 348339230Speter */ 349339230Speter DBPROCESS* proc = row->res->proc; 350339230Speter BYTE *ptr = dbdata(proc, n+1); 351339230Speter int t = dbcoltype(proc, n+1); 352339230Speter int l = dbcollen(proc, n+1); 353339230Speter if (dbwillconvert(t, SYBCHAR)) { 354339230Speter dbconvert(proc, t, ptr, l, SYBCHAR, (BYTE *)row->buf, -1); 355339230Speter return (const char*)row->buf; 356339230Speter } 357339230Speter return (char*)ptr; 358339230Speter} 359339230Speter 360339230Speterstatic const char *dbd_freetds_error(apr_dbd_t *sql, int n) 361339230Speter{ 362339230Speter /* XXX this doesn't seem to exist in the API ??? */ 363339230Speter return apr_psprintf(sql->pool, "Error %d", sql->err); 364339230Speter} 365339230Speter 366339230Speterstatic int dbd_freetds_query(apr_dbd_t *sql, int *nrows, const char *query) 367339230Speter{ 368339230Speter if (sql->trans && sql->trans->errnum) { 369339230Speter return sql->trans->errnum; 370339230Speter } 371339230Speter *nrows = 0; 372339230Speter sql->err = freetds_exec(sql->proc, query, 0, nrows); 373339230Speter 374339230Speter if (sql->err != SUCCEED) { 375339230Speter if (sql->trans) { 376339230Speter sql->trans->errnum = sql->err; 377339230Speter } 378339230Speter return 1; 379339230Speter } 380339230Speter return 0; 381339230Speter} 382339230Speter 383339230Speterstatic const char *dbd_freetds_escape(apr_pool_t *pool, const char *arg, 384339230Speter apr_dbd_t *sql) 385339230Speter{ 386339230Speter return arg; 387339230Speter} 388339230Speter 389339230Speterstatic apr_status_t freetds_regfree(void *rx) 390339230Speter{ 391339230Speter regfree((regex_t*)rx); 392339230Speter return APR_SUCCESS; 393339230Speter} 394339230Speterstatic int recurse_args(apr_pool_t *pool, int n, const char *query, 395339230Speter apr_dbd_prepared_t *stmt, int offs) 396339230Speter{ 397339230Speter 398339230Speter /* we only support %s arguments for now */ 399339230Speter int ret; 400339230Speter char arg[256]; 401339230Speter regmatch_t matches[3]; 402339230Speter if (regexec(&dbd_freetds_find_arg, query, 3, matches, 0) != 0) { 403339230Speter /* No more args */ 404339230Speter stmt->nargs = n; 405339230Speter stmt->taint = apr_palloc(pool, n*sizeof(regex_t*)); 406339230Speter stmt->sz = apr_palloc(pool, n*sizeof(int)); 407339230Speter ret = 0; 408339230Speter } 409339230Speter else { 410339230Speter int i; 411339230Speter int sz = 0; 412339230Speter int len = matches[1].rm_eo - matches[1].rm_so - 2; 413339230Speter if (len > 255) { 414339230Speter return 9999; 415339230Speter } 416339230Speter 417339230Speter ret = recurse_args(pool, n+1, query+matches[0].rm_eo, 418339230Speter stmt, offs+matches[0].rm_eo); 419339230Speter 420339230Speter memmove(stmt->fmt + offs + matches[1].rm_so, 421339230Speter stmt->fmt + offs + matches[0].rm_eo-1, 422339230Speter strlen(stmt->fmt+offs+matches[0].rm_eo)+2); 423339230Speter 424339230Speter /* compile untaint to a regex if found */ 425339230Speter if (matches[1].rm_so == -1) { 426339230Speter stmt->taint[n] = NULL; 427339230Speter } 428339230Speter else { 429339230Speter strncpy(arg, query+matches[1].rm_so+1, 430339230Speter matches[1].rm_eo - matches[1].rm_so - 2); 431339230Speter arg[matches[1].rm_eo - matches[1].rm_so - 2] = '\0'; 432339230Speter stmt->taint[n] = apr_palloc(pool, sizeof(regex_t)); 433339230Speter if (regcomp(stmt->taint[n], arg, REG_ICASE|REG_EXTENDED) != 0) { 434339230Speter ++ret; 435339230Speter } 436339230Speter else { 437339230Speter apr_pool_cleanup_register(pool, stmt->taint[n], freetds_regfree, 438339230Speter apr_pool_cleanup_null); 439339230Speter } 440339230Speter } 441339230Speter 442339230Speter /* record length if specified */ 443339230Speter for (i=matches[2].rm_so; i<matches[2].rm_eo; ++i) { 444339230Speter sz = 10*sz + (query[i]-'\0'); 445339230Speter } 446339230Speter } 447339230Speter return ret; 448339230Speter} 449339230Speter 450339230Speterstatic int dbd_freetds_prepare(apr_pool_t *pool, apr_dbd_t *sql, 451339230Speter const char *query, const char *label, 452339230Speter int nargs, int nvals, apr_dbd_type_e *types, 453339230Speter apr_dbd_prepared_t **statement) 454339230Speter{ 455339230Speter apr_dbd_prepared_t *stmt; 456339230Speter 457339230Speter if (label == NULL) { 458339230Speter label = apr_psprintf(pool, "%d", labelnum++); 459339230Speter } 460339230Speter 461339230Speter if (!*statement) { 462339230Speter *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t)); 463339230Speter } 464339230Speter stmt = *statement; 465339230Speter 466339230Speter#if 0 467339230Speter /* count args */ 468339230Speter stmt->fmt = apr_pstrdup(pool, query); 469339230Speter stmt->fmt = recurse_args(pool, 0, query, stmt, stmt->fmt); 470339230Speter 471339230Speter /* overestimate by a byte or two to simplify */ 472339230Speter len = strlen("CREATE PROC apr.") 473339230Speter + strlen(label) 474339230Speter + stmt->nargs * strlen(" @arg1 varchar(len1),") 475339230Speter + strlen(" AS begin ") 476339230Speter + strlen(stmt->fmt) 477339230Speter + strlen(" end "); /* extra byte for terminator */ 478339230Speter 479339230Speter pquery = apr_pcalloc(pool, len); 480339230Speter sprintf(pquery, "CREATE PROC apr.%s", label); 481339230Speter for (i=0; i<stmt->nargs; ++i) { 482339230Speter sprintf(pquery+strlen(pquery), " @arg%d varchar(%d)", i, stmt->sz[i]); 483339230Speter if (i < stmt->nargs-1) { 484339230Speter pquery[strlen(pquery)] = ','; 485339230Speter } 486339230Speter } 487339230Speter strcat(pquery, " AS BEGIN "); 488339230Speter strcat(pquery, stmt->fmt); 489339230Speter strcat(pquery, " END"); 490339230Speter 491339230Speter return (freetds_exec(sql->proc, pquery, 0, &i) == SUCCEED) ? 0 : 1; 492339230Speter#else 493339230Speter stmt->fmt = apr_pstrdup(pool, query); 494339230Speter return recurse_args(pool, 0, query, stmt, 0); 495339230Speter#endif 496339230Speter 497339230Speter} 498339230Speter 499339230Speterstatic int dbd_freetds_start_transaction(apr_pool_t *pool, apr_dbd_t *handle, 500339230Speter apr_dbd_transaction_t **trans) 501339230Speter{ 502339230Speter int dummy; 503339230Speter 504339230Speter /* XXX handle recursive transactions here */ 505339230Speter 506339230Speter handle->err = freetds_exec(handle->proc, "BEGIN TRANSACTION", 0, &dummy); 507339230Speter 508339230Speter if (dbd_freetds_is_success(handle->err)) { 509339230Speter if (!*trans) { 510339230Speter *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t)); 511339230Speter } 512339230Speter (*trans)->handle = handle; 513339230Speter handle->trans = *trans; 514339230Speter return 0; 515339230Speter } 516339230Speter 517339230Speter return 1; 518339230Speter} 519339230Speter 520339230Speterstatic int dbd_freetds_end_transaction(apr_dbd_transaction_t *trans) 521339230Speter{ 522339230Speter int dummy; 523339230Speter if (trans) { 524339230Speter /* rollback on error or explicit rollback request */ 525339230Speter if (trans->errnum) { 526339230Speter trans->errnum = 0; 527339230Speter trans->handle->err = freetds_exec(trans->handle->proc, 528339230Speter "ROLLBACK", 0, &dummy); 529339230Speter } 530339230Speter else { 531339230Speter trans->handle->err = freetds_exec(trans->handle->proc, 532339230Speter "COMMIT", 0, &dummy); 533339230Speter } 534339230Speter trans->handle->trans = NULL; 535339230Speter } 536339230Speter return (trans->handle->err == SUCCEED) ? 0 : 1; 537339230Speter} 538339230Speter 539339230Speterstatic DBPROCESS *freetds_open(apr_pool_t *pool, const char *params, 540339230Speter const char **error) 541339230Speter{ 542339230Speter char *server = NULL; 543339230Speter DBPROCESS *process; 544339230Speter LOGINREC *login; 545339230Speter static const char *delims = " \r\n\t;|,"; 546339230Speter char *ptr; 547339230Speter char *key; 548339230Speter char *value; 549339230Speter int vlen; 550339230Speter int klen; 551339230Speter char *buf; 552339230Speter char *databaseName = NULL; 553339230Speter 554339230Speter /* FIXME - this uses malloc */ 555339230Speter /* FIXME - pass error message back to the caller in case of failure */ 556339230Speter login = dblogin(); 557339230Speter if (login == NULL) { 558339230Speter return NULL; 559339230Speter } 560339230Speter /* now set login properties */ 561339230Speter for (ptr = strchr(params, '='); ptr; ptr = strchr(ptr, '=')) { 562339230Speter /* don't dereference memory that may not belong to us */ 563339230Speter if (ptr == params) { 564339230Speter ++ptr; 565339230Speter continue; 566339230Speter } 567339230Speter for (key = ptr-1; apr_isspace(*key); --key); 568339230Speter klen = 0; 569339230Speter while (apr_isalpha(*key)) { 570339230Speter --key; 571339230Speter ++klen; 572339230Speter } 573339230Speter ++key; 574339230Speter for (value = ptr+1; apr_isspace(*value); ++value); 575339230Speter 576339230Speter vlen = strcspn(value, delims); 577339230Speter buf = apr_pstrndup(pool, value, vlen); /* NULL-terminated copy */ 578339230Speter 579339230Speter if (!strncasecmp(key, "username", klen)) { 580339230Speter DBSETLUSER(login, buf); 581339230Speter } 582339230Speter else if (!strncasecmp(key, "password", klen)) { 583339230Speter DBSETLPWD(login, buf); 584339230Speter } 585339230Speter else if (!strncasecmp(key, "appname", klen)) { 586339230Speter DBSETLAPP(login, buf); 587339230Speter } 588339230Speter else if (!strncasecmp(key, "dbname", klen)) { 589339230Speter databaseName = buf; 590339230Speter } 591339230Speter else if (!strncasecmp(key, "host", klen)) { 592339230Speter DBSETLHOST(login, buf); 593339230Speter } 594339230Speter else if (!strncasecmp(key, "charset", klen)) { 595339230Speter DBSETLCHARSET(login, buf); 596339230Speter } 597339230Speter else if (!strncasecmp(key, "lang", klen)) { 598339230Speter DBSETLNATLANG(login, buf); 599339230Speter } 600339230Speter else if (!strncasecmp(key, "server", klen)) { 601339230Speter server = buf; 602339230Speter } 603339230Speter else { 604339230Speter /* unknown param */ 605339230Speter } 606339230Speter ptr = value+vlen; 607339230Speter } 608339230Speter 609339230Speter process = dbopen(login, server); 610339230Speter 611339230Speter if (process != NULL && databaseName != NULL) 612339230Speter { 613339230Speter dbuse(process, databaseName); 614339230Speter } 615339230Speter 616339230Speter dbloginfree(login); 617339230Speter if (process == NULL) { 618339230Speter return NULL; 619339230Speter } 620339230Speter 621339230Speter return process; 622339230Speter} 623339230Speterstatic apr_dbd_t *dbd_freetds_open(apr_pool_t *pool, const char *params, 624339230Speter const char **error) 625339230Speter{ 626339230Speter apr_dbd_t *sql; 627339230Speter /* FIXME - pass error message back to the caller in case of failure */ 628339230Speter DBPROCESS *process = freetds_open(pool, params, error); 629339230Speter if (process == NULL) { 630339230Speter return NULL; 631339230Speter } 632339230Speter sql = apr_pcalloc(pool, sizeof (apr_dbd_t)); 633339230Speter sql->pool = pool; 634339230Speter sql->proc = process; 635339230Speter sql->params = params; 636339230Speter return sql; 637339230Speter} 638339230Speter 639339230Speterstatic apr_status_t dbd_freetds_close(apr_dbd_t *handle) 640339230Speter{ 641339230Speter dbclose(handle->proc); 642339230Speter return APR_SUCCESS; 643339230Speter} 644339230Speter 645339230Speterstatic apr_status_t dbd_freetds_check_conn(apr_pool_t *pool, 646339230Speter apr_dbd_t *handle) 647339230Speter{ 648339230Speter if (dbdead(handle->proc)) { 649339230Speter /* try again */ 650339230Speter dbclose(handle->proc); 651339230Speter handle->proc = freetds_open(handle->pool, handle->params, NULL); 652339230Speter if (!handle->proc || dbdead(handle->proc)) { 653339230Speter return APR_EGENERAL; 654339230Speter } 655339230Speter } 656339230Speter /* clear it, in case this is called in error handling */ 657339230Speter dbcancel(handle->proc); 658339230Speter return APR_SUCCESS; 659339230Speter} 660339230Speter 661339230Speterstatic int dbd_freetds_select_db(apr_pool_t *pool, apr_dbd_t *handle, 662339230Speter const char *name) 663339230Speter{ 664339230Speter /* ouch, it's declared int. But we can use APR 0/nonzero */ 665339230Speter return (dbuse(handle->proc, (char*)name) == SUCCEED) ? APR_SUCCESS : APR_EGENERAL; 666339230Speter} 667339230Speter 668339230Speterstatic void *dbd_freetds_native(apr_dbd_t *handle) 669339230Speter{ 670339230Speter return handle->proc; 671339230Speter} 672339230Speter 673339230Speterstatic int dbd_freetds_num_cols(apr_dbd_results_t* res) 674339230Speter{ 675339230Speter return res->sz; 676339230Speter} 677339230Speter 678339230Speterstatic int dbd_freetds_num_tuples(apr_dbd_results_t* res) 679339230Speter{ 680339230Speter if (res->random) { 681339230Speter return res->ntuples; 682339230Speter } 683339230Speter else { 684339230Speter return -1; 685339230Speter } 686339230Speter} 687339230Speter 688339230Speterstatic apr_status_t freetds_term(void *dummy) 689339230Speter{ 690339230Speter dbexit(); 691339230Speter regfree(&dbd_freetds_find_arg); 692339230Speter return APR_SUCCESS; 693339230Speter} 694339230Speterstatic int freetds_err_handler(DBPROCESS *dbproc, int severity, int dberr, 695339230Speter int oserr, char *dberrstr, char *oserrstr) 696339230Speter{ 697339230Speter return INT_CANCEL; /* never exit */ 698339230Speter} 699339230Speterstatic void dbd_freetds_init(apr_pool_t *pool) 700339230Speter{ 701339230Speter int rv = regcomp(&dbd_freetds_find_arg, 702339230Speter "%(\\{[^}]*\\})?([0-9]*)[A-Za-z]", REG_EXTENDED); 703339230Speter if (rv != 0) { 704339230Speter char errmsg[256]; 705339230Speter regerror(rv, &dbd_freetds_find_arg, errmsg, 256); 706339230Speter fprintf(stderr, "regcomp failed: %s\n", errmsg); 707339230Speter } 708339230Speter dbinit(); 709339230Speter dberrhandle(freetds_err_handler); 710339230Speter apr_pool_cleanup_register(pool, NULL, freetds_term, apr_pool_cleanup_null); 711339230Speter} 712339230Speter 713339230Speter#ifdef COMPILE_STUBS 714339230Speter/* get_name is the only one of these that is implemented */ 715339230Speterstatic const char *dbd_freetds_get_name(const apr_dbd_results_t *res, int n) 716339230Speter{ 717339230Speter return (const char*) dbcolname(res->proc, n+1); /* numbering starts at 1 */ 718339230Speter} 719339230Speter 720339230Speter/* These are stubs: transaction modes not implemented here */ 721339230Speter#define DBD_NOTIMPL APR_ENOTIMPL; 722339230Speterstatic int dbd_freetds_transaction_mode_get(apr_dbd_transaction_t *trans) 723339230Speter{ 724339230Speter return trans ? trans->mode : APR_DBD_TRANSACTION_COMMIT; 725339230Speter} 726339230Speter 727339230Speterstatic int dbd_freetds_transaction_mode_set(apr_dbd_transaction_t *trans, 728339230Speter int mode) 729339230Speter{ 730339230Speter if (trans) { 731339230Speter trans->mode = mode & TXN_MODE_BITS; 732339230Speter return trans->mode; 733339230Speter } 734339230Speter return APR_DBD_TRANSACTION_COMMIT; 735339230Speter} 736339230Speterstatic int dbd_freetds_pvbquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows, 737339230Speter apr_dbd_prepared_t *statement, va_list args) 738339230Speter{ 739339230Speter return DBD_NOTIMPL; 740339230Speter} 741339230Speterstatic int dbd_freetds_pbquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows, 742339230Speter apr_dbd_prepared_t * statement, 743339230Speter const void **values) 744339230Speter{ 745339230Speter return DBD_NOTIMPL; 746339230Speter} 747339230Speter 748339230Speterstatic int dbd_freetds_pvbselect(apr_pool_t *pool, apr_dbd_t *sql, 749339230Speter apr_dbd_results_t **results, 750339230Speter apr_dbd_prepared_t *statement, 751339230Speter int seek, va_list args) 752339230Speter{ 753339230Speter return DBD_NOTIMPL; 754339230Speter} 755339230Speterstatic int dbd_freetds_pbselect(apr_pool_t *pool, apr_dbd_t *sql, 756339230Speter apr_dbd_results_t **results, 757339230Speter apr_dbd_prepared_t *statement, 758339230Speter int seek, const void **values) 759339230Speter{ 760339230Speter return DBD_NOTIMPL; 761339230Speter} 762339230Speterstatic apr_status_t dbd_freetds_datum_get(const apr_dbd_row_t *row, int n, 763339230Speter apr_dbd_type_e type, void *data) 764339230Speter{ 765339230Speter return APR_ENOTIMPL; 766339230Speter} 767339230Speter#endif 768339230Speter 769339230SpeterAPU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_freetds_driver = { 770339230Speter "freetds", 771339230Speter dbd_freetds_init, 772339230Speter dbd_freetds_native, 773339230Speter dbd_freetds_open, 774339230Speter dbd_freetds_check_conn, 775339230Speter dbd_freetds_close, 776339230Speter dbd_freetds_select_db, 777339230Speter dbd_freetds_start_transaction, 778339230Speter dbd_freetds_end_transaction, 779339230Speter dbd_freetds_query, 780339230Speter dbd_freetds_select, 781339230Speter dbd_freetds_num_cols, 782339230Speter dbd_freetds_num_tuples, 783339230Speter dbd_freetds_get_row, 784339230Speter dbd_freetds_get_entry, 785339230Speter dbd_freetds_error, 786339230Speter dbd_freetds_escape, 787339230Speter dbd_freetds_prepare, 788339230Speter dbd_freetds_pvquery, 789339230Speter dbd_freetds_pvselect, 790339230Speter dbd_freetds_pquery, 791339230Speter dbd_freetds_pselect, 792339230Speter /* this is only implemented to support httpd/2.2 standard usage, 793339230Speter * as in the original DBD implementation. Everything else is NOTIMPL. 794339230Speter */ 795339230Speter#ifdef COMPILE_STUBS 796339230Speter dbd_freetds_get_name, 797339230Speter dbd_freetds_transaction_mode_get, 798339230Speter dbd_freetds_transaction_mode_set, 799339230Speter "", 800339230Speter dbd_freetds_pvbquery, 801339230Speter dbd_freetds_pvbselect, 802339230Speter dbd_freetds_pbquery, 803339230Speter dbd_freetds_pbselect, 804339230Speter dbd_freetds_datum_get 805339230Speter#endif 806339230Speter}; 807339230Speter#endif 808339230Speter 809339230Speter#endif 810