~ubuntu-branches/ubuntu/trusty/bmf/trusty

5 by Jari Aalto
* debian/compat
1
/* $Id: dbdb.c,v 1.22 2002/10/19 09:59:35 tommy Exp $ */
2
3
/*
4
 * Copyright (c) 2002 Tom Marshall <tommy@tig-grr.com>
5
 *
6
 * This program is free software.  It may be distributed under the terms
7
 * in the file LICENSE, found in the top level of the distribution.
8
 *
9
 * dbdb.c: berkeley database handler
10
 */
11
12
#include "config.h"
13
#include "dbg.h"
14
#include "str.h"
15
#include "lex.h"
16
#include "vec.h"
17
18
#include "dbh.h"
19
#include "dbdb.h"
20
21
#ifdef HAVE_LIBDB
22
23
#define DBT_init( pdbt ) memset( pdbt, 0, sizeof(DBT) )
24
25
#if !defined(DB_VERSION_MAJOR) /* v1 */
26
#define dbx_get(dbp,kp,vp)  dbp->get( dbp, kp, vp, 0 )
27
#define dbx_put(dbp,kp,vp)  dbp->put( dbp, kp, vp, 0 )
28
#define dbx_fd(dbp,fd)      fd = dbp->fd( dbp )
29
#else /* v2+ */
30
#define dbx_get(dbp,kp,vp)  dbp->get( dbp, NULL, kp, vp, 0 )
31
#define dbx_put(dbp,kp,vp)  dbp->put( dbp, NULL, kp, vp, 0 )
32
#define dbx_fd(dbp,fd)      dbp->fd( dbp, &fd )
33
#endif /* DB_VERSION_MAJOR */
34
35
#if !defined(DB_VERSION_MAJOR) /* v1 */
36
typedef DB DBC; /* no separate cursor type */
37
#define dbx_createcursor(dbp,dbcp)  ((dbcp = dbp) ? 0 : -1)
38
#define dbx_destroycursor(dbcp)     (dbcp = NULL)
39
#define dbx_first(dbcp,kp,vp)       dbcp->seq(dbcp,kp,vp,R_FIRST)
40
#define dbx_next(dbcp,kp,vp)        dbcp->seq(dbcp,kp,vp,R_NEXT)
41
#define dbx_prev(dbcp,kp,vp)        dbcp->seq(dbcp,kp,vp,R_PREV)
42
#define dbx_last(dbcp,kp,vp)        dbcp->seq(dbcp,kp,vp,R_LAST)
43
#elif DB_VERSION_MAJOR == 2
44
#define dbx_createcursor(dbp,dbcp)  dbp->cursor(dbp,NULL,&csrp)
45
#define dbx_destroycursor(dbcp)     dbcp->c_close(dbcp)
46
#define dbx_first(dbcp,kp,vp)       dbcp->c_get(dbcp,kp,vp,DB_FIRST)
47
#define dbx_next(dbcp,kp,vp)        dbcp->c_get(dbcp,kp,vp,DB_NEXT)
48
#define dbx_prev(dbcp,kp,vp)        dbcp->c_get(dbcp,kp,vp,DB_PREV)
49
#define dbx_last(dbcp,kp,vp)        dbcp->c_get(dbcp,kp,vp,DB_LAST)
50
#else /* v3+ */
51
#define dbx_createcursor(dbp,dbcp)  dbp->cursor(dbp,NULL,&csrp,0)
52
#define dbx_destroycursor(dbcp)     dbcp->c_close(dbcp)
53
#define dbx_first(dbcp,kp,vp)       dbcp->c_get(dbcp,kp,vp,DB_FIRST)
54
#define dbx_next(dbcp,kp,vp)        dbcp->c_get(dbcp,kp,vp,DB_NEXT)
55
#define dbx_prev(dbcp,kp,vp)        dbcp->c_get(dbcp,kp,vp,DB_PREV)
56
#define dbx_last(dbcp,kp,vp)        dbcp->c_get(dbcp,kp,vp,DB_LAST)
57
#endif /* DB_VERSION_MAJOR */
58
59
static void char2DBT( DBT* pdbt, char* p )
60
{
61
    pdbt->data = p;
62
    pdbt->size = strlen(p);
63
}
64
65
static void uint2DBT( DBT* pdbt, uint* p )
66
{
67
    pdbt->data = p;
68
    pdbt->size = sizeof(uint);
69
}
70
71
static uint DBT2uint( DBT* pdbt )
72
{
73
    uint n;
74
    memcpy( &n, pdbt->data, sizeof(n) );
75
    return n;
76
}
77
78
dbh_t* dbdb_db_open( cpchar dbhost, cpchar dbname, cpchar dbuser, cpchar dbpass )
79
{
80
    dbhdb_t*    pthis;
81
82
    uint        dirlen;
83
    cpchar      phome;
84
    struct stat st;
85
86
    pthis = (dbhdb_t*)malloc( sizeof(dbhdb_t) );
87
    if( pthis == NULL )
88
    {
89
        goto bail;
90
    }
91
    pthis->close = dbdb_db_close;
92
    pthis->opentable = dbdb_db_opentable;
93
    if( dbname != NULL && *dbname != '\0' )
94
    {
95
        dirlen = strlen( dbname );
96
        pthis->dir = strdup( dbname );
97
        if( pthis->dir[dirlen-1] == '/' )
98
        {
99
            pthis->dir[dirlen-1] = '\0';
100
        }
101
    }
102
    else
103
    {
104
        phome = getenv( "HOME" );
105
        if( phome == NULL || *phome == '\0' )
106
        {
107
            phome = ".";
108
        }
109
        pthis->dir = (char*)malloc( strlen(phome)+5+1 );
110
        if( pthis->dir == NULL )
111
        {
112
            goto bail;
113
        }
114
        sprintf( pthis->dir, "%s/.bmf", phome );
115
    }
116
117
    /* ensure config directory exists */
118
    if( stat( pthis->dir, &st ) != 0 )
119
    {
120
        if( errno == ENOENT )
121
        {
122
            if( mkdir( pthis->dir, S_IRUSR|S_IWUSR|S_IXUSR ) != 0 )
123
            {
124
                goto bail;
125
            }
126
        }
127
        else
128
        {
129
            goto bail;
130
        }
131
    }
132
    else
133
    {
134
        if( !S_ISDIR( st.st_mode ) )
135
        {
136
            goto bail;
137
        }
138
    }
139
140
#if !defined(DB_VERSION_MAJOR) || DB_VERSION_MAJOR < 3
141
    /* no initialization */
142
#else /* DB_VERSION_MAJOR >= 3 */
143
    if( db_env_create( &pthis->envp, 0 ) != 0 )
144
    {
145
        goto bail;
146
    }
147
    if( pthis->envp->open( pthis->envp, pthis->dir, DB_INIT_LOCK|DB_INIT_MPOOL|DB_CREATE, 0644 ) != 0 )
148
    {
149
        goto bail;
150
    }
151
#endif /* DB_VERSION_MAJOR */
152
153
    return (dbh_t*)pthis;
154
155
bail:
156
    free( pthis );
157
    return NULL;
158
}
159
160
bool_t dbdb_db_close( dbhdb_t* pthis )
161
{
162
#if !defined(DB_VERSION_MAJOR) || DB_VERSION_MAJOR < 3
163
    /* no cleanup */
164
#else /* DB_VERSION_MAJOR >= 3 */
165
    pthis->envp->close( pthis->envp, 0 );
166
#endif /* DB_VERSION_MAJOR */
167
168
    free( pthis->dir );
169
    pthis->dir = NULL;
170
171
    return true;
172
}
173
174
dbt_t* dbdb_db_opentable( dbhdb_t* pthis, cpchar table, bool_t rdonly )
175
{
176
    dbtdb_t*    ptable;
177
    DB*         dbp;
178
    DBT         key;
179
    DBT         val;
180
181
    char        szpath[PATH_MAX];
182
183
    ptable = (dbtdb_t*)malloc( sizeof(dbtdb_t) );
184
    if( ptable == NULL )
185
    {
186
        return NULL;
187
    }
188
    ptable->close = dbdb_table_close;
189
    ptable->mergeclose = dbdb_table_mergeclose;
190
    ptable->unmergeclose = dbdb_table_unmergeclose;
191
    ptable->import = dbdb_table_import;
192
    ptable->export = dbdb_table_export;
193
    ptable->getmsgcount = dbdb_table_getmsgcount;
194
    ptable->getcount = dbdb_table_getcount;
195
    ptable->dbp = NULL;
196
197
    sprintf( szpath, "%s/%s.db", pthis->dir, table );
198
#if !defined(DB_VERSION_MAJOR)
199
    if( (dbp = dbopen( szpath, O_CREAT|O_RDWR, 0644, DB_BTREE, NULL)) == NULL )
200
    {
201
        goto bail;
202
    }
203
#elif DB_VERSION_MAJOR == 2
204
    if( db_open( szpath, DB_BTREE, DB_CREATE, 0644, NULL, NULL, &dbp ) != 0 )
205
    {
206
        goto bail;
207
    }
208
#elif (DB_VERSION_MAJOR == 3) || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR == 0)
209
    ptable->envp = pthis->envp;
210
    if( db_create( &dbp, NULL, 0 ) != 0 )
211
    {
212
        goto bail;
213
    }
214
    if( dbp->open( dbp, szpath, NULL, DB_BTREE, DB_CREATE, 0644 ) != 0 )
215
    {
216
        goto bail;
217
    }
218
#else /* v4.1+ */
219
    ptable->envp = pthis->envp;
220
    if( db_create( &dbp, NULL, 0 ) != 0 )
221
    {
222
        goto bail;
223
    }
224
    if( dbp->open( dbp, NULL, szpath, NULL, DB_BTREE, DB_CREATE, 0644 ) != 0 )
225
    {
226
        goto bail;
227
    }
228
#endif /* DB_VERSION_MAJOR */
229
    ptable->dbp = dbp;
230
231
    DBT_init( &key );
232
    DBT_init( &val );
233
    ptable->nmsgs = 0;
234
    char2DBT( &key, MSGCOUNT_KEY );
235
    if( dbx_get( dbp, &key, &val ) == 0 )
236
    {
237
        ptable->nmsgs = DBT2uint( &val );
238
    }
239
240
    return (dbt_t*)ptable;
241
242
bail:
243
    free( ptable );
244
    return NULL;
245
}
246
247
static bool_t dbdb_table_lock( dbtdb_t* pthis )
248
{
249
#ifndef NOLOCK
250
    struct flock lock;
251
    int fd;
252
253
    dbx_fd( pthis->dbp, fd );
254
    memset( &lock, 0, sizeof(lock) );
255
    lock.l_type = F_WRLCK;
256
    lock.l_start = 0;
257
    lock.l_whence = SEEK_SET;
258
    lock.l_len = 0;
259
    if( fcntl( fd, F_SETLKW, &lock ) != 0 )
260
    {
261
        return false;
262
    }
263
#endif /* ndef NOLOCK */
264
    return true;
265
}
266
267
static bool_t dbdb_table_unlock( dbtdb_t* pthis )
268
{
269
#ifndef NOLOCK
270
    struct flock lock;
271
    int fd;
272
273
    dbx_fd( pthis->dbp, fd );
274
    memset( &lock, 0, sizeof(lock) );
275
    lock.l_type = F_UNLCK;
276
    lock.l_start = 0;
277
    lock.l_whence = SEEK_SET;
278
    lock.l_len = 0;
279
    if( fcntl( fd, F_SETLK, &lock ) != 0 )
280
    {
281
        return false;
282
    }
283
#endif /* ndef NOLOCK */
284
    return true;
285
}
286
287
bool_t dbdb_table_close( dbtdb_t* pthis )
288
{
289
    DB* dbp = pthis->dbp;
290
291
    if( dbp != NULL )
292
    {
293
#if !defined(DB_VERSION_MAJOR) /* v1 */
294
        dbp->close( dbp );
295
#else /* v2+ */
296
        dbp->close( dbp, 0 );
297
#endif /* DB_VERSION_MAJOR */
298
        pthis->dbp = NULL;
299
    }
300
301
    return true;
302
}
303
304
bool_t dbdb_table_mergeclose( dbtdb_t* pthis, vec_t* pmsg )
305
{
306
    DB*         dbp = pthis->dbp;
307
    DBT         key;
308
    DBT         val;
309
310
    char        szword[MAXWORDLEN+1];
311
    uint        count;
312
    veciter_t   msgiter;
313
    str_t*      pmsgstr;
314
315
    if( pthis->dbp == NULL )
316
    {
317
        return false;
318
    }
319
320
    if( !dbdb_table_lock( pthis ) )
321
    {
322
        return false;
323
    }
324
325
    pthis->nmsgs++;
326
327
    DBT_init( &key );
328
    DBT_init( &val );
329
330
    char2DBT( &key, MSGCOUNT_KEY );
331
    uint2DBT( &val, &pthis->nmsgs );
332
    dbx_put( dbp, &key, &val );
333
334
    vec_first( pmsg, &msgiter );
335
    pmsgstr = veciter_get( &msgiter );
336
337
    while( pmsgstr != NULL )
338
    {
339
        assert( pmsgstr->len <= MAXWORDLEN );
340
        strncpylwr( szword, pmsgstr->p, pmsgstr->len );
341
        szword[pmsgstr->len] = '\0';
342
        count = db_getnewcount( &msgiter );
343
344
        char2DBT( &key, szword );
345
        if( dbx_get( dbp, &key, &val ) == 0 )
346
        {
347
            count += DBT2uint( &val );
348
        }
349
        char2DBT( &key, szword );
350
        uint2DBT( &val, &count );
351
        if( dbx_put( dbp, &key, &val ) != 0 )
352
        {
353
            goto bail;
354
        }
355
356
        veciter_next( &msgiter );
357
        pmsgstr = veciter_get( &msgiter );
358
    }
359
360
    veciter_destroy( &msgiter );
361
    dbdb_table_unlock( pthis );
362
    return dbdb_table_close( pthis );
363
364
bail:
365
    return false;
366
}
367
368
bool_t dbdb_table_unmergeclose( dbtdb_t* pthis, vec_t* pmsg )
369
{
370
    DB*         dbp = pthis->dbp;
371
    DBT         key;
372
    DBT         val;
373
374
    char        szword[MAXWORDLEN+1];
375
    uint        count;
376
    veciter_t   msgiter;
377
    str_t*      pmsgstr;
378
379
    if( pthis->dbp == NULL )
380
    {
381
        return false;
382
    }
383
384
    if( pthis->nmsgs > 0 )
385
    {
386
        pthis->nmsgs--;
387
    }
388
389
    if( !dbdb_table_lock( pthis ) )
390
    {
391
        return false;
392
    }
393
394
    DBT_init( &key );
395
    DBT_init( &val );
396
397
    char2DBT( &key, MSGCOUNT_KEY );
398
    uint2DBT( &val, &pthis->nmsgs );
399
    dbx_put( dbp, &key, &val );
400
401
    vec_first( pmsg, &msgiter );
402
    pmsgstr = veciter_get( &msgiter );
403
404
    while( pmsgstr != NULL )
405
    {
406
        assert( pmsgstr->len <= MAXWORDLEN );
407
        strncpylwr( szword, pmsgstr->p, pmsgstr->len );
408
        szword[pmsgstr->len] = '\0';
409
        count = db_getnewcount( &msgiter );
410
411
        char2DBT( &key, szword );
412
        if( dbx_get( dbp, &key, &val ) == 0 )
413
        {
414
            uint n = DBT2uint( &val );
415
            n = (n > count) ? (n - count) : 0;
416
            char2DBT( &key, szword );
417
            uint2DBT( &val, &n );
418
            if( dbx_put( dbp, &key, &val ) != 0 )
419
            {
420
                goto bail;
421
            }
422
        }
423
424
        veciter_next( &msgiter );
425
        pmsgstr = veciter_get( &msgiter );
426
    }
427
428
    veciter_destroy( &msgiter );
429
    dbdb_table_unlock( pthis );
430
    return dbdb_table_close( pthis );
431
432
bail:
433
    return false;
434
}
435
436
bool_t dbdb_table_import( dbtdb_t* pthis, cpchar filename )
437
{
438
    DB* dbp = pthis->dbp;
439
    int fd;
440
    struct stat st;
441
    char* pbuf;
442
    char* pbegin;
443
    char* pend;
444
    rec_t r;
445
    DBT key;
446
    DBT val;
447
    char szword[MAXWORDLEN+1];
448
449
    if( pthis->dbp == NULL )
450
    {
451
        return false;
452
    }
453
    if( (fd = open( filename, O_RDONLY, 0644 )) < 0 )
454
    {
455
        return false;
456
    }
457
    if( fstat( fd, &st ) != 0 )
458
    {
459
        goto bail;
460
    }
461
    if( st.st_size == 0 )
462
    {
463
        goto bail;
464
    }
465
    pbuf = (char*)malloc( st.st_size );
466
    if( pbuf == NULL )
467
    {
468
        goto bail;
469
    }
470
    if( read( fd, pbuf, st.st_size ) != st.st_size )
471
    {
472
        goto bail;
473
    }
474
475
    DBT_init( &key );
476
    DBT_init( &val );
477
478
    if( sscanf( pbuf, BOGOFILTER_HEADER, &pthis->nmsgs ) != 1 )
479
    {
480
        goto bail;
481
    }
482
    pbegin = pbuf;
483
    while( *pbegin != '\n' ) pbegin++;
484
    pbegin++;
485
486
    char2DBT( &key, MSGCOUNT_KEY );
487
    uint2DBT( &val, &pthis->nmsgs );
488
    if( dbx_put( dbp, &key, &val ) != 0 )
489
    {
490
        goto bail;
491
    }
492
493
    while( pbegin < pbuf + st.st_size )
494
    {
495
        pend = pbegin;
496
        r.w.p = pbegin;
497
        r.w.len = 0;
498
        r.n = 0;
499
500
        while( *pend != '\n' )
501
        {
502
            if( pend >= pbuf + st.st_size )
503
            {
504
                goto bail;
505
            }
506
            *pend = tolower(*pend);
507
            if( *pend == ' ' )
508
            {
509
                r.w.len = (pend-pbegin);
510
                r.n = strtol( pend+1, NULL, 10 );
511
            }
512
            pend++;
513
        }
514
        if( pend > pbegin && *pbegin != '#' && *pbegin != ';' )
515
        {
516
            if( r.w.len == 0 || r.w.len > MAXWORDLEN )
517
            {
518
                fprintf( stderr, "dbh_loadfile: bad file format\n" );
519
                goto bail;
520
            }
521
            strncpylwr( szword, r.w.p, r.w.len );
522
            szword[r.w.len] = '\0';
523
            char2DBT( &key, szword );
524
            uint2DBT( &val, &r.n );
525
            if( dbx_put( dbp, &key, &val ) != 0 )
526
            {
527
                goto bail;
528
            }
529
        }
530
        pbegin = pend+1;
531
    }
532
533
    free( pbuf );
534
    close( fd );
535
536
    return true;
537
538
bail:
539
    return false;
540
}
541
542
bool_t dbdb_table_export( dbtdb_t* pthis, cpchar filename )
543
{
544
    DB*     dbp = pthis->dbp;
545
    int     fd;
546
    char    iobuf[IOBUFSIZE];
547
    char*   p;
548
549
    DBC*    csrp;
550
    int     rc;
551
    DBT     key;
552
    DBT     val;
553
554
    if( (fd = open( filename, O_CREAT|O_WRONLY|O_TRUNC, 0644 )) < 0 )
555
    {
556
        goto bail;
557
    }
558
    if( dbx_createcursor( dbp, csrp ) != 0 )
559
    {
560
        goto bail;
561
    }
562
563
    DBT_init( &key );
564
    DBT_init( &val );
565
566
    p = iobuf;
567
    p += sprintf( p, BOGOFILTER_HEADER, pthis->nmsgs );
568
569
    rc = dbx_first( csrp, &key, &val );
570
    while( rc == 0 )
571
    {
572
        assert( key.data != NULL && key.size <= MAXWORDLEN );
573
        assert( val.data != NULL && val.size == sizeof(uint) );
574
        if( key.size != MSGCOUNT_KEY_LEN ||
575
            memcmp( key.data, MSGCOUNT_KEY, MSGCOUNT_KEY_LEN ) != 0 )
576
        {
577
            memcpy( p, key.data, key.size ); p += key.size;
578
            *p++ = ' ';
579
            p += sprintf( p, "%u\n", DBT2uint(&val) );
580
            if( p+TEXTDB_MAXLINELEN > (iobuf+1) )
581
            {
582
                write( fd, iobuf, p-iobuf );
583
                p = iobuf;
584
            }
585
        }
586
        rc = dbx_next( csrp, &key, &val );
587
    }
588
    dbx_destroycursor( csrp );
589
    if( p != iobuf )
590
    {
591
        write( fd, iobuf, p-iobuf );
592
    }
593
    close( fd );
594
    return true;
595
596
bail:
597
    return false;
598
}
599
600
uint dbdb_table_getmsgcount( dbtdb_t* pthis )
601
{
602
    return pthis->nmsgs;
603
}
604
605
uint dbdb_table_getcount( dbtdb_t* pthis, str_t* pword )
606
{
607
    DB*         dbp = pthis->dbp;
608
    DBT         key;
609
    DBT         val;
610
611
    char szword[MAXWORDLEN+1];
612
    uint count = 0;
613
614
    assert( pword->len <= MAXWORDLEN );
615
    strncpylwr( szword, pword->p, pword->len );
616
    szword[pword->len] = '\0';
617
    count = 0;
618
619
    DBT_init( &key );
620
    DBT_init( &val );
621
622
    char2DBT( &key, szword );
623
    if( dbx_get( dbp, &key, &val ) == 0 )
624
    {
625
        count = DBT2uint( &val );
626
    }
627
628
    return count;
629
}
630
631
#else /* def HAVE_LIBDB */
632
633
dbh_t* dbdb_db_open( cpchar dbhost, cpchar dbname, cpchar dbuser, cpchar dbpass )
634
{
635
    return NULL;
636
}
637
638
#endif /* def HAVE_LIBDB */
639
640
#ifdef UNIT_TEST
641
int main( int argc, char** argv )
642
{
643
    dbh_t*      pdb;
644
    veciter_t   iter;
645
    str_t*      pstr;
646
    uint        n;
647
648
    if( argc != 2 )
649
    {
650
        fprintf( stderr, "usage: %s <file>\n", argv[0] );
651
        return 1;
652
    }
653
654
    for( n = 0; n < 100; n++ )
655
    {
656
        pdb = dbh_open( "testlist", true );
657
658
        vec_first( &db, &iter );
659
        while( (pstr = veciter_get( &iter )) != NULL )
660
        {
661
            char  buf[MAXWORDLEN+32];
662
            char* p;
663
            if( pstr->len > 200 )
664
            {
665
                fprintf( stderr, "str too long: %u chars\n", pstr->len );
666
                break;
667
            }
668
            p = buf;
669
            strcpy( buf, "str: " );
670
            p += 6;
671
            memcpy( p, pstr->p, pstr->len );
672
            p += pstr->len;
673
            sprintf( p, " %u", pstr->count );
674
            puts( buf );
675
676
            veciter_next( &iter );
677
        }
678
679
        dbh_close( &db );
680
    }
681
682
    return 0;
683
}
684
#endif /* def UNIT_TEST */