source: npl/mailserver/dspam/dspam-3.10.2/src/sqlite_drv.c @ d36701a

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