1
/* $Id: sqlite3_drv.c,v 1.12 2006/01/20 17:28:33 jonz Exp $ */
5
COPYRIGHT (C) 2002-2006 DEEP LOGIC INC.
7
This program is free software; you can redistribute it and/or
8
modify it under the terms of the GNU General Public License
9
as published by the Free Software Foundation; version 2
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 General Public License for more details.
17
You should have received a copy of the GNU General Public License
18
along with this program; if not, write to the Free Software
19
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24
#include <auto-config.h>
28
#include <sys/types.h>
43
#ifdef TIME_WITH_SYS_TIME
44
# include <sys/time.h>
47
# ifdef HAVE_SYS_TIME_H
48
# include <sys/time.h>
54
#include "storage_driver.h"
55
#include "sqlite3_drv.h"
61
#include "config_shared.h"
65
# include "dir_win32.h"
69
dspam_init_driver (DRIVER_CTX *DTX)
75
dspam_shutdown_driver (DRIVER_CTX *DTX)
81
_sqlite_drv_get_spamtotals (DSPAM_CTX * CTX)
83
struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
85
char *err=NULL, **row;
91
LOGDEBUG ("_sqlite_drv_get_spamtotals: invalid database handle (NULL)");
95
memset(&s->control_totals, 0, sizeof(struct _ds_spam_totals));
96
memset(&CTX->totals, 0, sizeof(struct _ds_spam_totals));
98
snprintf (query, sizeof (query),
99
"select spam_learned, innocent_learned, "
100
"spam_misclassified, innocent_misclassified, "
101
"spam_corpusfed, innocent_corpusfed, "
102
"spam_classified, innocent_classified "
103
" from dspam_stats");
105
if ((sqlite3_get_table(s->dbh, query, &row, &nrow, &ncolumn, &err))!=SQLITE_OK)
107
_sqlite_drv_query_error (err, query);
111
if (nrow>0 && row != NULL) {
112
CTX->totals.spam_learned = strtol (row[ncolumn], NULL, 0);
113
CTX->totals.innocent_learned = strtol (row[ncolumn+1], NULL, 0);
114
CTX->totals.spam_misclassified = strtol (row[ncolumn+2], NULL, 0);
115
CTX->totals.innocent_misclassified = strtol (row[ncolumn+3], NULL, 0);
116
CTX->totals.spam_corpusfed = strtol (row[ncolumn+4], NULL, 0);
117
CTX->totals.innocent_corpusfed = strtol (row[ncolumn+5], NULL, 0);
118
CTX->totals.spam_classified = strtol (row[ncolumn+6], NULL, 0);
119
CTX->totals.innocent_classified = strtol (row[ncolumn+7], NULL, 0);
125
sqlite3_free_table(row);
127
memcpy(&s->control_totals, &CTX->totals, sizeof(struct _ds_spam_totals));
133
_sqlite_drv_set_spamtotals (DSPAM_CTX * CTX)
135
struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
142
LOGDEBUG ("_sqlite_drv_set_spamtotals: invalid database handle (NULL)");
146
if (CTX->operating_mode == DSM_CLASSIFY)
148
_sqlite_drv_get_spamtotals (CTX); /* undo changes to in memory totals */
152
/* dspam_stat_id insures only one stats record */
154
if (s->control_totals.innocent_learned == 0)
156
snprintf (query, sizeof (query),
157
"insert into dspam_stats(dspam_stat_id, spam_learned, "
158
"innocent_learned, spam_misclassified, innocent_misclassified, "
159
"spam_corpusfed, innocent_corpusfed, "
160
"spam_classified, innocent_classified) "
161
"values(%d, %ld, %ld, %ld, %ld, %ld, %ld, %ld, %ld)",
163
CTX->totals.spam_learned,
164
CTX->totals.innocent_learned,
165
CTX->totals.spam_misclassified,
166
CTX->totals.innocent_misclassified,
167
CTX->totals.spam_corpusfed,
168
CTX->totals.innocent_corpusfed,
169
CTX->totals.spam_classified,
170
CTX->totals.innocent_classified);
171
result = sqlite3_exec(s->dbh, query, NULL, NULL, &err);
174
if (s->control_totals.innocent_learned != 0 || result != SQLITE_OK)
176
snprintf (query, sizeof (query),
177
"update dspam_stats set spam_learned = spam_learned %s %d, "
178
"innocent_learned = innocent_learned %s %d, "
179
"spam_misclassified = spam_misclassified %s %d, "
180
"innocent_misclassified = innocent_misclassified %s %d, "
181
"spam_corpusfed = spam_corpusfed %s %d, "
182
"innocent_corpusfed = innocent_corpusfed %s %d, "
183
"spam_classified = spam_classified %s %d, "
184
"innocent_classified = innocent_classified %s %d ",
185
(CTX->totals.spam_learned >
186
s->control_totals.spam_learned) ? "+" : "-",
187
abs (CTX->totals.spam_learned -
188
s->control_totals.spam_learned),
189
(CTX->totals.innocent_learned >
190
s->control_totals.innocent_learned) ? "+" : "-",
191
abs (CTX->totals.innocent_learned -
192
s->control_totals.innocent_learned),
193
(CTX->totals.spam_misclassified >
194
s->control_totals.spam_misclassified) ? "+" : "-",
195
abs (CTX->totals.spam_misclassified -
196
s->control_totals.spam_misclassified),
197
(CTX->totals.innocent_misclassified >
198
s->control_totals.innocent_misclassified) ? "+" : "-",
199
abs (CTX->totals.innocent_misclassified -
200
s->control_totals.innocent_misclassified),
201
(CTX->totals.spam_corpusfed >
202
s->control_totals.spam_corpusfed) ? "+" : "-",
203
abs (CTX->totals.spam_corpusfed -
204
s->control_totals.spam_corpusfed),
205
(CTX->totals.innocent_corpusfed >
206
s->control_totals.innocent_corpusfed) ? "+" : "-",
207
abs (CTX->totals.innocent_corpusfed -
208
s->control_totals.innocent_corpusfed),
209
(CTX->totals.spam_classified >
210
s->control_totals.spam_classified) ? "+" : "-",
211
abs (CTX->totals.spam_classified -
212
s->control_totals.spam_classified),
213
(CTX->totals.innocent_classified >
214
s->control_totals.innocent_classified) ? "+" : "-",
215
abs (CTX->totals.innocent_classified -
216
s->control_totals.innocent_classified));
218
if ((sqlite3_exec(s->dbh, query, NULL, NULL, &err))!=SQLITE_OK)
220
_sqlite_drv_query_error (err, query);
229
_ds_getall_spamrecords (DSPAM_CTX * CTX, ds_diction_t diction)
231
struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
236
struct _ds_spam_stat stat;
237
unsigned long long token = 0;
238
char *err=NULL, **row;
239
int nrow, ncolumn, get_one = 0, i;
243
LOGDEBUG ("_ds_getall_spamrecords: invalid database handle (NULL)");
248
stat.innocent_hits = 0;
250
query = buffer_create (NULL);
253
LOG (LOG_CRIT, ERR_MEM_ALLOC);
257
snprintf (scratch, sizeof (scratch),
258
"select token, spam_hits, innocent_hits "
259
"from dspam_token_data where token in(");
261
buffer_cat (query, scratch);
262
ds_c = ds_diction_cursor(diction);
263
ds_term = ds_diction_next(ds_c);
266
snprintf (scratch, sizeof (scratch), "'%" LLU_FMT_SPEC "'", ds_term->key);
267
buffer_cat (query, scratch);
268
ds_term->s.innocent_hits = 0;
269
ds_term->s.spam_hits = 0;
270
ds_term->s.probability = 0;
271
ds_term->s.status &= ~TST_DISK;
272
ds_term = ds_diction_next(ds_c);
274
buffer_cat (query, ",");
277
ds_diction_close(ds_c);
278
buffer_cat (query, ")");
281
LOGDEBUG ("sqlite query length: %ld\n", query->used);
282
_sqlite_drv_query_error (strdup("VERBOSE DEBUG (INFO ONLY - NOT AN ERROR)"), query->data);
288
if ((sqlite3_get_table(s->dbh, query->data, &row, &nrow, &ncolumn, &err))
291
_sqlite_drv_query_error (err, query->data);
292
buffer_destroy(query);
297
sqlite3_free_table(row);
298
buffer_destroy(query);
305
stat.probability = 0;
306
stat.status |= TST_DISK;
307
for(i=1;i<=nrow;i++) {
308
token = strtoull (row[(i*ncolumn)], NULL, 0);
309
stat.spam_hits = strtol (row[1+(i*ncolumn)], NULL, 0);
310
stat.innocent_hits = strtol (row[2+(i*ncolumn)], NULL, 0);
312
if (stat.spam_hits < 0)
314
if (stat.innocent_hits < 0)
315
stat.innocent_hits = 0;
317
ds_diction_addstat(diction, token, &stat);
320
sqlite3_free_table(row);
322
ds_c = ds_diction_cursor(diction);
323
ds_term = ds_diction_next(ds_c);
324
while(ds_term && !s->control_token) {
325
if (ds_term->s.spam_hits && ds_term->s.innocent_hits) {
326
s->control_token = ds_term->key;
327
s->control_sh = ds_term->s.spam_hits;
328
s->control_ih = ds_term->s.innocent_hits;
330
ds_term = ds_diction_next(ds_c);
332
ds_diction_close(ds_c);
334
if (!s->control_token)
336
ds_c = ds_diction_cursor(diction);
337
ds_term = ds_diction_next(ds_c);
338
s->control_token = ds_term->key;
339
s->control_sh = ds_term->s.spam_hits;
340
s->control_ih = ds_term->s.innocent_hits;
341
ds_diction_close(ds_c);
344
buffer_destroy (query);
349
_ds_setall_spamrecords (DSPAM_CTX * CTX, ds_diction_t diction)
351
struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
352
struct _ds_spam_stat stat, stat2;
362
LOGDEBUG ("_ds_setall_spamrecords: invalid database handle (NULL)");
366
if (CTX->operating_mode == DSM_CLASSIFY &&
367
(CTX->training_mode != DST_TOE ||
368
(diction->whitelist_token == 0 && (!(CTX->flags & DSF_NOISE)))))
371
query = buffer_create (NULL);
374
LOG (LOG_CRIT, ERR_MEM_ALLOC);
378
if (s->control_token == 0)
380
ds_c = ds_diction_cursor(diction);
381
ds_term = ds_diction_next(ds_c);
385
stat.innocent_hits = 0;
389
stat.spam_hits = ds_term->s.spam_hits;
390
stat.innocent_hits = ds_term->s.innocent_hits;
392
ds_diction_close(ds_c);
396
ds_diction_getstat(diction, s->control_token, &stat);
399
snprintf (scratch, sizeof (scratch),
400
"update dspam_token_data set last_hit = date('now'), "
401
"spam_hits = max(0, spam_hits %s %d), "
402
"innocent_hits = max(0, innocent_hits %s %d) "
404
(stat.spam_hits > s->control_sh) ? "+" : "-",
405
abs (stat.spam_hits - s->control_sh),
406
(stat.innocent_hits > s->control_ih) ? "+" : "-",
407
abs (stat.innocent_hits - s->control_ih));
409
buffer_cat (query, scratch);
411
ds_c = ds_diction_cursor(diction);
412
ds_term = ds_diction_next(ds_c);
416
if (CTX->training_mode == DST_TOE &&
417
CTX->classification == DSR_NONE &&
418
CTX->operating_mode == DSM_CLASSIFY &&
419
diction->whitelist_token != ds_term->key &&
420
(!ds_term->name || strncmp(ds_term->name, "bnr.", 4)))
422
ds_term = ds_diction_next(ds_c);
426
if (!(ds_term->s.status & TST_DIRTY)) {
427
ds_term = ds_diction_next(ds_c);
431
ds_diction_getstat(diction, ds_term->key, &stat2);
433
if (!(stat2.status & TST_DISK))
437
snprintf(insert, sizeof (insert),
438
"insert into dspam_token_data(token, spam_hits, "
439
"innocent_hits, last_hit) values('%" LLU_FMT_SPEC "', %ld, %ld, "
442
stat2.spam_hits > 0 ? (long) 1 : (long) 0,
443
stat2.innocent_hits > 0 ? (long) 1 : (long) 0);
445
if ((sqlite3_exec(s->dbh, insert, NULL, NULL, &err)) != SQLITE_OK)
447
stat2.status |= TST_DISK;
452
if ((stat2.status & TST_DISK))
454
snprintf (scratch, sizeof (scratch), "'%" LLU_FMT_SPEC "'", ds_term->key);
455
buffer_cat (query, scratch);
458
ds_term->s.status |= TST_DISK;
460
ds_term = ds_diction_next(ds_c);
461
if (ds_term && wrote_this)
462
buffer_cat (query, ",");
464
ds_diction_close(ds_c);
466
if (query->used && query->data[strlen (query->data) - 1] == ',')
469
query->data[strlen (query->data) - 1] = 0;
473
buffer_cat (query, ")");
475
LOGDEBUG("Control: [%ld %ld] [%ld %ld]", s->control_sh, s->control_ih, stat.spam_hits, stat.innocent_hits);
479
if ((sqlite3_exec(s->dbh, query->data, NULL, NULL, &err))!=SQLITE_OK)
481
_sqlite_drv_query_error (err, query->data);
482
buffer_destroy(query);
487
buffer_destroy (query);
492
_ds_get_spamrecord (DSPAM_CTX * CTX, unsigned long long token,
493
struct _ds_spam_stat *stat)
495
struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
497
char *err=NULL, **row;
503
LOGDEBUG ("_ds_get_spamrecord: invalid database handle (NULL)");
507
snprintf (query, sizeof (query),
508
"select spam_hits, innocent_hits from dspam_token_data "
509
"where token = '%" LLU_FMT_SPEC "' ", token);
511
stat->probability = 0.0;
513
stat->innocent_hits = 0;
514
stat->status &= ~TST_DISK;
516
if ((sqlite3_get_table(s->dbh, query, &row, &nrow, &ncolumn, &err))!=SQLITE_OK)
518
_sqlite_drv_query_error (err, query);
523
sqlite3_free_table(row);
525
if (nrow < 1 || row == NULL)
528
stat->spam_hits = strtol (row[0], NULL, 0);
529
stat->innocent_hits = strtol (row[1], NULL, 0);
530
stat->status |= TST_DISK;
531
sqlite3_free_table(row);
536
_ds_set_spamrecord (DSPAM_CTX * CTX, unsigned long long token,
537
struct _ds_spam_stat *stat)
539
struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
546
LOGDEBUG ("_ds_set_spamrecord: invalid database handle (NULL)");
550
if (CTX->operating_mode == DSM_CLASSIFY)
553
/* It's either not on disk or the caller isn't using stat.disk */
554
if (!(stat->status & TST_DISK))
556
snprintf (query, sizeof (query),
557
"insert into dspam_token_data(token, spam_hits, "
558
"innocent_hits, last_hit)"
559
" values('%" LLU_FMT_SPEC "', %ld, %ld, date('now'))",
561
stat->spam_hits > 0 ? stat->spam_hits : 0,
562
stat->innocent_hits > 0 ? stat->innocent_hits : 0);
563
result = sqlite3_exec(s->dbh, query, NULL, NULL, &err);
566
if ((stat->status & TST_DISK) || result)
568
/* insert failed; try updating instead */
569
snprintf (query, sizeof (query), "update dspam_token_data "
570
"set spam_hits = %ld, "
571
"innocent_hits = %ld "
572
"where token = %" LLD_FMT_SPEC,
573
stat->spam_hits > 0 ? stat->spam_hits : 0,
574
stat->innocent_hits > 0 ? stat->innocent_hits : 0,
577
if ((sqlite3_exec(s->dbh, query, NULL, NULL, &err))!=SQLITE_OK)
579
_sqlite_drv_query_error (err, query);
588
_ds_init_storage (DSPAM_CTX * CTX, void *dbh)
590
struct _sqlite_drv_storage *s;
593
char filename[MAX_FILENAME_LENGTH];
603
if (CTX->flags & DSF_MERGED) {
604
LOG(LOG_ERR, ERR_DRV_NO_MERGED);
608
/* don't init if we're already initted */
609
if (CTX->storage != NULL)
611
LOGDEBUG ("_ds_init_storage: storage already initialized");
615
s = malloc (sizeof (struct _sqlite_drv_storage));
618
LOG (LOG_CRIT, ERR_MEM_ALLOC);
623
s->control_token = 0;
624
s->iter_token = NULL;
626
s->control_token = 0;
629
s->dbh_attached = (dbh) ? 1 : 0;
631
if (CTX->group == NULL || CTX->group[0] == 0)
632
_ds_userdir_path (filename, CTX->home, CTX->username, "sdb");
634
_ds_userdir_path (filename, CTX->home, CTX->group, "sdb");
636
_ds_prepare_path_for (filename);
638
noexist = stat(filename, &st);
643
if ((sqlite3_open(filename, &s->dbh))!=SQLITE_OK)
649
("_ds_init_storage: sqlite3_open: unable to initialize database: %s", err); return EUNKNOWN;
652
/* Commit timeout of 20 minutes */
653
sqlite3_busy_timeout(s->dbh, 1000 * 60 * 20);
655
/* Create database objects */
660
"create table dspam_token_data (token char(20) primary key, "
661
"spam_hits int, innocent_hits int, last_hit date)",
667
"create index id_token_data_02 on dspam_token_data"
674
"create table dspam_signature_data ("
675
"signature char(128) primary key, data blob, created_on date)",
681
"create table dspam_stats (dspam_stat_id int primary key, "
682
"spam_learned int, innocent_learned int, "
683
"spam_misclassified int, innocent_misclassified int, "
684
"spam_corpusfed int, innocent_corpusfed int, "
685
"spam_classified int, innocent_classified int)",
691
if (_ds_read_attribute(CTX->config->attributes, "SQLitePragma")) {
693
attribute_t t = _ds_find_attribute(CTX->config->attributes, "SQLitePragma");
695
snprintf(pragma, sizeof(pragma), "PRAGMA %s", t->value);
696
if ((sqlite3_exec(s->dbh, pragma, NULL, NULL, &err))!=SQLITE_OK)
698
LOG(LOG_WARNING, "sqlite.pragma function error: %s: %s", err, pragma);
699
_sqlite_drv_query_error (err, pragma);
704
snprintf(filename, MAX_FILENAME_LENGTH, "%s/sqlite.pragma", CTX->home);
705
file = fopen(filename, "r");
707
while((fgets(buff, sizeof(buff), file))!=NULL) {
709
if ((sqlite3_exec(s->dbh, buff, NULL, NULL, &err))!=SQLITE_OK)
711
LOG(LOG_WARNING, "sqlite.pragma function error: %s: %s", err, buff);
712
_sqlite_drv_query_error (err, buff);
720
s->dir_handles = nt_create (NT_INDEX);
722
s->control_token = 0;
726
/* get spam totals on successful init */
727
if (CTX->username != NULL)
729
if (_sqlite_drv_get_spamtotals (CTX))
731
LOGDEBUG ("unable to load totals. using zero values.");
736
memset (&CTX->totals, 0, sizeof (struct _ds_spam_totals));
737
memset (&s->control_totals, 0, sizeof (struct _ds_spam_totals));
744
_ds_shutdown_storage (DSPAM_CTX * CTX)
746
struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
747
struct nt_node *node_nt;
752
LOGDEBUG ("_ds_shutdown_storage: invalid database handle (NULL)");
756
node_nt = c_nt_first (s->dir_handles, &c_nt);
757
while (node_nt != NULL)
760
dir = (DIR *) node_nt->ptr;
762
node_nt = c_nt_next (s->dir_handles, &c_nt);
765
nt_destroy (s->dir_handles);
768
/* Store spam totals on shutdown */
769
if (CTX->username != NULL && CTX->operating_mode != DSM_CLASSIFY)
771
_sqlite_drv_set_spamtotals (CTX);
774
if (!s->dbh_attached)
775
sqlite3_close(s->dbh);
786
_ds_create_signature_id (DSPAM_CTX * CTX, char *buf, size_t len)
793
snprintf (session, sizeof (session), "%8lx%d", (long) time (NULL), pid);
795
for (j = 0; j < 2; j++)
797
snprintf (digit, 6, "%d", rand ());
798
strlcat (session, digit, 64);
801
strlcpy (buf, session, len);
806
_ds_get_signature (DSPAM_CTX * CTX, struct _ds_spam_signature *SIG,
807
const char *signature)
809
struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
812
const char *query_tail;
817
LOGDEBUG ("_ds_get_signature: invalid database handle (NULL)");
821
snprintf (query, sizeof (query),
822
"select data from dspam_signature_data where signature = \"%s\"",
825
if ((sqlite3_prepare(s->dbh, query, -1, &stmt, &query_tail))
828
_sqlite_drv_query_error (err, query);
832
if ((sqlite3_step(stmt))!=SQLITE_ROW) {
833
sqlite3_finalize(stmt);
837
SIG->length = sqlite3_column_bytes(stmt, 0);
838
SIG->data = malloc(SIG->length);
839
if (SIG->data == NULL) {
840
sqlite3_finalize(stmt);
841
LOG(LOG_CRIT, ERR_MEM_ALLOC);
845
memcpy(SIG->data, sqlite3_column_blob(stmt, 0), SIG->length);
847
if ((sqlite3_finalize(stmt)!=SQLITE_OK))
848
LOGDEBUG("sqlite3_finalize() failed: %s", strerror(errno));
854
_ds_set_signature (DSPAM_CTX * CTX, struct _ds_spam_signature *SIG,
855
const char *signature)
857
struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
861
const char *query_tail=NULL;
867
LOGDEBUG ("_ds_set_signature; invalid database handle (NULL)");
871
mem = calloc (1, 2 + (257*SIG->length)/254);
874
LOG (LOG_CRIT, ERR_MEM_ALLOC);
878
snprintf (scratch, sizeof (scratch),
879
"insert into dspam_signature_data(signature, created_on, data) "
880
"values(\"%s\", date('now'), ?)", signature);
882
if ((r = sqlite3_prepare(s->dbh, scratch, -1, &stmt, &query_tail))
885
_sqlite_drv_query_error ("sqlite3_prepare() failed", scratch);
889
sqlite3_bind_blob(stmt, 1, SIG->data, SIG->length, SQLITE_STATIC);
891
if ((sqlite3_step(stmt))!=SQLITE_DONE) {
892
_sqlite_drv_query_error (err, scratch);
896
sqlite3_finalize(stmt);
903
_ds_delete_signature (DSPAM_CTX * CTX, const char *signature)
905
struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
911
LOGDEBUG ("_ds_delete_signature: invalid database handle (NULL)");
915
snprintf (query, sizeof (query),
916
"delete from dspam_signature_data where signature = \"%s\"",
919
if ((sqlite3_exec(s->dbh, query, NULL, NULL, &err))!=SQLITE_OK)
921
_sqlite_drv_query_error (err, query);
929
_ds_verify_signature (DSPAM_CTX * CTX, const char *signature)
931
struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
933
char *err=NULL, **row;
938
LOGDEBUG ("_ds_verify_signature: invalid database handle (NULL)");
942
snprintf (query, sizeof (query),
943
"select signature from dspam_signature_data where signature = \"%s\"",
946
if ((sqlite3_get_table(s->dbh, query, &row, &nrow, &ncolumn, &err))!=SQLITE_OK) {
947
_sqlite_drv_query_error (err, query);
951
sqlite3_free_table(row);
961
_ds_get_nextuser (DSPAM_CTX * CTX)
963
static char user[MAX_FILENAME_LENGTH];
964
static char path[MAX_FILENAME_LENGTH];
965
struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
966
struct nt_node *node_nt, *prev;
971
struct dirent *entry;
973
if (s->dir_handles->items == 0)
975
char filename[MAX_FILENAME_LENGTH];
976
snprintf(filename, MAX_FILENAME_LENGTH, "%s/data", CTX->home);
977
dir = opendir (filename);
981
"unable to open directory '%s' for reading: %s",
982
CTX->home, strerror (errno));
986
nt_add (s->dir_handles, (void *) dir);
987
strlcpy (path, filename, sizeof (path));
991
node_nt = c_nt_first (s->dir_handles, &c_nt);
992
while (node_nt != NULL)
994
if (node_nt->next == NULL)
995
dir = (DIR *) node_nt->ptr;
996
node_nt = c_nt_next (s->dir_handles, &c_nt);
1000
while ((entry = readdir (dir)) != NULL)
1003
char filename[MAX_FILENAME_LENGTH];
1004
snprintf (filename, sizeof (filename), "%s/%s", path, entry->d_name);
1006
if (!strcmp (entry->d_name, ".") || !strcmp (entry->d_name, ".."))
1009
if (stat (filename, &st)) {
1013
/* push a new directory */
1014
if (st.st_mode & S_IFDIR)
1018
ndir = opendir (filename);
1021
strlcat (path, "/", sizeof (path));
1022
strlcat (path, entry->d_name, sizeof (path));
1023
nt_add (s->dir_handles, (void *) ndir);
1024
return _ds_get_nextuser (CTX);
1027
(entry->d_name + strlen (entry->d_name) - 4, ".sdb", 4))
1029
strlcpy (user, entry->d_name, sizeof (user));
1030
user[strlen (user) - 4] = 0;
1035
/* pop current directory */
1036
y = strchr (path, '/');
1040
y = strchr (x + 1, '/');
1045
/* pop directory handle from list */
1046
node_nt = c_nt_first (s->dir_handles, &c_nt);
1048
while (node_nt != NULL)
1050
if (node_nt->next == NULL)
1052
dir = (DIR *) node_nt->ptr;
1056
s->dir_handles->insert = NULL;
1059
s->dir_handles->first = NULL;
1061
s->dir_handles->items--;
1066
node_nt = c_nt_next (s->dir_handles, &c_nt);
1068
if (s->dir_handles->items > 0)
1069
return _ds_get_nextuser (CTX);
1077
struct _ds_storage_record *
1078
_ds_get_nexttoken (DSPAM_CTX * CTX)
1080
struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
1081
struct _ds_storage_record *st;
1084
const char *query_tail=NULL;
1089
LOGDEBUG ("_ds_get_nexttoken: invalid database handle (NULL)");
1093
st = calloc (1, sizeof (struct _ds_storage_record));
1096
LOG (LOG_CRIT, ERR_MEM_ALLOC);
1100
if (s->iter_token == NULL)
1102
snprintf (query, sizeof (query),
1103
"select token, spam_hits, innocent_hits, strftime('%%s', "
1104
"last_hit) from dspam_token_data");
1106
if ((sqlite3_prepare(s->dbh, query, -1, &s->iter_token, &query_tail))
1109
_sqlite_drv_query_error (err, query);
1115
if ((x = sqlite3_step(s->iter_token)) !=SQLITE_ROW) {
1116
if (x != SQLITE_DONE) {
1117
_sqlite_drv_query_error (err, query);
1118
s->iter_token = NULL;
1122
sqlite3_finalize((struct sqlite3_stmt *) s->iter_token);
1123
s->iter_token = NULL;
1128
st->token = strtoull (sqlite3_column_text(s->iter_token, 0), NULL, 0);
1129
st->spam_hits = strtol (sqlite3_column_text(s->iter_token, 1), NULL, 0);
1130
st->innocent_hits = strtol (sqlite3_column_text(s->iter_token, 2), NULL, 0);
1131
st->last_hit = (time_t) strtol (sqlite3_column_text(s->iter_token, 3), NULL, 0);
1136
struct _ds_storage_signature *
1137
_ds_get_nextsignature (DSPAM_CTX * CTX)
1139
struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
1140
struct _ds_storage_signature *st;
1141
unsigned long length;
1145
const char *query_tail=NULL;
1150
LOGDEBUG ("_ds_get_nextsignature: invalid database handle (NULL)");
1154
st = calloc (1, sizeof (struct _ds_storage_signature));
1157
LOG (LOG_CRIT, ERR_MEM_ALLOC);
1161
if (s->iter_sig == NULL)
1163
snprintf (query, sizeof (query),
1164
"select data, signature, strftime('%%s', created_on) "
1165
"from dspam_signature_data");
1167
if ((sqlite3_prepare(s->dbh, query, -1, &s->iter_sig, &query_tail))
1170
_sqlite_drv_query_error (err, query);
1176
if ((x = sqlite3_step(s->iter_sig)) !=SQLITE_ROW) {
1177
if (x != SQLITE_DONE) {
1178
_sqlite_drv_query_error (err, query);
1183
sqlite3_finalize((struct sqlite3_stmt *) s->iter_sig);
1189
length = sqlite3_column_bytes(s->iter_sig, 0);
1190
mem = malloc (length);
1193
LOG (LOG_CRIT, ERR_MEM_ALLOC);
1194
sqlite3_finalize(s->iter_sig);
1200
memcpy(mem, sqlite3_column_blob(s->iter_sig, 0), length);
1203
strlcpy(st->signature, sqlite3_column_text(s->iter_sig, 1), sizeof(st->signature));
1204
st->length = length;
1205
st->created_on = (time_t) strtol(sqlite3_column_text(s->iter_sig, 2), NULL, 0);
1211
_sqlite_drv_query_error (const char *error, const char *query)
1214
time_t tm = time (NULL);
1216
char fn[MAX_FILENAME_LENGTH];
1218
LOG (LOG_WARNING, "query error: %s: see sql.errors for more details",
1221
snprintf (fn, sizeof (fn), "%s/sql.errors", LOGDIR);
1223
snprintf (ct, sizeof (ct), "%s", ctime (&tm));
1226
file = fopen (fn, "a");
1230
LOG(LOG_ERR, ERR_IO_FILE_WRITE, fn, strerror (errno));
1234
fprintf (file, "[%s] %d: %s: %s\n", ct, (int) getpid (), error, query);
1238
free((char *)error);
1243
_ds_del_spamrecord (DSPAM_CTX * CTX, unsigned long long token)
1245
struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
1251
LOGDEBUG ("_ds_delete_signature: invalid database handle (NULL)");
1255
snprintf (query, sizeof (query),
1256
"delete from dspam_token_data where token = \"%" LLU_FMT_SPEC "\"",
1259
if ((sqlite3_exec(s->dbh, query, NULL, NULL, &err))!=SQLITE_OK)
1261
_sqlite_drv_query_error (err, query);
1268
int _ds_delall_spamrecords (DSPAM_CTX * CTX, ds_diction_t diction)
1270
struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage;
1276
char queryhead[1024];
1279
if (diction->items < 1)
1284
LOGDEBUG ("_ds_delall_spamrecords: invalid database handle (NULL)");
1288
query = buffer_create (NULL);
1291
LOG (LOG_CRIT, ERR_MEM_ALLOC);
1295
snprintf (queryhead, sizeof(queryhead),
1296
"delete from dspam_token_data "
1299
buffer_cat (query, queryhead);
1301
ds_c = ds_diction_cursor(diction);
1302
ds_term = ds_diction_next(ds_c);
1305
snprintf (scratch, sizeof (scratch), "'%" LLU_FMT_SPEC "'", ds_term->key);
1306
buffer_cat (query, scratch);
1307
ds_term = ds_diction_next(ds_c);
1309
if (writes > 2500 || ds_term == NULL) {
1310
buffer_cat (query, ")");
1312
if ((sqlite3_exec(s->dbh, query->data, NULL, NULL, &err))!=SQLITE_OK)
1314
_sqlite_drv_query_error (err, query->data);
1315
buffer_destroy(query);
1319
buffer_copy(query, queryhead);
1325
buffer_cat (query, ",");
1328
ds_diction_close(ds_c);
1331
buffer_cat (query, ")");
1333
if ((sqlite3_exec(s->dbh, query->data, NULL, NULL, &err))!=SQLITE_OK)
1335
_sqlite_drv_query_error (err, query->data);
1336
buffer_destroy(query);
1341
buffer_destroy (query);
1345
void *_ds_connect (DSPAM_CTX *CTX)
1351
/* Preference Stubs for Flat-File */
1353
agent_pref_t _ds_pref_load(config_t config, const char *user,
1354
const char *home, void *dbh)
1356
return _ds_ff_pref_load(config, user, home, dbh);
1359
int _ds_pref_set(config_t config, const char *user, const char *home,
1360
const char *attrib, const char *value, void *dbh)
1362
return _ds_ff_pref_set(config, user, home, attrib, value, dbh);
1365
int _ds_pref_del(config_t config, const char *user, const char *home,
1366
const char *attrib, void *dbh)
1368
return _ds_pref_del(config, user, home, attrib, dbh);