View | Details | Raw Unified | Return to bug 53666
Collapse All | Expand All

(-)dbd/apr_dbd_freetds.c (-177 / +167 lines)
Lines 41-44 Link Here
41
#endif
41
#endif
42
#ifdef HAVE_SYBDB_H
42
#ifdef HAVE_SYBDB_H
43
#include <sybfront.h>
43
#include <sybdb.h>
44
#include <sybdb.h>
44
#endif
45
#endif
Lines 46-50 Link Here
46
#include <stdio.h>
47
#include <stdio.h>
47
#include <sys/types.h>
48
#include <sys/types.h>
48
#include <regex.h>
49
49
50
/* This probably needs to change for different applications */
50
/* This probably needs to change for different applications */
Lines 68-71 Link Here
68
    apr_pool_t *pool;
68
    apr_pool_t *pool;
69
    const char *params;
69
    const char *params;
70
    const char *lasterror;
70
    RETCODE err;
71
    RETCODE err;
71
};
72
};
Lines 81-98 Link Here
81
struct apr_dbd_row_t {
82
struct apr_dbd_row_t {
82
    apr_dbd_results_t *res;
83
    apr_dbd_results_t *res;
83
    BYTE buf[MAX_COL_LEN];
84
    apr_pool_t *pool;
84
};
85
};
85
86
86
struct apr_dbd_prepared_t {
87
struct apr_dbd_prepared_t {
87
    int nargs;
88
    int nargs;
88
    regex_t **taint;
89
    int fmtlen;
89
    int *sz;
90
    const char *fmt, *label;
90
    char *fmt;
91
    apr_dbd_type_e *types;
92
    int offsets[];
91
};
93
};
92
94
93
#define dbd_freetds_is_success(x) (x == SUCCEED)
95
const apr_dbd_driver_t apr_dbd_freetds_driver; /* Forward declaration */
94
96
95
static int labelnum = 0; /* FIXME */
97
#define dbd_freetds_is_success(x) (x == SUCCEED)
96
static regex_t dbd_freetds_find_arg;
97
98
98
/* execute a query that doesn't return a result set, mop up,
99
/* execute a query that doesn't return a result set, mop up,
Lines 103-107 Link Here
103
{
104
{
104
    /* TBD */
105
    /* TBD */
105
    RETCODE rv = dbcmd(proc, query);
106
    RETCODE rv = dbcmd(proc,
107
#ifndef TDS_STATIC_CAST /* if not compiling against FreeTDS, drop const :-( */
108
	(char *)
109
#endif
110
	query);
106
    if (rv != SUCCEED) {
111
    if (rv != SUCCEED) {
107
        return rv;
112
        return rv;
Lines 144-147 Link Here
144
     */
149
     */
145
150
151
    sql->lasterror = NULL;
146
    sql->err = freetds_exec(sql->proc, query, 1, NULL);
152
    sql->err = freetds_exec(sql->proc, query, 1, NULL);
147
    if (!dbd_freetds_is_success(sql->err)) {
153
    if (!dbd_freetds_is_success(sql->err)) {
Lines 191-239 Link Here
191
    return (sql->err == SUCCEED) ? 0 : 1;
197
    return (sql->err == SUCCEED) ? 0 : 1;
192
}
198
}
193
static const char *dbd_untaint(apr_pool_t *pool, regex_t *rx, const char *val)
199
194
{
195
    regmatch_t match[1];
196
    if (rx == NULL) {
197
        /* no untaint expression */
198
        return val;
199
    }
200
    if (regexec(rx, val, 1, match, 0) == 0) {
201
        return apr_pstrndup(pool, val+match[0].rm_so,
202
                            match[0].rm_eo - match[0].rm_so);
203
    }
204
    return "";
205
}
206
static const char *dbd_statement(apr_pool_t *pool,
200
static const char *dbd_statement(apr_pool_t *pool,
207
                                 apr_dbd_prepared_t *stmt,
201
                                 const apr_dbd_prepared_t *stmt,
208
                                 int nargs, const char **args)
202
                                 const char **args)
209
{
203
{
210
    int i;
204
    int i;
211
    int len;
205
    int len;
212
    const char *var;
213
    char *ret;
206
    char *ret;
214
    const char *p_in;
207
    const char *p_in, *format;
215
    char *p_out;
208
    char *p_out;
216
    char *q;
217
   
209
   
218
    /* compute upper bound on length (since untaint shrinks) */
210
    len  = stmt->fmtlen;
219
    len  = strlen(stmt->fmt) +1;
211
    for (i=0; i < stmt->nargs; ++i) {
220
    for (i=0; i<nargs; ++i) {
212
        len += strlen(args[i]);
221
        len += strlen(args[i]) - 2;
213
	switch (stmt->types[i]) {
214
	case APR_DBD_TYPE_STRING: len += 2; break;	/* For the quotes */
215
	default: break;/* Other types are all taken verbatim at the moment */
216
	}
222
    }
217
    }
223
    i = 0;
224
    p_in = stmt->fmt;
218
    p_in = stmt->fmt;
225
    p_out = ret = apr_palloc(pool, len);
219
    p_out = ret = apr_palloc(pool, len);
226
    /* FIXME silly bug - this'll catch %%s */
220
    for (i = 0; i < stmt->nargs; i++) {
227
    while (q = strstr(p_in, "%s"), q != NULL) {
221
        len = stmt->offsets[i];
228
        len = q-p_in;
222
	switch (stmt->types[i]) {
229
        strncpy(p_out, p_in, len);
223
	case APR_DBD_TYPE_STRING:
230
        p_in += len;
224
	    format = "%.*s'%s'";
231
        p_out += len;
225
	    break;
232
        var = dbd_untaint(pool, stmt->taint[i], args[i]);
226
	default:
233
        len = strlen(var);
227
	    format = "%.*s%s";
234
        strncpy(p_out, var, len);
228
	}
235
        p_in += 2;
229
	p_out += sprintf(p_out, format, len, p_in, args[i]);
236
        p_out += len;
230
	p_in += (len + 1);
237
        ++i;
238
    }
231
    }
239
    strcpy(p_out, p_in);
232
    strcpy(p_out, p_in);
Lines 245-250 Link Here
245
                               int seek, const char **values)
238
                               int seek, const char **values)
246
{
239
{
247
    const char *query = dbd_statement(pool, statement,
240
    const char *query = dbd_statement(pool, statement, values);
248
                                      statement->nargs, values);
249
    return dbd_freetds_select(pool, sql, results, query, seek);
241
    return dbd_freetds_select(pool, sql, results, query, seek);
250
}
242
}
Lines 274-279 Link Here
274
                              const char **values)
266
                              const char **values)
275
{
267
{
276
    const char *query = dbd_statement(pool, statement,
268
    const char *query = dbd_statement(pool, statement, values);
277
                                      statement->nargs, values);
278
    return dbd_freetds_query(sql, nrows, query);
269
    return dbd_freetds_query(sql, nrows, query);
279
}
270
}
Lines 302-305 Link Here
302
    apr_dbd_row_t *row = *rowp;
293
    apr_dbd_row_t *row = *rowp;
303
    int sequential = ((rownum >= 0) && res->random) ? 0 : 1;
294
    int sequential = ((rownum >= 0) && res->random) ? 0 : 1;
295
    apr_dbd_t *sql = (void *)dbgetuserdata(res->proc);
304
296
305
    if (row == NULL) {
297
    if (row == NULL) {
Lines 307-310 Link Here
307
        *rowp = row;
299
        *rowp = row;
308
        row->res = res;
300
        row->res = res;
301
	row->pool = pool; /* For subsequent calls to get_entry */
309
    }
302
    }
310
    /*
303
    /*
Lines 322-325 Link Here
322
    }
315
    }
323
    else {
316
    else {
317
	/* XXX dbgetrow() only works if DBBUFFER-option is set */
324
        rv = (rownum >= 0) ? dbgetrow(res->proc, rownum) : NO_MORE_ROWS;
318
        rv = (rownum >= 0) ? dbgetrow(res->proc, rownum) : NO_MORE_ROWS;
325
    }
319
    }
Lines 328-337 Link Here
328
    case REG_ROW: return 0;
322
    case REG_ROW: return 0;
329
    case NO_MORE_ROWS:
323
    case NO_MORE_ROWS:
330
        apr_pool_cleanup_run(pool, res->proc, clear_result);
324
	if (dbisopt(res->proc, DBBUFFER, NULL) || sequential) {
325
	    sql->lasterror = apr_pstrcat(sql->pool,
326
		"NO_MORE_ROWS (count: ", apr_itoa(sql->pool, DBCOUNT(res->proc)),
327
		", first: ", apr_itoa(sql->pool, DBFIRSTROW(res->proc)),
328
		", current: ", apr_itoa(sql->pool, DBCURROW(res->proc)),
329
		", last: ", apr_itoa(sql->pool, DBLASTROW(res->proc)),
330
		")", NULL);;
331
	} else {
332
	    sql->lasterror = "NO_MORE_ROWS (DBBUFFER option must be on "
333
		"for dbgetrow() to work)";
334
	}
335
        apr_pool_cleanup_run(res->pool, res->proc, clear_result);
331
        *rowp = NULL;
336
        *rowp = NULL;
332
        return -1;
337
        return -1;
333
    case FAIL: return 1;
338
    case FAIL:
334
    case BUF_FULL: return 2; /* FIXME */
339
	sql->lasterror = "FAIL";
335
    default: return 3;
340
	return 1;
341
    case BUF_FULL:
342
	sql->lasterror = "BUF_FULL";
343
	return 2; /* FIXME */
344
    default:
345
	sql->lasterror = apr_pstrcat(res->pool,
346
	    "Unexpected error number ", apr_itoa(res->pool, rv), NULL);
347
	return 3;
336
    }
348
    }
337
349
Lines 341-355 Link Here
341
static const char *dbd_freetds_get_entry(const apr_dbd_row_t *row, int n)
353
static const char *dbd_freetds_get_entry(const apr_dbd_row_t *row, int n)
342
{
354
{
343
    /* FIXME: support different data types */
344
    /* this fails - bind gets some vars but not others
345
    return (const char*)row->res->vars[n].data;
346
     */
347
    DBPROCESS* proc = row->res->proc;
355
    DBPROCESS* proc = row->res->proc;
348
    BYTE *ptr = dbdata(proc, n+1);
356
    BYTE *ptr = dbdata(proc, n+1); /* XXX No error checking! */
349
    int t = dbcoltype(proc, n+1);
357
    int t = dbcoltype(proc, n+1);
350
    int l = dbcollen(proc, n+1);
358
    int l = dbdatlen(proc, n+1);
359
    if (l == -1)
360
      return NULL;
351
    if (dbwillconvert(t, SYBCHAR)) {
361
    if (dbwillconvert(t, SYBCHAR)) {
352
      dbconvert(proc, t, ptr, l, SYBCHAR, (BYTE *)row->buf, -1);
362
      char *buf = apr_palloc(row->pool, l + 1);
353
      return (const char*)row->buf;
363
      dbconvert(proc, t, ptr, l, SYBCHAR, buf, -1);
364
      return buf;
354
    }
365
    }
355
    return (char*)ptr;
366
    return (char*)ptr;
Lines 358-362 Link Here
358
static const char *dbd_freetds_error(apr_dbd_t *sql, int n)
369
static const char *dbd_freetds_error(apr_dbd_t *sql, int n)
359
{
370
{
360
    /* XXX this doesn't seem to exist in the API ??? */
371
    const char	*err = sql->lasterror;
372
373
    sql->lasterror = NULL; /* Reset, so we stop appending */
374
    if (err)
375
	return err;
361
    return apr_psprintf(sql->pool, "Error %d", sql->err);
376
    return apr_psprintf(sql->pool, "Error %d", sql->err);
362
}
377
}
Lines 368-371 Link Here
368
    }
383
    }
369
    *nrows = 0;
384
    *nrows = 0;
385
    sql->lasterror = NULL;
370
    sql->err = freetds_exec(sql->proc, query, 0, nrows);
386
    sql->err = freetds_exec(sql->proc, query, 0, nrows);
371
387
Lines 385-449 Link Here
385
}
401
}
386
402
387
static apr_status_t freetds_regfree(void *rx)
388
{
389
    regfree((regex_t*)rx);
390
    return APR_SUCCESS;
391
}
392
static int recurse_args(apr_pool_t *pool, int n, const char *query,
393
                        apr_dbd_prepared_t *stmt, int offs)
394
{
395
396
    /* we only support %s arguments for now */
397
    int ret;
398
    char arg[256];
399
    regmatch_t matches[3];
400
    if (regexec(&dbd_freetds_find_arg, query, 3, matches, 0) != 0) {
401
        /* No more args */
402
        stmt->nargs = n;
403
        stmt->taint = apr_palloc(pool, n*sizeof(regex_t*));
404
        stmt->sz = apr_palloc(pool, n*sizeof(int));
405
        ret = 0;
406
    }
407
    else {
408
        int i;
409
        int sz = 0;
410
        int len = matches[1].rm_eo - matches[1].rm_so - 2;
411
        if (len > 255) {
412
            return 9999;
413
        }
414
415
        ret = recurse_args(pool, n+1, query+matches[0].rm_eo,
416
                           stmt, offs+matches[0].rm_eo);
417
418
        memmove(stmt->fmt + offs + matches[1].rm_so,
419
                stmt->fmt + offs + matches[0].rm_eo-1,
420
                strlen(stmt->fmt+offs+matches[0].rm_eo)+2);
421
422
        /* compile untaint to a regex if found */
423
        if (matches[1].rm_so == -1) {
424
            stmt->taint[n] = NULL;
425
        }
426
        else {
427
            strncpy(arg, query+matches[1].rm_so+1,
428
                    matches[1].rm_eo - matches[1].rm_so - 2);
429
            arg[matches[1].rm_eo - matches[1].rm_so - 2] = '\0';
430
            stmt->taint[n] = apr_palloc(pool, sizeof(regex_t));
431
            if (regcomp(stmt->taint[n], arg, REG_ICASE|REG_EXTENDED) != 0) {
432
                ++ret;
433
            }
434
            else {
435
                apr_pool_cleanup_register(pool, stmt->taint[n], freetds_regfree,
436
                                          apr_pool_cleanup_null);
437
            }
438
        }
439
440
        /* record length if specified */
441
        for (i=matches[2].rm_so; i<matches[2].rm_eo; ++i) {
442
            sz = 10*sz + (query[i]-'\0');
443
        }
444
    }
445
    return ret;
446
}
447
448
static int dbd_freetds_prepare(apr_pool_t *pool, apr_dbd_t *sql,
403
static int dbd_freetds_prepare(apr_pool_t *pool, apr_dbd_t *sql,
449
                             const char *query, const char *label,
404
                             const char *query, const char *label,
Lines 451-496 Link Here
451
                             apr_dbd_prepared_t **statement)
406
                             apr_dbd_prepared_t **statement)
452
{
407
{
408
    int	i;
409
    const char *p, *op;
453
    apr_dbd_prepared_t *stmt;
410
    apr_dbd_prepared_t *stmt;
454
411
455
    if (label == NULL) {
412
    if (!*statement &&
456
        label = apr_psprintf(pool, "%d", labelnum++);
413
	(*statement = apr_palloc(pool,
414
	    sizeof(apr_dbd_prepared_t) + nargs*sizeof(int))) == NULL) {
415
	return APR_EGENERAL;
457
    }
416
    }
458
417
459
    if (!*statement) {
460
        *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
461
    }
462
    stmt = *statement;
418
    stmt = *statement;
419
    stmt->nargs = nargs;
420
    stmt->fmt = query;
421
    stmt->fmtlen = strlen(query) - nargs + 1;
422
    stmt->types = types;
423
    stmt->label = label; /* XXX Not used anywhere at the moment */
463
424
464
#if 0
425
    /*
465
    /* count args */
426
     * Run through the query-template looking for the special character,
466
    stmt->fmt = apr_pstrdup(pool, query);
427
     * which the dbd_prepare inserted into it on our behalf
467
    stmt->fmt = recurse_args(pool, 0, query, stmt, stmt->fmt);
428
     */
468
429
    for (i = 0, op = query; i < nargs; i++) {
469
    /* overestimate by a byte or two to simplify */
430
	p = strchr(op, apr_dbd_freetds_driver.pformat[0]);
470
    len = strlen("CREATE PROC apr.")
431
	stmt->offsets[i] = p - op;
471
            + strlen(label)
432
	op = p + 1;
472
            + stmt->nargs * strlen(" @arg1 varchar(len1),")
433
    }
473
            + strlen(" AS begin ")
474
            + strlen(stmt->fmt)
475
            + strlen(" end "); /* extra byte for terminator */
476
477
    pquery = apr_pcalloc(pool, len);
478
    sprintf(pquery, "CREATE PROC apr.%s", label);
479
    for (i=0; i<stmt->nargs; ++i) {
480
        sprintf(pquery+strlen(pquery), " @arg%d varchar(%d)", i, stmt->sz[i]);
481
        if (i < stmt->nargs-1) {
482
            pquery[strlen(pquery)] = ',';
483
        }
484
    }
485
    strcat(pquery, " AS BEGIN ");
486
    strcat(pquery, stmt->fmt);
487
    strcat(pquery, " END");
488
489
    return (freetds_exec(sql->proc, pquery, 0, &i) == SUCCEED) ? 0 : 1;
490
#else
491
    stmt->fmt = apr_pstrdup(pool, query);
492
    return recurse_args(pool, 0, query, stmt, 0);
493
#endif
494
434
435
    return APR_SUCCESS;
495
}
436
}
496
437
Lines 542-548 Link Here
542
    LOGINREC *login;
483
    LOGINREC *login;
543
    static const char *delims = " \r\n\t;|,";
484
    static const char *delims = " \r\n\t;|,";
544
    char *ptr;
485
    const char *ptr;
545
    char *key;
486
    const char *key;
546
    char *value;
487
    const char *value;
547
    int vlen;
488
    int vlen;
548
    int klen;
489
    int klen;
Lines 565-569 Link Here
565
        for (key = ptr-1; apr_isspace(*key); --key);
506
        for (key = ptr-1; apr_isspace(*key); --key);
566
        klen = 0;
507
        klen = 0;
567
        while (apr_isalpha(*key)) {
508
        while (key >= params && apr_isalpha(*key)) {
568
            --key;
509
            --key;
569
            ++klen;
510
            ++klen;
Lines 628-635 Link Here
628
        return NULL;
569
        return NULL;
629
    }
570
    }
630
    sql = apr_palloc (pool, sizeof (apr_dbd_t));
571
    sql = apr_pcalloc(pool, sizeof (apr_dbd_t));
631
    sql->pool = pool;
572
    sql->pool = pool;
632
    sql->proc = process;
573
    sql->proc = process;
633
    sql->params = params;
574
    sql->params = params;
575
    dbsetuserdata(process, (BYTE *)sql);
634
    return sql;
576
    return sql;
635
}
577
}
Lines 687-708 Link Here
687
{
629
{
688
    dbexit();
630
    dbexit();
689
    regfree(&dbd_freetds_find_arg);
690
    return APR_SUCCESS;
631
    return APR_SUCCESS;
691
}
632
}
633
634
static int freetds_msg_handler(DBPROCESS *dbproc, DBINT msgno, int msgstate,
635
                               int severity, char *msgtext, char *srvname,
636
                               char *procname, int line)
637
{
638
    char	*value;
639
    apr_dbd_t	*sql;
640
641
    if (dbproc == NULL || (sql = (void *)dbgetuserdata(dbproc)) == NULL) {
642
	/* Initial messages are not interesting */
643
	return 0;
644
    }
645
646
    value = apr_psprintf(sql->pool, "%s: %s", srvname, msgtext);
647
    if (procname && procname[0])
648
	value = apr_pstrcat(sql->pool, value, " In procedure ",
649
	    procname, NULL);
650
    if (line)
651
        value = apr_pstrcat(sql->pool, value,
652
	    procname && procname[0] ? ", line " : " Line ",
653
	    apr_itoa(sql->pool, line), NULL);
654
655
    if (sql->lasterror)
656
	sql->lasterror = apr_pstrcat(sql->pool, sql->lasterror, "\n", value, NULL);
657
    else
658
	sql->lasterror = value;
659
660
    return 0;
661
}
662
692
static int freetds_err_handler(DBPROCESS *dbproc, int severity, int dberr,
663
static int freetds_err_handler(DBPROCESS *dbproc, int severity, int dberr,
693
                               int oserr, char *dberrstr, char *oserrstr)
664
                               int oserr, char *dberrstr, char *oserrstr)
694
{
665
{
666
    char	*value;
667
    apr_dbd_t	*sql;
668
669
    if (dbproc == NULL || (sql = (void *)dbgetuserdata(dbproc)) == NULL) {
670
	fprintf(stderr, "%s\n", dberrstr);
671
	return INT_CANCEL;
672
    }
673
    /*
674
     * Do not append the useless "general error", if details
675
     * are already recorded (by the msg_handler). Just return.
676
     * XXX: this skipping currently only works with Sybase's OpenClient
677
     * XXX: because FreeTDS implementation uses bogus dberr values:
678
     * XXX: https://sourceforge.net/tracker/?func=detail&aid=3555777&group_id=33106&atid=407806
679
     */
680
    if (dberr == SYBESMSG && sql->lasterror)
681
	return INT_CANCEL;
682
683
    value = apr_psprintf(sql->pool, "%d: %s", dberr, dberrstr);
684
    if (oserrstr) {
685
	value = apr_pstrcat(sql->pool, ". (", oserrstr, ")", NULL);
686
    }
687
    if (sql->lasterror)
688
	sql->lasterror = apr_pstrcat(sql->pool, sql->lasterror, "\n", value, NULL);
689
    else
690
	sql->lasterror = value;
695
    return INT_CANCEL; /* never exit */
691
    return INT_CANCEL; /* never exit */
696
}
692
}
697
static void dbd_freetds_init(apr_pool_t *pool)
693
static void dbd_freetds_init(apr_pool_t *pool)
698
{
694
{
699
    int rv = regcomp(&dbd_freetds_find_arg,
695
    if (dbinit() == FAIL)
700
                     "%(\\{[^}]*\\})?([0-9]*)[A-Za-z]", REG_EXTENDED);
696
	fprintf(stderr, "dbinit() failed\n");
701
    if (rv != 0) {
697
    dbmsghandle(freetds_msg_handler);
702
        char errmsg[256];
703
        regerror(rv, &dbd_freetds_find_arg, errmsg, 256);
704
        fprintf(stderr, "regcomp failed: %s\n", errmsg);
705
    }
706
    dbinit();
707
    dberrhandle(freetds_err_handler);
698
    dberrhandle(freetds_err_handler);
708
    apr_pool_cleanup_register(pool, NULL, freetds_term, apr_pool_cleanup_null);
699
    apr_pool_cleanup_register(pool, NULL, freetds_term, apr_pool_cleanup_null);
Lines 766-770 Link Here
766
757
767
APU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_freetds_driver = {
758
APU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_freetds_driver = {
759
#ifndef TI_MODULE_NAME
768
    "freetds",
760
    "freetds",
761
#else
762
    TI_MODULE_NAME,
763
#endif
769
    dbd_freetds_init,
764
    dbd_freetds_init,
770
    dbd_freetds_native,
765
    dbd_freetds_native,
Lines 788-799 Link Here
788
    dbd_freetds_pquery,
783
    dbd_freetds_pquery,
789
    dbd_freetds_pselect,
784
    dbd_freetds_pselect,
790
    /* this is only implemented to support httpd/2.2 standard usage,
791
     * as in the original DBD implementation.  Everything else is NOTIMPL.
792
     */
793
#ifdef COMPILE_STUBS
794
    dbd_freetds_get_name,
785
    dbd_freetds_get_name,
795
    dbd_freetds_transaction_mode_get,
786
    dbd_freetds_transaction_mode_get,
796
    dbd_freetds_transaction_mode_set,
787
    dbd_freetds_transaction_mode_set,
797
    "",
788
    "\1", /* Would not occur in a real query... */
798
    dbd_freetds_pvbquery,
789
    dbd_freetds_pvbquery,
799
    dbd_freetds_pvbselect,
790
    dbd_freetds_pvbselect,
Lines 801-805 Link Here
801
    dbd_freetds_pbselect,
792
    dbd_freetds_pbselect,
802
    dbd_freetds_datum_get
793
    dbd_freetds_datum_get
803
#endif
804
};
794
};
805
#endif
795
#endif

Return to bug 53666