/* db.c: manage sendmail database files (/etc/mail/access.db for example)

   Written as a quick hack by Georg Horn <horn@koblenz-net.de>, so don't use
   it for educational purposes. Although, it may be used to demonstrate
   what "spaghetti code" is... ;-)

   It is used to remove IP-Adresses from sendmails access.db database, that
   have been inserted therein by a modified POP3 daemon to allow SMTP after
   POP, but it can also be used to list the contents of the database (or other
   databases) or to insert or delete records. */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/file.h>
#include <db.h>

void usage(void)
{
    printf("\nusage: db dbfile [action] [args]\n");
    printf("\nwhere [action] is one of:\n");
    printf("    ins - insert [args] into dbfile\n");
    printf("    del - delete [args] from dbfile\n");
    printf("    lst - list contents of dbfile\n");
    printf("\nand [args] are key-value pairs that are to be inserted,\n");
    printf("or keys that are to be deleted or listed.\n");
    printf("\nExamples:\n");
    printf("\ndb /etc/mail/access.db lst\n");
    printf("    will list all records from /etc/mail/access.db\n");
    printf("\ndb /etc/mail/access.db del 192.168.0.1\n");
    printf("    will delete the record with key 192.168.0.1\n");
    printf("\ndb /etc/mail/access.db add 192.168.0.1 RELAY\n");
    printf("    will add a record with key 192.168.0.1 and value RELAY\n");
    _exit(1);
}


int main(int argc, char *argv[])
{
    DB *dbp;
    DBC *dbcp;
    DBT key, data;
    enum { ins, del, lst } action;
    int arg, ret, fd;

    if (argc < 3)
	usage();

    if ((ret = db_create(&dbp, NULL, 0)) != 0) {
	fprintf(stderr, "db_create: %s\n", db_strerror(ret));
	usage();
    }
    if ((ret = dbp->open(dbp, argv[1], NULL, DB_HASH, 0, 0)) != 0) {
	dbp->err(dbp, ret, "%s", argv[1]);
	usage();
    }

    dbp->fd(dbp, &fd);
    if (flock(fd, LOCK_EX) == -1) {
	perror(argv[1]);
	dbp->close(dbp, 0);
	usage();
    }

    if (!strcmp(argv[2], "ins"))
	action = ins;
    else if (!strcmp(argv[2], "del"))
	action = del;
    else if (!strcmp(argv[2], "lst"))
	action = lst;
    else
	usage();

    arg = 3;
    switch (action) {
    case lst:
	if (argc >= 4) {
	    while (arg < argc) {
		memset(&key, 0, sizeof(key));
		memset(&data, 0, sizeof(data));
		key.data = argv[arg];
		key.size = strlen(key.data);
		arg++;
		if ((ret = dbp->get(dbp, NULL, &key, &data, 0)) == 0) {
		    printf("%.*s: %.*s\n", key.size, (char *)key.data,
			data.size, (char *)data.data);
		} else {
		    dbp->err(dbp, ret, "DB->get");
		}
	    }
	} else {
	    if ((ret = dbp->cursor(dbp, NULL, &dbcp, 0)) != 0) {
		dbp->err(dbp, ret, "DB->cursor");
	    }
	    memset(&key, 0, sizeof(key));
	    memset(&data, 0, sizeof(data));
	    while ((ret = dbcp->c_get(dbcp, &key, &data, DB_NEXT)) == 0) {
		printf("%.*s: %.*s\n", key.size, (char *)key.data,
		    data.size, (char *)data.data);
	    }
	    if (ret != DB_NOTFOUND) {
		dbp->err(dbp, ret, "DBcursor->c_get");
	    }
	    if ((ret = dbcp->c_close(dbcp)) != 0) {
		dbp->err(dbp, ret, "DBcursor->close");
	    }
	}
	break;
    case ins:
	while (arg < argc) {
	    memset(&key, 0, sizeof(key));
	    memset(&data, 0, sizeof(data));
	    key.data = argv[arg];
	    key.size = strlen(argv[arg]);
	    arg++;
	    data.data = argv[arg];
	    data.size = strlen(argv[arg]);
	    arg++;
	    if ((ret = dbp->put(dbp, NULL, &key, &data, 0)) == 0) {
		printf("db: %s: key stored.\n", (char *) key.data);
	    } else {
		dbp->err(dbp, ret, "DB->put");
	    }
	}
	break;
    case del:
	while (arg < argc) {
	    memset(&key, 0, sizeof(key));
	    memset(&data, 0, sizeof(data));
	    key.data = argv[arg];
	    key.size = strlen(key.data);
	    arg++;
	    if ((ret = dbp->del(dbp, NULL, &key, 0)) == 0) {
		printf("db: %s: key was deleted.\n", (char *) key.data);
	    } else {
		dbp->err(dbp, ret, "DB->del: %s", key.data);
	    }
	}
	break;
    }

    flock(fd, LOCK_UN);
    dbp->close(dbp, 0);
    return 0;
}


