/*
 * u_database.c
 * (C) James Craig 1998, Email: jcraig@mad.scientist.com
 * (C) Christian Magnusson 1999, Email: d1mag@dtek.chalmers.se
 * This program is copyright, and covered by the Gnu Public License.
 * The Q bot.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>

#include "bottypes.h"
#include "globals.h"
#include "hash.h"

extern int convert_database;		/* hack like hell */

/*
 * Updates a user's auth level from their current modes settings.
 * This isn't really very useful, except for one special case -
 * *ALL* IRCops automatically get auth=999;
 */

void updateauth(struct activeuser *u)
{
	if (!u)
		return;

	/* 
	 * Allow IRCops to deop themselves for testing.
	 */
	if (u->oper & OPER_O_MASK) {
		if (((u->mode) & (1 << ('o' - 'a'))) == 0) {
			u->oper &= ~OPER_O_MASK;
			u->oper &= ~OPER_DEBUG_MASK;
			change_server_opers(u->yxx[0], u->yxx[1], -1);
			return;
		}
	} else {
		if (((u->mode) & (1 << ('o' - 'a'))) != 0) {
			u->oper |= OPER_O_MASK;
			u->oper |= (1 << OPER_DEBUG_BIT);
			change_server_opers(u->yxx[0], u->yxx[1], 1);
		}
		return;
	}
	return;
}

/* Update a modes integer from a mode change string - e.g. "+tn". */
void updatemodes(unsigned int *modes, char *modstr)
{
	int i, dir, pos;
	unsigned int andmask, ormask;
	andmask = ~0;
	ormask = 0;
	dir = 0;											/* 0=ignore, -1=remove, +1=add */
	for (i = 0; i < strlen(modstr); i++) {
		if (modstr[i] == '+') {
			dir = 1;
		} else if (modstr[i] == '-') {
			dir = -1;
		} else if (isalpha((int) modstr[i])) {
			if (dir == 1) {
				pos = (toupper(modstr[i]) - 'A');
				if (pos >= 0 && pos < 30) {
					ormask |= (1 << pos);
				}
			} else if (dir == -1) {
				pos = (toupper(modstr[i]) - 'A');
				if (pos >= 0 && pos < 30) {
					andmask &= (~0 - (1 << pos));
				}
			}
		}
	}
	*modes = (*modes & andmask) | ormask;
	return;
}

/*
 * Regenerate a mode string from a mode number. Note that this doesn't "undo"
 * modes at all. For that, I'll write a modecmp function which compares two
 * mode numbers and generates a "difference" mode string.
 */
void createmodes(int modenum, char *buffer)
{
	int i, j;
	buffer[0] = '+';
	j = 1;
	buffer[1] = 0;
	for (i = 'a'; i < 'z'; i++) {
		if ((modenum & (1 << (i - 'a'))) != 0) {
			buffer[j] = i;
			buffer[++j] = 0;
		}
	}
	return;
}

/*
 * Compares two modes and gives the mode string needed to move from one
 * condition to the other.
 */
void modecmp(int source, int destination, char *buffer)
{
	int i, j;
	buffer[0] = 0;
	j = 0;
	for (i = 'a'; i < 'z'; i++) {
		if (((destination & (1 << (i - 'a')))) != ((source & (1 << (i - 'a'))))) {
			/* Waah! What a comparison!!!!!!!! */
			if ((destination & (1 << (i - 'a')))) {
				buffer[j++] = '+';
			} else {
				buffer[j++] = '-';
			}
			buffer[j++] = i;
			buffer[j] = 0;
		}
	}
	return;
}

/*
 * Get a user's userlevel from their current nick - need to check if they
 * have already ident'd. This is their net-wide userlevel, not their channel
 * userlevel or their userID.
 */
int userlevel(char *username)
{
	struct activeuser *u = findauser(username);
	if (u) {
		return (u->auth);
	}
	return 0;
}

int userlevel_u(struct activeuser *u)
{
	if (!u)
		return 0;
	return (u->auth);
}


int change_server_opers(char pyxx1, char pyxx2, int users)
{
	int i = (int) convert2n[(int) pyxx1] * 64 + (int) convert2n[(int) pyxx2];

	servers[i].opers += users;
	servers[MAX_SERVERS].opers += users;

	if (servers[i].opers < 0) {
#if DEBUG
		log("Warning: opers at [%c%c] = %d\n", pyxx1, pyxx2, servers[i].opers);
#endif
		servers[i].opers = 0;
	}
	if (servers[MAX_SERVERS].opers < 0) {
#if DEBUG
		log("Warning: Total opers [%c%c] = %d\n", pyxx1, pyxx2, servers[MAX_SERVERS].opers);
#endif
		servers[MAX_SERVERS].opers = 0;
	}
	return servers[i].opers;
}

int change_server_users(char pyxx1, char pyxx2, int users)
{
	int i = (int) convert2n[(int) pyxx1] * 64 + (int) convert2n[(int) pyxx2];

	servers[i].users += users;
	servers[MAX_SERVERS].users += users;

	if (servers[i].users < 0) {
#if DEBUG
		log("Warning: users at [%c%c] = %d\n", pyxx1, pyxx2, servers[i].users);
#endif
		servers[i].users = 0;
	}
	if (servers[MAX_SERVERS].users < 0) {
#if DEBUG
		log("Warning: Total users [%c%c] = %d\n", pyxx1, pyxx2, servers[MAX_SERVERS].users);
#endif
		servers[MAX_SERVERS].users = 0;
	}
	return servers[i].users;
}


void addauser(char *nick, char *username, char *hostname, char *fullname, char *server_name,
	char *modes, char *yxx)
{
	ListElem *el_bup = NULL;
	unsigned int m;
	char yxx2[YXXLEN + 1];
	char *pyxx;
	int i, user_found, len;
	time_t tnow;
	struct dbaseuser *d;
	struct activeuser *newuser;
	ListElem *el;

	/* 
	   make sure he doesn't exist before... Needed because of a memory leak somewhere... (called
	   when user changes nick too!!) */
	while ((el = Find(auser_nick, nick, compare_strcasecmp))) {
		disconnect_auser((struct activeuser *) el->data);
#if DEBUG
		log("addauser: already exist nick=[%s]\n", nick);
#endif
		/* return; */
	}

	len = strlen(yxx);
	if (len == 5) {
		/* User YXX */
		pyxx = yxx;
	} else if (len == 3) {
		yxx2[3] = yxx[1];
		yxx2[4] = yxx[2];
		yxx2[1] = yxx[0];
		yxx2[0] = 'A';
		yxx2[2] = 'A';
		yxx2[5] = '\0';
		pyxx = yxx2;
	} else if (len == 4) {
		log("addauser: len=%d [%s]\n", len, yxx);
		pyxx = &yxx[1];
	} else {
		pyxx = yxx;
		log("addauser: len=%d [%s]\n", len, yxx);
	}

	/* addauser: nick=[Proxyscan] oper=17 server=[A] yxx=[A7AA] */
	/* 
	   if(strlen(yxx) == 4) { pyxx = &yxx[1]; } else { pyxx = yxx; } */

	while ((el = Find(auser_yxx, pyxx, compare_strcmp))) {
		disconnect_auser((struct activeuser *) el->data);
#if DEBUG
		log("addauser: already exist yxx=[%s]\n", pyxx);
#endif
		/* return; */
	}

	newuser = (struct activeuser *) Malloc(sizeof(struct activeuser));
	if (!newuser) {
#if DEBUG
		log("addauser: Malloc() FAILED!\a\n");
#endif
		return;
	}
	memset(newuser, 0, sizeof(struct activeuser));

	/* printf("Debug: Space allocated for %s.\n",nick); */
	if (nick) {
		strncpy(newuser->nick, nick, NICKLEN);
		newuser->nick[NICKLEN] = '\0';
	} else
		newuser->nick[0] = '\0';
	if (username) {
		strncpy(newuser->username, username, USERLEN);
		newuser->username[USERLEN] = '\0';
	} else
		newuser->username[0] = '\0';
	if (hostname) {
		strncpy(newuser->hostname, hostname, HOSTLEN);
		newuser->hostname[HOSTLEN] = '\0';
	} else
		newuser->hostname[0] = '\0';
	if (fullname) {
		strncpy(newuser->fullname, fullname, REALLEN);
		newuser->fullname[REALLEN] = '\0';
	} else
		newuser->fullname[0] = '\0';

	m = 0;
	if (modes) {
		updatemodes(&m, modes);
	}
	newuser->mode = m;
	newuser->oper = ((m & (1 << ('o' - 'a'))) ? (OPER_O_MASK | (1 << OPER_DEBUG_BIT)) : 0);

	strncpy(newuser->yxx, pyxx, YXXLEN);
	newuser->yxx[YXXLEN] = '\0';

	/* change server statistics */
	if (newuser->oper)
		change_server_opers(newuser->yxx[0], newuser->yxx[1], 1);
	change_server_users(newuser->yxx[0], newuser->yxx[1], 1);

#if DEBUG2
	printf("addauser: nick=[%s] oper=%d server=[%c] yxx=[%s]\n", newuser->nick, newuser->oper,
		newuser->yxx[0], newuser->yxx);
#endif

	newuser->auth = 0;
	newuser->userid = 0;
#ifdef USERID_HISTORY_LEN
	for (i = 0; i < USERID_HISTORY_LEN; i++)
		newuser->userid_history[i] = 0;
#endif
	newuser->chanmodes = NULL;		/* linked list of channels empty */
	newuser->dbuser = NULL;				/* pointer to the database entry */

	el = NewElem(newuser);
	newuser->h_elem_yxx = el;
	AddElem(auser_yxx, el);

	el = NewElem(newuser);
	newuser->h_elem_nick = el;
	AddElem(auser_nick, el);

	el = NewElem(newuser);
	newuser->l_elem = el;
	AddTail(auser_list, el);

	/* 
	   check if this connection is the same as last time he authed Then AUTO auth. */
	user_found = 0;
	while ((el = Find(dbusers_lastyxx, newuser->yxx, compare_strcmp))) {
		d = (struct dbaseuser *) el->data;
		if (!user_found && !strcmp(d->last_hostname, newuser->hostname)
			&& !strcmp(d->last_username, newuser->username)) {
			d->last_auth = time(NULL);
			newuser->auth = d->globalauth;
			newuser->userid = d->userid;

			add_userid_history(newuser, newuser->userid);

			newuser->dbuser = d;			/* pointer to the database entry */
			log("addauser: AUTO AUTH [%s] [%s@%s]\n", newuser->yxx, newuser->username, newuser->hostname);

#if 0
			if (d->email) {
				log("addauser: email len=%d [%s]\n", strlen(d->email), d->email);
			} else {
				log("addauser: email is null\n");
			}
#endif
			d->h_elem_lastyxx = el;
			/* 
			   strncpy(d->last_yxx, newuser->yxx, YXXLEN); d->last_yxx[YXXLEN] = '\0'; */
			/* should perhaps make handlejoin to all channels here too */

#if 1
			el_bup = el;
			Remove(el_bup);
			user_found = 1;
			/* 
			   if a user was found with a possible auto auth, then we are pleased for the moment. Other
			   users can possible have same last_yxx, but it won't be any problem with them. They could
			   stay there and be deleted some other time. */
#else
			/* Could perhaps exit instead to speed up a bit... and not cleanup... */
			return;
#endif
		} else {
			/* remove the inactual hash object, there could be another correct */
			Remove(el);
			/* make sure to remove it from the user who had this yxx once before */
			if (d->h_elem_lastyxx) {
				log("addauser: some other already authed [%s] [%s]\n", d->nick, d->last_yxx);
			}
			d->h_elem_lastyxx = NULL;
			d->last_yxx[0] = '\0';		/* clear last yxx since someone else owns it */
#if 0
			/* Don't clear last hostname... could be nice to save it... */
			d->last_username[0] = '\0';
			d->last_hostname[0] = '\0';
#endif
			FreeElem(el);
		}
	}

	if (el_bup) {
		AddElem(dbusers_lastyxx, el_bup);
	}

	if (servers[MAX_SERVERS].users > max_users) {
		tnow = time(NULL);
		max_users = servers[MAX_SERVERS].users;
		max_users_time = tnow;
		/* don't print during the startup to avoid flooding */
		if (donewithburst) {
			if ((max_users % 10) == 0) {
				wallop("Currently %d users", max_users);
			}
		}
	}
	return;
}

struct chanmod *addausertochan_u(struct activeuser *u, char *channel)
{
	int len;
	struct chanmod *c;
	if (!u) {
#if DEBUG
		log("ERROR: *** addausertochan: couldn't find user\n");
		printf("ERROR: *** addausertochan: couldn't find user\n");
#endif
		return NULL;
	}
	/* found the active user */
	if (u->chanmodes) {
		/* user has joined some channels already */
		c = u->chanmodes;
		while (c) {
			if (!strcasecmp(c->channel, channel)) {
				/* channel already exist in list */
				return NULL;
			}
			c = c->next;
		}
	}
	c = (struct chanmod *) Malloc(sizeof(struct chanmod));
	if (c) {
		memset(c, 0, sizeof(struct chanmod));
		len = MIN(strlen(channel), CHANNELLEN);
		c->channel = Malloc(len + 1);
		strncpy(c->channel, channel, len);
		c->channel[len] = '\0';
		/* 
		   strncpy(c->channel, channel, CHANNELLEN); c->channel[CHANNELLEN] = '\0'; */
		c->mode = 0;
		c->next = u->chanmodes;
		u->chanmodes = c;
	}
	return c;
}

struct chanmod *addausertochan(char *user, char *channel)
{
	struct activeuser *u = findauser(user);
	if (!u) {
		log("addausertochan: u==NULL\n");
		printf("addausertochan: u==NULL\n");
		return NULL;
	}
	return addausertochan_u(u, channel);
}

int removeauserfromchan(struct activeuser *u, char *channel, struct chanopts *c)
{
	struct chanmod *cm, *lastcm;
	char *channel_name;
	if (!u) {
#if DEBUG
		log("ERROR: removeauserfromchan: u==NULL\n");
		printf("ERROR: removeauserfromchan: u==NULL\n");
#endif
		return 0;
	}
	/* 
	   in some cases c isn't set, try to set it from the channel string */
	if (!c) {
		c = findchannel(channel);
#if DEBUG
		if (!c) {
			/* the channel isn't under Q's protection and we can't find it */
		}
#endif
	}
	if (c) {
		channel_name = c->channel;
	} else {
		channel_name = channel;
	}

	lastcm = NULL;
	cm = u->chanmodes;
	while (cm) {
		if (!strcasecmp(cm->channel, channel_name)) {
			Free(cm->channel);
			cm->channel = NULL;
			if (!lastcm) {
				/* leaving first joined channel */
				u->chanmodes = cm->next;
				Free(cm);
				cm = u->chanmodes;
				if (c) {
					if (c->current_joined > 0) {
						c->current_joined--;
					} else {
#if DEBUG
						log("removeauserfromchan: current_joined < 0 ? [%d] [%s]\n", c->current_joined,
							channel_name);
#endif
					}
				}
				/* we are pretty sure the user haven't joined the channel twice */
				return 1;
			} else {
				/* leaving some other channel, fix pointers */
				lastcm->next = cm->next;
				cm->next = NULL;
				Free(cm);
				cm = lastcm->next;
				if (c) {
					if (c->current_joined > 0) {
						c->current_joined--;
					} else {
#if DEBUG
						log("removeauserfromchan: current_joined < 0 ? [%d] [%s]\n", c->current_joined,
							channel_name);
#endif
					}
				}
				/* we are pretty sure the user haven't joined the channel twice */
				return 1;
			}
		} else {
			lastcm = cm;
			cm = cm->next;
		}
	}
	return 0;
}

/*
 * MAIN USER USERID BASED DATABASE
 * THIS DATABASE IS THE MAIN PERSISTENT BOT DATABASE
 * THE FOLLOWING FUNCTIONS ARE PROBABLY THE MOST CRITICAL
 * ROUTINES IN THE WHOLE PROGRAM.
 */

int dbasecheck(struct dbaseuser *u, char *password)
{
	if (u && (!strncmp(u->password, password, PASSLEN)))
		return 1;
	return 0;
}

int save_everything()
{
	saveudbase();
	saveglinelist();
	savechannellist();
	save_chantypelist();
	save_max_users();
	savetrustedlist();
	return 0;
}

int load_max_users(void)
{
	FILE *fp;
	int err = 0;
	int rec_nr = 0;
	time_t rec_time = 0;

	fp = fopen(MAX_USERS_FILE, "r");
	if (fp == NULL) {
		fprintf(stderr, "Couldn't find [%s]\a\n", MAX_USERS_FILE);
		log("load_max_users: Couldn't find [%s]\a\n", MAX_USERS_FILE);
		return 0;
	}

	if (fscanf(fp, "%d", &rec_nr) != 1) { err = 1; }
	if (!err && fscanf(fp, "%lu\n", &rec_time) != 1) { err = 1; }
	fclose(fp);

	if (err) {
		log("load_max_users: failed during loading\n");
		rec_nr = 16858;
		rec_time = 974663515;
	} else {
		log("load_max_users: %d\n", rec_nr);
  }
	max_users = rec_nr;
	max_users_time = rec_time;

	return 1;
}

void save_max_users(void)
{
	FILE *fp;
	char file_new[256];
	char file_old[256];

	sprintf(file_new, "%s.new", MAX_USERS_FILE);
	sprintf(file_old, "%s.old", MAX_USERS_FILE);

	fp = fopen(file_new, "w");
	if (!fp) {
		wallop("WARNING: CANNOT SAVE MAX_USERS_FILE");
		return;
	}
	fprintf(fp, "%d\n", max_users);
	fprintf(fp, "%lu\n", max_users_time);
	fclose(fp);

	unlink(file_old);
	link(MAX_USERS_FILE, file_old);
	rename(file_new, MAX_USERS_FILE);
	return;
}

int load_chantypelist(void)
{
	FILE *fp;
	ListElem *el;
	struct chantype *ct;
	char name[255];
	int len;
	int err = 1;
	int nr = 0;
	int priority, join_burst, id;

	fp = fopen(CHANTYPE_DBASE_FILE, "r");
	if (fp == NULL) {
		fprintf(stderr, "Couldn't find [%s]\a\n", CHANTYPE_DBASE_FILE);
		log("load_chantypelist: Couldn't find [%s]\a\n", CHANTYPE_DBASE_FILE);
		return 0;
	}
	/* 
	 * OK, the unpleasant bit.
	 * Need to think about the file-format more than just a little bit.
	 */
	while (!feof(fp)) {
		err = 1;
		if (fscanf(fp, "%s\n", name) != 1)
			break;
		if (fscanf(fp, "%d\n", &id) != 1)
			break;
		if (fscanf(fp, "%d\n", &join_burst) != 1)
			break;
		if (fscanf(fp, "%d\n", &priority) != 1)
			break;

		ct = Malloc(sizeof(struct chantype));
		memset(ct, 0, sizeof(struct chantype));
		len = MIN(strlen(name), CHANTYPELEN);
		ct->name = Malloc(len + 1);
		strncpy(ct->name, name, len);
		ct->name[len] = '\0';
		ct->id = id;
		ct->join_burst = join_burst;
		ct->priority = priority;

		el = NewElem(ct);
		AddTail(chantype_list, el);
		err = 0;
		nr++;
	}
	log("load_chantypelist: %d channeltypes added.\n", nr);
	if (err) {
		log("load_chantypelist: failed during loading\n");
	}
	fclose(fp);
	return 1;
}


void save_chantypelist(void)
{
	FILE *fp;
	struct chantype *ct;
	ListElem *el;
	char file_new[256];
	char file_old[256];

	sprintf(file_new, "%s.new", CHANTYPE_DBASE_FILE);
	sprintf(file_old, "%s.old", CHANTYPE_DBASE_FILE);

	fp = fopen(file_new, "w");
	if (!fp) {
		wallop("WARNING: CANNOT SAVE CHANNEL-TYPE LIST");
		return;
	}
	el = GetHead(chantype_list);
	while (el) {
		ct = (struct chantype *) el->data;
		fprintf(fp, "%s\n", ct->name);
		fprintf(fp, "%d\n", ct->id);
		fprintf(fp, "%d\n", ct->join_burst);
		fprintf(fp, "%d\n", ct->priority);
		el = GetSucc(el);
	}
	fclose(fp);

	unlink(file_old);
	link(CHANTYPE_DBASE_FILE, file_old);
	rename(file_new, CHANTYPE_DBASE_FILE);
	return;
}


int loadudbase(void)
{
	/* This, and the save routine, are the really unpleasant bits. */
	struct dbaseuser *u;
	FILE *udbase;
	char tmp[256];
	char nick[2 * NICKLEN + 1];
	char password[2 * PASSLEN + 1];
	char email[2 * EMAILLEN + 1];
	char channel[2 * CHANNELLEN + 1];	/* just make sure there are enough buffer */
	int userid;
	int len;
	int globalauth;
	int chanlevel;
	struct chanpriv *p;
	time_t last_auth;
	time_t last_email_request;
	char last_yxx[255];
	char last_hostname[255];
	char last_username[255];
	ListElem *el;

#ifdef ONLY_STATBOT
	return 1;
#endif

	udbase = fopen(USER_DBASE_FILE, "r");
	if (!udbase) {
		fprintf(stderr, "NO USER DATABASE FILE PRESENT - AT ALL. CRITICAL ERROR. HALT.\a\n");
		return 0;
	}
	/* 
	 * OK, the unpleasant bit.
	 * Need to think about the file-format more than just a little bit.
	 */
	while (!feof(udbase)) {
		/* 
		 * Global auth levels are for IRCops and their helpers.
		 * Basically, #feds. :)
		 */
		if (fscanf(udbase, "%s\n", nick) != 1)
			break;
		if (fscanf(udbase, "%d\n", &userid) != 1)
			break;
		if (fscanf(udbase, "%s\n", password) != 1)
			break;
		if (fscanf(udbase, "%d\n", &globalauth) != 1)
			break;
		if (fscanf(udbase, "%ld\n", &last_auth) != 1)
			break;
		if (fscanf(udbase, "%s\n", last_yxx) != 1)
			break;
		if (fscanf(udbase, "%s\n", last_username) != 1)
			break;
		if (fscanf(udbase, "%s\n", last_hostname) != 1)
			break;
		if (!convert_database) {
			if (fscanf(udbase, "%ld\n", &last_email_request) != 1)
				break;
			if (fscanf(udbase, "%s\n", email) != 1)
				break;
		} else {
			last_email_request = 0L;
			strcpy(email, "???");
		}
		u = (struct dbaseuser *) Malloc(sizeof(struct dbaseuser));
		memset(u, 0, sizeof(struct dbaseuser));
		len = MIN(strlen(nick), NICKLEN);
		u->nick = (char *) Malloc(len + 1);
		strncpy(u->nick, nick, len);
		u->nick[len] = '\0';
		/* 
		   strncpy(u->nick,nick,NICKLEN); u->nick[NICKLEN] = '\0'; */
		strncpy(u->password, password, PASSLEN);
		u->password[PASSLEN] = '\0';
		u->globalauth = globalauth;
		u->userid = userid;
		strncpy(u->last_yxx, last_yxx, YXXLEN);
		u->last_yxx[YXXLEN] = '\0';
		strncpy(u->last_username, last_username, USERLEN);
		u->last_username[USERLEN] = '\0';
		len = MIN(strlen(last_hostname), HOSTLEN);
		u->last_hostname = Malloc(len + 1);
		strncpy(u->last_hostname, last_hostname, len);
		u->last_hostname[len] = '\0';
		/* 
		   strncpy(u->last_hostname, last_hostname, HOSTLEN); u->last_hostname[HOSTLEN] = '\0'; */
		if (!strcmp(email, "???")) {
			u->email = NULL;
			u->last_email_request = 0L;
		} else {
			u->last_email_request = last_email_request;
			len = MIN(strlen(email), EMAILLEN);
			u->email = Malloc(len + 1);
			strncpy(u->email, email, len);
			u->email[len] = '\0';
		}

		if (userid > maxuid) {
			maxuid = userid;
		}
		u->last_auth = last_auth;

		el = NewElem(u);
		u->h_elem_nick = el;
		AddElem(dbusers_nick, el);

		el = NewElem(u);
		u->h_elem_lastyxx = el;
		AddElem(dbusers_lastyxx, el);

#if 1
		el = NewElem(u);
		u->h_elem_userid = el;
		AddElem(dbusers_userid, el);
#else
		/* don't set this until user auth... save some memory don't know if there are any search needed 
		   before user has authed.. looking for a channel founder has only userid to search for. skip
		   this so far.. */
		u->h_elem_userid = NULL;
#endif

		el = NewElem(u);
		u->l_elem = el;
		AddHead(dbuser_list, el);

		/* Right, that bit was *easy*. Now to actually read the channels bit. */
		u->privs = NULL;
		while (!feof(udbase)) {
			/* 
			 * I SHOULD REALLY USE CHANNELLEN IN THIS NEXT LINE TO RESTRICT THE
			 * LENGTH OF THE CHANNEL NAME BEING READ IN. HOWEVER, SINCE THE BOT
			 * IS THE ONLY THING TO WRITE THIS FILE, IT'S SAFE TO BE LAZY ABOUT
			 * CHECKS WHEN READING.
			 */
#if 1
			/* fix a stupid bug of wrong database casued by last_* values */
			while (1) {
				fscanf(udbase, "%s\n", tmp);
				if ((strncmp(tmp, "end", 3) == 0) || (tmp[0] == '#')) {
					strncpy(channel, tmp, CHANNELLEN);
					channel[CHANNELLEN] = '\0';
					break;
				}
				log("*** re-read due to [%s]\n", tmp);
			}
#else
			if (fscanf(udbase, "%s\n", channel) != 1)
				break;
#endif
			/* 
			   It could be either '#chan' or 'end' here */
			if (channel[0] != '#')
				break;

			if (fscanf(udbase, "%d\n", &chanlevel) != 1)
				break;
#if 0
			chanlevel &= (CHAN_ALL | CHAN_NEWLEVS);
#endif
			chanlevel &= CHAN_ALL;

			if (u->privs == NULL) {
				u->privs = (struct chanpriv *) Malloc(sizeof(struct chanpriv));
				p = u->privs;
				p->next = NULL;
			} else {
				p = (struct chanpriv *) Malloc(sizeof(struct chanpriv));
				p->next = u->privs;
				u->privs = p;
			}
			strncpy(p->channel, channel, CHANNELLEN);

			p->chanlevel = chanlevel;
#if 0
			if (!((p->chanlevel) & CHAN_NEWLEVS)) {
				if ((p->chanlevel) >= 950) {
					p->chanlevel = CHAN_OPER + CHAN_VOICE + CHAN_TOPIC + CHAN_MASTER + CHAN_OWNER;
				} else if ((p->chanlevel) >= 900) {
					p->chanlevel = CHAN_OPER + CHAN_VOICE + CHAN_TOPIC + CHAN_MASTER;
				} else if ((p->chanlevel) >= 100) {
					p->chanlevel = CHAN_OPER + CHAN_VOICE + CHAN_TOPIC;
				}
			} else
				p->chanlevel = p->chanlevel & (~0 - CHAN_NEWLEVS);
#endif
		}
	}
	fclose(udbase);
	return 1;
}

void print_userlist(long from_age, long to_age)
{
	char tmp[100];
	int nr = 0;
	long age;
	struct tm *tm_;
	char print_it = 0;
	struct chanpriv *p;
	struct dbaseuser *u;
	ListElem *el;

	el = GetHead(dbuser_list);
	while (el) {
		u = (struct dbaseuser *) el->data;
		if (from_age && to_age) {
			if (u->last_auth > from_age && u->last_auth < to_age) {
				print_it = 1;
			}
		} else if (from_age) {
			if (u->last_auth > from_age) {
				print_it = 1;
			}
		} else if (to_age) {
			if (u->last_auth < to_age) {
				print_it = 1;
			}
		}
		if (print_it) {
			age = u->last_auth;
			tm_ = localtime(&age);
			strftime(tmp, 60, "%c", tm_);
			printf("%s\t%s", tmp, u->nick);
			nr++;
			print_it = 0;
			p = u->privs;
			if (p) {
				printf(" ***");
			}
			while (p) {
				if (p->chanlevel != 0) {
					printf(" %s", p->channel);
				}
				p = p->next;
			}
			printf("\n");
		}
		el = GetSucc(el);
	}
	printf("%d matching nicknames found\n", nr);
	return;
}

void saveudbase(void)
{
	FILE *udbase;
	struct dbaseuser *u;
	struct chanpriv *p;
	ListElem *el;
	char file_new[256];
	char file_old[256];

#ifdef ONLY_STATBOT
	return;
#endif

	sprintf(file_new, "%s.new", USER_DBASE_FILE);
	sprintf(file_old, "%s.old", USER_DBASE_FILE);

	udbase = fopen(file_new, "w");
	if (!udbase) {
		wallop("WARNING: CANNOT SAVE USER DATABASE.");
		return;
	}
	/* 
	   wallop("Saving user database."); */
	el = GetHead(dbuser_list);
	while (el) {
		u = (struct dbaseuser *) el->data;
		fprintf(udbase, "%s\n", u->nick);
		fprintf(udbase, "%d\n", u->userid);
		fprintf(udbase, "%s\n", u->password);
		fprintf(udbase, "%d\n", u->globalauth);
		fprintf(udbase, "%ld\n", u->last_auth);

		/* 
		   save info about last auth */
		if (strlen(u->last_yxx))
			fprintf(udbase, "%s\n", u->last_yxx);
		else
			fprintf(udbase, "???\n");
		if (u->last_username && strlen(u->last_username))
			fprintf(udbase, "%s\n", u->last_username);
		else
			fprintf(udbase, "???\n");
		if (u->last_hostname && strlen(u->last_hostname))
			fprintf(udbase, "%s\n", u->last_hostname);
		else
			fprintf(udbase, "???\n");
		fprintf(udbase, "%ld\n", u->last_email_request);
		if (u->email && strlen(u->email))
			fprintf(udbase, "%s\n", u->email);
		else
			fprintf(udbase, "???\n");

		p = u->privs;
		while (p) {
			p->chanlevel &= CHAN_ALL;
			if (p->chanlevel) {
				/* save all channels where user have flags set */
				fprintf(udbase, "%s\n", p->channel);
				fprintf(udbase, "%d\n", p->chanlevel);
			}
			p = p->next;
		}
		fprintf(udbase, "end\n");
		el = GetSucc(el);
	}
	fflush(udbase);
	fclose(udbase);
/*  sleep(1); *//* (fox) sleep?? Why? */
	/* 
	 * This messing about makes sure that USER_DBASE_FILE is always an OK
	 * copy of the database, albeit possibly an old one.
	 */
	unlink(file_old);
	link(USER_DBASE_FILE, file_old);
	rename(file_new, USER_DBASE_FILE);
	return;
}

/*
 * The bad news. This is probably going to be the most frequently called
 * function in the bot, after getline(), and it's almost certainly the slowest.
 */

int userlevelonchan_d(struct dbaseuser *d, char *chan)
{
	struct chanpriv *p, *q;

	if (!d) { return 0; }

	if (!strcasecmp(d->nick, BOT_NAME)) { return CHAN_OPER + CHAN_VOICE + CHAN_TOPIC + CHAN_MASTER + CHAN_OWNER; }

	q = NULL;
	p = d->privs;
	while (p) {
		if (!strcasecmp(chan, p->channel))
			break;
		q = p;
		p = p->next;
	}
	if (p) {
		/* 
		   extra check to see if user is on channel, but NOT have any chanlevel set !   Could happen if 
		   the database is corrupted. */
		if (p->chanlevel) {
			return p->chanlevel;
		} else {
			/* remove that old entry */
			if (q == NULL) {
				d->privs = p->next;
			} else {
				q->next = p->next;
			}
			Free(p);
		}
	}
	/* user not known in that channel (anymore) */
	return 0;
}

int loadglinelist(void)
{
	FILE *cdbase;
	char gline[GLINELEN + 1];
	time_t duration, expire;
	time_t duration_new, t_now;
	int placedby;
	char reason[REASONLEN + 1];
	char duration_str[50];
	struct gline *g;
	int nr, added;

#ifdef ONLY_STATBOT
	return 0;
#endif

	nr = 0;
	added = 0;

	cdbase = fopen(GLINE_DBASE_FILE, "r");
	if (cdbase == NULL) {
		log("loadglinelist: error open file\n");
		return 0;
	}
	while (!feof(cdbase)) {
		gline[0] = '\0';
		if (fscanf(cdbase, "%s\n", gline) != 1) {
			log("loadglinelist: error get gline\n");
			fclose(cdbase);
			if (nr == 0)
				return 1;
			return 0;
		}
		fscanf(cdbase, "%lu\n", &expire);
		fscanf(cdbase, "%lu\n", &duration);
		fscanf(cdbase, "%d\n", &placedby);
		fscanf(cdbase, "%[^\n]\n", reason);
		if (strcmp(reason, "!") == 0) {
			reason[0] = '\0';
		}

		nr++;

		t_now = time(NULL);
		duration_new = expire - t_now;
		if (expire > t_now) {
			/* && (duration_new < 3*365*24*3600)) { */
			sprintf(duration_str, "%lu", duration_new);

			g = gline_add(gline, duration_str, reason, placedby, 1);
			if (!g) {
				log("loadglinelist: gline_add failed [%s] [%s] [%s] [%d]\n", gline, duration_str, reason,
					placedby);
			} else {
				added++;
			}
		} else {
			log("loadglinelist: gline already expired [%s] [%s] [%s] [%d]\n", gline, duration_str, reason,
				placedby);
		}
	}

	log("loadglinelist: %d glines found (%d added)\n", nr, added);

	fclose(cdbase);
	return 1;
}

void saveglinelist(void)
{
	FILE *cdbase;
	struct gline *g;
	struct ListElem *el;
	char file_new[512 + 1];
	char file_old[512 + 1];

#ifdef ONLY_STATBOT
	return;
#endif

	sprintf(file_new, "%s.new", GLINE_DBASE_FILE);
	sprintf(file_old, "%s.old", GLINE_DBASE_FILE);
	cdbase = fopen(file_new, "w");
	if (cdbase == NULL) {
		wallop("WARNING: CANNOT SAVE GLINE DATABASE!");
		return;
	}

	el = GetHead(gline_list);
	while (el) {
		g = (struct gline *) el->data;

		fprintf(cdbase, "%s\n", g->gline);
		fprintf(cdbase, "%lu\n", g->expire);
		fprintf(cdbase, "%lu\n", g->duration);
		fprintf(cdbase, "%d\n", g->placedby);
		if (!g->reason || !strlen(g->reason)) {
			fprintf(cdbase, "!\n");
		} else {
			fprintf(cdbase, "%s\n", g->reason);
		}

		el = GetSucc(el);
	}
	fclose(cdbase);
	sleep(1);

	unlink(file_old);
	link(GLINE_DBASE_FILE, file_old);
	rename(file_new, GLINE_DBASE_FILE);
	return;
}

int loadtrustedlist(void)
{
	FILE *cdbase;
	char host[HOSTLEN + 1];
	time_t expire, t_now;
	int placedby, nr_users;
	char reason[REASONLEN + 1];
	struct trusted *tr;
	int nr, added;

	nr = 0;
	added = 0;

	cdbase = fopen(TRUSTED_DBASE_FILE, "r");
	if (cdbase == NULL) {
		log("loadglinelist: error open file\n");
		return 0;
	}
	while (!feof(cdbase)) {
		host[0] = '\0';
		if (fscanf(cdbase, "%s\n", host) != 1) {
			fclose(cdbase);
			return 1;
		}
		fscanf(cdbase, "%lu\n", &expire);
		fscanf(cdbase, "%d\n", &nr_users);
		fscanf(cdbase, "%d\n", &placedby);
		fscanf(cdbase, "%[^\n]\n", reason);
		if (strcmp(reason, "!") == 0) {
			reason[0] = '\0';
		}

		nr++;

		t_now = time(NULL);
		if (expire > t_now) {
			tr = trusted_add(host, nr_users, expire, reason, placedby);
			if (!tr) {
				log("loadtrustedlist: trust_add failed [%s] [%d] [%d] [%s] [%d]\n", host, nr_users, expire,
					reason, placedby);
			} else {
				added++;
			}
		} else {
			log("loadtrustedlist: host already expired [%s] [%d] [%d] [%s] [%d]\n", host, nr_users,
				expire, reason, placedby);
		}
	}

	log("loadtrustedlist: %d trusted hosts found (%d added)\n", nr, added);

	fclose(cdbase);
	return 1;
}

void savetrustedlist(void)
{
	FILE *cdbase;
	struct trusted *tr;
	struct ListElem *el;
	char file_new[512 + 1];
	char file_old[512 + 1];

	sprintf(file_new, "%s.new", TRUSTED_DBASE_FILE);
	sprintf(file_old, "%s.old", TRUSTED_DBASE_FILE);
	cdbase = fopen(file_new, "w");
	if (cdbase == NULL) {
		wallop("WARNING: CANNOT SAVE GLINE DATABASE!");
		return;
	}

	el = GetHead(trusted_list);
	while (el) {
		tr = (struct trusted *) el->data;
		fprintf(cdbase, "%s\n", tr->host);
		fprintf(cdbase, "%lu\n", tr->expire);
		fprintf(cdbase, "%d\n", tr->nr_users);
		fprintf(cdbase, "%d\n", tr->placedby);
		if (!tr->reason || !strlen(tr->reason)) {
			fprintf(cdbase, "!\n");
		} else {
			fprintf(cdbase, "%s\n", tr->reason);
		}

		el = GetSucc(el);
	}
	fclose(cdbase);

	unlink(file_old);
	link(TRUSTED_DBASE_FILE, file_old);
	rename(file_new, TRUSTED_DBASE_FILE);
	return;
}


int loadchannellist(void)
{
	FILE *cdbase;
	char channel[CHANNELLEN + 1];
	int flags, type;
	int i;
	int owner, created_by;
	int suspended, suspend_who;
	char suspend_reason[SUSPENDREASONLEN + 1];
	char key[KEYLEN + 1];
	char limit[512 + 1];
	char topic[512 + 1];
	char last_topic[512 + 1];
	char welcome[512 + 1];
	struct chanopts *c;
	char banstring[BANLEN + 1];
	time_t first_joined, last_joined, suspend_time;
	unsigned int nr_joined;

#ifdef ONLY_STATBOT
	return 0;
#endif

	cdbase = fopen(CHANNEL_DBASE_FILE, "r");
	if (cdbase == NULL) {
		wallop("NO CHANNEL DATABASE PRESENT. ABORTING.");
		return 0;
	}
	while (!feof(cdbase)) {
		channel[0] = '\0';
		topic[0] = '\0';
		if (fscanf(cdbase, "%s\n", channel) != 1)
			return 0;
		fscanf(cdbase, "%d\n", &flags);
		fscanf(cdbase, "%d\n", &owner);
		fscanf(cdbase, "%d\n", &created_by);
		fscanf(cdbase, "%d\n", &suspended);
		fscanf(cdbase, "%d\n", &suspend_who);
		fscanf(cdbase, "%lu\n", &suspend_time);
		fscanf(cdbase, "%[^\n]\n", suspend_reason);
		if (strcmp(suspend_reason, "!") == 0) {
			suspend_reason[0] = '\0';
		}
		fscanf(cdbase, "%d\n", &type);

		fscanf(cdbase, "%[^\n]\n", topic);
		fscanf(cdbase, "%[^\n]\n", last_topic);

		if (last_topic[0] == '\012' || last_topic[0] == '\015')
			fscanf(cdbase, "%[^\n]\n", last_topic);

		if (strcmp(last_topic, "!") == 0) {
			last_topic[0] = '\0';
		}

		fscanf(cdbase, "%[^\n]\n", welcome);
		if (strcmp(welcome, "!") == 0) {
			welcome[0] = '\0';
		}
		fscanf(cdbase, "%[^\n]\n", key);
		if (key[0] == '!') {
			flags &= (~0 - (1 << ('k' - 'a')));
			key[0] = '\0';
		}
		fscanf(cdbase, "%[^\n]\n", limit);
		if (limit[0] == '!') {
			flags &= (~0 - (1 << ('l' - 'a')));
			limit[0] = '\0';
		}
		fscanf(cdbase, "%lu\n", &first_joined);
		fscanf(cdbase, "%lu\n", &last_joined);
		fscanf(cdbase, "%u\n", &nr_joined);

		/* these flags are only forced temporary. They cause problem if set wrong */
#ifdef FORCE_AUTOJOIN
		flags |= CO_AUTO_JOIN;
#endif

		c = addchannel(channel, flags, 0, owner, topic);
		if (!c) {
			/* 
			   this should never happen ?? */
			log("addchannel failed\n");
			exit(201);
		}
		c->created_by = created_by;

		if (last_topic[0] == ':') {
			strncpy(c->last_topic, &last_topic[1], TOPICLEN);
		} else {
			strncpy(c->last_topic, last_topic, TOPICLEN);
		}
		c->last_topic[TOPICLEN] = '\0';
		strncpy(c->welcome, welcome, WELCOMELEN);
		c->welcome[WELCOMELEN] = '\0';
		strncpy(c->key, key, KEYLEN);
		c->key[KEYLEN] = '\0';
		strncpy(c->limit, limit, LIMITLEN);
		c->limit[LIMITLEN] = '\0';
		
		c->suspended = suspended;
		if (suspend_reason[0]) {
			c->suspend_reason = Malloc(SUSPENDREASONLEN + 1);
			strncpy(c->suspend_reason, suspend_reason, SUSPENDREASONLEN);
			c->suspend_reason[SUSPENDREASONLEN] = '\0';
		} else {
			c->suspend_reason = NULL;
		}
		c->suspend_who = suspend_who;
		c->suspend_time = suspend_time;

		c->first_joined = first_joined;
		c->last_joined = last_joined;
		c->nr_joined = nr_joined;
		c->type = type;

#ifdef AUTH_HISTORY
		for (i = 0; i < AUTH_HISTORY_LEN; i++) {
			c->auth_history_nick[i][0] = '\0';
			c->auth_history_userid[i] = 0;
		}
		c->auth_history_pos = 0;
#endif
		banstring[0] = 0;
		while (!feof(cdbase)) {
			fscanf(cdbase, "%s\n", banstring);
			fscanf(cdbase, "%d\n", &owner);
			if (!strcmp(banstring, "!") && (owner == 0)) {
				/* this was the endmark of all bans */
				break;
			} else {
				/* 
				   At this point we have no clue about the channels since the burst hasn't occoured No use
				   to kick now. */
				addban_ch(c, banstring, owner, 0);
			}
		}
	}
	fclose(cdbase);
	return 1;
}

int savechannellist(void)
{
	FILE *cdbase;
	struct ban *b;
	struct chanopts *c;
	struct ListElem *el;
	char file_new[512 + 1];
	char file_old[512 + 1];

#ifdef ONLY_STATBOT
	return;
#endif

	sprintf(file_new, "%s.new", CHANNEL_DBASE_FILE);
	sprintf(file_old, "%s.old", CHANNEL_DBASE_FILE);
	cdbase = fopen(file_new, "w");
	if (cdbase == NULL) {
		wallop("WARNING: CANNOT SAVE CHANNEL DATABASE!");
		return 0;
	}
	/* 
	   wallop("Saving channel database..."); */
	el = GetHead(channel_list);
	while (el) {
		c = (struct chanopts *) el->data;
		/* 
		   only save those channels where Q actually belong, even if we take care of ALL channels now */
		if (c->bot_joined || c->suspended || convert_database) {
			fprintf(cdbase, "%s\n", c->channel);
			fprintf(cdbase, "%d\n", c->flags);
			fprintf(cdbase, "%d\n", c->owner);
			fprintf(cdbase, "%d\n", c->created_by);
			fprintf(cdbase, "%d\n", c->suspended);
			fprintf(cdbase, "%d\n", c->suspend_who);
			fprintf(cdbase, "%lu\n", c->suspend_time);
			if (!c->suspend_reason) {
				fprintf(cdbase, "!\n");
			} else {
				fprintf(cdbase, "%s\n", c->suspend_reason);
			}
			fprintf(cdbase, "%d\n", c->type);

			fprintf(cdbase, "%s\n", c->topic);
			if (!strlen(c->last_topic)) {
				fprintf(cdbase, "!\n");
			} else {
				fprintf(cdbase, "%s\n", c->last_topic);
			}
			if (!strlen(c->welcome)) {
				fprintf(cdbase, "!\n");
			} else {
				fprintf(cdbase, "%s\n", c->welcome);
			}
			if (!strlen(c->key)) {
				fprintf(cdbase, "!\n");
			} else {
				fprintf(cdbase, "%s\n", c->key);
			}
			if (!strlen(c->limit)) {
				fprintf(cdbase, "!\n");
			} else {
				fprintf(cdbase, "%s\n", c->limit);
			}
			fprintf(cdbase, "%lu\n", c->first_joined);
			fprintf(cdbase, "%lu\n", c->last_joined);
			fprintf(cdbase, "%u\n", c->nr_joined);

			b = c->bans;
			while (b) {
				if (b->placedby) {
					/* save the permanent bans */
					fprintf(cdbase, "%s\n", b->ban);
					fprintf(cdbase, "%d\n", b->placedby);
				}
				b = b->next;
			}
			fprintf(cdbase, "!\n0\n");
		}
		el = GetSucc(el);
	}
	fclose(cdbase);
	sleep(1);

	unlink(file_old);
	link(CHANNEL_DBASE_FILE, file_old);
	rename(file_new, CHANNEL_DBASE_FILE);
	/* 
	   wallop("Channel database saved."); */
	return 1;
}

struct chanopts *addchannel(char *channel, int flags, int modes, int owner, char *topic)
{
	int i;
	struct chanopts *c;
	ListElem *el;
	if (!channel)
		return NULL;
	c = (struct chanopts *) Malloc(sizeof(struct chanopts));
	if (!c)
		return NULL;
	memset(c, 0, sizeof(struct chanopts));
	strncpy(c->channel, channel, CHANNELLEN);
	c->channel[CHANNELLEN] = '\0';
	c->bot_joined = 0;
	c->suspended = 0;
	c->suspend_who = 0;
	c->suspend_time = 0L;
	c->suspend_reason = NULL;
	c->flags = flags;
	c->modes = modes;
	c->owner = owner;
	c->bans = NULL;
	c->key[0] = '\0';
	c->limit[0] = '\0';
	c->cur_limit[0] = '\0';
	c->welcome[0] = '\0';
	c->last_topic[0] = '\0';
	c->first_joined = 0L;
	c->last_joined = 0L;
	c->nr_joined = 0;
	c->current_joined = 0;
	c->limit_next_check = 0;
	c->limit_last_joined = 0;
	c->limit_delay = 60;
	c->limit_change = 10;
	/* c->l_auser = NULL; */

#ifdef AUTH_HISTORY
	for (i = 0; i < AUTH_HISTORY_LEN; i++) {
		c->auth_history_nick[i][0] = '\0';
		c->auth_history_userid[i] = 0;
	}
	c->auth_history_pos = 0;
#endif

	if (topic) {
		strncpy(c->topic, topic, TOPICLEN);
		c->topic[TOPICLEN] = '\0';
	} else {
		c->topic[0] = '\0';
	}

	el = NewElem(c);
	c->h_elem = el;
	AddElem(channels, el);

	el = NewElem(c);
	c->l_elem = el;
	AddTail(channel_list, el);
	return c;
}

/*
 * Since the channel list is linked in one direction only, this function is
 * more than averagely complex given the task it is performing.
 */
int delchannel(char *channel)
{
	struct chanpriv *p, *op, *lp;
	struct dbaseuser *u;
	struct chanopts *c;
	ListElem *el;
	int nr_chans = 0;

	el = Find(channels, channel, compare_strcasecmp);
	if (el) {
		c = ((struct chanopts *) el->data);
		partchannel(c);							/* leave the channel at irc */
		Remove(c->h_elem);
		FreeElem(c->h_elem);
		c->h_elem = NULL;
		Remove(c->l_elem);
		FreeElem(c->l_elem);				/* This will free the banlist & the object itself */
		// c->l_elem = NULL;  // bugfix or??? 13 Dec 2000
	} else {
		log("delchannel: ERROR couldn't find channel [%s]\n", channel);
		printf("delchannel: ERROR couldn't find channel [%s]\n", channel);
	}

	el = GetHead(dbuser_list);
	while (el) {
		u = (struct dbaseuser *) el->data;
		/* loop through all users in database */
		p = u->privs;
		lp = NULL;
		while (p) {
			/* if user are registrerred in that channel, remove it */
			if (!strcasecmp(channel, p->channel)) {
				if (p == u->privs) {
					u->privs = p->next;
					Free(p);
					p = u->privs;
				} else {
					lp->next = p->next;
					op = p->next;
					Free(p);
					p = op;
				}
				nr_chans++;							/* chan removed */
				break;
			} else {
				lp = p;
				p = p->next;
			}
		}
		el = GetSucc(el);
	}
	return nr_chans;
}

void reloadchannels(void)
{
	struct dbaseuser *db;
	struct chanpriv *cp, *cp2;
	struct ban *b, *b2;
	struct chanopts *c;
	struct activeuser *u;
	struct ListElem *el, *el2;

	saveudbase();
	savechannellist();

	/* remove all active users */
	el = GetHead(auser_list);
	while (el) {
		u = (struct activeuser *) el->data;
		el2 = GetSucc(el);
		disconnect_auser(u);
		el = el2;
	}

	el = GetHead(channel_list);
	while (el) {
		c = (struct chanopts *) el->data;
		b = c->bans;
		while (b) {
			b2 = b->next;
			Free(b);
			b = b2;
		}
		c->bans = NULL;
		Remove(c->h_elem);
		FreeElem(c->h_elem);
		c->h_elem = NULL;

		el2 = GetSucc(el);
		Remove(c->l_elem);
		FreeElem(c->l_elem);
		el = el2;
	}

	/* remove all active users */
	el = GetHead(dbuser_list);
	while (el) {
		db = (struct dbaseuser *) el->data;
		cp = db->privs;
		while (cp) {
			cp2 = cp->next;
			Free(cp);
			cp = cp2;
		}
		db->privs = NULL;
		Remove(db->h_elem_nick);
		FreeElem(db->h_elem_nick);
		db->h_elem_nick = NULL;
		Remove(db->h_elem_lastyxx);
		FreeElem(db->h_elem_lastyxx);
		db->h_elem_lastyxx = NULL;
		Remove(db->h_elem_userid);
		FreeElem(db->h_elem_userid);
		db->h_elem_userid = NULL;
		el2 = GetSucc(el);
		Remove(db->l_elem);
		FreeElem(db->l_elem);				/* this will free db pointer */
		el = el2;
	}

	/* load the channel list */
	if (!loadchannellist()) {
		log("reloadchannels: Couldn't load channeldatabase\n");
		exit(1);
	}
	if (!loadglinelist()) {
		printf("reloadchannels: Couldn't load G-line database\n");
		exit(1);
	}
	if (!loadudbase()) {
		log("reloadchannels: Couldn't load userdatabase\n");
		exit(1);
	}

	/* add Q to the channels first */
	/* join_all_channels(0); */
	/* 
	   let the burst add the users to all channels later... The connecting server will provide us
	   with this information */
	return;
}
