#line 1026 "../../src/builtin/snarf.m4"
/* -*- buffer-read-only: t -*- vi: set ro:
   THIS FILE IS GENERATED AUTOMATICALLY.  PLEASE DO NOT EDIT.
*/
#line 1026
#ifdef HAVE_CONFIG_H
#line 1026
# include <config.h>
#line 1026
#endif
#line 1026
#include <sys/types.h>
#line 1026

#line 1026
#include "mailfromd.h"
#line 1026
#include "prog.h"
#line 1026
#include "builtin.h"
#line 1026

#line 211 "db.bi"
static mu_debug_handle_t debug_handle;
#line 1026 "../../src/builtin/snarf.m4"

#line 1066 "../../src/builtin/snarf.m4"

/* End of snarf.m4 */
#line 1 "db.bi"
/* This file is part of Mailfromd.             -*- c -*-
   Copyright (C) 2006-2025 Sergey Poznyakoff

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */


#define DEFAULT_DB_MODE 0640
#include <fnmatch.h>

struct db_prop {                /* Database properties */
	char *pat;              /* Database name pattern */
	mode_t mode;            /* File mode */
	int null;               /* Null byte */
	mu_url_t hint;          /* Hint to use instead of the default one */
};

static mu_list_t db_prop_list;

static int
db_prop_compare(const void *item, const void *data)
{
	const struct db_prop *prop = item;
	const char *name = data;
	return strcmp(prop->pat, name);
}

static int
strtomode(char *str, mode_t *pmode)
{
	mode_t mode = 0;
	struct { char c; unsigned mask; } modetab[] = {
		{ 'r', S_IRUSR },
		{ 'w', S_IWUSR },
		{ 'x', S_IXUSR },
		{ 'r', S_IRGRP },
		{ 'w', S_IWGRP },
		{ 'x', S_IXGRP },
		{ 'r', S_IROTH },
		{ 'w', S_IWOTH },
		{ 'x', S_IXOTH },
		{ 0 }
	};
	int i;

	for (i = 0; modetab[i].c; i++) {
		if (!str[i])
			return i + 1;
		if (str[i] == modetab[i].c)
			mode |= modetab[i].mask;
		else if (str[i] != '-')
			return i + 1;
	}
	if (str[i])
		return i + 1;
	*pmode = mode;
	return 0;
}

static int
is_url(const char *p)
{
	for (; *p && mu_isalnum(*p); p++)
		;
	return *p == ':';
}
		
/* #pragma dbprop <name> [null] [mode] [hint]
   At least one of the bracketed parameters must be present.  Two or more
   parameters can be given in arbitrary order.
 */
#line 82 "db.bi"

#line 82
static void _pragma_dbprop (int argc, char **argv, const char *text)
#line 82

{
	int null = 0;
	mode_t mode = DEFAULT_DB_MODE;
	struct db_prop *prop;
	int rc;
	char *pat;
	mu_url_t hint = NULL;

	--argc;
	pat = *++argv;
	
	while (--argc) {
		char *p = *++argv;

		if (strcmp(p, "null") == 0)
			null = 1;
		else if (mu_isdigit(*p)) {
			unsigned long n = strtoul(p, &p, 8);
			if (*p || (mode = n) != n) {
				parse_error(_("bad numeric file mode"));
				return;
			}
		} else if (is_url(p)) {
			rc = mu_url_create(&hint, p);
			if (rc) {
				parse_error(_("not a valid URL: %s"),
					    mu_strerror(rc));
				return;
			}
		} else if (rc = strtomode(p, &mode)) {
			parse_error(_("bad symbolic file mode (near %s)"),
				    p + rc - 1);
			return;
		}
	}

	if (!db_prop_list) {
		rc = mu_list_create(&db_prop_list);
		if (rc) {
			parse_error(_("cannot create list: %s"),
				    mu_strerror(rc));
			return;
		}
		mu_list_set_comparator(db_prop_list, db_prop_compare);
	}

	if (mu_list_locate(db_prop_list, pat, (void**) &prop)) {
		prop = mu_alloc(sizeof(*prop));
		prop->pat = mu_strdup(pat);
		rc = mu_list_append(db_prop_list, prop);
		if (rc) {
			parse_error(_("Cannot create list: %s"),
				    mu_strerror(rc));
			return;
		}
	}
	prop->mode = mode;
	prop->null = null;
	prop->hint = hint;
}

const struct db_prop *
db_prop_lookup(const char *name)
{
	mu_iterator_t itr;
	const struct db_prop *found = NULL;
	
	if (db_prop_list
	    && mu_list_get_iterator(db_prop_list, &itr) == 0) {
		for (mu_iterator_first(itr);
		     !mu_iterator_is_done(itr);
		     mu_iterator_next(itr)) {
			const struct db_prop *p;
			mu_iterator_current(itr, (void**)&p);
			if (strcmp(p->pat, name) == 0) {
				found = p;
				break;
			} else if (fnmatch(p->pat, name, 0) == 0)
				found = p;
		}
		mu_iterator_destroy(&itr);
	}
	return found;	
}

int 
_open_dbm(mu_dbm_file_t *pdb, char *dbname, int access, int mode,
	  mu_url_t hint)
{
	mu_dbm_file_t db;
	int rc;
	mu_url_t url;

	if (!hint)
		hint = mu_dbm_get_hint();
	rc = mu_url_create_hint(&url, dbname, 0, hint);
	if (rc) {
		mu_error(_("cannot create database URL for %s: %s"),
			 dbname, mu_strerror(rc));
		return rc;
	}
	rc = mu_dbm_create_from_url(url, &db,
				    mf_file_mode_to_safety_criteria(mode));
	mu_url_destroy(&url);
	if (rc)
		return rc;

	rc = mu_dbm_safety_check(db);
	if (rc && rc != ENOENT) {
		mu_error(_("%s fails safety check: %s"),
			 dbname, mu_strerror(rc));
		mu_dbm_destroy(&db);
		return rc;
	}

	rc = mu_dbm_open(db, access, mode);
	if (rc) {
		mu_dbm_destroy(&db);
		return rc;
	}
	*pdb = db;
	return rc;
}


#define LOOKUP_NULL_BYTE 0x1
#define LOOKUP_TEST_ONLY 0x2


#line 211

#line 211
static int
#line 211
dbmap_lookup(eval_environ_t env, char *dbname, const char *keystr,
#line 211
	     const char *defval, const struct db_prop *prop, int flags)
#line 211
{
#line 211
	int rc;
#line 211
	mu_dbm_file_t db;
#line 211
	struct mu_dbm_datum key;
#line 211
	struct mu_dbm_datum contents;
#line 211
	
#line 211
	if (!defval)
#line 211
		defval = "";
#line 211
	rc = _open_dbm(&db, dbname, MU_STREAM_READ,
#line 211
		       prop ? prop->mode : DEFAULT_DB_MODE,
#line 211
		       prop ? prop->hint : NULL);
#line 211
	if (rc)
#line 211
		(
#line 211
	env_throw_bi(env, mfe_dbfailure, NULL, _("mf_dbm_open(%s) failed: %s"),dbname,mu_strerror(rc))
#line 211
);
#line 211

#line 211
	memset(&key, 0, sizeof key);
#line 211
	memset(&contents, 0, sizeof contents);
#line 211
	key.mu_dptr = (void*) keystr;
#line 211
	key.mu_dsize = strlen(keystr);
#line 211
	if (flags & LOOKUP_NULL_BYTE)
#line 211
		key.mu_dsize++;
#line 211
	rc = mu_dbm_fetch(db, &key, &contents);
#line 211
	if (rc && rc != MU_ERR_NOENT) {
#line 211
		mu_dbm_destroy(&db);
#line 211
		(
#line 211
	env_throw_bi(env, mfe_dbfailure, NULL, _("cannot fetch data: %s"),mu_dbm_strerror(db))
#line 211
);
#line 211
	}
#line 211
	
#line 211 "db.bi"

#line 211
mu_debug(debug_handle, MU_DEBUG_TRACE5,("Looking up %s: %s", keystr, rc ? "false" : "true"));
#line 211
	if (flags & LOOKUP_TEST_ONLY) 
#line 211
		push(env, (STKVAL) (mft_number) (rc == 0));
#line 211
	else {
#line 211
		if (rc) {
#line 211
			if (defval)
#line 211
				pushs(env, defval);
#line 211
			else
#line 211
				push(env, (STKVAL) 0L);
#line 211
		} else if (((char*)contents.mu_dptr)[contents.mu_dsize-1]) {
#line 211
			size_t off;
#line 211
			size_t len = contents.mu_dsize;
#line 211
			char *s = (char*) env_data_ref(env, (off = heap_reserve(env, len + 1)));
#line 211
			memcpy(s, contents.mu_dptr, len);
#line 211
			s[len] = 0;
#line 211
			push(env, (STKVAL) off);
#line 211
		} else
#line 211
			pushs(env, contents.mu_dptr);
#line 211
	}
#line 211
	mu_dbm_datum_free(&contents);
#line 211
	mu_dbm_destroy(&db);
#line 211
	return rc;
#line 211
}
#line 211

#line 211

#line 211

#line 269



void
#line 272
bi_dbmap(eval_environ_t env)
#line 272

#line 272

#line 272 "db.bi"
{
#line 272
	
#line 272

#line 272
        char * MFL_DATASEG dbname;
#line 272
        char * MFL_DATASEG key;
#line 272
        long  null;
#line 272
        
#line 272
        long __bi_argcnt;
#line 272
        get_numeric_arg(env, 0, &__bi_argcnt);
#line 272
        get_string_arg(env, 1, &dbname);
#line 272
        get_string_arg(env, 2, &key);
#line 272
        if (__bi_argcnt > 2)
#line 272
                get_numeric_arg(env, 3, &null);
#line 272
        
#line 272
        adjust_stack(env, __bi_argcnt + 1);
#line 272

#line 272

#line 272
	if (builtin_module_trace(BUILTIN_IDX_db))
#line 272
		prog_trace(env, "dbmap %s %s %lu",dbname, key, ((__bi_argcnt > 2) ? null : 0));;
#line 272

{
	const struct db_prop *prop = db_prop_lookup(dbname);
	
#line 275
dbmap_lookup(env,dbname,key,NULL,prop,LOOKUP_TEST_ONLY | 
		         (((__bi_argcnt > 2) ? null : prop && prop->null)
			   ? LOOKUP_NULL_BYTE : 0));
#line 280
}

#line 281
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 281
	return;
#line 281
}

void
#line 283
bi_dbget(eval_environ_t env)
#line 283

#line 283

#line 283 "db.bi"
{
#line 283
	
#line 283

#line 283
        char * MFL_DATASEG dbname;
#line 283
        char * MFL_DATASEG key;
#line 283
        char * MFL_DATASEG defval;
#line 283
        long  null;
#line 283
        
#line 283
        long __bi_argcnt;
#line 283
        get_numeric_arg(env, 0, &__bi_argcnt);
#line 283
        get_string_arg(env, 1, &dbname);
#line 283
        get_string_arg(env, 2, &key);
#line 283
        if (__bi_argcnt > 2)
#line 283
                get_string_arg(env, 3, &defval);
#line 283
        if (__bi_argcnt > 3)
#line 283
                get_numeric_arg(env, 4, &null);
#line 283
        
#line 283
        adjust_stack(env, __bi_argcnt + 1);
#line 283

#line 283

#line 283
	if (builtin_module_trace(BUILTIN_IDX_db))
#line 283
		prog_trace(env, "dbget %s %s %s %lu",dbname, key, ((__bi_argcnt > 2) ? defval : ""), ((__bi_argcnt > 3) ? null : 0));;

{
	const struct db_prop *prop = db_prop_lookup(dbname);
	
#line 287
dbmap_lookup(env,dbname,key,((__bi_argcnt > 2) ? defval : 0),prop,((__bi_argcnt > 3) ? null : prop && prop->null)
		      ? LOOKUP_NULL_BYTE : 0);
#line 292
}

#line 293
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 293
	return;
#line 293
}

void
#line 295
bi_dbput(eval_environ_t env)
#line 295

#line 295

#line 295 "db.bi"
{
#line 295
	
#line 295

#line 295
        char *  dbname;
#line 295
        char *  keystr;
#line 295
        char *  value;
#line 295
        long  null;
#line 295
        long  mode;
#line 295
        
#line 295
        long __bi_argcnt;
#line 295
        get_numeric_arg(env, 0, &__bi_argcnt);
#line 295
        get_string_arg(env, 1, &dbname);
#line 295
        get_string_arg(env, 2, &keystr);
#line 295
        get_string_arg(env, 3, &value);
#line 295
        if (__bi_argcnt > 3)
#line 295
                get_numeric_arg(env, 4, &null);
#line 295
        if (__bi_argcnt > 4)
#line 295
                get_numeric_arg(env, 5, &mode);
#line 295
        
#line 295
        adjust_stack(env, __bi_argcnt + 1);
#line 295

#line 295

#line 295
	if (builtin_module_trace(BUILTIN_IDX_db))
#line 295
		prog_trace(env, "dbput %s %s %s %lu %lu",dbname, keystr, value, ((__bi_argcnt > 3) ? null : 0), ((__bi_argcnt > 4) ? mode : 0));;

{
	int rc;
	mu_dbm_file_t db;
	struct mu_dbm_datum key;
	struct mu_dbm_datum contents;
	const struct db_prop *prop = db_prop_lookup(dbname);
	
	rc = _open_dbm(&db, dbname, MU_STREAM_RDWR,
		       ((__bi_argcnt > 4) ? mode : (prop ? prop->mode : DEFAULT_DB_MODE)),
#line 307
		       prop ? prop->hint : NULL);
	if (rc)
		(
#line 309
	env_throw_bi(env, mfe_dbfailure, "dbput", _("mf_dbm_open(%s) failed: %s"),dbname,mu_strerror(rc))
#line 309
);
#line 313
	memset(&key, 0, sizeof key);
	key.mu_dptr = keystr;
	key.mu_dsize = strlen(keystr);
	if (((__bi_argcnt > 3) ? null : prop && prop->null))
		key.mu_dsize++;

	memset(&contents, 0, sizeof contents);
	contents.mu_dptr = value;
	contents.mu_dsize = strlen(value) + 1;
	
	rc = mu_dbm_store(db, &key, &contents, 1);
	if (rc) {
		const char *errstr = mu_dbm_strerror(db);
		mu_dbm_destroy(&db);
		(
#line 327
	env_throw_bi(env, mfe_dbfailure, "dbput", _("failed to insert data to %s: %s %s: %s"),dbname,keystr,value,errstr)
#line 327
);
#line 333
	}
	mu_dbm_destroy(&db);
}

#line 336
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 336
	return;
#line 336
}

void
#line 338
bi_dbinsert(eval_environ_t env)
#line 338

#line 338

#line 338 "db.bi"
{
#line 338
	
#line 338

#line 338
        char *  dbname;
#line 338
        char *  keystr;
#line 338
        char *  value;
#line 338
        long  replace;
#line 338
        long  null;
#line 338
        long  mode;
#line 338
        
#line 338
        long __bi_argcnt;
#line 338
        get_numeric_arg(env, 0, &__bi_argcnt);
#line 338
        get_string_arg(env, 1, &dbname);
#line 338
        get_string_arg(env, 2, &keystr);
#line 338
        get_string_arg(env, 3, &value);
#line 338
        if (__bi_argcnt > 3)
#line 338
                get_numeric_arg(env, 4, &replace);
#line 338
        if (__bi_argcnt > 4)
#line 338
                get_numeric_arg(env, 5, &null);
#line 338
        if (__bi_argcnt > 5)
#line 338
                get_numeric_arg(env, 6, &mode);
#line 338
        
#line 338
        adjust_stack(env, __bi_argcnt + 1);
#line 338

#line 338

#line 338
	if (builtin_module_trace(BUILTIN_IDX_db))
#line 338
		prog_trace(env, "dbinsert %s %s %s %lu %lu %lu",dbname, keystr, value, ((__bi_argcnt > 3) ? replace : 0), ((__bi_argcnt > 4) ? null : 0), ((__bi_argcnt > 5) ? mode : 0));;

{
	int rc;
	mu_dbm_file_t db;
	struct mu_dbm_datum key;
	struct mu_dbm_datum contents;
	const struct db_prop *prop = db_prop_lookup(dbname);
	const char *errstr;
	
	rc = _open_dbm(&db, dbname, MU_STREAM_RDWR,
		       ((__bi_argcnt > 5) ? mode : (prop ? prop->mode : DEFAULT_DB_MODE)),
#line 351
		       prop ? prop->hint : NULL);
	if (rc)
		(
#line 353
	env_throw_bi(env, mfe_dbfailure, "dbinsert", _("mf_dbm_open(%s) failed: %s"),dbname,mu_strerror(rc))
#line 353
);
#line 357
	memset(&key, 0, sizeof key);
	key.mu_dptr = keystr;
	key.mu_dsize = strlen(keystr);
	if (((__bi_argcnt > 4) ? null : prop && prop->null))
		key.mu_dsize++;

	memset(&contents, 0, sizeof contents);
	contents.mu_dptr = value;
	contents.mu_dsize = strlen(value) + 1;
	
	rc = mu_dbm_store(db, &key, &contents, ((__bi_argcnt > 3) ? replace : 0));
	if (rc && rc != MU_ERR_EXISTS)
		errstr = mu_dbm_strerror(db);
	mu_dbm_destroy(&db);
	if (rc == MU_ERR_EXISTS)
		(
#line 372
	env_throw_bi(env, mfe_exists, "dbinsert", _("record already exists"))
#line 372
);
	
		if (!(rc == 0))
#line 374
		(
#line 374
	env_throw_bi(env, mfe_dbfailure, "dbinsert", _("failed to insert data to %s: %s %s: %s"),dbname,keystr,value,errstr)
#line 374
)
#line 380
;
}

#line 382
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 382
	return;
#line 382
}

void
#line 384
bi_dbdel(eval_environ_t env)
#line 384

#line 384

#line 384 "db.bi"
{
#line 384
	
#line 384

#line 384
        char *  dbname;
#line 384
        char *  keystr;
#line 384
        long  null;
#line 384
        long  mode;
#line 384
        
#line 384
        long __bi_argcnt;
#line 384
        get_numeric_arg(env, 0, &__bi_argcnt);
#line 384
        get_string_arg(env, 1, &dbname);
#line 384
        get_string_arg(env, 2, &keystr);
#line 384
        if (__bi_argcnt > 2)
#line 384
                get_numeric_arg(env, 3, &null);
#line 384
        if (__bi_argcnt > 3)
#line 384
                get_numeric_arg(env, 4, &mode);
#line 384
        
#line 384
        adjust_stack(env, __bi_argcnt + 1);
#line 384

#line 384

#line 384
	if (builtin_module_trace(BUILTIN_IDX_db))
#line 384
		prog_trace(env, "dbdel %s %s %lu %lu",dbname, keystr, ((__bi_argcnt > 2) ? null : 0), ((__bi_argcnt > 3) ? mode : 0));;

{
	mu_dbm_file_t db;
	struct mu_dbm_datum key;
	int rc;
	const struct db_prop *prop = db_prop_lookup(dbname);
	
	rc = _open_dbm(&db, dbname, MU_STREAM_RDWR,
		       ((__bi_argcnt > 3) ? mode : (prop ? prop->mode : DEFAULT_DB_MODE)),
#line 395
		       prop ? prop->hint : NULL);
		if (!(rc == 0))
#line 396
		(
#line 396
	env_throw_bi(env, mfe_dbfailure, "dbdel", _("mf_dbm_open(%s) failed: %s"),dbname,mu_strerror(rc))
#line 396
)
#line 400
;
	memset(&key, 0, sizeof key);
	key.mu_dptr = keystr;
	key.mu_dsize = strlen(keystr);
	if (((__bi_argcnt > 2) ? null : prop && prop->null))
		key.mu_dsize++;
	rc = mu_dbm_delete(db, &key);
	mu_dbm_destroy(&db);
		if (!(rc == 0 || rc == MU_ERR_NOENT))
#line 408
		(
#line 408
	env_throw_bi(env, mfe_dbfailure, "dbdel", _("failed to delete data `%s' from `%s': %s"),keystr,dbname,mu_strerror(rc))
#line 408
)
#line 413
;
}

#line 415
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 415
	return;
#line 415
}


#define NUMDB 128

struct db_tab {
	int used;
	mu_dbm_file_t db;
	struct mu_dbm_datum key;
};

static void *
alloc_db_tab()
{
	return mu_calloc(NUMDB, sizeof(struct db_tab));
}

static void
close_db_tab(struct db_tab *dbt)
{
	if (dbt->used) {
		mu_dbm_datum_free(&dbt->key);
		mu_dbm_destroy(&dbt->db);
		dbt->used = 0;
	}
}

static void
destroy_db_tab(void *data)
{
	int i;
	struct db_tab *db = data;
	for (i = 0; i < NUMDB; i++) 
		close_db_tab(db + i);
	free(db);
}


#line 452

#line 452
static int DBTAB_id;
#line 452 "db.bi"
;

static int
new_db_tab(struct db_tab *dbt)
{
	int i;
	for (i = 0; i < NUMDB; i++)
		if (!dbt[i].used) {
			dbt[i].used = 1;
			return i;
		}
	return -1;
}
			


void
#line 468
bi_dbfirst(eval_environ_t env)
#line 468

#line 468

#line 468 "db.bi"
{
#line 468
	
#line 468

#line 468
        char *  dbname;
#line 468
        
#line 468

#line 468
        get_string_arg(env, 0, &dbname);
#line 468
        
#line 468
        adjust_stack(env, 1);
#line 468

#line 468

#line 468
	if (builtin_module_trace(BUILTIN_IDX_db))
#line 468
		prog_trace(env, "dbfirst %s",dbname);;
#line 468

{
	int rc;
	int n;
	struct db_tab *dbt = env_get_builtin_priv(env,DBTAB_id);
	mu_dbm_file_t db;
	struct mu_dbm_datum key;
	const struct db_prop *prop = db_prop_lookup(dbname);

	rc = _open_dbm(&db, dbname, MU_STREAM_READ,
		       prop ? prop->mode : DEFAULT_DB_MODE,
		       prop ? prop->hint : NULL);
		if (!(rc == 0))
#line 480
		(
#line 480
	env_throw_bi(env, mfe_dbfailure, "dbfirst", _("mf_dbm_open(%s) failed: %s"),dbname,mu_strerror(rc))
#line 480
)
#line 484
;
	memset(&key, 0, sizeof key);
	rc = mu_dbm_firstkey(db, &key);
	if (rc) {
		if (rc == MU_ERR_NOENT) {
			mu_dbm_destroy(&db);
			
#line 490
do {
#line 490
  push(env, (STKVAL)(mft_number)(0));
#line 490
  goto endlab;
#line 490
} while (0);
		} else if (rc) {
			mu_dbm_destroy(&db);
			(
#line 493
	env_throw_bi(env, mfe_dbfailure, "dbfirst", _("mf_dbm_firstkey failed: %s"),mu_strerror(rc))
#line 493
);
#line 496
		}
	}
	n = new_db_tab(dbt);
		if (!(n >= 0))
#line 499
		(
#line 499
	env_throw_bi(env, mfe_failure, "dbfirst", _("no more database entries available"))
#line 499
)
#line 501
;
	dbt += n;
	dbt->db = db;
	dbt->key = key;
	
#line 505
do {
#line 505
  push(env, (STKVAL)(mft_number)(n));
#line 505
  goto endlab;
#line 505
} while (0);
}
endlab:
#line 507
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 507
	return;
#line 507
}

void
#line 509
bi_dbnext(eval_environ_t env)
#line 509

#line 509

#line 509 "db.bi"
{
#line 509
	
#line 509

#line 509
        long  dn;
#line 509
        
#line 509

#line 509
        get_numeric_arg(env, 0, &dn);
#line 509
        
#line 509
        adjust_stack(env, 1);
#line 509

#line 509

#line 509
	if (builtin_module_trace(BUILTIN_IDX_db))
#line 509
		prog_trace(env, "dbnext %lu",dn);;
#line 509

{
	struct db_tab *dbt = env_get_builtin_priv(env,DBTAB_id) + dn;
	struct mu_dbm_datum nextkey;
	int rc;
	
		if (!(dn >= 0 && dn < NUMDB && dbt->used))
#line 515
		(
#line 515
	env_throw_bi(env, mfe_range, "dbnext", _("invalid database descriptor"))
#line 515
)
#line 517
;

	mu_dbm_datum_free(&dbt->key);
	memset(&nextkey, 0, sizeof nextkey);
	rc = mu_dbm_nextkey(dbt->db, &nextkey);
	if (rc) {
		if (rc == MU_ERR_FAILURE)
			mu_error(_("mu_dbm_nextkey: %s"),
				 mu_dbm_strerror(dbt->db));
		close_db_tab(dbt);
		
#line 527
do {
#line 527
  push(env, (STKVAL)(mft_number)(0));
#line 527
  goto endlab;
#line 527
} while (0);
	}
	dbt->key = nextkey;
	
#line 530
do {
#line 530
  push(env, (STKVAL)(mft_number)(1));
#line 530
  goto endlab;
#line 530
} while (0);
}
endlab:
#line 532
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 532
	return;
#line 532
}

void
#line 534
bi_dbkey(eval_environ_t env)
#line 534

#line 534

#line 534 "db.bi"
{
#line 534
	
#line 534

#line 534
        long  dn;
#line 534
        
#line 534

#line 534
        get_numeric_arg(env, 0, &dn);
#line 534
        
#line 534
        adjust_stack(env, 1);
#line 534

#line 534

#line 534
	if (builtin_module_trace(BUILTIN_IDX_db))
#line 534
		prog_trace(env, "dbkey %lu",dn);;
#line 534

{
	struct db_tab *dbt = env_get_builtin_priv(env,DBTAB_id) + dn;
	
		if (!(dn >= 0 && dn < NUMDB && dbt->used))
#line 538
		(
#line 538
	env_throw_bi(env, mfe_range, "dbkey", _("invalid database descriptor"))
#line 538
)
#line 540
;
	
	heap_obstack_begin(env);
	heap_obstack_grow(env, dbt->key.mu_dptr, dbt->key.mu_dsize);
	do { char __c = 0; heap_obstack_grow(env, &__c, 1); } while(0);
	
#line 545
do {
#line 545
  push(env, (STKVAL) (heap_obstack_finish(env)));
#line 545
  goto endlab;
#line 545
} while (0);
}
endlab:
#line 547
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 547
	return;
#line 547
}

void
#line 549
bi_dbvalue(eval_environ_t env)
#line 549

#line 549

#line 549 "db.bi"
{
#line 549
	
#line 549

#line 549
        long  dn;
#line 549
        
#line 549

#line 549
        get_numeric_arg(env, 0, &dn);
#line 549
        
#line 549
        adjust_stack(env, 1);
#line 549

#line 549

#line 549
	if (builtin_module_trace(BUILTIN_IDX_db))
#line 549
		prog_trace(env, "dbvalue %lu",dn);;
#line 549

{
	int rc;
	struct db_tab *dbt = env_get_builtin_priv(env,DBTAB_id) + dn;
	struct mu_dbm_datum contents;
	
		if (!(dn >= 0 && dn < NUMDB && dbt->used))
#line 555
		(
#line 555
	env_throw_bi(env, mfe_range, "dbvalue", _("invalid database descriptor"))
#line 555
)
#line 557
;

	memset(&contents, 0, sizeof contents);
	rc = mu_dbm_fetch(dbt->db, &dbt->key, &contents);
		if (!(rc == 0))
#line 561
		(
#line 561
	env_throw_bi(env, mfe_dbfailure, "dbvalue", _("cannot fetch key: %s"),rc == MU_ERR_FAILURE ?
		   mu_dbm_strerror(dbt->db) : mu_strerror (rc))
#line 561
)
#line 565
;

	heap_obstack_begin(env);
	heap_obstack_grow(env, contents.mu_dptr, contents.mu_dsize);
	do { char __c = 0; heap_obstack_grow(env, &__c, 1); } while(0);

	mu_dbm_datum_free(&contents);
	
#line 572
do {
#line 572
  push(env, (STKVAL) (heap_obstack_finish(env)));
#line 572
  goto endlab;
#line 572
} while (0);
}
endlab:
#line 574
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 574
	return;
#line 574
}

void
#line 576
bi_dbbreak(eval_environ_t env)
#line 576

#line 576

#line 576 "db.bi"
{
#line 576
	
#line 576

#line 576
        long  dn;
#line 576
        
#line 576

#line 576
        get_numeric_arg(env, 0, &dn);
#line 576
        
#line 576
        adjust_stack(env, 1);
#line 576

#line 576

#line 576
	if (builtin_module_trace(BUILTIN_IDX_db))
#line 576
		prog_trace(env, "dbbreak %lu",dn);;
#line 576

{
	struct db_tab *dbt = env_get_builtin_priv(env,DBTAB_id) + dn;
		if (!(dn >= 0 && dn < NUMDB && dbt->used))
#line 579
		(
#line 579
	env_throw_bi(env, mfe_range, "dbbreak", _("invalid database descriptor"))
#line 579
)
#line 581
;
	close_db_tab(dbt);
}

#line 584
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 584
	return;
#line 584
}

enum greylist_semantics
{
	greylist_traditional,
	greylist_ct
};

static enum greylist_semantics greylist_semantics = greylist_traditional;

/* #pragma greylist {traditional|gray|ct|con-tassios}*/
#line 595 "db.bi"

#line 595
static void _pragma_greylist (int argc, char **argv, const char *text)
#line 595

{
	if (strcmp(argv[1], "traditional") == 0
	    || strcmp(argv[1], "gray") == 0)
		greylist_semantics = greylist_traditional;
	else if (strcmp(argv[1], "ct") == 0
		 || strcmp(argv[1], "con-tassios") == 0)
		greylist_semantics = greylist_ct;
	else
		/* TRANSLATORS: Do not translate keywords:
		   traditional, gray, ct, con-tassios */
		parse_error(_("unknown semantics; allowed values are: "
			      "traditional (or gray) and "
			      "ct (or con-tassios)"));
}

/* FIXME: Duplicated in lib/cache.c */
static char *
format_timestr(time_t timestamp, char *timebuf, size_t bufsize)
{
	struct tm tm;
	gmtime_r(&timestamp, &tm);
	strftime(timebuf, bufsize, "%c", &tm);
	return timebuf;
}

static size_t greylist_seconds_left_loc
#line 621 "db.bi"
;

/* The traditional (aka gray's) greylist implementation: the greylist
   database keeps the time the greylisting was activated.
 */
static int
do_greylist_traditional(eval_environ_t env, char *email, long interval)
{
	int rc;
	mu_dbm_file_t db;
	struct mu_dbm_datum key;
	struct mu_dbm_datum contents;
	int readonly = 0;
	time_t now;

	rc = _open_dbm(&db, greylist_format->dbname, MU_STREAM_RDWR, 0600,
		       NULL);
	if (rc) {
		rc = _open_dbm(&db, greylist_format->dbname,
			       MU_STREAM_READ, 0600, NULL);
		readonly = 1;
	}
        	if (!(rc == 0))
#line 643
		(
#line 643
	env_throw_bi(env, mfe_dbfailure, NULL, _("mf_dbm_open(%s) failed: %s"),greylist_format->dbname,mu_strerror(rc))
#line 643
)
;
	
	memset(&key, 0, sizeof key);
	memset(&contents, 0, sizeof contents);
	key.mu_dptr = email;
	key.mu_dsize = strlen(email)+1;

	time(&now);
	rc = mu_dbm_fetch(db, &key, &contents);
	if (rc == 0) {
		time_t timestamp, diff;

			if (!(contents.mu_dsize == sizeof timestamp))
#line 656
		(
#line 656
	env_throw_bi(env, mfe_dbfailure, NULL, _("greylist database %s has wrong data size"),greylist_format->dbname)
#line 656
)
#line 659
;
		
		timestamp = *(time_t*) contents.mu_dptr;
		diff = now - timestamp;

		if (mu_debug_level_p(debug_handle, MU_DEBUG_TRACE5)) {
			char timebuf[32];
			mu_debug_log("%s entered greylist database on %s, "
				     "%ld seconds ago",
				     email,
				     format_timestr(timestamp, timebuf,
						    sizeof timebuf),
				     (long) diff);
		}
		
		if (diff < interval) {
			diff = interval - diff;
			
			mf_c_val(*env_data_ref(env, greylist_seconds_left_loc),ulong) = (diff);//FIXME
			
			
#line 679

#line 679
mu_debug(debug_handle, MU_DEBUG_TRACE6,("%s still greylisted (for %lu sec.)",
			          email,
			          (unsigned long) diff));
#line 683
			rc = 1;
		} else if (diff > greylist_format->expire_interval) {
			
#line 685

#line 685
mu_debug(debug_handle, MU_DEBUG_TRACE6,("greylist record for %s expired", email));
#line 687
			mf_c_val(*env_data_ref(env, greylist_seconds_left_loc),long) = (interval);
			if (!readonly) {
				memcpy(contents.mu_dptr, &now, sizeof now);
				rc = mu_dbm_store(db, &key, &contents, 1);
				if (rc)
					mu_error(_("cannot insert datum `%-.*s' in "
						   "greylist database %s: %s"),
			                         (int)key.mu_dsize,
						 (char*)key.mu_dptr,
						 greylist_format->dbname,
						 rc == MU_ERR_FAILURE ?
						  mu_dbm_strerror(db) :
						  mu_strerror(rc));
			} else
				
#line 701

#line 701
mu_debug(debug_handle, MU_DEBUG_TRACE6,("database opened in readonly mode: "
			  	          "not updating"));
#line 704
			rc = 1;
		} else {
			
#line 706

#line 706
mu_debug(debug_handle, MU_DEBUG_TRACE6,("%s finished greylisting period", email));
#line 708
			rc = 0;
		}
		mu_dbm_datum_free(&contents);
	} else if (!readonly) {
		
#line 712

#line 712
mu_debug(debug_handle, MU_DEBUG_TRACE6,("greylisting %s", email));
		mf_c_val(*env_data_ref(env, greylist_seconds_left_loc),long) = (interval);
		contents.mu_dptr = (void*)&now;
		contents.mu_dsize = sizeof now;
		rc = mu_dbm_store(db, &key, &contents, 1);
		if (rc)
			mu_error(_("Cannot insert datum `%-.*s' in greylist "
				   "database %s: %s"),
		                 (int)key.mu_dsize, (char*)key.mu_dptr,
				 greylist_format->dbname,
				 rc == MU_ERR_FAILURE ?
				  mu_dbm_strerror(db) : mu_strerror(rc));
		rc = 1;
	} else
		rc = 0;

	mu_dbm_destroy(&db);

	return rc;
}

/* Implementation of the is_greylisted predicate has no sense for 
   traditional greylist databases, because greylisting interval is
   not known beforehand.
   FIXME: keep the reference below up to date.
 */
static int
is_greylisted_traditional(eval_environ_t env, char *email)
{
	(
#line 741
	env_throw_bi(env, mfe_failure, NULL, _("is_greylisted is not implemented for traditional greylist databases; "
		   "see documentation, chapter %s %s for more info"),"5.31","Greylisting functions")
#line 741
);
#line 745
	return 0;
}

/* New greylist implementation (by Con Tassios): the database keeps
   the time the greylisting period is set to expire (`interval' seconds
   from now)
*/
static int
do_greylist_ct(eval_environ_t env, char *email, long interval)
{
	int rc;
	mu_dbm_file_t db;
	struct mu_dbm_datum key;
	struct mu_dbm_datum contents;
	int readonly = 0;
	time_t now;

	rc = _open_dbm(&db, greylist_format->dbname, MU_STREAM_RDWR, 0600,
		       NULL);
	if (rc) {
		rc = _open_dbm(&db, greylist_format->dbname,
			       MU_STREAM_READ, 0600, NULL);
		readonly = 1;
	}
        	if (!(rc == 0))
#line 769
		(
#line 769
	env_throw_bi(env, mfe_dbfailure, NULL, _("mf_dbm_open(%s) failed: %s"),greylist_format->dbname,mu_strerror(rc))
#line 769
)
;
	
	memset(&key, 0, sizeof key);
	memset(&contents, 0, sizeof contents);
	key.mu_dptr = email;
	key.mu_dsize = strlen(email) + 1;

	time(&now);
	rc = mu_dbm_fetch(db, &key, &contents);
	if (rc == 0) {
		time_t timestamp;

			if (!(contents.mu_dsize == sizeof timestamp))
#line 782
		(
#line 782
	env_throw_bi(env, mfe_dbfailure, NULL, _("greylist database %s has wrong data size"),greylist_format->dbname)
#line 782
)
#line 785
;
		
		timestamp = *(time_t*) contents.mu_dptr;

		if (now < timestamp) {
			time_t diff = timestamp - now;
			mf_c_val(*env_data_ref(env, greylist_seconds_left_loc),long) = (diff);
			
			
#line 793

#line 793
mu_debug(debug_handle, MU_DEBUG_TRACE6,("%s still greylisted (for %lu sec.)",
			          email,
                                  (unsigned long) diff));
#line 797
			rc = 1;
		} else if (now - timestamp >
			   greylist_format->expire_interval) {
			
#line 800

#line 800
mu_debug(debug_handle, MU_DEBUG_TRACE6,("greylist record for %s expired", email));
#line 802
			mf_c_val(*env_data_ref(env, greylist_seconds_left_loc),long) = (interval);
			if (!readonly) {
				now += interval;
				memcpy(contents.mu_dptr, &now, sizeof now);
				rc = mu_dbm_store(db, &key, &contents, 1);
				if (rc)
					mu_error(_("Cannot insert datum "
						   "`%-.*s' in greylist "
						   "database %s: %s"),
			                         (int)key.mu_dsize,
						 (char*)key.mu_dptr,
						 greylist_format->dbname,
						 rc == MU_ERR_FAILURE ?
						   mu_dbm_strerror(db) :
						   mu_strerror(rc));
			} else
				
#line 818

#line 818
mu_debug(debug_handle, MU_DEBUG_TRACE6,("database opened in read-only mode: "
				          "not updating"));
#line 821
			rc = 1;
		} else {
			
#line 823

#line 823
mu_debug(debug_handle, MU_DEBUG_TRACE6,("%s finished greylisting period", email));
#line 825
			rc = 0;
		}
		mu_dbm_datum_free(&contents);
	} else if (!readonly) {
		
#line 829

#line 829
mu_debug(debug_handle, MU_DEBUG_TRACE6,("greylisting %s", email));
		mf_c_val(*env_data_ref(env, greylist_seconds_left_loc),long) = (interval);
		now += interval;
		contents.mu_dptr = (void*)&now;
		contents.mu_dsize = sizeof now;
		rc = mu_dbm_store(db, &key, &contents, 1);
		if (rc)
			mu_error(_("Cannot insert datum `%-.*s' in greylist "
				   "database %s: %s"),
		                 (int)key.mu_dsize, (char*)key.mu_dptr,
				 greylist_format->dbname,
				 rc == MU_ERR_FAILURE ?
				  mu_dbm_strerror(db) : mu_strerror(rc));
		rc = 1;
	} else
		rc = 0;
	
	mu_dbm_destroy(&db);
	
	return rc;
}

/* The `is_greylisted' predicate for new databases */
static int
is_greylisted_ct(eval_environ_t env, char *email)
{
	int rc;
	mu_dbm_file_t db;
	struct mu_dbm_datum key;
	struct mu_dbm_datum contents;
	time_t now;

	rc = _open_dbm(&db, greylist_format->dbname, MU_STREAM_RDWR, 0600,
		       NULL);
	if (rc)
		rc = _open_dbm(&db, greylist_format->dbname,
			       MU_STREAM_READ, 0600, NULL);
		if (!(rc == 0))
#line 866
		(
#line 866
	env_throw_bi(env, mfe_dbfailure, NULL, _("mf_dbm_open(%s) failed: %s"),greylist_format->dbname,mu_strerror(rc))
#line 866
)
;

	memset(&key, 0, sizeof key);
	memset(&contents, 0, sizeof contents);
	key.mu_dptr = email;
	key.mu_dsize = strlen(email) + 1;

	time(&now);
	rc = mu_dbm_fetch(db, &key, &contents);
	if (rc == 0) {
		time_t timestamp;

			if (!(contents.mu_dsize == sizeof timestamp))
#line 879
		(
#line 879
	env_throw_bi(env, mfe_dbfailure, NULL, _("greylist database %s has wrong data size"),greylist_format->dbname)
#line 879
)
#line 882
;
                
		timestamp = *(time_t*) contents.mu_dptr;
                
		rc = timestamp > now;
                if (rc)
			mf_c_val(*env_data_ref(env, greylist_seconds_left_loc),long) = (timestamp - now);

		mu_dbm_datum_free(&contents);
	} else  
		rc = 0;

	mu_dbm_destroy(&db);

	return rc;
}

struct greylist_class {
	int (*gl_fun)(eval_environ_t, char *, long);
	int (*gl_pred)(eval_environ_t, char *);
};

struct greylist_class greylist_class[] = {
	{ do_greylist_traditional, is_greylisted_traditional },
	{ do_greylist_ct, is_greylisted_ct }
};

/* greylist(key, interval)

   Returns true if the key is greylisted, false if it's OK to
   deliver mail.
 */
void
#line 914
bi_greylist(eval_environ_t env)
#line 914

#line 914

#line 914 "db.bi"
{
#line 914
	
#line 914

#line 914
        char *  email;
#line 914
        long  interval;
#line 914
        
#line 914

#line 914
        get_string_arg(env, 0, &email);
#line 914
        get_numeric_arg(env, 1, &interval);
#line 914
        
#line 914
        adjust_stack(env, 2);
#line 914

#line 914

#line 914
	if (builtin_module_trace(BUILTIN_IDX_db))
#line 914
		prog_trace(env, "greylist %s %lu",email, interval);;
#line 914

{
	
#line 916
do {
#line 916
  push(env, (STKVAL)(mft_number)(greylist_class[greylist_semantics].gl_fun(env, email,
#line 916
							    interval)));
#line 916
  goto endlab;
#line 916
} while (0);
#line 918
}
endlab:
#line 919
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 919
	return;
#line 919
}

/* is_greylisted(key)
   
   Returns true if the key is greylisted, otherwise false
   
*/
void
#line 926
bi_is_greylisted(eval_environ_t env)
#line 926

#line 926

#line 926 "db.bi"
{
#line 926
	
#line 926

#line 926
        char *  email;
#line 926
        
#line 926

#line 926
        get_string_arg(env, 0, &email);
#line 926
        
#line 926
        adjust_stack(env, 1);
#line 926

#line 926

#line 926
	if (builtin_module_trace(BUILTIN_IDX_db))
#line 926
		prog_trace(env, "is_greylisted %s",email);;
#line 926

{
	
#line 928
do {
#line 928
  push(env, (STKVAL)(mft_number)(greylist_class[greylist_semantics].gl_pred(env, email)));
#line 928
  goto endlab;
#line 928
} while (0);
}
endlab:
#line 930
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 930
	return;
#line 930
}

void
#line 932
bi_db_name(eval_environ_t env)
#line 932

#line 932

#line 932 "db.bi"
{
#line 932
	
#line 932

#line 932
        char * MFL_DATASEG fmtid;
#line 932
        
#line 932

#line 932
        get_string_arg(env, 0, &fmtid);
#line 932
        
#line 932
        adjust_stack(env, 1);
#line 932

#line 932

#line 932
	if (builtin_module_trace(BUILTIN_IDX_db))
#line 932
		prog_trace(env, "db_name %s",fmtid);;
#line 932

{
	struct db_format *fmt = db_format_lookup(fmtid);
		if (!(fmt != NULL))
#line 935
		(
#line 935
	env_throw_bi(env, mfe_not_found, "db_name", _("no such db format: %s"),fmtid)
#line 935
)
#line 937
;
	
#line 938
do {
#line 938
  pushs(env, fmt->dbname);
#line 938
  goto endlab;
#line 938
} while (0);
}
endlab:
#line 940
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 940
	return;
#line 940
}

void
#line 942
bi_db_get_active(eval_environ_t env)
#line 942

#line 942

#line 942 "db.bi"
{
#line 942
	
#line 942

#line 942
        char *  fmtid;
#line 942
        
#line 942

#line 942
        get_string_arg(env, 0, &fmtid);
#line 942
        
#line 942
        adjust_stack(env, 1);
#line 942

#line 942

#line 942
	if (builtin_module_trace(BUILTIN_IDX_db))
#line 942
		prog_trace(env, "db_get_active %s",fmtid);;
#line 942

{
	struct db_format *fmt = db_format_lookup(fmtid);
		if (!(fmt != NULL))
#line 945
		(
#line 945
	env_throw_bi(env, mfe_not_found, "db_get_active", _("no such db format: %s"),fmtid)
#line 945
)
#line 947
;
	
#line 948
do {
#line 948
  push(env, (STKVAL)(mft_number)(fmt->enabled));
#line 948
  goto endlab;
#line 948
} while (0);
}
endlab:
#line 950
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 950
	return;
#line 950
}

void
#line 952
bi_db_set_active(eval_environ_t env)
#line 952

#line 952

#line 952 "db.bi"
{
#line 952
	
#line 952

#line 952
        char *  fmtid;
#line 952
        long  active;
#line 952
        
#line 952

#line 952
        get_string_arg(env, 0, &fmtid);
#line 952
        get_numeric_arg(env, 1, &active);
#line 952
        
#line 952
        adjust_stack(env, 2);
#line 952

#line 952

#line 952
	if (builtin_module_trace(BUILTIN_IDX_db))
#line 952
		prog_trace(env, "db_set_active %s %lu",fmtid, active);;
#line 952

{
	struct db_format *fmt = db_format_lookup(fmtid);
		if (!(fmt != NULL))
#line 955
		(
#line 955
	env_throw_bi(env, mfe_not_found, "db_set_active", _("no such db format: %s"),fmtid)
#line 955
)
#line 957
;
	fmt->enabled = active;
}

#line 960
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 960
	return;
#line 960
}

void
#line 962
bi_db_expire_interval(eval_environ_t env)
#line 962

#line 962

#line 962 "db.bi"
{
#line 962
	
#line 962

#line 962
        char *  fmtid;
#line 962
        
#line 962

#line 962
        get_string_arg(env, 0, &fmtid);
#line 962
        
#line 962
        adjust_stack(env, 1);
#line 962

#line 962

#line 962
	if (builtin_module_trace(BUILTIN_IDX_db))
#line 962
		prog_trace(env, "db_expire_interval %s",fmtid);;
#line 962

{
	struct db_format *fmt = db_format_lookup(fmtid);
		if (!(fmt != NULL))
#line 965
		(
#line 965
	env_throw_bi(env, mfe_not_found, "db_expire_interval", _("no such db format: %s"),fmtid)
#line 965
)
#line 967
;
	
#line 968
do {
#line 968
  push(env, (STKVAL)(mft_number)(fmt->expire_interval));
#line 968
  goto endlab;
#line 968
} while (0);
}
endlab:
#line 970
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 970
	return;
#line 970
}

#line 1026 "../../src/builtin/snarf.m4"

#line 1026

#line 1026

#line 1026
void
#line 1026
db_init_builtin(void)
#line 1026
{
#line 1026
		debug_handle = mu_debug_register_category("bi_db");
#line 1026

#line 1026
	#line 82 "db.bi"

#line 82
install_pragma("dbprop", 3, 5, _pragma_dbprop);
#line 272 "db.bi"
va_builtin_install_ex("dbmap", bi_dbmap, 0, dtype_number, 3, 1, 0|0, dtype_string, dtype_string, dtype_number);
#line 283 "db.bi"
va_builtin_install_ex("dbget", bi_dbget, 0, dtype_string, 4, 2, 0|0, dtype_string, dtype_string, dtype_string, dtype_number);
#line 295 "db.bi"
va_builtin_install_ex("dbput", bi_dbput, 0, dtype_unspecified, 5, 2, 0|0, dtype_string, dtype_string, dtype_string, dtype_number, dtype_number);
#line 338 "db.bi"
va_builtin_install_ex("dbinsert", bi_dbinsert, 0, dtype_unspecified, 6, 3, 0|0, dtype_string, dtype_string, dtype_string, dtype_number, dtype_number, dtype_number);
#line 384 "db.bi"
va_builtin_install_ex("dbdel", bi_dbdel, 0, dtype_unspecified, 4, 2, 0|0, dtype_string, dtype_string, dtype_number, dtype_number);
#line 452 "db.bi"
DBTAB_id = builtin_priv_register(alloc_db_tab, destroy_db_tab,
#line 452
NULL);
#line 468 "db.bi"
va_builtin_install_ex("dbfirst", bi_dbfirst, 0, dtype_number, 1, 0, 0|0, dtype_string);
#line 509 "db.bi"
va_builtin_install_ex("dbnext", bi_dbnext, 0, dtype_number, 1, 0, 0|0, dtype_number);
#line 534 "db.bi"
va_builtin_install_ex("dbkey", bi_dbkey, 0, dtype_string, 1, 0, 0|0, dtype_number);
#line 549 "db.bi"
va_builtin_install_ex("dbvalue", bi_dbvalue, 0, dtype_string, 1, 0, 0|0, dtype_number);
#line 576 "db.bi"
va_builtin_install_ex("dbbreak", bi_dbbreak, 0, dtype_unspecified, 1, 0, 0|0, dtype_number);
#line 595 "db.bi"

#line 595
install_pragma("greylist", 2, 2, _pragma_greylist);
#line 621 "db.bi"
	builtin_variable_install("greylist_seconds_left", dtype_number, SYM_VOLATILE, &greylist_seconds_left_loc);
#line 914 "db.bi"
va_builtin_install_ex("greylist", bi_greylist, 0, dtype_number, 2, 0, 0|0, dtype_string, dtype_number);
#line 926 "db.bi"
va_builtin_install_ex("is_greylisted", bi_is_greylisted, 0, dtype_number, 1, 0, 0|0, dtype_string);
#line 932 "db.bi"
va_builtin_install_ex("db_name", bi_db_name, 0, dtype_string, 1, 0, 0|0, dtype_string);
#line 942 "db.bi"
va_builtin_install_ex("db_get_active", bi_db_get_active, 0, dtype_number, 1, 0, 0|0, dtype_string);
#line 952 "db.bi"
va_builtin_install_ex("db_set_active", bi_db_set_active, 0, dtype_unspecified, 2, 0, 0|0, dtype_string, dtype_number);
#line 962 "db.bi"
va_builtin_install_ex("db_expire_interval", bi_db_expire_interval, 0, dtype_number, 1, 0, 0|0, dtype_string);

#line 1026 "../../src/builtin/snarf.m4"
	
#line 1026
}
#line 1026 "../../src/builtin/snarf.m4"

