source: npl/mailserver/dspam/dspam-3.10.2/src/sqlite3_drv.c @ c5c522c

gcc484ntopperl-5.22
Last change on this file since c5c522c was c5c522c, checked in by Edwin Eefting <edwin@datux.nl>, 8 years ago

initial commit, transferred from cleaned syn3 svn tree

  • Property mode set to 100644
File size: 39.2 KB
Line 
1/* $Id: sqlite3_drv.c,v 1.187 2011/06/28 00:13:48 sbajic Exp $ */
2
3/*
4 DSPAM
5 COPYRIGHT (C) 2002-2012 DSPAM PROJECT
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU Affero General Public License as
9 published by the Free Software Foundation, either version 3 of the
10 License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU Affero General Public License for more details.
16
17 You should have received a copy of the GNU Affero General Public License
18 along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20*/
21
22#ifdef HAVE_CONFIG_H
23#include <auto-config.h>
24#endif
25
26#include <string.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29#ifndef _WIN32
30#   include <pwd.h>
31#   include <dirent.h>
32#endif
33#ifdef HAVE_UNISTD_H
34#   include <unistd.h>
35#endif
36#include <errno.h>
37#include <stdlib.h>
38#include <stdio.h>
39#include <fcntl.h>
40#include <signal.h>
41
42#ifdef TIME_WITH_SYS_TIME
43#   include <sys/time.h>
44#   include <time.h>
45#else
46#   ifdef HAVE_SYS_TIME_H
47#       include <sys/time.h>
48#   else
49#       include <time.h>
50#   endif
51#endif
52
53#include "storage_driver.h"
54#include "sqlite3_drv.h"
55#include "libdspam.h"
56#include "config.h"
57#include "error.h"
58#include "language.h"
59#include "util.h"
60#include "config_shared.h"
61
62#ifdef _WIN32
63#   include <process.h>
64#   include "dir_win32.h"
65#endif
66
67int
68dspam_init_driver (DRIVER_CTX *DTX)
69{
70  return 0;
71}
72
73int
74dspam_shutdown_driver (DRIVER_CTX *DTX)
75{
76  return 0;
77}
78
79int
80_sqlite_drv_get_spamtotals (DSPAM_CTX * CTX)
81{
82  struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
83  char query[1024];
84  char *err=NULL, **row;
85  int nrow, ncolumn;
86  int rc;
87
88  if (s->dbh == NULL)
89  {
90    LOGDEBUG ("_sqlite_drv_get_spamtotals: invalid database handle (NULL)");
91    return EINVAL;
92  }
93
94  memset(&s->control_totals, 0, sizeof(struct _ds_spam_totals));
95  memset(&CTX->totals, 0, sizeof(struct _ds_spam_totals));
96
97  snprintf (query, sizeof (query),
98            "SELECT spam_learned,innocent_learned,"
99            "spam_misclassified,innocent_misclassified,"
100            "spam_corpusfed,innocent_corpusfed,"
101            "spam_classified,innocent_classified"
102            " FROM dspam_stats");
103
104  if ((sqlite3_get_table(s->dbh, query, &row, &nrow, &ncolumn, &err))!=SQLITE_OK)
105  {
106    _sqlite_drv_query_error (err, query);
107    return EFAILURE;
108  }
109
110  if (nrow>0 && row != NULL) {
111    CTX->totals.spam_learned            = strtoul (row[ncolumn], NULL, 0);
112    if (CTX->totals.spam_learned == ULONG_MAX && errno == ERANGE) {
113      LOGDEBUG("_sqlite_drv_get_spamtotals: failed converting %s to CTX->totals.spam_learned", row[ncolumn]);
114      rc = EFAILURE;
115      goto FAIL;
116    }
117    CTX->totals.innocent_learned        = strtoul (row[ncolumn+1], NULL, 0);
118    if (CTX->totals.innocent_learned == ULONG_MAX && errno == ERANGE) {
119      LOGDEBUG("_sqlite_drv_get_spamtotals: failed converting %s to CTX->totals.innocent_learned", row[ncolumn+1]);
120      rc = EFAILURE;
121      goto FAIL;
122    }
123    CTX->totals.spam_misclassified      = strtoul (row[ncolumn+2], NULL, 0);
124    if (CTX->totals.spam_misclassified == ULONG_MAX && errno == ERANGE) {
125      LOGDEBUG("_sqlite_drv_get_spamtotals: failed converting %s to CTX->totals.spam_misclassified", row[ncolumn+2]);
126      rc = EFAILURE;
127      goto FAIL;
128    }
129    CTX->totals.innocent_misclassified  = strtoul (row[ncolumn+3], NULL, 0);
130    if (CTX->totals.innocent_misclassified == ULONG_MAX && errno == ERANGE) {
131      LOGDEBUG("_sqlite_drv_get_spamtotals: failed converting %s to CTX->totals.innocent_misclassified", row[ncolumn+3]);
132      rc = EFAILURE;
133      goto FAIL;
134    }
135    CTX->totals.spam_corpusfed          = strtoul (row[ncolumn+4], NULL, 0);
136    if (CTX->totals.spam_corpusfed == ULONG_MAX && errno == ERANGE) {
137      LOGDEBUG("_sqlite_drv_get_spamtotals: failed converting %s to CTX->totals.spam_corpusfed", row[ncolumn+4]);
138      rc = EFAILURE;
139      goto FAIL;
140    }
141    CTX->totals.innocent_corpusfed      = strtoul (row[ncolumn+5], NULL, 0);
142    if (CTX->totals.innocent_corpusfed == ULONG_MAX && errno == ERANGE) {
143      LOGDEBUG("_sqlite_drv_get_spamtotals: failed converting %s to CTX->totals.innocent_corpusfed", row[ncolumn+5]);
144      rc = EFAILURE;
145      goto FAIL;
146    }
147    if (row[ncolumn+6] != NULL && row[ncolumn+7] != NULL) {
148      CTX->totals.spam_classified       = strtoul (row[ncolumn+6], NULL, 0);
149      if (CTX->totals.spam_classified == ULONG_MAX && errno == ERANGE) {
150        LOGDEBUG("_sqlite_drv_get_spamtotals: failed converting %s to CTX->totals.spam_classified", row[ncolumn+6]);
151        rc = EFAILURE;
152        goto FAIL;
153      }
154      CTX->totals.innocent_classified   = strtoul (row[ncolumn+7], NULL, 0);
155      if (CTX->totals.innocent_classified == ULONG_MAX && errno == ERANGE) {
156        LOGDEBUG("_sqlite_drv_get_spamtotals: failed converting %s to CTX->totals.innocent_classified", row[ncolumn+7]);
157        rc = EFAILURE;
158        goto FAIL;
159      }
160    } else {
161      CTX->totals.spam_classified       = 0;
162      CTX->totals.innocent_classified   = 0;
163    }
164    rc = 0;
165  } else {
166    rc = EFAILURE;
167  }
168
169FAIL:
170  sqlite3_free_table(row);
171  if ( !rc )
172    memcpy(&s->control_totals, &CTX->totals, sizeof(struct _ds_spam_totals));
173
174  return rc;
175}
176
177int
178_sqlite_drv_set_spamtotals (DSPAM_CTX * CTX)
179{
180  struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
181  char query[1024];
182  char *err=NULL;
183  int result = SQLITE_OK;
184
185  if (s->dbh == NULL)
186  {
187    LOGDEBUG ("_sqlite_drv_set_spamtotals: invalid database handle (NULL)");
188    return EINVAL;
189  }
190
191  if (CTX->operating_mode == DSM_CLASSIFY)
192  {
193    _sqlite_drv_get_spamtotals (CTX);    /* undo changes to in memory totals */
194    return 0;
195  }
196
197  /* dspam_stat_id insures only one stats record */
198
199  if (s->control_totals.innocent_learned == 0)
200  {
201    snprintf (query, sizeof (query),
202              "INSERT INTO dspam_stats (dspam_stat_id,spam_learned,"
203              "innocent_learned,spam_misclassified,innocent_misclassified,"
204              "spam_corpusfed,innocent_corpusfed,"
205              "spam_classified,innocent_classified)"
206              " VALUES (%d,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu)",
207              0,
208              CTX->totals.spam_learned,
209              CTX->totals.innocent_learned, CTX->totals.spam_misclassified,
210              CTX->totals.innocent_misclassified, CTX->totals.spam_corpusfed,
211              CTX->totals.innocent_corpusfed, CTX->totals.spam_classified,
212              CTX->totals.innocent_classified);
213    result = sqlite3_exec(s->dbh, query, NULL, NULL, NULL);
214  }
215
216  if (s->control_totals.innocent_learned != 0 || result != SQLITE_OK)
217  {
218    snprintf (query, sizeof (query),
219              "UPDATE dspam_stats SET spam_learned=spam_learned%s%d,"
220              "innocent_learned=innocent_learned%s%d,"
221              "spam_misclassified=spam_misclassified%s%d,"
222              "innocent_misclassified=innocent_misclassified%s%d,"
223              "spam_corpusfed=spam_corpusfed%s%d,"
224              "innocent_corpusfed=innocent_corpusfed%s%d,"
225              "spam_classified=spam_classified%s%d,"
226              "innocent_classified=innocent_classified%s%d",
227              (CTX->totals.spam_learned >
228               s->control_totals.spam_learned) ? "+" : "-",
229              abs (CTX->totals.spam_learned -
230                   s->control_totals.spam_learned),
231              (CTX->totals.innocent_learned >
232               s->control_totals.innocent_learned) ? "+" : "-",
233              abs (CTX->totals.innocent_learned -
234                   s->control_totals.innocent_learned),
235              (CTX->totals.spam_misclassified >
236               s->control_totals.spam_misclassified) ? "+" : "-",
237              abs (CTX->totals.spam_misclassified -
238                   s->control_totals.spam_misclassified),
239              (CTX->totals.innocent_misclassified >
240               s->control_totals.innocent_misclassified) ? "+" : "-",
241              abs (CTX->totals.innocent_misclassified -
242                   s->control_totals.innocent_misclassified),
243              (CTX->totals.spam_corpusfed >
244               s->control_totals.spam_corpusfed) ? "+" : "-",
245              abs (CTX->totals.spam_corpusfed -
246                   s->control_totals.spam_corpusfed),
247              (CTX->totals.innocent_corpusfed >
248               s->control_totals.innocent_corpusfed) ? "+" : "-",
249              abs (CTX->totals.innocent_corpusfed -
250                   s->control_totals.innocent_corpusfed),
251              (CTX->totals.spam_classified >
252               s->control_totals.spam_classified) ? "+" : "-",
253              abs (CTX->totals.spam_classified -
254                  s->control_totals.spam_classified),
255              (CTX->totals.innocent_classified >
256               s->control_totals.innocent_classified) ? "+" : "-",
257              abs (CTX->totals.innocent_classified -
258                  s->control_totals.innocent_classified));
259
260    if ((sqlite3_exec(s->dbh, query, NULL, NULL, &err))!=SQLITE_OK)
261    {
262      _sqlite_drv_query_error (err, query);
263      return EFAILURE;
264    }
265  }
266
267  return 0;
268}
269
270int
271_ds_getall_spamrecords (DSPAM_CTX * CTX, ds_diction_t diction)
272{
273  struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
274  buffer *query;
275  ds_term_t ds_term;
276  ds_cursor_t ds_c;
277  char scratch[1024];
278  char queryhead[1024];
279  struct _ds_spam_stat stat;
280  unsigned long long token = 0;
281  char *err=NULL, **row=NULL;
282  int nrow, ncolumn, i;
283
284  if (diction->items < 1)
285    return 0;
286
287  if (s->dbh == NULL)
288  {
289    LOGDEBUG ("_ds_getall_spamrecords: invalid database handle (NULL)");
290    return EINVAL;
291  }
292
293  stat.spam_hits = 0;
294  stat.innocent_hits = 0;
295  stat.probability   = 0.00000;
296
297  query = buffer_create (NULL);
298  if (query == NULL)
299  {
300    LOG (LOG_CRIT, ERR_MEM_ALLOC);
301    return EUNKNOWN;
302  }
303
304  snprintf (queryhead, sizeof (queryhead),
305            "SELECT token,spam_hits,innocent_hits"
306            " FROM dspam_token_data WHERE token IN (");
307
308  ds_c = ds_diction_cursor(diction);
309  ds_term = ds_diction_next(ds_c);
310  while (ds_term) {
311    scratch[0] = 0;
312    buffer_copy(query, queryhead);
313    while (ds_term) {
314      snprintf (scratch, sizeof (scratch), "'%" LLU_FMT_SPEC "'", ds_term->key);
315      buffer_cat (query, scratch);
316      ds_term->s.innocent_hits = 0;
317      ds_term->s.spam_hits = 0;
318      ds_term->s.probability = 0.00000;
319      ds_term->s.status = 0;
320      if((query->used + 1024) > 1000000) {
321        LOGDEBUG("_ds_getall_spamrecords: Splitting query at %ld characters", query->used);
322        break;
323      }
324      ds_term = ds_diction_next(ds_c);
325      if (ds_term)
326        buffer_cat (query, ",");
327    }
328    buffer_cat (query, ")");
329
330#ifdef VERBOSE
331    LOGDEBUG ("SQLite query length: %ld\n", query->used);
332    _sqlite_drv_query_error (strdup("VERBOSE DEBUG (INFO ONLY - NOT AN ERROR)"), query->data);
333#endif
334
335    if ((sqlite3_get_table(s->dbh, query->data, &row, &nrow, &ncolumn, &err))
336        !=SQLITE_OK)
337    {
338      _sqlite_drv_query_error (err, query->data);
339      LOGDEBUG ("_ds_getall_spamrecords: unable to run query: %s", query->data);
340      buffer_destroy(query);
341      ds_diction_close(ds_c);
342      return EFAILURE;
343    }
344
345    if (nrow < 1) {
346      sqlite3_free_table(row);
347      buffer_destroy(query);
348      ds_diction_close(ds_c);
349      return 0;
350    }
351
352    if (row == NULL) {
353      buffer_destroy(query);
354      ds_diction_close(ds_c);
355      return 0;
356    }
357
358    for(i=1;i<=nrow;i++) {
359      token = strtoull (row[(i*ncolumn)], NULL, 0);
360      stat.spam_hits = strtoul (row[1+(i*ncolumn)], NULL, 0);
361      if (stat.spam_hits == ULONG_MAX && errno == ERANGE) {
362        LOGDEBUG("_ds_getall_spamrecords: failed converting %s to stat.spam_hits", row[1+(i*ncolumn)]);
363        sqlite3_free_table(row);
364        return EFAILURE;
365      }
366      stat.innocent_hits = strtoul (row[2+(i*ncolumn)], NULL, 0);
367      if (stat.innocent_hits == ULONG_MAX && errno == ERANGE) {
368        LOGDEBUG("_ds_getall_spamrecords: failed converting %s to stat.innocent_hits", row[2+(i*ncolumn)]);
369        sqlite3_free_table(row);
370        return EFAILURE;
371      }
372      stat.status = 0;
373      stat.status |= TST_DISK;
374      if (stat.spam_hits < 0)
375        stat.spam_hits = 0;
376      if (stat.innocent_hits < 0)
377        stat.innocent_hits = 0;
378      ds_diction_addstat(diction, token, &stat);
379    }
380    if (row != NULL)
381      sqlite3_free_table(row);
382    row = NULL;
383    ds_term = ds_diction_next(ds_c);
384  }
385  ds_diction_close(ds_c);
386  buffer_destroy (query);
387  if (row != NULL)
388    sqlite3_free_table(row);
389  row = NULL;
390
391  /* Control token */
392  stat.spam_hits = 10;
393  stat.innocent_hits = 10;
394  stat.status = 0;
395  ds_diction_touch(diction, CONTROL_TOKEN, "$$CONTROL$$", 0);
396  ds_diction_addstat(diction, CONTROL_TOKEN, &stat);
397  s->control_token = CONTROL_TOKEN;
398  s->control_ih = 10;
399  s->control_sh = 10;
400
401  return 0;
402}
403
404int
405_ds_setall_spamrecords (DSPAM_CTX * CTX, ds_diction_t diction)
406{
407  struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
408  struct _ds_spam_stat control, stat;
409  ds_term_t ds_term;
410  ds_cursor_t ds_c;
411  char queryhead[1024];
412  buffer *query;
413  char scratch[1024];
414  char *err=NULL;
415  int update_any = 0;
416
417  if (diction->items < 1)
418    return 0;
419
420  if (s->dbh == NULL)
421  {
422    LOGDEBUG ("_ds_setall_spamrecords: invalid database handle (NULL)");
423    return EINVAL;
424  }
425
426  if (CTX->operating_mode == DSM_CLASSIFY &&
427        (CTX->training_mode != DST_TOE ||
428          (diction->whitelist_token == 0 && (!(CTX->flags & DSF_NOISE)))))
429    return 0;
430
431  query = buffer_create (NULL);
432  if (query == NULL)
433  {
434    LOG (LOG_CRIT, ERR_MEM_ALLOC);
435    return EUNKNOWN;
436  }
437
438  ds_diction_getstat(diction, s->control_token, &control);
439
440  snprintf (queryhead, sizeof (queryhead),
441            "UPDATE dspam_token_data SET last_hit=date('now'),"
442            "spam_hits=max(0,spam_hits%s%d),"
443            "innocent_hits=max(0,innocent_hits%s%d)"
444            " WHERE token IN (",
445            (control.spam_hits > s->control_sh) ? "+" : "-",
446            abs (control.spam_hits - s->control_sh),
447            (control.innocent_hits > s->control_ih) ? "+" : "-",
448            abs (control.innocent_hits - s->control_ih));
449
450  buffer_copy (query, queryhead);
451
452
453  /*
454   *  Add each token in the diction to either an update or an insert queue
455   */
456
457  ds_c = ds_diction_cursor(diction);
458  ds_term = ds_diction_next(ds_c);
459  while(ds_term)
460  {
461    int use_comma = 0;
462    if (ds_term->key == s->control_token) {
463      ds_term = ds_diction_next(ds_c);
464      continue;
465    }
466
467    /* Don't write lexical tokens if we're in TOE mode classifying */
468
469    if (CTX->training_mode == DST_TOE            &&
470        CTX->operating_mode == DSM_CLASSIFY      &&
471        ds_term->key != diction->whitelist_token &&
472        (!ds_term->name || strncmp(ds_term->name, "bnr.", 4)))
473    {
474      ds_term = ds_diction_next(ds_c);
475      continue;
476    }
477
478    ds_diction_getstat(diction, ds_term->key, &stat);
479
480    /* Changed tokens are marked as "dirty" by libdspam */
481
482    if (!(stat.status & TST_DIRTY)) {
483      ds_term = ds_diction_next(ds_c);
484      continue;
485    } else {
486      stat.status &= ~TST_DIRTY;
487    }
488
489    /* This token wasn't originally loaded from disk, so try an insert */
490
491    if (!(stat.status & TST_DISK))
492    {
493      char ins[1024];
494      snprintf(ins, sizeof (ins),
495               "INSERT INTO dspam_token_data (token,spam_hits,"
496               "innocent_hits,last_hit) VALUES ('%" LLU_FMT_SPEC "',%d,%d,"
497               "date('now'))",
498               ds_term->key,
499               stat.spam_hits > 0 ? 1 : 0,
500               stat.innocent_hits > 0 ? 1 : 0);
501      if ((sqlite3_exec(s->dbh, ins, NULL, NULL, NULL)) != SQLITE_OK)
502        stat.status |= TST_DISK;
503    }
504
505    if (stat.status & TST_DISK) {
506      snprintf (scratch, sizeof (scratch), "'%" LLU_FMT_SPEC "'", ds_term->key);
507      buffer_cat (query, scratch);
508      update_any = 1;
509      use_comma = 1;
510    }
511
512    ds_term->s.status |= TST_DISK;
513
514    ds_term = ds_diction_next(ds_c);
515    if((query->used + 1024) > 1000000) {
516      LOGDEBUG("_ds_setall_spamrecords: Splitting update query at %ld characters", query->used);
517      buffer_cat (query, ")");
518      if (update_any) {
519        if ((sqlite3_exec(s->dbh, query->data, NULL, NULL, &err)) != SQLITE_OK) {
520          _sqlite_drv_query_error (err, query->data);
521          LOGDEBUG ("_ds_setall_spamrecords: unable to run query: %s", query->data);
522          ds_diction_close(ds_c);
523          buffer_destroy(query);
524          return EFAILURE;
525        }
526      }
527      buffer_copy (query, queryhead);
528    } else if (ds_term && use_comma)
529      buffer_cat (query, ",");
530  }
531  ds_diction_close(ds_c);
532
533  /* Just incase */
534
535  if (query->used && query->data[strlen (query->data) - 1] == ',')
536  {
537    query->used--;
538    query->data[strlen (query->data) - 1] = 0;
539  }
540
541  buffer_cat (query, ")");
542
543  LOGDEBUG("Control: [%ld %ld] [%lu %lu] Delta: [%lu %lu]",
544    s->control_sh, s->control_ih,
545    control.spam_hits, control.innocent_hits,
546    control.spam_hits - s->control_sh, control.innocent_hits - s->control_ih);
547
548  if (update_any)
549  {
550    if ((sqlite3_exec(s->dbh, query->data, NULL, NULL, &err))!=SQLITE_OK)
551    {
552      _sqlite_drv_query_error (err, query->data);
553      LOGDEBUG ("_ds_setall_spamrecords: unable to run query: %s", query->data);
554      buffer_destroy(query);
555      return EFAILURE;
556    }
557  }
558
559  buffer_destroy (query);
560  return 0;
561}
562
563int
564_ds_get_spamrecord (DSPAM_CTX * CTX, unsigned long long token,
565                    struct _ds_spam_stat *stat)
566{
567  struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
568  char query[1024];
569  char *err=NULL, **row;
570  int nrow, ncolumn;
571
572
573  if (s->dbh == NULL)
574  {
575    LOGDEBUG ("_ds_get_spamrecord: invalid database handle (NULL)");
576    return EINVAL;
577  }
578
579  snprintf (query, sizeof (query),
580            "SELECT spam_hits,innocent_hits FROM dspam_token_data"
581            " WHERE token='%" LLU_FMT_SPEC "'", token);
582
583  stat->probability = 0.00000;
584  stat->spam_hits = 0;
585  stat->innocent_hits = 0;
586  stat->status &= ~TST_DISK;
587
588  if ((sqlite3_get_table(s->dbh, query, &row, &nrow, &ncolumn, &err))!=SQLITE_OK)
589  {
590    _sqlite_drv_query_error (err, query);
591    LOGDEBUG ("_ds_get_spamrecord: unable to run query: %s", query);
592    return EFAILURE;
593  }
594
595  if (nrow < 1)
596    sqlite3_free_table(row);
597
598  if (nrow < 1 || row == NULL)
599    return 0;
600
601  stat->spam_hits = strtoul (row[0], NULL, 0);
602  if (stat->spam_hits == ULONG_MAX && errno == ERANGE) {
603    LOGDEBUG("_ds_get_spamrecord: failed converting %s to stat->spam_hits", row[0]);
604    sqlite3_free_table(row);
605    return EFAILURE;
606  }
607  stat->innocent_hits = strtoul (row[1], NULL, 0);
608  if (stat->innocent_hits == ULONG_MAX && errno == ERANGE) {
609    LOGDEBUG("_ds_get_spamrecord: failed converting %s to stat->innocent_hits", row[1]);
610    sqlite3_free_table(row);
611    return EFAILURE;
612  }
613  stat->status |= TST_DISK;
614  sqlite3_free_table(row);
615  return 0;
616}
617
618int
619_ds_set_spamrecord (DSPAM_CTX * CTX, unsigned long long token,
620                    struct _ds_spam_stat *stat)
621{
622  struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
623  char query[1024];
624  char *err=NULL;
625  int result = 0;
626
627  if (s->dbh == NULL)
628  {
629    LOGDEBUG ("_ds_set_spamrecord: invalid database handle (NULL)");
630    return EINVAL;
631  }
632
633  if (CTX->operating_mode == DSM_CLASSIFY)
634    return 0;
635
636  /* It's either not on disk or the caller isn't using stat.disk */
637  if (!(stat->status & TST_DISK))
638  {
639    snprintf (query, sizeof (query),
640              "INSERT INTO dspam_token_data (token,spam_hits,innocent_hits,last_hit)"
641              " VALUES ('%" LLU_FMT_SPEC "',%lu,%lu,date('now'))",
642              token,
643              stat->spam_hits > 0 ? stat->spam_hits : 0,
644              stat->innocent_hits > 0 ? stat->innocent_hits : 0);
645    result = sqlite3_exec(s->dbh, query, NULL, NULL, NULL);
646  }
647
648  if ((stat->status & TST_DISK) || result)
649  {
650    /* insert failed; try updating instead */
651    snprintf (query, sizeof (query), "UPDATE dspam_token_data"
652              " SET spam_hits=%lu,"
653              "innocent_hits=%lu"
654              " WHERE token='%" LLU_FMT_SPEC "'",
655              stat->spam_hits > 0 ? stat->spam_hits : 0,
656              stat->innocent_hits > 0 ? stat->innocent_hits : 0,
657              token);
658
659    if ((sqlite3_exec(s->dbh, query, NULL, NULL, &err))!=SQLITE_OK) {
660      _sqlite_drv_query_error (err, query);
661      LOGDEBUG ("_ds_set_spamrecord: unable to run query: %s", query);
662      return EFAILURE;
663    }
664  }
665
666  return 0;
667}
668
669int
670_ds_init_storage (DSPAM_CTX * CTX, void *dbh)
671{
672  struct _sqlite_drv_storage *s;
673  FILE *file;
674  char buff[1024];
675  char filename[MAX_FILENAME_LENGTH];
676  char *err=NULL;
677  struct stat st;
678  int noexist;
679
680  buff[0] = 0;
681
682  if (CTX == NULL)
683    return EINVAL;
684
685  if (CTX->flags & DSF_MERGED) {
686    LOG(LOG_ERR, ERR_DRV_NO_MERGED);
687    return EINVAL;
688  }
689
690  /* don't init if we're already initted */
691  if (CTX->storage != NULL)
692  {
693    LOGDEBUG ("_ds_init_storage: storage already initialized");
694    return EINVAL;
695  }
696
697  s = calloc (1, sizeof (struct _sqlite_drv_storage));
698  if (s == NULL)
699  {
700    LOG (LOG_CRIT, ERR_MEM_ALLOC);
701    return EUNKNOWN;
702  }
703
704  s->dbh = NULL;
705  s->control_token = 0;
706  s->iter_token = NULL;
707  s->iter_sig = NULL;
708  s->control_token = 0;
709  s->control_sh = 0;
710  s->control_ih = 0;
711  s->dbh_attached = (dbh) ? 1 : 0;
712
713  if (CTX->group == NULL || CTX->group[0] == 0)
714    _ds_userdir_path (filename, CTX->home, CTX->username, "sdb");
715  else
716    _ds_userdir_path (filename, CTX->home, CTX->group, "sdb");
717
718  _ds_prepare_path_for (filename);
719
720  noexist = stat(filename, &st);
721
722  if (dbh)
723    s->dbh = dbh;
724  else if ((sqlite3_open(filename, &s->dbh))!=SQLITE_OK)
725    s->dbh = NULL;
726
727  if (s->dbh == NULL)
728  {
729    free(s);
730    LOGDEBUG
731      ("_ds_init_storage: unable to initialize database: %s", filename);
732    return EFAILURE;
733  }
734
735  /* Commit timeout of 20 minutes */
736  sqlite3_busy_timeout(s->dbh, 1000 * 60 * 20);
737
738  /* Create database objects */
739
740  if (noexist) {
741
742    LOGDEBUG ("_ds_init_storage: Creating object structure in database: %s", filename);
743
744    buff[0] = 0;
745    snprintf (buff, sizeof (buff),
746                "CREATE TABLE dspam_token_data (token CHAR(20) PRIMARY KEY,"
747                "spam_hits INT,innocent_hits INT,last_hit DATE)");
748    if ((sqlite3_exec(s->dbh, buff, NULL, NULL, &err))!=SQLITE_OK) {
749      _sqlite_drv_query_error (err, buff);
750      free(s);
751      return EFAILURE;
752    }
753
754    buff[0] = 0;
755    snprintf (buff, sizeof (buff),
756                "CREATE INDEX id_token_data_02 ON dspam_token_data"
757                "(innocent_hits)");
758    if ((sqlite3_exec(s->dbh, buff, NULL, NULL, &err))!=SQLITE_OK) {
759      _sqlite_drv_query_error (err, buff);
760      free(s);
761      return EFAILURE;
762    }
763
764    buff[0] = 0;
765    snprintf (buff, sizeof (buff),
766                "CREATE TABLE dspam_signature_data ("
767                "signature CHAR(128) PRIMARY KEY,data BLOB,created_on DATE)");
768    if ((sqlite3_exec(s->dbh, buff, NULL, NULL, &err))!=SQLITE_OK) {
769      _sqlite_drv_query_error (err, buff);
770      free(s);
771      return EFAILURE;
772    }
773
774    buff[0] = 0;
775    snprintf (buff, sizeof (buff),
776                "CREATE TABLE dspam_stats (dspam_stat_id INT PRIMARY KEY,"
777                "spam_learned INT,innocent_learned INT,"
778                "spam_misclassified INT,innocent_misclassified INT,"
779                "spam_corpusfed INT,innocent_corpusfed INT,"
780                "spam_classified INT,innocent_classified INT)");
781    if ((sqlite3_exec(s->dbh, buff, NULL, NULL, &err))!=SQLITE_OK) {
782      _sqlite_drv_query_error (err, buff);
783      free(s);
784      return EFAILURE;
785    }
786
787    buff[0] = 0;
788  }
789
790  if (_ds_read_attribute(CTX->config->attributes, "SQLitePragma")) {
791    char pragma[1024];
792    attribute_t t = _ds_find_attribute(CTX->config->attributes, "SQLitePragma");
793    while(t != NULL) {
794      snprintf(pragma, sizeof(pragma), "PRAGMA %s", t->value);
795      if ((sqlite3_exec(s->dbh, pragma, NULL, NULL, &err))!=SQLITE_OK)
796      {
797        LOG(LOG_WARNING, "sqlite.pragma function error: %s: %s", err, pragma);
798        _sqlite_drv_query_error (err, pragma);
799      }
800      t = t->next;
801    }
802  } else {
803    snprintf(filename, MAX_FILENAME_LENGTH, "%s/sqlite.pragma", CTX->home);
804    file = fopen(filename, "r");
805    if (file != NULL) {
806      while((fgets(buff, sizeof(buff), file))!=NULL) {
807        chomp(buff);
808        if ((sqlite3_exec(s->dbh, buff, NULL, NULL, &err))!=SQLITE_OK)
809        {
810          LOG(LOG_WARNING, "sqlite.pragma function error: %s: %s", err, buff);
811          _sqlite_drv_query_error (err, buff);
812        }
813      }
814      fclose(file);
815    }
816  }
817
818  CTX->storage = s;
819
820  s->dir_handles = nt_create (NT_INDEX);
821  s->control_token = 0;
822  s->control_sh = 0;
823  s->control_ih = 0;
824
825  /* get spam totals on successful init */
826  if (CTX->username != NULL)
827  {
828      if (_sqlite_drv_get_spamtotals (CTX))
829      {
830        LOGDEBUG ("_ds_init_storage: unable to load totals. Using zero values.");
831      }
832  }
833  else
834  {
835    memset (&CTX->totals, 0, sizeof (struct _ds_spam_totals));
836    memset (&s->control_totals, 0, sizeof (struct _ds_spam_totals));
837  }
838
839  return 0;
840}
841
842int
843_ds_shutdown_storage (DSPAM_CTX * CTX)
844{
845  struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
846  struct nt_node *node_nt;
847  struct nt_c c_nt;
848
849  if (s->dbh == NULL)
850  {
851    LOGDEBUG ("_ds_shutdown_storage: invalid database handle (NULL)");
852    return EINVAL;
853  }
854
855  node_nt = c_nt_first (s->dir_handles, &c_nt);
856  while (node_nt != NULL)
857  {
858    DIR *dir;
859    dir = (DIR *) node_nt->ptr;
860    closedir (dir);
861    node_nt = c_nt_next (s->dir_handles, &c_nt);
862  }
863
864  nt_destroy (s->dir_handles);
865
866
867  /* Store spam totals on shutdown */
868  if (CTX->username != NULL && CTX->operating_mode != DSM_CLASSIFY)
869  {
870      _sqlite_drv_set_spamtotals (CTX);
871  }
872
873  if (!s->dbh_attached)
874    sqlite3_close(s->dbh);
875
876  s->dbh = NULL;
877
878  free(s);
879  CTX->storage = NULL;
880
881  return 0;
882}
883
884int
885_ds_create_signature_id (DSPAM_CTX * CTX, char *buf, size_t len)
886{
887  char session[64];
888  char digit[6];
889  int pid, j;
890
891  pid = getpid ();
892  snprintf (session, sizeof (session), "%8lx%d", (long) time (NULL), pid);
893
894  for (j = 0; j < 2; j++)
895  {
896    snprintf (digit, 6, "%d", rand ());
897    strlcat (session, digit, 64);
898  }
899
900  strlcpy (buf, session, len);
901  return 0;
902}
903
904int
905_ds_get_signature (DSPAM_CTX * CTX, struct _ds_spam_signature *SIG,
906                   const char *signature)
907{
908  struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
909  char query[128];
910  char *err=NULL;
911  const char *query_tail;
912  sqlite3_stmt *stmt;
913
914  if (s->dbh == NULL)
915  {
916    LOGDEBUG ("_ds_get_signature: invalid database handle (NULL)");
917    return EINVAL;
918  }
919
920  snprintf (query, sizeof (query),
921            "SELECT data FROM dspam_signature_data WHERE signature=\"%s\"",
922            signature);
923
924  if ((sqlite3_prepare(s->dbh, query, -1, &stmt, &query_tail))
925        !=SQLITE_OK)
926  {
927    _sqlite_drv_query_error (err, query);
928    return EFAILURE;
929  }
930
931  if ((sqlite3_step(stmt))!=SQLITE_ROW) {
932    sqlite3_finalize(stmt);
933    return EFAILURE;
934  }
935
936  SIG->length = sqlite3_column_bytes(stmt, 0);
937  SIG->data = malloc(SIG->length);
938  if (SIG->data == NULL) {
939    sqlite3_finalize(stmt);
940    LOG(LOG_CRIT, ERR_MEM_ALLOC);
941    return EUNKNOWN;
942  }
943
944  memcpy(SIG->data, sqlite3_column_blob(stmt, 0), SIG->length);
945
946  if ((sqlite3_finalize(stmt)!=SQLITE_OK))
947    LOGDEBUG("_ds_get_signature: sqlite3_finalize() failed: %s", strerror(errno));
948
949  return 0;
950}
951
952int
953_ds_set_signature (DSPAM_CTX * CTX, struct _ds_spam_signature *SIG,
954                   const char *signature)
955{
956  struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
957  char scratch[1024];
958  char *err=NULL;
959  const char *query_tail=NULL;
960  sqlite3_stmt *stmt;
961
962  if (s->dbh == NULL)
963  {
964    LOGDEBUG ("_ds_set_signature: invalid database handle (NULL)");
965    return EINVAL;
966  }
967
968  snprintf (scratch, sizeof (scratch),
969            "INSERT INTO dspam_signature_data (signature,created_on,data)"
970            " VALUES (\"%s\",date('now'),?)", signature);
971
972  if ((sqlite3_prepare(s->dbh, scratch, -1, &stmt, &query_tail))!=SQLITE_OK)
973  {
974    _sqlite_drv_query_error ("_ds_set_signature: sqlite3_prepare() failed", scratch);
975    return EFAILURE;
976  }
977
978  sqlite3_bind_blob(stmt, 1, SIG->data, SIG->length, SQLITE_STATIC);
979
980  if ((sqlite3_step(stmt))!=SQLITE_DONE) {
981    _sqlite_drv_query_error (err, scratch);
982    return EFAILURE;
983  }
984
985  sqlite3_finalize(stmt);
986
987  return 0;
988}
989
990int
991_ds_delete_signature (DSPAM_CTX * CTX, const char *signature)
992{
993  struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
994  char query[128];
995  char *err=NULL;
996
997  if (s->dbh == NULL)
998  {
999    LOGDEBUG ("_ds_delete_signature: invalid database handle (NULL)");
1000    return EINVAL;
1001  }
1002
1003  snprintf (query, sizeof (query),
1004            "DELETE FROM dspam_signature_data WHERE signature=\"%s\"",
1005             signature);
1006
1007  if ((sqlite3_exec(s->dbh, query, NULL, NULL, &err))!=SQLITE_OK)
1008  {
1009    _sqlite_drv_query_error (err, query);
1010    return EFAILURE;
1011  }
1012
1013  return 0;
1014}
1015
1016int
1017_ds_verify_signature (DSPAM_CTX * CTX, const char *signature)
1018{
1019  struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
1020  char query[128];
1021  char *err=NULL, **row;
1022  int nrow, ncolumn;
1023
1024  if (s->dbh == NULL)
1025  {
1026    LOGDEBUG ("_ds_verify_signature: invalid database handle (NULL)");
1027    return EINVAL;
1028  }
1029
1030  snprintf (query, sizeof (query),
1031        "SELECT signature FROM dspam_signature_data WHERE signature=\"%s\"",
1032        signature);
1033
1034  if ((sqlite3_get_table(s->dbh, query, &row, &nrow, &ncolumn, &err))!=SQLITE_OK)  {
1035    _sqlite_drv_query_error (err, query);
1036    return EFAILURE;
1037  }
1038
1039  sqlite3_free_table(row);
1040
1041  if (nrow<1) {
1042    return -1;
1043  }
1044
1045  return 0;
1046}
1047
1048char *
1049_ds_get_nextuser (DSPAM_CTX * CTX)
1050{
1051  static char user[MAX_FILENAME_LENGTH];
1052  static char path[MAX_FILENAME_LENGTH];
1053  struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
1054  struct nt_node *node_nt, *prev;
1055  struct nt_c c_nt;
1056  char *x = NULL, *y;
1057  DIR *dir = NULL;
1058
1059  struct dirent *entry;
1060
1061  if (s->dir_handles->items == 0)
1062  {
1063    char filename[MAX_FILENAME_LENGTH];
1064    snprintf(filename, MAX_FILENAME_LENGTH, "%s/data", CTX->home);
1065    dir = opendir (filename);
1066    if (dir == NULL)
1067    {
1068      LOG (LOG_WARNING,
1069           "_ds_get_nextuser: unable to open directory '%s' for reading: %s",
1070           CTX->home, strerror (errno));
1071      return NULL;
1072    }
1073
1074    nt_add (s->dir_handles, (void *) dir);
1075    strlcpy (path, filename, sizeof (path));
1076  }
1077  else
1078  {
1079    node_nt = c_nt_first (s->dir_handles, &c_nt);
1080    while (node_nt != NULL)
1081    {
1082      if (node_nt->next == NULL)
1083        dir = (DIR *) node_nt->ptr;
1084      node_nt = c_nt_next (s->dir_handles, &c_nt);
1085    }
1086  }
1087
1088  if (dir != NULL) {
1089    while ((entry = readdir (dir)) != NULL)
1090    {
1091      struct stat st;
1092      char filename[MAX_FILENAME_LENGTH];
1093      snprintf (filename, sizeof (filename), "%s/%s", path, entry->d_name);
1094
1095      if (!strcmp (entry->d_name, ".") || !strcmp (entry->d_name, ".."))
1096        continue;
1097
1098      if (stat (filename, &st)) {
1099        continue;
1100      }
1101
1102      /* push a new directory */
1103      if (st.st_mode & S_IFDIR)
1104      {
1105        DIR *ndir;
1106
1107        ndir = opendir (filename);
1108        if (ndir == NULL)
1109          continue;
1110        strlcat (path, "/", sizeof (path));
1111        strlcat (path, entry->d_name, sizeof (path));
1112        nt_add (s->dir_handles, (void *) ndir);
1113        return _ds_get_nextuser (CTX);
1114      }
1115      else if (!strncmp
1116               (entry->d_name + strlen (entry->d_name) - 4, ".sdb", 4))
1117      {
1118        strlcpy (user, entry->d_name, sizeof (user));
1119        user[strlen (user) - 4] = 0;
1120        return user;
1121      }
1122    }
1123  }
1124
1125  /* pop current directory */
1126  y = strchr (path, '/');
1127  while (y != NULL)
1128  {
1129    x = y;
1130    y = strchr (x + 1, '/');
1131  }
1132  if (x)
1133    x[0] = 0;
1134
1135  /* pop directory handle from list */
1136  node_nt = c_nt_first (s->dir_handles, &c_nt);
1137  prev = NULL;
1138  while (node_nt != NULL)
1139  {
1140    if (node_nt->next == NULL)
1141    {
1142      dir = (DIR *) node_nt->ptr;
1143      closedir (dir);
1144      if (prev != NULL) {
1145        prev->next = NULL;
1146        s->dir_handles->insert = NULL;
1147      }
1148      else
1149        s->dir_handles->first = NULL;
1150      free (node_nt);
1151      s->dir_handles->items--;
1152      break;
1153    }
1154    prev = node_nt;
1155    node_nt = c_nt_next (s->dir_handles, &c_nt);
1156  }
1157  if (s->dir_handles->items > 0)
1158    return _ds_get_nextuser (CTX);
1159
1160  /* done */
1161
1162  user[0] = 0;
1163  return NULL;
1164}
1165
1166struct _ds_storage_record *
1167_ds_get_nexttoken (DSPAM_CTX * CTX)
1168{
1169  struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
1170  struct _ds_storage_record *st;
1171  char query[128];
1172  char *err=NULL;
1173  const char *query_tail=NULL;
1174  int x;
1175
1176  if (s->dbh == NULL)
1177  {
1178    LOGDEBUG ("_ds_get_nexttoken: invalid database handle (NULL)");
1179    return NULL;
1180  }
1181
1182  st = calloc (1, sizeof (struct _ds_storage_record));
1183  if (st == NULL)
1184  {
1185    LOG (LOG_CRIT, ERR_MEM_ALLOC);
1186    return NULL;
1187  }
1188
1189  if (s->iter_token == NULL)
1190  {
1191    snprintf (query, sizeof (query),
1192              "SELECT token,spam_hits,innocent_hits,strftime('%%s',"
1193              "last_hit) FROM dspam_token_data");
1194
1195    if ((sqlite3_prepare(s->dbh, query, -1, &s->iter_token, &query_tail))
1196        !=SQLITE_OK)
1197    {
1198      _sqlite_drv_query_error (err, query);
1199      free(st);
1200      return NULL;
1201    }
1202  }
1203
1204  if ((x = sqlite3_step(s->iter_token)) !=SQLITE_ROW) {
1205    if (x != SQLITE_DONE) {
1206      _sqlite_drv_query_error (err, query);
1207      s->iter_token = NULL;
1208      free(st);
1209      return NULL;
1210    }
1211    sqlite3_finalize((struct sqlite3_stmt *) s->iter_token);
1212    s->iter_token = NULL;
1213    free(st);
1214    return NULL;
1215  }
1216
1217  st->token = strtoull ((const char *) sqlite3_column_text(s->iter_token, 0), NULL, 0);
1218  st->spam_hits = strtoul ((const char *) sqlite3_column_text(s->iter_token, 1), NULL, 0);
1219  if (st->spam_hits == ULONG_MAX && errno == ERANGE) {
1220    LOGDEBUG("_ds_get_nexttoken: failed converting %s to st->spam_hits", (const char *) sqlite3_column_text(s->iter_token, 1));
1221    s->iter_token = NULL;
1222    free(st);
1223    return NULL;
1224  }
1225  st->innocent_hits = strtoul ((const char *) sqlite3_column_text(s->iter_token, 2), NULL, 0);
1226  if (st->innocent_hits == ULONG_MAX && errno == ERANGE) {
1227    LOGDEBUG("_ds_get_nexttoken: failed converting %s to st->innocent_hits", (const char *) sqlite3_column_text(s->iter_token, 2));
1228    s->iter_token = NULL;
1229    free(st);
1230    return NULL;
1231  }
1232  st->last_hit = (time_t) strtol ((const char *) sqlite3_column_text(s->iter_token, 3), NULL, 0);
1233
1234  return st;
1235}
1236
1237struct _ds_storage_signature *
1238_ds_get_nextsignature (DSPAM_CTX * CTX)
1239{
1240  struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
1241  struct _ds_storage_signature *st;
1242  unsigned long length;
1243  char query[128];
1244  char *mem;
1245  char *err=NULL;
1246  const char *query_tail=NULL;
1247  int x;
1248
1249  if (s->dbh == NULL)
1250  {
1251    LOGDEBUG ("_ds_get_nextsignature: invalid database handle (NULL)");
1252    return NULL;
1253  }
1254
1255  st = calloc (1, sizeof (struct _ds_storage_signature));
1256  if (st == NULL)
1257  {
1258    LOG (LOG_CRIT, ERR_MEM_ALLOC);
1259    return NULL;
1260  }
1261
1262  if (s->iter_sig == NULL)
1263  {
1264    snprintf (query, sizeof (query),
1265              "SELECT data,signature,strftime('%%s',created_on)"
1266              " FROM dspam_signature_data");
1267
1268   if ((sqlite3_prepare(s->dbh, query, -1, &s->iter_sig, &query_tail))
1269        !=SQLITE_OK)
1270    {
1271      _sqlite_drv_query_error (err, query);
1272      free(st);
1273      return NULL;
1274    }
1275  }
1276
1277  if ((x = sqlite3_step(s->iter_sig)) !=SQLITE_ROW) {
1278    if (x != SQLITE_DONE) {
1279      _sqlite_drv_query_error (err, query);
1280      s->iter_sig = NULL;
1281      free(st);
1282      return NULL;
1283    }
1284    sqlite3_finalize((struct sqlite3_stmt *) s->iter_sig);
1285    s->iter_sig = NULL;
1286    free(st);
1287    return NULL;
1288  }
1289
1290  length = sqlite3_column_bytes(s->iter_sig, 0);
1291  mem = malloc (length);
1292  if (mem == NULL)
1293  {
1294    LOG (LOG_CRIT, ERR_MEM_ALLOC);
1295    sqlite3_finalize(s->iter_sig);
1296    s->iter_sig = NULL;
1297    free(st);
1298    return NULL;
1299  }
1300
1301  memcpy(mem, sqlite3_column_blob(s->iter_sig, 0), length);
1302
1303  st->data = mem;
1304  strlcpy(st->signature, (const char *) sqlite3_column_text(s->iter_sig, 1), sizeof(st->signature));
1305  st->length = length;
1306  st->created_on = (time_t) strtol( (const char *) sqlite3_column_text(s->iter_sig, 2), NULL, 0);
1307
1308  return st;
1309}
1310
1311void
1312_sqlite_drv_query_error (const char *error, const char *query)
1313{
1314  FILE *file;
1315  time_t tm = time (NULL);
1316  char ct[128];
1317  char fn[MAX_FILENAME_LENGTH];
1318
1319  LOG (LOG_WARNING, "query error: %s: see sql.errors for more details",
1320       error);
1321
1322  snprintf (fn, sizeof (fn), "%s/sql.errors", LOGDIR);
1323
1324  snprintf (ct, sizeof (ct), "%s", ctime (&tm));
1325  chomp (ct);
1326
1327  file = fopen (fn, "a");
1328
1329  if (file == NULL)
1330  {
1331    LOG(LOG_ERR, ERR_IO_FILE_WRITE, fn, strerror (errno));
1332  }
1333  else
1334  {
1335    fprintf (file, "[%s] %d: %s: %s\n", ct, (int) getpid (), error, query);
1336    fclose (file);
1337  }
1338
1339  free((char *)error);
1340  return;
1341}
1342
1343int
1344_ds_del_spamrecord (DSPAM_CTX * CTX, unsigned long long token)
1345{
1346  struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
1347  char query[128];
1348  char *err=NULL;
1349
1350  if (s->dbh == NULL)
1351  {
1352    LOGDEBUG ("_ds_del_spamrecord: invalid database handle (NULL)");
1353    return EINVAL;
1354  }
1355
1356  snprintf (query, sizeof (query),
1357            "DELETE FROM dspam_token_data WHERE token='%" LLU_FMT_SPEC "'",
1358            token);
1359
1360  if ((sqlite3_exec(s->dbh, query, NULL, NULL, &err))!=SQLITE_OK)
1361  {
1362    _sqlite_drv_query_error (err, query);
1363    return EFAILURE;
1364  }
1365
1366  return 0;
1367}
1368
1369int _ds_delall_spamrecords (DSPAM_CTX * CTX, ds_diction_t diction)
1370{
1371  struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
1372  ds_term_t ds_term;
1373  ds_cursor_t ds_c;
1374  buffer *query;
1375  char *err=NULL;
1376  char scratch[1024];
1377  char queryhead[1024];
1378  int writes = 0;
1379
1380  if (diction->items < 1)
1381    return 0;
1382
1383  if (s->dbh == NULL)
1384  {
1385    LOGDEBUG ("_ds_delall_spamrecords: invalid database handle (NULL)");
1386    return EINVAL;
1387  }
1388
1389  query = buffer_create (NULL);
1390  if (query == NULL)
1391  {
1392    LOG (LOG_CRIT, ERR_MEM_ALLOC);
1393    return EUNKNOWN;
1394  }
1395
1396  snprintf (queryhead, sizeof(queryhead),
1397            "DELETE FROM dspam_token_data"
1398            " WHERE token IN (");
1399
1400  buffer_cat (query, queryhead);
1401
1402  ds_c = ds_diction_cursor(diction);
1403  ds_term = ds_diction_next(ds_c);
1404  while (ds_term)
1405  {
1406    snprintf (scratch, sizeof (scratch), "'%" LLU_FMT_SPEC "'", ds_term->key);
1407    buffer_cat (query, scratch);
1408    ds_term = ds_diction_next(ds_c);
1409
1410    if (writes > 2500 || ds_term == NULL) {
1411      buffer_cat (query, ")");
1412
1413      if ((sqlite3_exec(s->dbh, query->data, NULL, NULL, &err))!=SQLITE_OK)
1414      {
1415        _sqlite_drv_query_error (err, query->data);
1416        buffer_destroy(query);
1417        return EFAILURE;
1418      }
1419
1420      buffer_copy(query, queryhead);
1421      writes = 0;
1422
1423    } else {
1424      writes++;
1425      if (ds_term)
1426        buffer_cat (query, ",");
1427    }
1428  }
1429  ds_diction_close(ds_c);
1430
1431  if (writes) {
1432    buffer_cat (query, ")");
1433
1434    if ((sqlite3_exec(s->dbh, query->data, NULL, NULL, &err))!=SQLITE_OK)
1435    {
1436      _sqlite_drv_query_error (err, query->data);
1437      buffer_destroy(query);
1438      return EFAILURE;
1439    }
1440  }
1441
1442  buffer_destroy (query);
1443  return 0;
1444}
1445
1446void *_ds_connect (DSPAM_CTX *CTX)
1447{
1448  return NULL;
1449}
1450
1451
1452/* Preference Stubs for Flat-File */
1453
1454agent_pref_t _ds_pref_load(config_t config, const char *user,
1455  const char *home, void *dbh)
1456{
1457  return _ds_ff_pref_load(config, user, home, dbh);
1458}
1459
1460int _ds_pref_set(config_t config, const char *user, const char *home,
1461  const char *attrib, const char *value, void *dbh)
1462{
1463  return _ds_ff_pref_set(config, user, home, attrib, value, dbh);
1464}
1465
1466int _ds_pref_del(config_t config, const char *user, const char *home,
1467  const char *attrib, void *dbh)
1468{
1469  return _ds_ff_pref_del(config, user, home, attrib, dbh);
1470}
1471
Note: See TracBrowser for help on using the repository browser.