/* 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 <fcntl.h>
#include <sys/file.h>
#include <db_185.h>

void usage(void)
{
    printf("usage: db dbfile [action] [args]\n");
    printf("where [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("and [args] are key-value pairs that are to be inserted,\n");
    printf("or keys that are to be deleted or listed.\n");
    printf("Examples:\n");
    printf("db /etc/mail/access.db lst\n");
    printf("    will list all records from /etc/mail/access.db\n");
    printf("db /etc/mail/access.db del 192.168.0.1\n");
    printf("    will delete the record with key 192.168.0.1\n");
    printf("db /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 *db;
    DBT key, data;
    DBT *pk = &key, *pd = &data;
    enum { ins, del, lst } action;
    int arg, rc, fd;
    char k[128], d[128];

    if (argc < 3)
	usage();

    db = dbopen(argv[1], O_RDWR, 0, DB_HASH, NULL);
    if (db == NULL) {
	perror(argv[1]);
	usage();
    }

    fd = (db->fd)(db);
    if (flock(fd, LOCK_EX) == -1) {
	perror(argv[1]);
	(db->close)(db);
	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) {
		key.data = argv[arg];
		key.size = strlen(key.data);
		rc = (db->get)(db, pk, pd, 0);
		switch (rc) {
		case 0:
		    strncpy(d, data.data, data.size);
		    d[data.size] = 0;
		    printf("%s\t%s\n", argv[arg], d);
		    break;
		case 1:
		    fprintf(stderr, "key %s not found!\n", argv[arg]);
		    break;
		default:
		    perror(argv[arg]);
		    break;
		}
		arg++;
	    }
	} else {
	    while ((db->seq)(db, pk, pd, R_NEXT) == 0) {
		strncpy(k, key.data, key.size);
		k[key.size] = 0;
		strncpy(d, data.data, data.size);
		d[data.size] = 0;
		printf("%s\t%s\n", k, d);
	    }
	}
	break;
    case ins:
	while (arg < argc) {
	    key.data = argv[arg];
	    key.size = strlen(key.data);
	    arg++;
	    data.data = argv[arg];
	    data.size = strlen(data.data);
	    rc = (db->put)(db, pk, pd, 0);
	    switch (rc) {
	    case 0:
		printf("%s\t%s inserted\n", argv[arg - 1], argv[arg]);
		break;
	    case 1:
		printf("key %s already exists\n", argv[arg - 1]);
		break;
	    default:
		perror(key.data);
		break;
	    }
	    arg++;
	}
	break;
    case del:
	while (arg < argc) {
	    key.data = argv[arg];
	    key.size = strlen(key.data);
	    rc = (db->del)(db, pk, 0);
	    switch (rc) {
	    case 0:
		printf("%s deleted\n", argv[arg]);
		break;
	    case 1:
		printf("key %s does not exist\n", argv[arg]);
		break;
	    default:
		perror(key.data);
		break;
	    }
	    arg++;
	}
	break;
    }

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


