/*
**    spasm - The spasm anti-spam milter
**    Copyright (C) 2002 Sean Hanson <aradia@theasylum.org>
**
**    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 2 of the License, 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, write to the Free Software
**    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <ctype.h>
#include <db2/db.h>
#include <pwd.h>
#include <sys/types.h>

#include "spasm.h"

void showhelp(char *command) {
    printf("spasmbl v%s\n", SPASM_VERSION);
    printf("syntax: %s [-h] [-c|-s] [-o options] [-u [username@]domain.com]\n", command);
    printf("where:\n");
    printf("-h				Ummmm...\n");
    printf("-c				Clear blacklist options\n");
    printf("-s				Set blacklist options\n");
    printf("-o options                  Blacklist options\n");
    printf("                            (bitwise-OR of desired filters, in decimal)\n");
    printf("[username@]domain.com	Username and domain(superuser only)\n");
    printf("See man page for more details.\n");
}

int main(int argc, char **argv)
{
    char data[256];
    int datanum = 0;
    DB *db;
    DB_ENV dbenv;
    DB_INFO dbinfo;
    DBT dbk, dbv;
    int dbfd;
    struct flock lfd;
    unsigned long int userbl;
    unsigned long int i, j, k;
    int c;
    const char *args = "cho:su:";
    int SET = 0;
    int CLEAR = 0;
    char *username = NULL;
    char *options = NULL;
    uid_t uid;
    struct passwd *pw;

    /* Who am I? */
    uid = getuid();
    pw = getpwuid(uid);
    if(pw == NULL) {
	printf("Error looking up userid: %m\n");
	exit(1);
    }

    /* Get options */
    while((c = getopt(argc, argv, args)) != -1) {
	switch(c) {
	    case 'c':
		CLEAR = 1;
		break;
	    case 'o':
		options = strdup(optarg);
		if(options == NULL) {
		    printf("Error allocating memory for options: %m\n");
		    if(username != NULL) free(username);
		    exit(1);
		}
		break;
	    case 's':
		SET = 1;
		break;
	    case 'u':
		if(uid == 0) {
		    username = strdup(optarg);
		    if(username == NULL) {
		        printf("Error allocating memory for username: %m\n");
		        if(options != NULL) free(options);
		        exit(1);
		    }
		} else {
		    printf("You don't have permission to specify a username\n");
		    if(options != NULL) free(options);
		    exit(1);
		}
		break;
	    case 'h':
	    case '?':
	    default:
		showhelp(argv[0]);
		exit(0);
	}
    }

    if((SET ^ CLEAR) == 0) {
	showhelp(argv[0]);
	if(username != NULL) free(username);
	if(options != NULL) free(options);
	exit(0);
    }

    if(uid != 0) {
	memset(data, '\0', sizeof(data));
	sprintf(data, "%s@%s", pw->pw_name, DEFAULT_DOMAIN);
	username = strdup(data);
	if(username == NULL) {
	    printf("Error allocating memory for username: %m\n");
	    if(options != NULL) free(options);
	    exit(1);
	}
    }

    if(username == NULL) {
	memset(data, '\0', sizeof(data));
	printf("Username (user@domain.com): ");
	scanf("%s", data);
	username = strdup(data);
	if(username == NULL) {
	    printf("Error allocating memory for username: %m\n");
	    if(options != NULL) free(options);
	    exit(1);
	}
    }

    if(chdir(SPASM_DIR) != 0) {
	printf("Error changing directory: %m\n");
	exit(1);
    }

    memset(&dbenv, '\0', sizeof(DB_ENV));
    memset(&dbinfo, '\0', sizeof(DB_INFO));
    memset(&dbk, '\0', sizeof(DBT));
    memset(&dbv, '\0', sizeof(DBT));
    memset(&lfd, '\0', sizeof(lfd));

    dbinfo.db_pagesize = 512;
    lfd.l_type = F_WRLCK;

    if(db_open(USERBL_DB, DB_HASH, DB_CREATE, 0644, &dbenv, &dbinfo, &db) != 0) {
	printf("Error opening database file %s: %m\n", USERBL_DB);
	free(username);
	if(options != NULL) free(options);
	exit(1);
    }
    if(db->fd(db, &dbfd) != 0) {
	printf("Error reading file descriptor for database: %m\n");
	db->close(db, 0);
	free(username);
	if(options != NULL) free(options);
	exit(1);
    }
    if(fcntl(dbfd, F_SETLKW, &lfd) == -1) {
	printf("Error setting write lock on database: %m\n");
	db->close(db, 0);
	free(username);
	if(options != NULL) free(options);
	exit(1);
    }

    dbk.data = username;
    dbk.size = strlen(username);

    if(SET && (options == NULL)) {
	db->get(db, NULL, &dbk, &dbv, 0);
	if(dbv.data != NULL) userbl = strtoul(dbv.data, NULL, 10);
	else userbl = 0;

	do {
	    j = 0;
	    for(i=0;filters[i].num != 0;i++) {
	        if(userbl & filters[i].num) printf("[X] ");
	        else printf("[ ] ");
	        printf("[%lu] %s (%lu)\n", i+1, filters[i].name, filters[i].num);
	    }
	    printf("Toggle filter [enter '0' when finished]: ");
	    scanf("%d", &datanum);
	    j = (unsigned long int) datanum;
	    if(j > 0 && j < sizeof(filters)/sizeof(*filters)) {
	        for(k=1;j>1;j--) k *= 2;
		userbl ^= k;
	    }
	} while(datanum != 0);
	memset(data, '\0', sizeof(data));
	sprintf(data, "%lu", userbl);
	options = data;
    }

    if(CLEAR) {
	db->del(db, NULL, &dbk, 0);
    } else if(SET) {
        dbv.data = options;
        dbv.size = strlen(options);
        db->put(db, NULL, &dbk, &dbv, 0);
    }

    if(db->sync(db, 0) != 0) {
        printf("Error syncing database: %m\n");
    }

    db->close(db, 0);

    exit(0);
}
