/*
 Copyright (C) 1999-2004 IC & S  dbmail@ic-s.nl

 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/* $Id: user.c 1712 2005-03-26 20:23:18Z aaron $
 * This is the dbmail-user program
 * It makes adding users easier */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "user.h"
#include "auth.h"
#include <stdio.h>
#include <termios.h>
#include <stdlib.h>
#include <string.h>
#include "dbmail.h"
#include "list.h"
#include "debug.h"
#include "db.h"
#include <time.h>
#include <stdarg.h>
#include <sys/types.h> /* for pid_t */
#include <unistd.h> /* for getpid() */
#include "dm_getopt.h"
#ifdef HAVE_CRYPT_H
#include <crypt.h>
#endif
#include "dbmd5.h"


char *configFile = DEFAULT_CONFIG_FILE;

#define SHADOWFILE "/etc/shadow"

char *getToken(char **str, const char *delims);
char csalt[] = "........";
char *bgetpwent(const char *filename, const char *name);
char *cget_salt(void);

/* database login data */
extern db_param_t _db_params;

/* valid characters for passwd/username */
const char ValidChars[] =
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    "_.!@#$%^&*()-+=~[]{}<>:;\\/";

/* Loudness and assumptions. */
int yes_to_all = 0;
int no_to_all = 0;
int verbose = 0;
/* Don't be helpful. */
int quiet = 0;
/* Don't print errors. */
int reallyquiet = 0;

#define qprintf(fmt, args...) \
	(quiet ? 0 : printf(fmt, ##args) )

#define qerrorf(fmt, args...) \
	(reallyquiet ? 0 : fprintf(stderr, fmt, ##args) )

#define null_strncpy(dst, src, len) \
	(src ? strncpy(dst, src, len) : 0 )

#define null_crypt(src, dst) \
	(src ? crypt(src, dst) : "" )

struct change_flags {
	unsigned int newuser         : 1;
	unsigned int newmaxmail      : 1;
	unsigned int newclientid     : 1;
	unsigned int newpasswd       : 1;
	unsigned int newpasswdfile   : 1;
	unsigned int newpasswdstdin  : 1;
	unsigned int newpasswdshadow : 1;
};

/* The prodigious use of const ensures that programming
 * mistakes inside of these functions don't cause us to
 * use incorrect values when calling auth_ and db_ internals.
 * */

/* Core operations */
int do_add(const char * const user,
           const char * const password,
           const char * const enctype,
           const u64_t maxmail, const u64_t clientid,
	   struct list * const alias_add,
	   struct list * const alias_del);
int do_delete(const u64_t useridnr, const char * const user);
int do_show(const char * const user);
int do_empty(const u64_t useridnr);
/* Change operations */
int do_username(const u64_t useridnr, const char *newuser);
int do_maxmail(const u64_t useridnr, const u64_t maxmail);
int do_clientid(const u64_t useridnr, const u64_t clientid);
int do_password(const u64_t useridnr,
                const char * const password,
                const char * const enctype);
int do_aliases(const u64_t useridnr,
               struct list * const alias_add,
               struct list * const alias_del);
/* External forwards */
int do_forwards(const char *alias, const u64_t clientid,
                struct list * const fwds_add,
                struct list * const fwds_del);

/* Helper functions */
int is_valid(const char * const str);
u64_t strtomaxmail(const char * const str);
int mkpassword(const char * const user, const char * const passwd,
               const char * const passwdtype, const char * const passwdfile,
               char ** password, char ** enctype);

int do_showhelp(void) {
	printf("*** dbmail-users ***\n");

	printf("Use this program to manage your DBMail users.\n");
	printf("See the man page for more info. Modes of operation:\n\n");
//	add, flush, delete, change, modify, password, alias, forward, list
	printf("     -a user   add a user\n");
	printf("     -d user   delete a user\n");
	printf("     -c user   change details for a user\n");
	printf("     -e user   empty all mailboxes for a user\n");
	printf("     -l uspec  list information for matching users\n");
	printf("     -x alias  create an external forwarding address\n");
//	printf("     -i        enter an interactive user management console\n");

	printf("\nSummary of options for all modes:\n");
	printf("     -w passwd specify user's password on the command line\n");
	printf("     -W [file] read from a file or prompt for a user's password\n");
	printf("     -p pwtype password type may be one of the following:\n"
	       "               plaintext, crypt, md5-hash, md5-digest,\n"
	       "               crypt-raw, md5-hash-raw, md5-digest-raw\n");
	printf("     -P [file] pull encrypted password from the shadow file\n");
	printf("     -u user   new username (only useful for -c, change)\n");
	printf("     -g client assign the user to a client\n");
	printf("     -m max    set the maximum mail quota in <bytes>B,\n"
	       "               <kbytes>K, or <mbytes>M, default in bytes\n"
	       "               specify 0 to remove any mail quota limits\n");
	printf("     -s alia.. adds a list of recipient aliases\n");
	printf("     -S alia.. removes a list of recipient aliases (wildcards supported)\n");
	printf("     -t fwds.. adds a list of deliver-to forwards\n");
	printf("     -T fwds.. removes a list of deliver-to forwards (wildcards supported)\n");

        printf("\nCommon options for all DBMail utilities:\n");
	printf("     -f file   specify an alternative config file\n");
	printf("     -q        quietly skip interactive prompts\n"
	       "               use twice to suppress error messages\n");
/*	printf("     -n        show the intended action but do not perform it, no to all\n");*/
/*	printf("     -y        perform all proposed actions, as though yes to all\n");*/
	printf("     -v        verbose details\n");
	printf("     -V        show the version\n");
	printf("     -h        show this help message\n");

	return 0;
}

int main(int argc, char *argv[])
{
	int opt = 0, opt_prev = 0;
	int show_help = 0;
	int result = 0, mode = 0, mode_toomany = 0;
	char *user = NULL, *newuser = NULL,
	     *userspec = NULL, *alias = NULL;
	char *passwd = NULL, *passwdtype = NULL,
	     *passwdfile = NULL;
	char *password = NULL, *enctype = NULL;
	u64_t useridnr = 0, clientid = 0, maxmail = 0;
	struct list alias_add, alias_del, fwds_add, fwds_del;
	struct change_flags change_flags;
	size_t len = 0;

	list_init(&alias_add);
	list_init(&alias_del);
	list_init(&fwds_add);
	list_init(&fwds_del);

	openlog(PNAME, LOG_PID, LOG_MAIL);
	setvbuf(stdout, 0, _IONBF, 0);

	/* Set all changes to false. */
	memset(&change_flags, 0, sizeof(change_flags));

	/* get options */
	dm_opterr = 0;		/* suppress error message from getopt() */
	while ((opt = dm_getopt(argc, argv,
		"-a:d:c:e:l::x:" /* Major modes */
		"W::w:P::p:u:g:m:t:s:S:T:" /* Minor options */
		"i" "f:qnyvVh" /* Common options */ )) != -1) {
		/* The initial "-" of optstring allows unaccompanied
		 * options and reports them as the optarg to opt 1 (not '1') */
		if (opt == 1)
			opt = opt_prev;
		opt_prev = opt;

		switch (opt) {
		/* Major modes of operation
		 * (exactly one of these is required) */
		case 'a':
		case 'd':
		case 'c':
		case 'e':
			if (mode)
				mode_toomany = 1;
			mode = opt;
			if (dm_optarg && strlen(dm_optarg))
				user = dm_strdup(dm_optarg);
			break;

		case 'x':
			if (mode)
				mode_toomany = 1;
			mode = opt;
			if (dm_optarg && strlen(dm_optarg))
				alias = dm_strdup(dm_optarg);
			break;

		case 'l':
			/* It seems that the optional argument may
			 * be passed as a second instance of this flag. */
			if (mode != 0 && mode != 'l')
				mode_toomany = 1;
			mode = opt;
			if (dm_optarg && strlen(dm_optarg))
				userspec = dm_strdup(dm_optarg);
			break;

		case 'i':
			printf("Interactive console is not supported in this release.\n");
			return 1;


		/* Minor options */
		case 'w':
			change_flags.newpasswd = 1;
			passwd = dm_strdup(dm_optarg);
			break;

		case 'W':
			change_flags.newpasswd = 1;
			if (dm_optarg && strlen(dm_optarg)) {
				passwdfile = dm_strdup(dm_optarg);
				change_flags.newpasswdfile = 1;
			} else {
				change_flags.newpasswdstdin = 1;
			}
			break;

		case 'u':
			change_flags.newuser = 1;
			newuser = dm_strdup(dm_optarg);
			break;

		case 'p':
			if (!passwdtype)
				passwdtype = dm_strdup(dm_optarg);
			// else
				// Complain about only one type allowed.
			break;

		case 'P':
			change_flags.newpasswdshadow = 1;
			if (dm_optarg && strlen(dm_optarg))
				passwdfile = dm_strdup(dm_optarg);
			else
				passwdfile = SHADOWFILE;
			passwdtype = "shadow";
			break;

		case 'g':
			change_flags.newclientid = 1;
			clientid = strtoull(dm_optarg, NULL, 10);
			break;

		case 'm':
			change_flags.newmaxmail = 1;
			maxmail = strtomaxmail(dm_optarg);
			break;

		case 's':
			// Add this item to the user's aliases.
			if (dm_optarg && (len = strlen(dm_optarg)))
				list_nodeadd(&alias_add, dm_optarg, len+1);
			break;

		case 'S':
			// Delete this item from the user's aliases.
			if (dm_optarg && (len = strlen(dm_optarg)))
				list_nodeadd(&alias_del, dm_optarg, len+1);
			break;

		case 't':
			// Add this item to the alias's forwards.
			if (dm_optarg && (len = strlen(dm_optarg)))
				list_nodeadd(&fwds_add, dm_optarg, len+1);
			break;

		case 'T':
			// Delete this item from the alias's forwards.
			if (dm_optarg && (len = strlen(dm_optarg)))
				list_nodeadd(&fwds_del, dm_optarg, len+1);
			break;

		/* Common options */
		case 'f':
			if (dm_optarg && strlen(dm_optarg) > 0)
				configFile = dm_optarg;
			else {
				qerrorf("dbmail-users: -f requires a filename\n\n");
				result = 1;
			}
			break;

		case 'h':
			show_help = 1;
			break;

		case 'n':
			printf("-n option is not supported in this "
			       "release.\n");
			return 1;

		case 'y':
			printf("-y option is not supported in this "
			       "release.\n");
			return 1;

		case 'q':
			/* If we get q twice, be really quiet! */
			if (quiet)
				reallyquiet = 1;
			if (!verbose)
				quiet = 1;
			break;

		case 'v':
			if (!quiet)
				verbose = 1;
			break;

		case 'V':
			/* Show the version and return non-zero. */
			printf("DBMail: dbmail-users\n"
			       "Version: %s\n"
			       "$Revision: 1712 $\n"
			       "Copyright: %s\n", VERSION, COPYRIGHT);
			result = 1;
			break;

		default:
			/* printf("unrecognized option [%c], continuing...\n",optopt); */
			break;
		}

		/* If there's a non-negative return code,
		 * it's time to free memory and bail out. */
		if (result)
			goto freeall;
	}	

	/* If nothing is happening, show the help text. */
	if (!mode || mode_toomany || show_help) {
		do_showhelp();
		result = 1;
		goto freeall;
	}

	/* read the config file */
	ReadConfig("DBMAIL", configFile);
	SetTraceLevel("DBMAIL");
	GetDBParams(&_db_params);

	/* open database connection */
	qprintf("Opening connection to database...\n");
	if (db_connect() != 0) {
		qerrorf
		    ("Failed. Could not connect to database (check log)\n");
		result = -1;
		goto freeall;
	}

	/* open authentication connection */
	qprintf("Opening connection to authentication...\n");
	if (auth_connect() != 0) {
		qerrorf
		    ("Failed. Could not connect to authentication (check log)\n");
		result = -1;
		goto freeall;
	}
	if (db_check_version() != 0) {
		qerrorf
			("Failed. You need to upgrade your database.\n");
		result = -1;
		goto freeall;
	}

	qprintf("Ok. Connected\n");
	//configure_debug(TRACE_ERROR, 1, 0);

	switch (mode) {
	case 'c':
	case 'd':
	case 'e':
		/* Verify the existence of this user */
		if (auth_user_exists(user, &useridnr) == -1) {
			qerrorf("Error: cannot verify existence of user [%s].\n",
			     user);
			result = -1;
			goto freeall;
		}
		if (useridnr == 0) {
			qerrorf("Error: user [%s] does not exist.\n",
				     user);
			result = -1;
			goto freeall;
		}
	}

	/* Only get a password for those modes which require it. */
	switch (mode) {
	case 'a':
	case 'c':
		if (change_flags.newpasswdstdin) {
			char pw[50];
			struct termios oldattr, newattr;

			/* Get the current terminal state, then disable echo. */
			tcgetattr(fileno(stdin), &oldattr);
			newattr = oldattr;
			newattr.c_lflag &= ~ECHO;
			tcsetattr(fileno(stdin), TCSAFLUSH, &newattr);

			/* Prompt for a password and read until \n or EOF. */
			qprintf("Please enter a password (will not echo): ");
			fflush(stdout);
			fgets(pw, 50, stdin);

			/* We don't want the trailing newline. */
			len = strlen(pw);
			if (pw[len-1] == '\n')
			        pw[len-1] = '\0';
			/* fgets guarantees a nul terminated string. */
			passwd = dm_strdup(pw);

			/* Restore the previous terminal state (with echo back on). */
			tcsetattr(fileno(stdin), TCSANOW, &oldattr);
			qprintf("\n");
		}

		/* If no password type was specified, and
		 * the user already exists, get their password type. */
		if (!passwdtype && useridnr)
			passwdtype = auth_getencryption(useridnr);
		/* Convert the password and password type into a 
		 * fully coded format, ready for the database. */
		if (mkpassword(user, passwd, passwdtype, passwdfile,
		               &password, &enctype)) {
			qerrorf("Error: unable to create a password.\n");
			result = -1;
			goto freeall;
		}
	}


	switch (mode) {
	case 'a':
		result = do_add(user, password, enctype, maxmail, clientid,
				&alias_add, &alias_del);
		break;
	case 'd':
		result = do_delete(useridnr, user);
		break;
	case 'c':
		qprintf("Performing changes for user [%s]...\n", user);
		if (change_flags.newuser) {
			result |= do_username(useridnr, newuser);
		}
		if (change_flags.newpasswd) {
			result |= do_password(useridnr, password, enctype);
		}
		if (change_flags.newclientid) {
			result |= do_clientid(useridnr, clientid);
		}
		if (change_flags.newmaxmail) {
			result |= do_maxmail(useridnr, maxmail);
		}
		result |= do_aliases(useridnr, &alias_add, &alias_del);
		break;
	case 'e':
		result = do_empty(useridnr);
		break;
	case 'l':
		result = do_show(userspec);
		break;
	case 'x':
		result = do_forwards(alias, clientid, &fwds_add, &fwds_del);
		break;
	default:
		result = 1;
	}

	/* Here's where we free memory and quit.
	 * Be sure that all of these are NULL safe! */
freeall:

	if (user)
		dm_free(user);
	if (passwd)
		dm_free(passwd);
	if (alias)
		dm_free(alias);
	if (userspec)
		dm_free(userspec);
	if (passwdfile)
		dm_free(passwdfile);
	if (newuser)
		dm_free(newuser);

	/* Free the lists. */
	if (alias_del.start)
		list_freelist(&alias_del.start);
	if (alias_add.start)
		list_freelist(&alias_add.start);
	if (fwds_del.start)
		list_freelist(&alias_del.start);
	if (fwds_add.start)
		list_freelist(&alias_add.start);

	db_disconnect();
	auth_disconnect();
	config_free();

	if (result < 0)
		qerrorf("Command failed.\n");
	return result;
}

int do_add(const char * const user,
           const char * const password, const char * const enctype,
           const u64_t maxmail, const u64_t clientid,
	   struct list * const alias_add,
	   struct list * const alias_del)
{
	u64_t useridnr;
	u64_t mailbox_idnr;
	int add_user_result, result;

	if (!is_valid(user)) {
		qerrorf("Error: invalid characters in username [%s]\n",
		     user);
		return -1;
	}

	qprintf("Adding user %s with password type %s,"
	     "%llu bytes mailbox limit and clientid %llu... ",
	     user, enctype, maxmail, clientid);

	switch (auth_user_exists(user, &useridnr))
	{
	case -1:
		/* Database failure */
		qerrorf("Failed\n\nCheck logs for details\n\n");
		return -1;
	default:
		if (useridnr != 0) {
			qprintf("Failed: user exists [%llu]\n",
				     useridnr);
			return -1;
		} else {
			/* If useridnr is 0, create the user */
			add_user_result = auth_adduser(user, password, enctype,
				clientid, maxmail, &useridnr);
		}
		break;
	}

	qprintf("Ok, user added id [%llu]\n", useridnr);

	/* Add an INBOX for the user. */
	qprintf("Adding INBOX for new user\n");
	switch(db_createmailbox("INBOX", useridnr, &mailbox_idnr)) {
	case -1:
		qprintf("Failed.. User is added but we failed to add "
			     "the mailbox INBOX for this user\n");
		result = -1;
		break;
	case 0:
	default:
		qprintf("Ok. added\n");
		result = 0;
		break;
	} 

	if(do_aliases(useridnr, alias_add, alias_del) < 0)
		result = -1;

	return result;
}

/* Change of username */
int do_username(const u64_t useridnr, const char * const newuser)
{
	int result = 0;

	if (newuser && is_valid(newuser)) {
		if (auth_change_username(useridnr, newuser) != 0) {
			qerrorf("Error: could not change username.\n");
			result = -1;
		}
	} else {
		qerrorf("Error: new username contains invalid characters.\n");
		result = -1;
	}

	return result;
}

/* Change of password */
int do_password(const u64_t useridnr,
                const char * const password, const char * const enctype)
{
	int result = 0;

	result = auth_change_password(useridnr, password, enctype);
	if (result != 0) {
		qerrorf("Error: could not change password.\n");
	}

	return result;
}

/* These are the available password types. */
typedef enum {
	PLAINTEXT = 0, PLAINTEXT_RAW, CRYPT, CRYPT_RAW,
	MD5_HASH, MD5_HASH_RAW, MD5_DIGEST, MD5_DIGEST_RAW,
	SHADOW, PWTYPE_NULL
} pwtype_t;

/* These are the easy text names. */
static const char * const pwtypes[] = {
	"plaintext", "plaintext-raw", "crypt", "crypt-raw",
	"md5", "md5-raw", "md5sum", "md5sum-raw",
	"md5-hash", "md5-hash-raw", "md5-digest", "md5-digest-raw",
	"shadow", "", NULL
};

/* These must correspond to the easy text names. */
static const pwtype_t pwtypecodes[] = {
	PLAINTEXT, PLAINTEXT_RAW, CRYPT, CRYPT_RAW,
	MD5_HASH, MD5_HASH_RAW, MD5_DIGEST, MD5_DIGEST_RAW,
	MD5_HASH, MD5_HASH_RAW, MD5_DIGEST, MD5_DIGEST_RAW,
	SHADOW, PLAINTEXT, PWTYPE_NULL
};

int mkpassword(const char * const user, const char * const passwd,
               const char * const passwdtype, const char * const passwdfile,
	       char ** password, char ** enctype)
{

	pwtype_t pwtype;
	int pwindex = 0;
	int result = 0;
	char *entry = NULL;
	char pw[50];

	memset(pw, 0, 50);

	/* Only search if there's a string to compare. */
	if (passwdtype)
		/* Find a matching pwtype. */
		for (pwindex = 0; pwtypecodes[pwindex] < PWTYPE_NULL; pwindex++)
			if (strcasecmp(passwdtype, pwtypes[pwindex]) == 0)
				break;

	/* If no search took place, pwindex is 0, PLAINTEXT. */
	pwtype = pwtypecodes[pwindex];
	switch (pwtype) {
		case PLAINTEXT:
		case PLAINTEXT_RAW:
			null_strncpy(pw, passwd, 49);
			*enctype = "";
			break;
		case CRYPT:
			strcat(pw, null_crypt(passwd, cget_salt()));
			*enctype = "crypt";
			break;
		case CRYPT_RAW:
			null_strncpy(pw, passwd, 49);
			*enctype = "crypt";
			break;
		case MD5_HASH:
			sprintf(pw, "%s%s%s", "$1$", cget_salt(), "$");
			null_strncpy(pw, null_crypt(passwd, pw), 49);
			*enctype = "md5";
			break;
		case MD5_HASH_RAW:
			null_strncpy(pw, passwd, 49);
			*enctype = "md5";
			break;
		case MD5_DIGEST:
			null_strncpy(pw, makemd5(passwd), 49);
			*enctype = "md5sum";
			break;
		case MD5_DIGEST_RAW:
			null_strncpy(pw, passwd, 49);
			*enctype = "md5sum";
			break;
		case SHADOW:
			entry = bgetpwent(passwdfile, user);
			if (!entry) {
				qerrorf("Error: cannot read file [%s], "
					"please make sure that you have "
					"permission to read this file.\n",
					passwdfile);
				result = -1;
				break;
			}
                
			null_strncpy(pw, entry, 49);
			if (strcmp(pw, "") == 0) {
				qerrorf("Error: password for user [%s] not found in file [%s].\n",
				     user, passwdfile);
				result = -1;
				break;
			}

			/* Safe because we know pw is 50 long. */
			if (strncmp(pw, "$1$", 3) == 0) {
				*enctype = "md5";
			} else {
				*enctype = "crypt";
			}
			break;
		default:
			qerrorf("Error: password type not supported [%s].\n",
				passwdtype);
			result = -1;
			break;
	}

	/* Pass this out of the function. */
	*password = dm_strdup(pw);

	return result;
}

/* Change of client id. */
int do_clientid(u64_t useridnr, u64_t clientid)
{	
	int result = 0;

	if (auth_change_clientid(useridnr, clientid) != 0) {
		qprintf("\nWarning: could not change client id ");
		result = -1;
	}

	return result;
}

/* Change of quota / max mail. */
int do_maxmail(u64_t useridnr, u64_t maxmail)
{
	int result = 0;

	if (auth_change_mailboxsize(useridnr, maxmail) != 0) {
		qerrorf("Error: could not change max mail size.\n");
		result = -1;
	}

	return result;
}

int do_forwards(const char * const alias, const u64_t clientid,
                struct list * const fwds_add,
                struct list * const fwds_del)
{
	int result = 0;
	char *forward;
	struct element *tmp;

	/* Delete aliases for the user. */
	if (fwds_del) {
		tmp = list_getstart(fwds_del);
		while (tmp) {
			forward = (char *)tmp->data;

			qprintf("[%s]\n", forward);

			if (db_removealias_ext(alias, forward) < 0) {
				qerrorf("Error: could not remove forward [%s] \n",
				     forward);
				result = -1;
			}
			tmp = tmp->nextnode;
		}
	}

	/* Add aliases for the user. */
	if (fwds_add) {
		tmp = list_getstart(fwds_add);
		while (tmp) {
			forward = (char *)tmp->data;
			qprintf("[%s]\n", forward);

			if (db_addalias_ext(alias, forward, clientid) < 0) {
				qerrorf("Error: could not add forward [%s]\n",
				     alias);
				result = -1;
			}
			tmp = tmp->nextnode;
		}
	}

	/*
	qprintf("Adding alias [%s] --> [%s]...", alias, forward);
	switch ((result = db_addalias_ext(alias, forward, 0))) {
	case -1:
		qerrorf("Error: cannot add forwarding address.\n");
		break;

	case 0:
		qprintf("Ok. Forwarding address added.\n");
		break;

	case 1:
		qprintf("Already exists. no extra alias added\n");
		result = -1;	/ * return error * /
		break;

	}
	*/

	qprintf("Done\n");

	return result;
}

int do_aliases(const u64_t useridnr,
               struct list * const alias_add,
               struct list * const alias_del)
{
	int result = 0;
	u64_t clientid;

	auth_getclientid(useridnr, &clientid);

	/* Delete aliases for the user. */
	if (alias_del) {
		char *alias;
		struct element *tmp;

		tmp = list_getstart(alias_del);
		while (tmp) {
			alias = (char *)tmp->data;

			qprintf("[%s]\n", alias);

			if (db_removealias(useridnr, alias) <
			    0) {
				qerrorf("Error: could not remove alias [%s] \n",
				     alias);
				result = -1;
			}
			tmp = tmp->nextnode;
		}
	}

	/* Add aliases for the user. */
	if (alias_add) {
		char *alias;
		struct element *tmp;
		

		tmp = list_getstart(alias_add);
		while (tmp) {
			alias = (char *)tmp->data;
			qprintf("[%s]\n", alias);

			if (db_addalias
			    (useridnr, alias, clientid) < 0) {
				qerrorf("Error: could not add alias [%s]\n",
				     alias);
				result = -1;
			}
			tmp = tmp->nextnode;
		}
	}

	qprintf("Done\n");

	return result;
}


int do_delete(const u64_t useridnr, const char * const name)
{
	int result;
	struct list aliases;

	qprintf("Deleting aliases for user [%s]...\n", name);
	db_get_user_aliases(useridnr, &aliases);
	do_aliases(useridnr, NULL, &aliases);

	qprintf("Deleting user [%s]...\n", name);
	result = auth_delete_user(name);

	if (result < 0) {
		qprintf("Failed. Please check the log\n");
		return -1;
	}

	qprintf("Done\n");
	return 0;
}

int do_show(const char * const name)
{
	u64_t useridnr, cid, quotum, quotumused;
	struct list userlist;
	struct element *tmp;
	char *deliver_to;

	if (!name) {
		/* show all users */
		qprintf("Existing users:\n");

		auth_get_known_users(&userlist);

		tmp = list_getstart(&userlist);
		while (tmp) {
			qprintf("[%s]\n", (char *) tmp->data);
			tmp = tmp->nextnode;
		}

		if (userlist.start)
			list_freelist(&userlist.start);
	} else {
		qprintf("Info for user [%s]", name);

		if (auth_user_exists(name, &useridnr) == -1) {
			qerrorf("Error: cannot verify existence of user [%s].\n",
			     name);
			return -1;
		}

		if (useridnr == 0) {
			/* 'name' is not a user, try it as an alias */
			qprintf
			    ("..is not a user, trying as an alias");

			deliver_to = db_get_deliver_from_alias(name);

			if (!deliver_to) {
				qerrorf("Error: cannot verify existence of alias [%s].\n",
				     name);
				return -1;
			}

			if (deliver_to[0] == '\0') {
				qprintf("..is not an alias.\n");
				return -1;
			}

			useridnr = strtoul(deliver_to, NULL, 10);
			if (useridnr == 0) {
				qprintf
				    ("\n[%s] is an alias for [%s]\n", name,
				     deliver_to);
				dm_free(deliver_to);
				return 0;
			}

			dm_free(deliver_to);
			qprintf("\nFound user for alias [%s]:\n\n",
				     name);
		}

		auth_getclientid(useridnr, &cid);
		auth_getmaxmailsize(useridnr, &quotum);
		db_get_quotum_used(useridnr, &quotumused);

		qprintf("\n");
		qprintf("User ID         : %llu\n", useridnr);
		qprintf("Username        : %s\n",
			     auth_get_userid(useridnr));
		qprintf("Client ID       : %llu\n", cid);
		qprintf("Max. mailboxsize: %.02f MB\n",
			     (double) quotum / (1024.0 * 1024.0));
		qprintf("Quotum used     : %.02f MB (%.01f%%)\n",
			     (double) quotumused / (1024.0 * 1024.0),
			     (100.0 * quotumused) / (double) quotum);
		qprintf("\n");

		qprintf("Aliases:\n");
		db_get_user_aliases(useridnr, &userlist);

		tmp = list_getstart(&userlist);
		while (tmp) {
			qprintf("%s\n", (char *) tmp->data);
			tmp = tmp->nextnode;
		}

		qprintf("\n");
		if (userlist.start)
			list_freelist(&userlist.start);
	}

	return 0;
}


/*
 * empties the mailbox associated with user 'name'
 */
int do_empty(u64_t useridnr)
{
	int result;

	qprintf("Emptying mailbox...");
	fflush(stdout);

	result = db_empty_mailbox(useridnr);
	if (result != 0)
		qerrorf("Error. Please check the log.\n");
	else
		qprintf("Ok.\n");

	return result;
}


int is_valid(const char *str)
{
	int i;

	for (i = 0; str[i]; i++)
		if (strchr(ValidChars, str[i]) == NULL)
			return 0;

	return 1;
}

/*eddy
  This two function was base from "cpu" by Blake Matheny <matheny@dbaseiv.net>
  bgetpwent : get hash password from /etc/shadow
  cget_salt : generate salt value for crypt
*/
char *bgetpwent(const char *filename, const char *name)
{
	FILE *passfile = NULL;
	char pass_char[512];
	int pass_size = 511;
	char *pw = NULL;
	char *user = NULL;

	if ((passfile = fopen(filename, "r")) == NULL)
		return NULL;

	while (fgets(pass_char, pass_size, passfile) != NULL) {
		char *m = pass_char;
		int num_tok = 0;
		char *toks;

		while (m != NULL && *m != 0) {
			toks = getToken(&m, ":");
			if (num_tok == 0)
				user = toks;
			else if (num_tok == 1)
				/*result->pw_passwd = toks; */
				pw = toks;
			else
				break;
			num_tok++;
		}
		if (strcmp(user, name) == 0)
			return pw;

	}
	return "";
}

char *cget_salt()
{
	unsigned long seed[2];
	const char *const seedchars =
	    "./0123456789ABCDEFGHIJKLMNOPQRST"
	    "UVWXYZabcdefghijklmnopqrstuvwxyz";
	int i;

	seed[0] = time(NULL);
	seed[1] = getpid() ^ (seed[0] >> 14 & 0x30000);
	for (i = 0; i < 8; i++)
		csalt[i] = seedchars[(seed[i / 5] >> (i % 5) * 6) & 0x3f];

	return csalt;
}


/*
  This function was base on function of "cpu"
        by Blake Matheny <matheny@dbaseiv.net>
  getToken : break down username and password from a file
*/
char *getToken(char **str, const char *delims)
{
	char *token;

	if (*str == NULL) {
		/* No more tokens */
		return NULL;
	}

	token = *str;
	while (**str != '\0') {
		if (strchr(delims, **str) != NULL) {
			**str = '\0';
			(*str)++;
			return token;
		}
		(*str)++;
	}

	/* There is no other token */
	*str = NULL;
	return token;
}

u64_t strtomaxmail(const char * const str)
{
	u64_t maxmail;
	char *endptr = NULL;

	maxmail = strtoull(str, &endptr, 10);
	switch (*endptr) {
	case 'g':
	case 'G':
		maxmail *= (1024 * 1024 * 1024);
		break;

	case 'm':
	case 'M':
		maxmail *= (1024 * 1024);
		break;

	case 'k':
	case 'K':
		maxmail *= 1024;
		break;
	}

	return maxmail;
}



syntax highlighted by Code2HTML, v. 0.9.1