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