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