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

(-)dbd/apr_dbd_freetds.c (-182 / +162 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 79-98 Link Here
79
};
80
};
80
81
81
struct apr_dbd_row_t {
82
    apr_dbd_results_t *res;
83
    BYTE buf[MAX_COL_LEN];
84
};
85
86
struct apr_dbd_prepared_t {
82
struct apr_dbd_prepared_t {
87
    int nargs;
83
    int nargs;
88
    regex_t **taint;
84
    int fmtlen;
89
    int *sz;
85
    const char *fmt, *label;
90
    char *fmt;
86
    apr_dbd_type_e *types;
87
    int offsets[];
91
};
88
};
92
89
93
#define dbd_freetds_is_success(x) (x == SUCCEED)
90
const apr_dbd_driver_t apr_dbd_freetds_driver; /* Forward declaration */
94
91
95
static int labelnum = 0; /* FIXME */
92
#define dbd_freetds_is_success(x) (x == SUCCEED)
96
static regex_t dbd_freetds_find_arg;
97
93
98
/* execute a query that doesn't return a result set, mop up,
94
/* execute a query that doesn't return a result set, mop up,
Lines 103-107 Link Here
103
{
99
{
104
    /* TBD */
100
    /* TBD */
105
    RETCODE rv = dbcmd(proc, query);
101
    RETCODE rv = dbcmd(proc,
102
#ifndef TDS_STATIC_CAST /* if not compiling against FreeTDS, drop const :-( */
103
	(char *)
104
#endif
105
	query);
106
    if (rv != SUCCEED) {
106
    if (rv != SUCCEED) {
107
        return rv;
107
        return rv;
Lines 144-147 Link Here
144
     */
144
     */
145
145
146
    sql->lasterror = NULL;
146
    sql->err = freetds_exec(sql->proc, query, 1, NULL);
147
    sql->err = freetds_exec(sql->proc, query, 1, NULL);
147
    if (!dbd_freetds_is_success(sql->err)) {
148
    if (!dbd_freetds_is_success(sql->err)) {
Lines 191-239 Link Here
191
    return (sql->err == SUCCEED) ? 0 : 1;
192
    return (sql->err == SUCCEED) ? 0 : 1;
192
}
193
}
193
static const char *dbd_untaint(apr_pool_t *pool, regex_t *rx, const char *val)
194
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,
195
static const char *dbd_statement(apr_pool_t *pool,
207
                                 apr_dbd_prepared_t *stmt,
196
                                 const apr_dbd_prepared_t *stmt,
208
                                 int nargs, const char **args)
197
                                 const char **args)
209
{
198
{
210
    int i;
199
    int i;
211
    int len;
200
    int len;
212
    const char *var;
213
    char *ret;
201
    char *ret;
214
    const char *p_in;
202
    const char *p_in, *format;
215
    char *p_out;
203
    char *p_out;
216
    char *q;
217
   
204
   
218
    /* compute upper bound on length (since untaint shrinks) */
205
    len  = stmt->fmtlen;
219
    len  = strlen(stmt->fmt) +1;
206
    for (i=0; i < stmt->nargs; ++i) {
220
    for (i=0; i<nargs; ++i) {
207
        len += strlen(args[i]);
221
        len += strlen(args[i]) - 2;
208
	switch (stmt->types[i]) {
209
	case APR_DBD_TYPE_STRING: len += 2; break;	/* For the quotes */
210
	default: break;/* Other types are all taken verbatim at the moment */
211
	}
222
    }
212
    }
223
    i = 0;
224
    p_in = stmt->fmt;
213
    p_in = stmt->fmt;
225
    p_out = ret = apr_palloc(pool, len);
214
    p_out = ret = apr_palloc(pool, len);
226
    /* FIXME silly bug - this'll catch %%s */
215
    for (i = 0; i < stmt->nargs; i++) {
227
    while (q = strstr(p_in, "%s"), q != NULL) {
216
        len = stmt->offsets[i];
228
        len = q-p_in;
217
	switch (stmt->types[i]) {
229
        strncpy(p_out, p_in, len);
218
	case APR_DBD_TYPE_STRING:
219
	    format = "%.*s'%s'";
220
	    break;
221
	default:
222
	    format = "%.*s%s";
223
	}
224
	p_out += sprintf(p_out, format, len, p_in, args[i]);
230
        p_in += len;
225
        p_in += len;
231
        p_out += len;
232
        var = dbd_untaint(pool, stmt->taint[i], args[i]);
233
        len = strlen(var);
234
        strncpy(p_out, var, len);
235
        p_in += 2;
236
        p_out += len;
237
        ++i;
238
    }
226
    }
239
    strcpy(p_out, p_in);
227
    strcpy(p_out, p_in);
Lines 245-250 Link Here
245
                               int seek, const char **values)
234
                               int seek, const char **values)
246
{
235
{
247
    const char *query = dbd_statement(pool, statement,
236
    const char *query = dbd_statement(pool, statement, values);
248
                                      statement->nargs, values);
249
    return dbd_freetds_select(pool, sql, results, query, seek);
237
    return dbd_freetds_select(pool, sql, results, query, seek);
250
}
238
}
Lines 274-279 Link Here
274
                              const char **values)
262
                              const char **values)
275
{
263
{
276
    const char *query = dbd_statement(pool, statement,
264
    const char *query = dbd_statement(pool, statement, values);
277
                                      statement->nargs, values);
278
    return dbd_freetds_query(sql, nrows, query);
265
    return dbd_freetds_query(sql, nrows, query);
279
}
266
}
Lines 300-311 Link Here
300
{
287
{
301
    RETCODE rv = 0;
288
    RETCODE rv = 0;
302
    apr_dbd_row_t *row = *rowp;
303
    int sequential = ((rownum >= 0) && res->random) ? 0 : 1;
289
    int sequential = ((rownum >= 0) && res->random) ? 0 : 1;
290
    apr_dbd_t *sql = (void *)dbgetuserdata(res->proc);
304
291
305
    if (row == NULL) {
292
    *rowp = (void *)res;
306
        row = apr_palloc(pool, sizeof(apr_dbd_row_t));
307
        *rowp = row;
308
        row->res = res;
309
    }
310
    /*
293
    /*
311
    else {
294
    else {
Lines 322-325 Link Here
322
    }
305
    }
323
    else {
306
    else {
307
	/* XXX dbgetrow() only works if DBBUFFER-option is set */
324
        rv = (rownum >= 0) ? dbgetrow(res->proc, rownum) : NO_MORE_ROWS;
308
        rv = (rownum >= 0) ? dbgetrow(res->proc, rownum) : NO_MORE_ROWS;
325
    }
309
    }
Lines 328-337 Link Here
328
    case REG_ROW: return 0;
312
    case REG_ROW: return 0;
329
    case NO_MORE_ROWS:
313
    case NO_MORE_ROWS:
330
        apr_pool_cleanup_run(pool, res->proc, clear_result);
314
	if (dbisopt(res->proc, DBBUFFER, NULL) || sequential) {
315
	    sql->lasterror = apr_pstrcat(res->pool,
316
		"NO_MORE_ROWS (count: ", apr_itoa(res->pool, DBCOUNT(res->proc)),
317
		", first: ", apr_itoa(res->pool, DBFIRSTROW(res->proc)),
318
		", current: ", apr_itoa(res->pool, DBCURROW(res->proc)),
319
		", last: ", apr_itoa(res->pool, DBLASTROW(res->proc)),
320
		")", NULL);;
321
	} else {
322
	    sql->lasterror = "NO_MORE_ROWS (DBBUFFER option must be on "
323
		"for dbgetrow() to work)";
324
	}
325
        apr_pool_cleanup_run(res->pool, res->proc, clear_result);
331
        *rowp = NULL;
326
        *rowp = NULL;
332
        return -1;
327
        return -1;
333
    case FAIL: return 1;
328
    case FAIL:
334
    case BUF_FULL: return 2; /* FIXME */
329
	sql->lasterror = "FAIL";
335
    default: return 3;
330
	return 1;
331
    case BUF_FULL:
332
	sql->lasterror = "BUF_FULL";
333
	return 2; /* FIXME */
334
    default:
335
	sql->lasterror = apr_pstrcat(res->pool,
336
	    "Unexpected error number ", apr_itoa(res->pool, rv), NULL);
337
	return 3;
336
    }
338
    }
337
339
Lines 345-355 Link Here
345
    return (const char*)row->res->vars[n].data;
347
    return (const char*)row->res->vars[n].data;
346
     */
348
     */
347
    DBPROCESS* proc = row->res->proc;
349
    apr_dbd_results_t *res = (apr_dbd_results_t *)row;
350
    DBPROCESS* proc = res->proc;
348
    BYTE *ptr = dbdata(proc, n+1);
351
    BYTE *ptr = dbdata(proc, n+1);
349
    int t = dbcoltype(proc, n+1);
352
    int t = dbcoltype(proc, n+1);
350
    int l = dbcollen(proc, n+1);
353
    int l = dbdatlen(proc, n+1);
351
    if (dbwillconvert(t, SYBCHAR)) {
354
    if (dbwillconvert(t, SYBCHAR)) {
352
      dbconvert(proc, t, ptr, l, SYBCHAR, (BYTE *)row->buf, -1);
355
      char *buf;
353
      return (const char*)row->buf;
356
      int blen = (t == SYBCHAR ? l + 1 : MAX_COL_LEN);
357
      buf = apr_palloc(res->pool, blen);
358
      l = dbconvert(proc, t, ptr, l, SYBCHAR, buf, -1);
359
      return buf;
354
    }
360
    }
355
    return (char*)ptr;
361
    return (char*)ptr;
Lines 358-362 Link Here
358
static const char *dbd_freetds_error(apr_dbd_t *sql, int n)
364
static const char *dbd_freetds_error(apr_dbd_t *sql, int n)
359
{
365
{
360
    /* XXX this doesn't seem to exist in the API ??? */
366
    const char	*err = sql->lasterror;
367
368
    sql->lasterror = NULL; /* Reset, so we stop appending */
369
    if (err)
370
	return err;
361
    return apr_psprintf(sql->pool, "Error %d", sql->err);
371
    return apr_psprintf(sql->pool, "Error %d", sql->err);
362
}
372
}
Lines 368-371 Link Here
368
    }
378
    }
369
    *nrows = 0;
379
    *nrows = 0;
380
    sql->lasterror = NULL;
370
    sql->err = freetds_exec(sql->proc, query, 0, nrows);
381
    sql->err = freetds_exec(sql->proc, query, 0, nrows);
371
382
Lines 385-449 Link Here
385
}
396
}
386
397
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,
398
static int dbd_freetds_prepare(apr_pool_t *pool, apr_dbd_t *sql,
449
                             const char *query, const char *label,
399
                             const char *query, const char *label,
Lines 451-496 Link Here
451
                             apr_dbd_prepared_t **statement)
401
                             apr_dbd_prepared_t **statement)
452
{
402
{
403
    int	i;
404
    const char *p, *op;
453
    apr_dbd_prepared_t *stmt;
405
    apr_dbd_prepared_t *stmt;
454
406
455
    if (label == NULL) {
407
    if (!*statement &&
456
        label = apr_psprintf(pool, "%d", labelnum++);
408
	(*statement = apr_palloc(pool,
409
	    sizeof(apr_dbd_prepared_t) + nargs*sizeof(int))) == NULL) {
410
	return APR_EGENERAL;
457
    }
411
    }
458
412
459
    if (!*statement) {
460
        *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
461
    }
462
    stmt = *statement;
413
    stmt = *statement;
414
    stmt->nargs = nargs;
415
    stmt->fmt = query;
416
    stmt->fmtlen = strlen(query) - nargs + 1;
417
    stmt->types = types;
418
    stmt->label = label; /* XXX Not used anywhere at the moment */
463
419
464
#if 0
420
    /*
465
    /* count args */
421
     * Run through the query-template looking for the special character,
466
    stmt->fmt = apr_pstrdup(pool, query);
422
     * which the dbd_prepare inserted into it on our behalf
467
    stmt->fmt = recurse_args(pool, 0, query, stmt, stmt->fmt);
423
     */
468
424
    for (i = 0, op = query; i < nargs; i++) {
469
    /* overestimate by a byte or two to simplify */
425
	p = strchr(op + 1, apr_dbd_freetds_driver.pformat[0]);
470
    len = strlen("CREATE PROC apr.")
426
	stmt->offsets[i] = p - op;
471
            + strlen(label)
427
	op = p;
472
            + stmt->nargs * strlen(" @arg1 varchar(len1),")
428
    }
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
429
430
    return APR_SUCCESS;
495
}
431
}
496
432
Lines 542-548 Link Here
542
    LOGINREC *login;
478
    LOGINREC *login;
543
    static const char *delims = " \r\n\t;|,";
479
    static const char *delims = " \r\n\t;|,";
544
    char *ptr;
480
    const char *ptr;
545
    char *key;
481
    const char *key;
546
    char *value;
482
    const char *value;
547
    int vlen;
483
    int vlen;
548
    int klen;
484
    int klen;
Lines 565-569 Link Here
565
        for (key = ptr-1; apr_isspace(*key); --key);
501
        for (key = ptr-1; apr_isspace(*key); --key);
566
        klen = 0;
502
        klen = 0;
567
        while (apr_isalpha(*key)) {
503
        while (key >= params && apr_isalpha(*key)) {
568
            --key;
504
            --key;
569
            ++klen;
505
            ++klen;
Lines 628-635 Link Here
628
        return NULL;
564
        return NULL;
629
    }
565
    }
630
    sql = apr_palloc (pool, sizeof (apr_dbd_t));
566
    sql = apr_pcalloc(pool, sizeof (apr_dbd_t));
631
    sql->pool = pool;
567
    sql->pool = pool;
632
    sql->proc = process;
568
    sql->proc = process;
633
    sql->params = params;
569
    sql->params = params;
570
    dbsetuserdata(process, (BYTE *)sql);
634
    return sql;
571
    return sql;
635
}
572
}
Lines 687-696 Link Here
687
{
624
{
688
    dbexit();
625
    dbexit();
689
    regfree(&dbd_freetds_find_arg);
690
    return APR_SUCCESS;
626
    return APR_SUCCESS;
691
}
627
}
628
629
static int freetds_msg_handler(DBPROCESS *dbproc, DBINT msgno, int msgstate,
630
                               int severity, char *msgtext, char *srvname,
631
                               char *procname, int line)
632
{
633
    char	*value;
634
    apr_dbd_t	*sql;
635
636
    if (dbproc == NULL || (sql = (void *)dbgetuserdata(dbproc)) == NULL) {
637
	fprintf(stderr, "%s: %s\n", srvname, msgtext);
638
	return 0;
639
    }
640
641
    value = apr_psprintf(sql->pool, "%s: %s", srvname, msgtext);
642
    if (procname && procname[0])
643
	value = apr_pstrcat(sql->pool, value, " In procedure ",
644
	    procname, NULL);
645
    if (line)
646
        value = apr_pstrcat(sql->pool, value,
647
	    procname && procname[0] ? ", line " : " Line ",
648
	    apr_itoa(sql->pool, line), NULL);
649
650
    if (sql->lasterror)
651
	sql->lasterror = apr_pstrcat(sql->pool, sql->lasterror, "\n", value, NULL);
652
    else
653
	sql->lasterror = value;
654
655
    return 0;
656
}
657
692
static int freetds_err_handler(DBPROCESS *dbproc, int severity, int dberr,
658
static int freetds_err_handler(DBPROCESS *dbproc, int severity, int dberr,
693
                               int oserr, char *dberrstr, char *oserrstr)
659
                               int oserr, char *dberrstr, char *oserrstr)
694
{
660
{
661
    char	*value;
662
    apr_dbd_t	*sql;
663
664
    if (dbproc == NULL || (sql = (void *)dbgetuserdata(dbproc)) == NULL) {
665
	fprintf(stderr, "%s\n", dberrstr);
666
	return INT_CANCEL;
667
    }
668
    /*
669
     * Do not append the useless "general error", if details
670
     * are already recorded (by the msg_handler). Just return.
671
     * XXX: this skipping currently only works with Sybase's OpenClient
672
     * XXX: because FreeTDS implementation uses bogus dberr values:
673
     * XXX: https://sourceforge.net/tracker/?func=detail&aid=3555777&group_id=33106&atid=407806
674
     */
675
    if (dberr == SYBESMSG && sql->lasterror)
676
	return INT_CANCEL;
677
678
    value = apr_psprintf(sql->pool, "%d: %s", dberr, dberrstr);
679
    if (oserrstr) {
680
	value = apr_pstrcat(sql->pool, ". (", oserrstr, ")", NULL);
681
    }
682
    if (sql->lasterror)
683
	sql->lasterror = apr_pstrcat(sql->pool, sql->lasterror, "\n", value, NULL);
684
    else
685
	sql->lasterror = value;
695
    return INT_CANCEL; /* never exit */
686
    return INT_CANCEL; /* never exit */
696
}
687
}
Lines 697-708 Link Here
697
static void dbd_freetds_init(apr_pool_t *pool)
689
static void dbd_freetds_init(apr_pool_t *pool)
698
{
690
{
699
    int rv = regcomp(&dbd_freetds_find_arg,
691
    if (dbinit() == FAIL)
700
                     "%(\\{[^}]*\\})?([0-9]*)[A-Za-z]", REG_EXTENDED);
692
	fprintf(stderr, "dbinit() failed\n");
701
    if (rv != 0) {
693
    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);
694
    dberrhandle(freetds_err_handler);
708
    apr_pool_cleanup_register(pool, NULL, freetds_term, apr_pool_cleanup_null);
695
    apr_pool_cleanup_register(pool, NULL, freetds_term, apr_pool_cleanup_null);
Lines 788-799 Link Here
788
    dbd_freetds_pquery,
779
    dbd_freetds_pquery,
789
    dbd_freetds_pselect,
780
    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,
781
    dbd_freetds_get_name,
795
    dbd_freetds_transaction_mode_get,
782
    dbd_freetds_transaction_mode_get,
796
    dbd_freetds_transaction_mode_set,
783
    dbd_freetds_transaction_mode_set,
797
    "",
784
    "\1", /* Would not occur in a real query... */
798
    dbd_freetds_pvbquery,
785
    dbd_freetds_pvbquery,
799
    dbd_freetds_pvbselect,
786
    dbd_freetds_pvbselect,
Lines 801-805 Link Here
801
    dbd_freetds_pbselect,
788
    dbd_freetds_pbselect,
802
    dbd_freetds_datum_get
789
    dbd_freetds_datum_get
803
#endif
804
};
790
};
805
#endif
791
#endif

Return to bug 53666