/*

oer - IRC bot

See ftp://nic.funet.fi/pub/unix/irc/docs/FAQ.gz section 11 for the
definition of the word bot.

Copyright (C) 2000-2001 EQU <equ@equnet.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 "oer-common.h"
#include "oer.h"
#include "misc.h"
#include "reg.h"

/* prototype definitions */
char *getlamer(struct channel *);
char *safeban(struct channel *, char *, char *);
char *getfirsthostless(struct channel *, char *);
char *isnickbk(struct channel *, char *);
char *getkickreason(char *);
char *deltopic(struct channel *, int, char *);
int partcount(struct channel *, char *, char *);
int loadall(void);
int loadconf(void);
int loadchannels(void);
int loadtrusted(void);
int loadservices(void);
int loadadmins(void);
int loadkickreasons(void);
int loadchaninfo(void);
int saveall(void);
int saveconf(void);
int savechannels(void);
int savetrusted(void);
int saveservices(void);
int saveadmins(void);
int savekickreasons(void);
int savechaninfo(void);
int parseconf(char *);
int parsechannels(char *);
int parsetrusted(char *);
int parseservices(char *);
int parseadmins(char *);
int parsekickreasons(char *);
int parsechaninfo(char *);
int allhostsknown(struct channel *);
int burstjoins(struct channel *);
int isq(char *);
int isservice(char *);
int istrusted(char *);
int isme(char *);
int isvoicenow(struct channel *, char *);
int isatleastopnow(struct channel *, char *, char *);
int isatleastop(struct channel *, char *, char *);
int isallowedop(struct channel *, char *);
int isop(struct channel *, char *);
int isvalidlogon(struct channel *, char *);
int isopa(char *);
int ishostless(char *);
int delopa(char *);
int isadmin(struct channel *, char *);
int isonchan(struct channel *, char *);
int issameuser(struct channel *, char *, char *);
int isopnow(struct channel *, char *);
int iswordbk(struct channel *, char *);
int isflood(struct channel *, char *, char *, char *);
int ispermban(struct channel *, char *);
int ismoderated(struct channel *);
int permbancount(struct channel *);
int topiccount(struct channel *);
int haschanflags(struct channel *, char *);
int hasadminflags(char *, char *);
int nickchange(struct channel *, char *, char *);
int nthmode(char *, int);
int userleft(struct channel *, char *, char *);
int changeuser(struct channel *, char *, int, int, int, int);
int whichctcp(char *);
int whichcommand(char *, int);
int gettobek(struct channel *, char *);
int checkforansi(struct channel *, char *, char *, char *);
int checkforflood(struct channel *, char *, char *, char *);
int checkforbadword(struct channel *, char *, char *, char *);
int checkforbadnick(struct channel *, char *, char *);
int checkforautorejoin(struct channel *, char *, char *);
int checkforpartrejoin(struct channel *, char *, char *);
int delchankey(struct channel *);
int delpermban(struct channel *, char *);
int delnickbk(struct channel *, char *);
int delwordbk(struct channel *, char *);
int uptime(time_t *, time_t *);
int deladmin(char *);
int deladminmask(char *, char *);
int deluser(struct channel *, char *);
int delusermask(struct channel *, char *, char *);
int deltrusted(char *);
int delservice(char *);
int delchannel(struct channel *);
int delkickreason(char *);
int deladverts(struct channel *, char *);
int getondiskmsgcount(struct channel *, char *);
int getondiskjoincount(struct channel *, char *);
int getjoincount(struct channel *, char *);
int getrandom(int);
int getrandommsg(struct channel *, char *, int, int, char *, int);
int delserver(char *, int, int, int, int, int);
int setpassword(struct channel *, char *, char *);
int logoff(struct channel *, char *);
int logon(struct channel *, char *, char *, char *, int);
int timed_cmp(struct timed *, struct timed *);
int clonechannel(char *, char *);
int isfriend(struct channel *, char *, char *);
int deltachanflags(struct channel *, char *, char);
int isvalidchannel(char *);
struct authed *addnewauthed(struct channel *, time_t, struct botuser *, char *);
struct botuser *addbotuser(char *, char *);
struct botuser *addnewadmin(char *, char *);
struct botuser *addnewuser(struct channel *, char *, char *);
struct channel *addnewchannel(char *);
struct channel *getchptr(char *);
struct chanuser *userjoined(struct channel *, char *, char *, int, int, int, int);
struct chanuser *getcuptr(struct channel *, char *);
struct state *emptystate(void);
struct server *getserver(void);
struct server *addnewserver(char *, int, int, int, int, int, char *);
struct advert *addnewadvert(struct channel *, char *, char *);
struct maskstruct *addnewpermban(struct channel *, char *);
struct maskstruct *addnewnickbk(struct channel *, char *, char *);
struct maskstruct *addnewwordbk(struct channel *, char *);
struct maskstruct *addnewtrusted(char *);
struct maskstruct *addnewservice(char *);
struct maskstruct *addnewkickreason(char *);
struct maskstruct *addnewusermask(struct channel *, char *, char *);
struct maskstruct *addnewadminmask(char *, char *);
struct maskstruct *editmask(struct maskstruct *, char *, char *);
struct pubmsg *addnewpubmsg(struct channel *, time_t, char *, char *, char *);
struct part *addnewpart(struct channel *, time_t, char *, char *);
struct join *addnewjoin(struct channel *, time_t, char *, char *);
struct topic *addnewtopic(struct channel *, char *, char *);
struct topic *insertnewtopic(struct channel *, char *, char *, int);
struct topic *edittopic(struct channel *, char *, char *, int);
struct chanuser *getrandomuser(struct channel *);
struct ls_wrapstruct *addnew_ls(struct ls_wrapstruct *, int, time_t, char *, char *, char *);
struct timed *timed_new(struct channel *, time_t, int, int, char *);
struct mmode *mmode_new(struct channel *, time_t, char *, char *);
struct botuser *hasuserflags(struct channel *, char *, char *);
time_t lastoff(struct channel *, char *, char *);
void sendadverts(struct channel *, char *, char *);
void resetparts(struct channel *, char *, char *);
void initpubmsgs(struct channel *);
void freepubmsguser(struct channel *, char *, char *);
void lockchan(struct channel *, char *, int, char *, char *);
void unlockchan(struct channel *, char *, char *);
void initchannel(struct channel *);
void initall(void);
void initnicks(struct channel *);
void initmmodes(struct channel *);
void initparts(struct channel *);
void initjoins(struct channel *);
void initnickbks(struct channel *);
void initwordbks(struct channel *);
void initpermbans(struct channel *);
void initusers(struct channel *);
void linenoise(void);
void checkstoned(void);
void delalltopics(struct channel *);
void swaptopic(struct channel *, int, int);
void gettopic(struct channel *, char *);
void listtopic(struct channel *, char *, int);
void setuserhost(char *, char *);
void sethostquerystatus(char *, int);
void banuser(struct channel *, time_t, char *);
void unbanuser(struct channel *, time_t, char *);
void kickuser(struct channel *, time_t, char *, char *);
void sendreply(char *, int, int, int, char *);
void sendchannelnotice(struct channel *, int, char *);
void listcommand(struct channel *, char *, int, char *, char *, char *);
void addcommand(struct channel *, char *, int, char *, char *, char *);
void delcommand(struct channel *, char *, int, char *, char *, char *);
void editcommand(struct channel *, char *, int, char *, char *, char *);
void logoffcommand(struct channel *, char *, int, char *, char *, char *);
void logoncommand(struct channel *, char *, int, char *, char *, char *);
void isjoined(struct channel *);
void changetobek(struct channel *, char *, int);
void setnick(void);
void setumode(void);
void syncvoices(struct channel *);
void syncops(struct channel *);
void syncbans(struct channel *);
void syncnickbks(struct channel *);
void syncuserhosts(struct channel *);
void settopic(struct channel *);
void processenv(void);
void processnetjoin(struct channel *);
void mmodes2timeds(struct channel *);
void processlock(struct channel *);
void joinchannel(struct channel *);
void updatelast(struct channel *, char *, char *, char *);
void updateseen(struct channel *, char *, char *);
void showlast(struct channel *, char *, int, char *);
void showseen(struct channel *, char *, int, char *);
void setchanmode(struct channel *);
void setchankey(struct channel *, char *);
void channelsync(struct channel *);
void cleartobeks(struct channel *);
void getnthmode(char *, int, char *);
void massmessage(char *, char *);
void flushall(void);
void quit(void);
void parsectcp(char *, char *, int, char *);
void nstats(struct channel *, char *, int, char *);
void wall(struct channel *, char *, char *, char *);
void cleanautheds(struct channel *);
void listchanusers(void);
void timed_del(struct timed *);
void mmode_del(struct channel *, struct mmode *);

void listchanusers()
{
        struct channel *this;
        struct chanuser *cu;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "listchanusers()\n");
#endif
        for(this = mystate->channels; this != NULL; this = this->next) {
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "there are %d users on %s%s\n", this->nickcount, this->name, this->i_am_op ? "" : " *");
#endif
                cu = this->nicks;
                while(cu != NULL) {
#ifdef DEBUG
                        oer_debug(OER_DEBUG_FLOOD, "channel: %s, nick: %s, host: %s, oper: %s, op: %s, voice: %s\n", this->name, cu->nick, (cu->userhost == NULL) ? "(null)" : cu->userhost, (cu->ircop) ? "YES" : "NO", (cu->chanop) ? "YES" : "NO", (cu->voice) ? "YES" : "NO");
#endif
                        cu = cu->next;
		}
	}
}

void quit(void)
{
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "quit()\n");
#endif
	if(mystate->current_server == NULL || mystate->current_server->registered != OER_REGISTERCONNECTION_STATUS_DONE) {
		close(mystate->sockfd);
		exit(EXIT_SUCCESS);
	}
	snprintf(timed_str, WRITE_BUFFER_LENGTH, "QUIT :%s", mystate->signoff);
	timed_new(NULL, mystate->now, OER_TIMED_TYPE_NORMAL, OER_TIMED_PRIORITY_CHANNEL_HANDLING, timed_str);
	mystate->quitting = 1;
}

void flushall(void)
{
	struct channel *this;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "flushall()");
#endif
	for(this = mystate->channels; this != NULL; this = this->next) {
		if(this->seen != NULL) {
			fflush(this->seen);
		}
		if(this->last != NULL) {
			fflush(this->last);
		}
	}
	if(mystate->logfp != NULL) {
		fflush(mystate->logfp);
	}
	if(mystate->logto != NULL) {
		fflush(mystate->logto);
	}
}

int uptime(time_t *uptime, time_t *idle)
{
        float this_uptime;
        float this_idle;
	FILE *fp;
        char stringbuffer[STRINGLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "uptime()\n");
#endif
	/* read uptime & idle from /proc */
        if((fp = fopen("/proc/uptime", "r")) == NULL) {
		return 0;
	}
        while(fgets(stringbuffer, STRINGLEN, fp) != NULL) {
                sscanf(stringbuffer, "%f %f", &this_uptime, &this_idle);
        }
	fclose(fp);
	*uptime = this_uptime;
	*idle = this_idle;
	return 1;
}

int delpermban(struct channel *this, char *mask)
{
	struct maskstruct *ms;
	struct maskstruct *ms2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "delparmban(\"%s\", \"%s\")\n", this->name, mask);
#endif
	/* find & remove mask */
	ms = this->permbans;
	ms2 = ms;
	while(ms != NULL) {
		ms2 = ms;
		if(!strcasecmp(ms->mask, mask)) {
			break;
		}
		ms = ms2->next;
	}
	if(ms == NULL) {
		return 0;
	}
	/* adjust lists */
	if(ms->prev == NULL) {
		/* first mask in list */
		this->permbans = ms->next;
		/* ... check if also last */
		if(ms->next != NULL) {
			ms->next->prev = NULL;
		}
	}
	if(ms->next == NULL) {
		/* last mask in list */
		/* ... check also if the only one */
		if(ms->prev != NULL) {
			ms->prev->next = NULL;
		}
	}
	if(ms->prev != NULL && ms->next != NULL) {
		/* between 2 or more masks */
		ms->prev->next = ms->next;
		ms->next->prev = ms->prev;
	}
	free(ms->mask);
	free(ms->optstring);
	free(ms);
	return 1;
}

int delkickreason(char *kickreason)
{
	struct maskstruct *ms;
	struct maskstruct *ms2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "delkickreason(\"%s\")\n", kickreason);
#endif
	/* find & remove mask */
	ms = mystate->kickreasons;
	ms2 = ms;
	while(ms != NULL) {
		ms2 = ms;
		if(!strcasecmp(ms->mask, kickreason)) {
			break;
		}
		ms = ms2->next;
	}
	if(ms == NULL) {
		return 0;
	}
	/* adjust lists */
	if(ms->prev == NULL) {
		/* first mask in list */
		mystate->kickreasons = ms->next;
		/* ... check if also last */
		if(ms->next != NULL) {
			ms->next->prev = NULL;
		}
	}
	if(ms->next == NULL) {
		/* last mask in list */
		/* ... check also if the only one */
		if(ms->prev != NULL) {
			ms->prev->next = NULL;
		}
	}
	if(ms->prev != NULL && ms->next != NULL) {
		/* between 2 or more masks */
		ms->prev->next = ms->next;
		ms->next->prev = ms->prev;
	}
	free(ms->mask);
	free(ms->optstring);
	free(ms);
	mystate->kickrs--;
	return 1;
}

int delserver(char *serverhost, int serverport, int servermodes, int pingfrequency, int protected_ircops, int linenoise)
{
	struct server *s;
	struct server *s2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "delserver(\"%s\", %d, %d, %d, %d, %d)\n", serverhost, serverport, servermodes, pingfrequency, protected_ircops, linenoise);
#endif
	/* find & remove server */
	s = mystate->servers;
        s2 = s;
        while(s != NULL) {
                s2 = s;
                if(!strcasecmp(s->serverhost, serverhost) && s->serverport == serverport && s->servermodes == servermodes && s->pingfrequency == pingfrequency && s->protected_ircops == protected_ircops && s->linenoise == linenoise) {
			break;
                }
                s = s2->next;
        }
	if(s == NULL) {
		return 0;
	}
	if(s == mystate->current_server) {
		/* we won't remove the current server */
		return 0;
	}
	/* adjust lists */
	if(s->prev == NULL) {
		/* first mask in list */
		mystate->servers = s->next;
		/* ... check if also last */
		if(s->next != NULL) {
			s->next->prev = NULL;
		}
	}
	if(s->next == NULL) {
		/* last server in list */
		/* ... check also if the only one */
		if(s->prev != NULL) {
			s->prev->next = NULL;
		}
	}
	if(s->prev != NULL && s->next != NULL) {
		/* between 2 or more servers */
		s->prev->next = s->next;
		s->next->prev = s->prev;
	}
	free(s);
	return 1;
}

int delnickbk(struct channel *this, char *mask)
{
	struct maskstruct *ms;
	struct maskstruct *ms2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "delnickbk(\"%s\", \"%s\")\n", this->name, mask);
#endif
	/* find & remove mask */
	ms = this->nickbks;
	ms2 = ms;
	while(ms != NULL) {
		ms2 = ms;
		if(!strcasecmp(ms->mask, mask)) {
			break;
		}
		ms = ms2->next;
	}
	if(ms == NULL) {
		return 0;
	}
	/* adjust lists */
	if(ms->prev == NULL) {
		/* first mask in list */
		this->nickbks = ms->next;
		/* ... check if also last */
		if(ms->next != NULL) {
			ms->next->prev = NULL;
		}
	}
	if(ms->next == NULL) {
		/* last mask in list */
		/* ... check also if the only one */
		if(ms->prev != NULL) {
			ms->prev->next = NULL;
		}
	}
	if(ms->prev != NULL && ms->next != NULL) {
		/* between 2 or more masks */
		ms->prev->next = ms->next;
		ms->next->prev = ms->prev;
	}
	free(ms->mask);
	free(ms->optstring);
	free(ms);
	return 1;
}

int delwordbk(struct channel *this, char *mask) 
{
	struct maskstruct *ms;
	struct maskstruct *ms2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "delwordbk(\"%s\", \"%s\")\n", this->name, mask);
#endif
	/* find & remove mask */
	ms = this->wordbks;
	ms2 = ms;
	while(ms != NULL) {
		ms2 = ms;
		if(!strcasecmp(ms->mask, mask)) {
			break;
		}
		ms = ms2->next;
	}
	if(ms == NULL) {
		return 0;
	}
	/* adjust lists */
	if(ms->prev == NULL) {
		/* first mask in list */
		this->wordbks = ms->next;
		/* ... check if also last */
		if(ms->next != NULL) {
			ms->next->prev = NULL;
		}
	}
	if(ms->next == NULL) {
		/* last mask in list */
		/* ... check also if the only one */
		if(ms->prev != NULL) {
			ms->prev->next = NULL;
		}
	}
	if(ms->prev != NULL && ms->next != NULL) {
		/* between 2 or more masks */
		ms->prev->next = ms->next;
		ms->next->prev = ms->prev;
	}
	free(ms->mask);
	free(ms->optstring);
	free(ms);
	return 1;
}

int getondiskmsgcount(struct channel *this, char *thisnick)
{
	int i;
	int msgcount;
	int len;
	char outstring[TXT_BUFFER + 1];
	char nick[NICKLEN + 1];
	char *p;
	char *q;
	FILE *fp;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "getondiskmsgcount(\"%s\", \"%s\")\n", this->name, thisnick);
#endif
	if(this->last == NULL) {
		return -1;
	}
	fflush(this->last);
	snprintf(outstring, HUGESTRINGLEN, "last-%s", this->name);
	if((fp = fopen(outstring, "r")) == NULL) {
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "getondiskmsgcount->failed to open %s: %s\n", outstring, strerror(errno));
#endif
		return -1;
	}
	msgcount = 0;
	while(fgets(outstring, TXT_BUFFER, fp)) {
		p = outstring;
		/* striplf() */
		for(i = strlen(p) - 1; isspace((int)p[i]) && i >= 0; i--) {
			p[i] = '\0';
		}
		/* first field is the timestamp, skip to next */
		for(;*p != '\0'; p++) {
			if(!strncasecmp(p, OER_DEFAULT_DELIM, OER_DEFAULT_DELIM_LEN)) {
				p += 2;
				break;
			}
		}
		if(*p == '\0') {
			continue;
		}
		q = p;
		/* 2nd field is the nick */
		for(;*p != '\0'; p++) {
			if(!strncasecmp(p, OER_DEFAULT_DELIM, OER_DEFAULT_DELIM_LEN)) {
				p += 2;
				break;
			}
		}
		if(*p == '\0') {
			continue;
		}
		len = (p - q) - OER_DEFAULT_DELIM_LEN;
		len = (len > NICKLEN) ? NICKLEN : len;
		strncpy(nick, q, len);
		nick[len] = '\0';
		if(!strcasecmp(thisnick, nick)) {
			msgcount++;
		}
	}
	fclose(fp);
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "getondiskmsgcount->returning %d\n", msgcount);
#endif
	return msgcount;
}

int getondiskjoincount(struct channel *this, char *thisnick)
{
	int i;
	int joincount;
	int len;
	char outstring[TXT_BUFFER + 1];
	char nick[NICKLEN + 1];
	char *p;
	char *q;
	FILE *fp;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "getondiskjoincount(\"%s\", \"%s\")\n", this->name, thisnick);
#endif
	if(this->seen == NULL) {
		return -1;
	}
	fflush(this->seen);
	snprintf(outstring, HUGESTRINGLEN, "seen-%s", this->name);
	if((fp = fopen(outstring, "r")) == NULL) {
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "getondiskjoincount->failed to open %s: %s\n", outstring, strerror(errno));
#endif
		return -1;
	}
	joincount = 0;
	while(fgets(outstring, TXT_BUFFER, fp)) {
		p = outstring;
		/* striplf() */
		for(i = strlen(p) - 1; isspace((int)p[i]) && i >= 0; i--) {
			p[i] = '\0';
		}
		/* first field is the timestamp, skip to next */
		for(;*p != '\0'; p++) {
			if(!strncasecmp(p, OER_DEFAULT_DELIM, OER_DEFAULT_DELIM_LEN)) {
				p += 2;
				break;
			}
		}
		if(*p == '\0') {
			continue;
		}
		q = p;
		/* 2nd field is the nick */
		for(;*p != '\0'; p++) {
			if(!strncasecmp(p, OER_DEFAULT_DELIM, OER_DEFAULT_DELIM_LEN)) {
				p += 2;
				break;
			}
		}
		if(*p == '\0') {
			continue;
		}
		len = (p - q) - OER_DEFAULT_DELIM_LEN;
		len = (len > NICKLEN) ? NICKLEN : len;
		strncpy(nick, q, len);
		nick[len] = '\0';
		if(!strcasecmp(thisnick, nick)) {
			joincount++;
		}
	}
	fclose(fp);
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "getondiskjoincount->returning %d\n", joincount);
#endif
	return joincount;
}

int getrandommsg(struct channel *this, char *thisnick, int nicklen, int which, char *msgstore, int msgstorelen)
{
	int i;
	int pos;
	int strpos;
	int len;
	int rc;
	char outstring[TXT_BUFFER + 1];
	char nick[NICKLEN + 1];
	char message[HUGESTRINGLEN + 1];
	char *p;
	char *q;
	FILE *fp;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "getrandommsg(\"%s\", \"%s\", %d, %d, %d)\n", this->name, thisnick, nicklen, which, msgstorelen);
#endif
	if(this->last == NULL) {
		return -1;
	}
	fflush(this->last);
	snprintf(outstring, TXT_BUFFER, "last-%s", this->name);
	if((fp = fopen(outstring, "r")) == NULL) {
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "getrandommsg->failed to open %s: %s\n", outstring, strerror(errno));
#endif
		return -1;
	}
	pos = 0;
	rc = -1;
	while(fgets(outstring, TXT_BUFFER, fp)) {
		p = outstring;
		/* striplf() */
		for(i = strlen(p) - 1; isspace((int)p[i]) && i >= 0; i--) {
			p[i] = '\0';
		}
		/* first field is the timestamp, skip to next */
		for(;*p != '\0'; p++) {
			if(!strncasecmp(p, OER_DEFAULT_DELIM, OER_DEFAULT_DELIM_LEN)) {
				p += 2;
				break;
			}
		}
		if(*p == '\0') {
			continue;
		}
		q = p;
		/* 2nd field is the nick */
		for(;*p != '\0'; p++) {
			if(!strncasecmp(p, OER_DEFAULT_DELIM, OER_DEFAULT_DELIM_LEN)) {
				p += 2;
				break;
			}
		}
		if(*p == '\0') {
			continue;
		}
		len = (p - q) - OER_DEFAULT_DELIM_LEN;
		len = (len > NICKLEN) ? NICKLEN : len;
		strncpy(nick, q, len);
		nick[len] = '\0';
		if(strcasecmp(thisnick, nick)) {
			/* not the nick we are after */
			continue;
		}
		pos++;
		if(pos < which) {
			/* not the msg we are after */
			continue;
		}
		/* 3rd field is the host */
		for(;*p != '\0'; p++) {
			if(!strncasecmp(p, OER_DEFAULT_DELIM, OER_DEFAULT_DELIM_LEN)) {
				p += 2;
				break;
			}
		}
		if(*p == '\0') {
			continue;
		}
		/* 4th field is the message */
		strpos = 0;
		while(*p != '\0' && strpos < HUGESTRINGLEN) {
			message[strpos++] = *p++;
		}
		message[strpos] = '\0';
		/* don't retrieve too short/long messages */
		if(strlen(message) < OER_QUOTE_MIN_LENGTH || strlen(message) > OER_QUOTE_MAX_LENGTH) {
			continue;
		}
		strncpy(thisnick, nick, nicklen);
		strncpy(msgstore, message, msgstorelen);
		rc = 0;
		break;
	}
	fclose(fp);
	return rc;
}

void showlast(struct channel *this, char *to, int tochan, char *commandline)
{
	int i;
	int strpos;
	int len;
	int ppos;
	int params;
	int last_show;
	char outstring[TXT_BUFFER + 1];
	char pattern[NICKLEN + 1];
	char at[NICKLEN + 1];
	char ts[TINYSTRINGLEN + 1];
	char nick[NICKLEN + 1];
	char host[USERHOSTLEN + 1];
	char message[HUGESTRINGLEN + 1];
        char p1[STRINGLEN + 1];
	char *p;
	char *q;
	struct ls_wrapstruct *first;
	struct ls_wrapstruct *ls;
	struct ls_wrapstruct *ls2;
	FILE *fp;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "showlast(\"%s\", \"%s\", %d, \"%s\")\n", this->name, to, tochan, commandline);
#endif
	last_show = OER_LAST;
	strncpy(pattern, "*", NICKLEN);
	params = wordcount(commandline);
	switch(params) {
		/* case 0 is default */
	case 1:
		ppos = 0;
		if((ppos = parse(commandline, ppos, " ", p1, STRINGLEN, 0)) < 0) {
			return;
		}
		strncpy(pattern, p1, NICKLEN);
		last_show = OER_LAST;
		break;
	case 2:
		ppos = 0;
		if((ppos = parse(commandline, ppos, " ", p1, STRINGLEN, 0)) < 0) {
			return;
		}
		strncpy(pattern, p1, NICKLEN);
		if((ppos = parse(commandline, ppos, " ", p1, STRINGLEN, 0)) < 0) {
			return;
		}
		last_show = atoi(p1);
		if(last_show <= 0 || last_show > OER_LAST_MAX) {
			return;
		}
		break;
	}
	if(this->last == NULL) {
		return;
	}
	fflush(this->last);
	snprintf(outstring, TXT_BUFFER, "last-%s", this->name);
	if((fp = fopen(outstring, "r")) == NULL) {
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "showlast->failed to open %s: %s\n", outstring, strerror(errno));
#endif
		return;
	}
        first = NULL;
	while(fgets(outstring, TXT_BUFFER, fp)) {
		p = outstring;
		/* striplf() */
		for(i = strlen(p) - 1; isspace((int)p[i]) && i >= 0; i--) {
			p[i] = '\0';
		}
		/* first field is the timestamp */
		q = p;
		for(;*p != '\0'; p++) {
			if(!strncasecmp(p, OER_DEFAULT_DELIM, OER_DEFAULT_DELIM_LEN)) {
				p += 2;
				break;
			}
		}
		if(*p == '\0') {
			continue;
		}
		len = (p - q) - OER_DEFAULT_DELIM_LEN;
		len = (len > NICKLEN) ? NICKLEN : len;
		strncpy(at, q, len);
		at[len] = '\0';
		/* 2nd field is the nick */
		q = p;
		for(;*p != '\0'; p++) {
			if(!strncasecmp(p, OER_DEFAULT_DELIM, OER_DEFAULT_DELIM_LEN)) {
				p += 2;
				break;
			}
		}
		if(*p == '\0') {
			continue;
		}
		len = (p - q) - OER_DEFAULT_DELIM_LEN;
		len = (len > NICKLEN) ? NICKLEN : len;
		strncpy(nick, q, len);
		nick[len] = '\0';
		if(!wild_match(pattern, nick)) {
			/* not the nick we are after */
			continue;
		}
		/* 3rd field is the host */
		q = p;
		for(;*p != '\0'; p++) {
			if(!strncasecmp(p, OER_DEFAULT_DELIM, OER_DEFAULT_DELIM_LEN)) {
				p += 2;
				break;
			}
		}
		if(*p == '\0') {
			continue;
		}
		len = (p - q) - OER_DEFAULT_DELIM_LEN;
		len = (len > USERHOSTLEN) ? USERHOSTLEN : len;
		strncpy(host, q, len);
		host[len] = '\0';
		/* last field is the message */
		strpos = 0;
		while(*p != '\0' && strpos < HUGESTRINGLEN) {
			message[strpos++] = *p++;
		}
		message[strpos] = '\0';
		first = addnew_ls(first, last_show, atoi(at), nick, host, message);
	}
	fclose(fp);
	ls = first;
	while(ls != NULL) {
		strncpy(ts, ctime(&ls->at), TINYSTRINGLEN);
		striplf(ts);
		memset(outstring, 0, TXT_BUFFER + 1);
		memset(message, 0, HUGESTRINGLEN + 1);
		if(strlen(ls->str3) > OER_LAST_MAX_LENGTH) {
			strncpy(outstring, ls->str3, OER_LAST_MAX_LENGTH);
			snprintf(message, HUGESTRINGLEN, "%s (truncated)", outstring);
		} else {
			strncpy(message, ls->str3, HUGESTRINGLEN);
		}
		tzset();
		snprintf(outstring, HUGESTRINGLEN, "%s (%s, %s, %s %s %s)", message, ls->str1, ls->str2, ts, tzname[0], tzname[1]);
		sendreply(to, tochan, 0, 0, outstring);
		ls2 = ls->next;
		free(ls->str1);
		free(ls->str2);
		free(ls->str3);
		free(ls);
		ls = ls2;
	}
}

void updatelast(struct channel *this, char *nick, char *userhost, char *message)
{
	char outstring[WRITE_BUFFER_LENGTH + 1];

	if(this->last == NULL) {
		return;
	}
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "updatelast(\"%s\", \"%s\", \"%s\", \"%s\")\n", this->name, nick, userhost, message);
#endif
	snprintf(outstring, WRITE_BUFFER_LENGTH, "%lu::%s::%s::%s\n", mystate->now, nick, userhost, message);
	fputs(outstring, this->last);
}

void showseen(struct channel *this, char *to, int tochan, char *commandline)
{
	int i;
	int strpos;
	int len;
	int ppos;
	int params;
	int seen_show;
	char outstring[TXT_BUFFER + 1];
	char pattern[NICKLEN + 1];
	char at[NICKLEN + 1];
	char ts[TINYSTRINGLEN + 1];
	char nick[NICKLEN + 1];
	char host[USERHOSTLEN + 1];
        char p1[STRINGLEN + 1];
	char *p;
	char *q;
	struct ls_wrapstruct *first;
	struct ls_wrapstruct *ls;
	struct ls_wrapstruct *ls2;
	FILE *fp;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "showseen(\"%s\", \"%s\", %d, \"%s\")\n", this->name, to, tochan, commandline);
#endif
	seen_show = OER_SEEN;
	strncpy(pattern, "*", NICKLEN);
	params = wordcount(commandline);
	switch(params) {
		/* case 0 is default */
	case 1:
		ppos = 0;
		if((ppos = parse(commandline, ppos, " ", p1, STRINGLEN, 0)) < 0) {
			return;
		}
		strncpy(pattern, p1, NICKLEN);
		seen_show = OER_SEEN;
		break;
	case 2:
		ppos = 0;
		if((ppos = parse(commandline, ppos, " ", p1, STRINGLEN, 0)) < 0) {
			return;
		}
		strncpy(pattern, p1, NICKLEN);
		if((ppos = parse(commandline, ppos, " ", p1, STRINGLEN, 0)) < 0) {
			return;
		}
		seen_show = atoi(p1);
		if(seen_show <= 0 || seen_show > OER_SEEN_MAX) {
			return;
		}
		break;
	}
	if(this->seen == NULL) {
		return;
	}
	fflush(this->seen);
	snprintf(outstring, BIGSTRINGLEN, "seen-%s", this->name);
	if((fp = fopen(outstring, "r")) == NULL) {
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "showseen->failed to open %s: %s\n", outstring, strerror(errno));
#endif
		return;
	}
        first = NULL;
	while(fgets(outstring, TXT_BUFFER, fp)) {
		p = outstring;
		/* striplf() */
		for(i = strlen(p) - 1; isspace((int)p[i]) && i >= 0; i--) {
			p[i] = '\0';
		}
		/* first field is the timestamp */
		q = p;
		for(;*p != '\0'; p++) {
			if(!strncasecmp(p, OER_DEFAULT_DELIM, OER_DEFAULT_DELIM_LEN)) {
				p += 2;
				break;
			}
		}
		if(*p == '\0') {
			continue;
		}
		len = (p - q) - OER_DEFAULT_DELIM_LEN;
		len = (len > NICKLEN) ? NICKLEN : len;
		strncpy(at, q, len);
		at[len] = '\0';
		/* 2nd field is the nick */
		q = p;
		for(;*p != '\0'; p++) {
			if(!strncasecmp(p, OER_DEFAULT_DELIM, OER_DEFAULT_DELIM_LEN)) {
				p += 2;
				break;
			}
		}
		if(*p == '\0') {
			continue;
		}
		len = (p - q) - OER_DEFAULT_DELIM_LEN;
		len = (len > NICKLEN) ? NICKLEN : len;
		strncpy(nick, q, len);
		nick[len] = '\0';
		if(!wild_match(pattern, nick)) {
			/* not the nick we are after */
			continue;
		}
		/* last field is the host */
		strpos = 0;
		while(*p != '\0' && strpos < USERHOSTLEN) {
			host[strpos++] = *p++;
		}
		host[strpos] = '\0';
		first = addnew_ls(first, seen_show, atoi(at), nick, host, NULL);
	}
	fclose(fp);
	ls = first;
	while(ls != NULL) {
		strncpy(ts, ctime(&ls->at), TINYSTRINGLEN);
		striplf(ts);
		tzset();
		snprintf(outstring, HUGESTRINGLEN, "I saw %s with host %s last %s %s %s", ls->str1, ls->str2, ts, tzname[0], tzname[1]);
		sendreply(to, tochan, 0, 0, outstring);
		ls2 = ls->next;
		free(ls->str1);
		free(ls->str2);
		free(ls->str3);
		free(ls);
		ls = ls2;
	}
}

void updateseen(struct channel *this, char *nick, char *userhost)
{
	char outstring[BIGSTRINGLEN + 1];

	if(this->seen == NULL) {
		return;
	}
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "updateseen(\"%s\", \"%s\", \"%s\")\n", this->name, nick, userhost);
#endif
	snprintf(outstring, BIGSTRINGLEN, "%lu::%s::%s\n", mystate->now, nick, userhost);
	fputs(outstring, this->seen);
}

void nstats(struct channel *this, char *to, int tochan, char *nick)
{
	int msgs;
	int joins;
	char outstring[BIGSTRINGLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "nstats(\"%s\", \"%s\", %d, \"%s\")\n", this->name, to, tochan, nick);
#endif
	msgs = getondiskmsgcount(this, nick);
	joins = getondiskjoincount(this, nick);
	if(!msgs && !joins) {
		return;
	}
	snprintf(outstring, BIGSTRINGLEN, "I have seen %d joins and %d messages from %s on channel %s", joins, msgs, nick, this->name);
	sendreply(to, tochan, 0, 1, outstring);
}

int checkforpartrejoin(struct channel *this, char *nick, char *userhost)
{
	int parts;
        char ban[USERHOSTLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "checkforpartrejoin(\"%s\", \"%s\", \"%s\")\n", this->name, nick, userhost);
#endif
	if(!haschanflags(this, "r")) {
                return 0;
        }
	if(isq(nick)) {
		return 0;
	}
        if(isadmin(this, userhost)) {
                return 0;
        }
        if(isop(this, userhost)) {
                return 0;
        }
        if(isopnow(this, nick)) {
                return 0;
        }
	if(haschanflags(this, "F") && isfriend(this, nick, userhost)) {
		return 0;
	}
	parts = partcount(this, nick, userhost);
#ifdef DEBUG
        oer_debug(OER_DEBUG_INFO, "checkforpartrejoin->credited parts on %s for %s!%s: %d\n", this->name, nick, userhost, parts);
#endif
        if(parts < OER_ALLOWED_PARTS) {
                return 0;
        }
        strncpy(ban, userhost, USERHOSTLEN);
        if(safeban(this, ban, nick) == NULL) {
                return 0;
        }
	mmode_new(this, mystate->now - 1, "+b", ban);
        snprintf(timed_str, WRITE_BUFFER_LENGTH, "KICK %s %s :part-rejoin protection triggered, come back when you have made up your mind", this->name, nick);
        timed_new(NULL, mystate->now, OER_TIMED_TYPE_NORMAL, OER_TIMED_PRIORITY_CHANNEL_PROTECTION, timed_str);
	mmode_new(this, mystate->now + this->banvars.part_rejoin, "-b", ban);
        return 1;
}

int checkforautorejoin(struct channel *this, char *nick, char *userhost)
{
        char ban[USERHOSTLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "checkforautorejoin(\"%s\", \"%s\", \"%s\")\n", this->name, nick, userhost);
#endif
        if(!haschanflags(this, "r")) {
                return 0;
        }
	if(isq(nick)) {
                return 0;
        }
        if(isadmin(this, userhost)) {
                return 0;
        }
        if(isop(this, userhost)) {
                return 0;
        }
        if(isopnow(this, nick)) {
                return 0;
        }
        if(haschanflags(this, "F") && isfriend(this, nick, userhost)) {
		return 0;
	}
        if(lastoff(this, nick, userhost) > OER_AUTO_REJOIN_TIME) {
                return 0;
        }
        strncpy(ban, userhost, USERHOSTLEN);
        if(safeban(this, ban, nick) == NULL) {
                return 0;
        }
	mmode_new(this, mystate->now - 1, "+b", ban);
        snprintf(timed_str, WRITE_BUFFER_LENGTH, "KICK %s %s :auto-rejoin, your prize: a %d second ban", this->name, nick, this->banvars.auto_rejoin);
        timed_new(NULL, mystate->now, OER_TIMED_TYPE_NORMAL, OER_TIMED_PRIORITY_CHANNEL_PROTECTION, timed_str);
	mmode_new(this, mystate->now + this->banvars.auto_rejoin, "-b", ban);
        return 1;
}

int checkforansi(struct channel *this, char *nick, char *userhost, char *message)
{
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "checkforansi(\"%s\", \"%s\", \"%s\")\n", this->name, nick, userhost);
#endif
        if(!haschanflags(this, "a")) {
                return 0;
        }
	if(isq(nick)) {
                return 0;
        }
        if(isadmin(this, userhost)) {
                return 0;
        }
        if(isop(this, userhost)) {
                return 0;
        }
        if(isopnow(this, nick)) {
                return 0;
        }
        if(haschanflags(this, "F") && isfriend(this, nick, userhost)) {
		return 0;
	}
        if(!isansi(message)) {
                return 0;
        }
#ifdef DEBUG
        oer_debug(OER_DEBUG_INFO, "checkforansi->ansi/control-codes detected on %s from %s\n", this->name, nick);
#endif
	snprintf(timed_str, WRITE_BUFFER_LENGTH, "KICK %s %s :mIRC or non-standard control codes not permitted on this channel", this->name, nick);
        timed_new(NULL, mystate->now, OER_TIMED_TYPE_NORMAL, OER_TIMED_PRIORITY_CHANNEL_PROTECTION, timed_str);
        return 1;
}

int ismoderated(struct channel *this)
{
	int moderated;
	char *p;
	char *q;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "ismoderated(\"%s\")\n", this->name);
#endif
	moderated = 0;
	if((p = index(this->mode, 'm')) != NULL) {
		for(q = this->mode; q != p; q++) {
			if(*q == '+') {
				moderated = 1;
			}
			if(*q == '-') {
				moderated = 0;
			}
		}
	}
	return moderated;
}

int checkforflood(struct channel *this, char *nick, char *userhost, char *message)
{
        int floodtype;
        char ban[USERHOSTLEN + 1];
        char outstring[STRINGLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "checkforflood(\"%s\", \"%s\", \"%s\", \"%s\")\n", this->name, nick, userhost, message);
#endif
        if(!haschanflags(this, "f")) {
                return 0;
        }
	if(isq(nick)) {
                return 0;
        }
        if(isadmin(this, userhost)) {
                return 0;
        }
        if(!haschanflags(this, "G")) {
		if(isop(this, userhost)) {
			return 0;
		}
		if(isopnow(this, nick)) {
			return 0;
		}
		if(haschanflags(this, "F") && isfriend(this, nick, userhost)) {
			return 0;
		}
	}
        floodtype = isflood(this, nick, userhost, message);
        if(floodtype != OER_PUBMSG_FLOOD_NORMAL && floodtype != OER_PUBMSG_FLOOD_REPEAT) {
                return 0;
        }
        strncpy(ban, userhost, USERHOSTLEN);
        if(safeban(this, ban, nick) == NULL) {
                return 0;
        }
        switch(floodtype) {
        case OER_PUBMSG_FLOOD_NORMAL:
		if(isvoicenow(this, nick) && ismoderated(this)) {
			mmode_new(this, mystate->now, "-v", nick);
			mmode_new(this, mystate->now + this->banvars.public_flood, "+v", nick);
		}
		mmode_new(this, mystate->now, "+b", ban);
                snprintf(outstring, STRINGLEN, "flood protection triggered, you can talk again in %d seconds", this->banvars.public_flood);
                sendreply(nick, 0, 0, 1, outstring);
		mmode_new(this, mystate->now + this->banvars.public_flood, "-b", ban);
		freepubmsguser(this, nick, userhost);
                return 1;
        case OER_PUBMSG_FLOOD_REPEAT:
		mmode_new(this, mystate->now - 1, "+b", ban);
                snprintf(timed_str, WRITE_BUFFER_LENGTH, "KICK %s %s :bad choice of channel for your repeating fetish", this->name, nick);
		timed_new(NULL, mystate->now, OER_TIMED_TYPE_NORMAL, OER_TIMED_PRIORITY_CHANNEL_PROTECTION, timed_str);
		mmode_new(this, mystate->now + this->banvars.public_flood_repeat, "-b", ban);
		changetobek(this, nick, 1);
		freepubmsguser(this, nick, userhost);
                return 1;
        case OER_PUBMSG_FLOOD_NONE:
                return 0;
        }
        return 0;
}

int checkforbadword(struct channel *this, char *nick, char *userhost, char *message)
{
        char ban[USERHOSTLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "checkforbadword(\"%s\", \"%s\", \"%s\", \"%s\")\n", this->name, nick, userhost, message);
#endif
        if(!haschanflags(this, "w")) {
                return 0;
        }
	if(isq(nick)) {
                return 0;
        }
        if(isadmin(this, userhost)) {
                return 0;
        }
        if(isop(this, userhost)) {
                return 0;
        }
        if(isopnow(this, nick)) {
                return 0;
        }
        if(haschanflags(this, "F") && isfriend(this, nick, userhost)) {
		return 0;
	}
        if(!iswordbk(this, message)) {
                return 0;
        }
        strncpy(ban, userhost, USERHOSTLEN);
        if(safeban(this, ban, nick) == NULL) {
                return 0;
        }
        mmode_new(this, mystate->now - 1, "+b", ban);
        snprintf(timed_str, WRITE_BUFFER_LENGTH, "KICK %s %s :word ban-kick triggered, have a nice life...", this->name, nick);
        timed_new(NULL, mystate->now, OER_TIMED_TYPE_NORMAL, OER_TIMED_PRIORITY_CHANNEL_PROTECTION, timed_str);
        mmode_new(this, mystate->now + this->banvars.bad_word, "-b", ban);
        return 1;
}

int checkforbadnick(struct channel *this, char *nick, char *userhost)
{
	char *reason;
        char ban[USERHOSTLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "checkforbadnick(\"%s\", \"%s\", \"%s\")\n", this->name, nick, userhost);
#endif
        if(!haschanflags(this, "n")) {
                return 0;
        }
	if(isq(nick)) {
                return 0;
        }
        if(isadmin(this, userhost)) {
                return 0;
        }
        if(isop(this, userhost)) {
                return 0;
        }
        if(isopnow(this, nick)) {
                return 0;
        }
	/* we don't allow friends to use prohibited nicks,
	   syncnickbks would kick them anyway */
	if((reason = isnickbk(this, nick)) == NULL) {
                return 0;
        }
        strncpy(ban, userhost, USERHOSTLEN);
        if(safeban(this, ban, nick) == NULL) {
                return 0;
        }
	mmode_new(this, mystate->now - 1, "+b", ban);
	/* if there was no ban reason, reason = nick */
	snprintf(timed_str, WRITE_BUFFER_LENGTH, "KICK %s %s :%s", this->name, nick, DEFAULT_NICKBK_MESSAGE);
	if(strcasecmp(nick, reason)) {
		snprintf(timed_str, WRITE_BUFFER_LENGTH, "KICK %s %s :%s, reason: %s", this->name, nick, DEFAULT_NICKBK_MESSAGE, reason);
	}
        timed_new(NULL, mystate->now, OER_TIMED_TYPE_NORMAL, OER_TIMED_PRIORITY_CHANNEL_PROTECTION, timed_str);
	mmode_new(this, mystate->now + this->banvars.bad_nick, "-b", ban);
        return 1;
}

void processenv()
{
        struct channel *this;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "processenv()\n");
#endif
        /* check if connected */
        if(mystate->current_server == NULL) {
                return;
        }
        /* check if registered */
        if(mystate->current_server->registered != OER_REGISTERCONNECTION_STATUS_DONE) {
                return;
        }
	/* these are channel independent actions and 
           are processed dependless of netjoin */
	linenoise();
	checkstoned();
	setumode();
	setnick();
	/* check when configs were last saved */
	if(mystate->now -  mystate->last_saved > OER_SAVE_INTERVAL) {
		saveall();
		mystate->last_saved = mystate->now;
	}
	/* check if we are in netjoin */
        if(mystate->netjoining && mystate->now < mystate->postnj_checks_at) {
		/* don't process anything until netjoin is over */
		return;
	}
	/* main loop, process everything for all channels
           NOTE: all functions in the following loop pass a 
           pointer to the channel structure, a optimization */
	for(this = mystate->channels; this != NULL; this = this->next) {
		/* join unjoined channel */
		joinchannel(this);
		/* check if we actually have joined the channel */
		if(!this->joined) {
			continue;
		}
		/* process post netjoin actions */
		processnetjoin(this);
		/* check that all channels are synced */
		if(!this->synced) {
			channelsync(this);
		}
		/* check for unset channel modes */
		setchanmode(this);
		/* convert all mmodes to timeds for this channel */
		mmodes2timeds(this);
		/* get missing hostmasks of nicks on channel */
		syncuserhosts(this);
		/* process possible lock for channel */
		processlock(this);
		/* set topics for this channel */
		settopic(this);
	}
	/* reset netjoin flag if post netjoin actions were processed */
	if(mystate->netjoining && mystate->now >= mystate->postnj_checks_at) {
		mystate->netjoining = 0;
		mystate->postnj_checks_at = 0;
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "processenv->end of netjoin\n");
#endif
	}
}

void setchanmode(struct channel *this)
{
	char outstring[STRINGLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "setchanmode(\"%s\")\n", this->name);
#endif
	if(!this->setchanmode || !this->i_am_op) {
		return;
	}
	if(strlen(this->mode)) {
		if(this->haskey) {
			snprintf(outstring, STRINGLEN, "%sk", this->mode);
			mmode_new(this, mystate->now, outstring, this->key);
		} else {
			mmode_new(this, mystate->now, this->mode, NULL);
		}
	}
	this->setchanmode = 0;
}

void joinchannel(struct channel *this)
{
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "joinchannel(\"%s\")\n", this->name);
#endif
	if(this->joined || this->joining || mystate->now < this->rejoin_at || haschanflags(this, "!")) {
		return;
	}
	this->joined = 0;
	this->joining = 1;
	this->rejoin_at = 0;
	this->last_quote = 0;
	this->synced = 0;
	snprintf(timed_str, WRITE_BUFFER_LENGTH, "JOIN %s", this->name);
	if(this->haskey) {
		snprintf(timed_str, WRITE_BUFFER_LENGTH, "JOIN %s %s", this->name, this->key);
	}
        timed_new(NULL, mystate->now, OER_TIMED_TYPE_NORMAL, OER_TIMED_PRIORITY_CHANNEL_HANDLING, timed_str);
}

void processnetjoin(struct channel *this)
{
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "processnetjoin(\"%s\")\n", this->name);
#endif
	if(!mystate->netjoining || mystate->now < mystate->postnj_checks_at) {
		return;
	}
	if(!haschanflags(this, "p") || !this->i_am_op) {
		return;
	}
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "processnetjoind->netjoin actions for %s\n", this->name);
#endif
	syncvoices(this);
	syncops(this);
	syncbans(this);
	syncnickbks(this);
}

void channelsync(struct channel *this)
{
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "channelsync(\"%s\")\n", this->name);
#endif
	/* are we through with USERHOST queries? */
	if(!allhostsknown(this)) {
		return;
	}
	if(!this->i_am_op) {
		return;
	}
	syncvoices(this);
	syncops(this);
	syncbans(this);
	syncnickbks(this);
	this->synced = 1;
}

void processlock(struct channel *this)
{
	int nicks;
        char *nick;
        char stringbuffer[BIGSTRINGLEN + 1];
        char stringbuffer2[BIGSTRINGLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "processlock(\"%s\")\n", this->name);
#endif
	if(!this->joined || !this->locked.locked) {
		return;
	}
	if(this->locked.unlocked) {
		/* channel has been unlocked due to manual prevention */
		this->locked.locked = 0;
		return;
	}
	memset(&stringbuffer, 0, BIGSTRINGLEN + 1);
	memset(&stringbuffer2, 0, BIGSTRINGLEN + 1);
	nicks = 0;
	nick = NULL;
	while((nicks < OER_NICKS_PER_KICK) && ((nick = getlamer(this)) != NULL)) {
		strncat(stringbuffer, nick, BIGSTRINGLEN - strlen(stringbuffer));
		strncat(stringbuffer2, this->name, BIGSTRINGLEN - strlen(stringbuffer2));
		if((nicks + 1) < OER_NICKS_PER_KICK) {
			strncat(stringbuffer, ",", BIGSTRINGLEN - strlen(stringbuffer));
			strncat(stringbuffer2, ",", BIGSTRINGLEN - strlen(stringbuffer2));
		}
		nicks++;
        }
	if(nicks) {
		snprintf(timed_str, WRITE_BUFFER_LENGTH, "KICK %s %s :locking channel", stringbuffer2, stringbuffer);
		if(strlen(this->locked.reason)) {
			snprintf(timed_str, WRITE_BUFFER_LENGTH, "KICK %s %s :%s", stringbuffer2, stringbuffer, this->locked.reason);
		}
		timed_new(this, mystate->now, OER_TIMED_TYPE_KICK, OER_TIMED_PRIORITY_CHANNEL_PROTECTION, timed_str);
	}
	if(nick == NULL) {
		/* no more lamers, unlocking if auto */
		if(this->locked.auto_unlock) {
			mmode_new(this, mystate->now, "-i", NULL);
			this->locked.locked = 0;
		}
	}
}

void settopic(struct channel *this)
{
        int i;
        int j;
        int k;
        int l;
        char stringbuffer[WRITE_BUFFER_LENGTH + 1];
        struct topic *t;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "settopic(\"%s\")\n", this->name);
#endif
	if(!this->joined || !this->topic_change) {
		return;
	}
	snprintf(stringbuffer, WRITE_BUFFER_LENGTH, "TOPIC %s :", this->name);
	i = strlen(stringbuffer);
	l = i;
	t = this->topics;
	while(t != NULL && i < WRITE_BUFFER_LENGTH) {
		for(k = 0, j = strlen(t->topic); k < j && i < WRITE_BUFFER_LENGTH; k++) {
			if(isprint((int)t->topic[k]) || !iscntrl((int)t->topic[k])) {
				stringbuffer[i++] = t->topic[k];
			}
		}
		if(t->next != NULL && i < (WRITE_BUFFER_LENGTH - 3)) {
			stringbuffer[i++] = ' ';
			stringbuffer[i++] = '|';
			stringbuffer[i++] = ' ';
		}
		t = t->next;
	}
	stringbuffer[i++] = '\0';
	this->topic_change = 0;
	/* cosmetic */
	strncpy(timed_str, stringbuffer, WRITE_BUFFER_LENGTH);
        timed_new(NULL, mystate->now, OER_TIMED_TYPE_NORMAL, OER_TIMED_PRIORITY_CHANNEL_HANDLING, timed_str);
}

void syncvoices(struct channel *this)
{
        struct chanuser *cu;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "syncvoices(\"%s\")\n", this->name);
#endif
        for(cu = this->nicks; cu != NULL; cu = cu->next) {
                if(!cu->voice && (hasuserflags(this, cu->userhost, "v") != NULL)) {
			mmode_new(this, mystate->now, "+v", cu->nick);
                }
        }
}

void syncops(struct channel *this)
{
        struct chanuser *cu;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "syncops(\"%s\")\n", this->name);
#endif
	for(cu = this->nicks; cu != NULL; cu = cu->next) {
                /* check for non-opped & to be opped */
                if(!cu->chanop && isop(this, cu->userhost)) {
			mmode_new(this, mystate->now, "+o", cu->nick);
                        continue;
                }
                /* check for opped & to be de-opped */
                if(cu->chanop && (haschanflags(this, "u") && !isallowedop(this, cu->nick))) {
			mmode_new(this, mystate->now, "-o", cu->nick);
                }
        }
}

void syncbans(struct channel *this)
{
        struct maskstruct *ms;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "syncbans(\"%s\")\n", this->name);
#endif
        for(ms = this->permbans; ms != NULL; ms = ms->next) {
		/* bans are set each time, doing a query would be too heavy */
		mmode_new(this, mystate->now, "+b", ms->mask);
        }
}

void syncnickbks(struct channel *this)
{
        struct chanuser *cu;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "syncnickbks(\"%s\")\n", this->name);
#endif
	for(cu = this->nicks; cu != NULL; cu = cu->next) {
		if(cu->userhost != NULL && !cu->tobek) {
			checkforbadnick(this, cu->nick, cu->userhost);
		}
	}
}

void setumode()
{
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "setumode()\n");
#endif
	if(!mystate->newmode) {
                return;
        }
        snprintf(timed_str, WRITE_BUFFER_LENGTH, "MODE %s %s", (mystate->use_alt_nick) ? mystate->altnick : mystate->nick, mystate->mode);
        timed_new(NULL, mystate->now, OER_TIMED_TYPE_NORMAL, OER_TIMED_PRIORITY_CHANNEL_HANDLING, timed_str);
        mystate->newmode = 0;
}

void setnick()
{
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "setnick()\n");
#endif
	if(!mystate->newnick) {
		return;
	}
	snprintf(timed_str, WRITE_BUFFER_LENGTH, "NICK %s", (mystate->use_alt_nick) ? mystate->altnick : mystate->nick);
	timed_new(NULL, mystate->now, OER_TIMED_TYPE_NORMAL, OER_TIMED_PRIORITY_CHANNEL_HANDLING, timed_str);
	mystate->newnick = 0;
}

void syncuserhosts(struct channel *this)
{
	int nicks;
        char nick[NICKLEN + 1];
        char stringbuffer[BIGSTRINGLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "syncuserhosts(\"%s\")\n", this->name);
#endif
	if(allhostsknown(this)) {
		return;
	}
	nicks = 0;
	memset(&stringbuffer, 0, BIGSTRINGLEN + 1);
	while((nicks < OER_NICKS_PER_USERHOST) && (getfirsthostless(this, nick) != NULL)) {
		sethostquerystatus(nick, 1);
		strncat(stringbuffer, nick, BIGSTRINGLEN - strlen(stringbuffer));
		if((nicks + 1) < OER_NICKS_PER_USERHOST) {
			strncat(stringbuffer, " ", BIGSTRINGLEN - strlen(stringbuffer));
		}
		nicks++;
        }
	if(!strlen(stringbuffer)) {
		return;
	}
        snprintf(timed_str, WRITE_BUFFER_LENGTH, "USERHOST %s", stringbuffer);
        timed_new(NULL, mystate->now, OER_TIMED_TYPE_NORMAL, OER_TIMED_PRIORITY_FLOOD, timed_str);
}

struct server *getserver()
{
        struct server *this;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "getserver()\n");
#endif
	/* if admin specified a server, we'll take that no matter
	   what (if it exists, doh) */
	if(strlen(mystate->preferredserver) > 0) {
		for(this = mystate->servers; this != NULL; this = this->next) {
			if(!strcasecmp(this->serverhost, mystate->preferredserver)) {
				memset(mystate->preferredserver, 0, HOSTLEN + 1);
				return this;
			}
		}
	}
        for(this = mystate->servers; this != NULL; this = this->next) {
                if(this->used) {
                        /* this will prevent from us getting stuck
                           with no usable server */
                        this->used = 0;
			continue;
		}
		/* check if server is stoned and whether the stoned
                   time has been elapsed */
                if(!this->stoned) {
                        return this;
                }
                if(mystate->now >= (this->stoned + OER_SERVER_IS_STONED_FOR)) {
                        this->stoned = 0;
                        return this;
                }
        }
        return NULL;
}

void massmessage(char *from, char *message)
{
	int delay;
        char stringbuffer[WRITE_BUFFER_LENGTH + 1];
	struct channel *this;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "massmessage(\"%s\", \"%s\")\n", from, message);
#endif
	snprintf(stringbuffer, WRITE_BUFFER_LENGTH, "notice from admin %s: %s", from, message);
	for(this = mystate->channels, delay = 0; this != NULL; this = this->next, delay++) {
		sendchannelnotice(this, delay, stringbuffer);
	}
}

void initchannel(struct channel *this)
{
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "initchannel(\"%s\")\n", this->name);
#endif
	this->joined = 0;
	this->joining = 0;
	this->setchanmode = 0;
	this->rejoin_at = 0;
	this->last_quote = 0;
	this->i_am_op = 0;
	this->synced = 0;
	if(this->last != NULL) {
		fclose(this->last);
		this->last = NULL;
	}
	if(this->seen != NULL) {
		fclose(this->seen);
		this->seen = NULL;
	}
	initjoins(this);
	initparts(this);
	initpubmsgs(this);
	memset(&this->locked, 0, sizeof(struct locked));
	initnicks(this);
	initmmodes(this);
}

void initall(void)
{
	struct channel *this;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "initall()\n");
#endif
	for(this = mystate->channels; this != NULL; this = this->next) {
		initchannel(this);
	}
}

void initnicks(struct channel *this)
{
	struct chanuser *cu;
	struct chanuser *cu2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "initnicks(\"%s\")\n", this->name);
#endif
	cu = this->nicks;
	while(cu != NULL) {
		cu2 = cu->next;
		if(cu->userhost != NULL) {
			free(cu->userhost);
		}
		free(cu->nick);
		free(cu);
		cu = cu2;
	}
	this->nicks = NULL;
	this->nickcount = 0;
}

void initmmodes(struct channel *this)
{
	struct mmode *m1;
	struct mmode *m2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "initmmodes(\"%s\")\n", this->name);
#endif
	m1 = this->mmodes;
	while(m1 != NULL) {
		m2 = m1->next;
		mmode_del(this, m1);
		m1 = m2;
	}
}

void initpubmsgs(struct channel *this)
{
	struct pubmsg *pm;
	struct pubmsg *pm2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "initpubmsgs(\"%s\")\n", this->name);
#endif
	pm = this->pubmsgs;
	while(pm != NULL) {
		pm2 = pm->next;
		free(pm->message);
		free(pm->nick);
		free(pm->userhost);
		free(pm);
		pm = pm2;
	}
	this->pubmsgs = NULL;
}

void initparts(struct channel *this)
{
	struct part *p;
	struct part *p2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "initparts(\"%s\")\n", this->name);
#endif
	p = this->parts;
	while(p != NULL) {
		p2 = p->next;
		free(p->nick);
		free(p->userhost);
		free(p);
		p = p2;
	}
	this->parts = NULL;
}

void initjoins(struct channel *this)
{
	struct join *j;
	struct join *j2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "initnicks(\"%s\")\n", this->name);
#endif
	j = this->joins;
	while(j != NULL) {
		j2 = j->next;
		free(j->nick);
		free(j->userhost);
		free(j);
		j = j2;
	}
	this->joins = NULL;
}

void initpermbans(struct channel *this)
{
	struct maskstruct *ms;
	struct maskstruct *ms2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "initpermbans(\"%s\")\n", this->name);
#endif
	ms = this->permbans;
	ms2 = ms;
	while(ms != NULL) {
		ms2 = ms->next;
		free(ms);
		ms = ms2;
	}
	this->permbans = NULL;
}

void initnickbks(struct channel *this)
{
	struct maskstruct *ms;
	struct maskstruct *ms2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "initnicks(\"%s\")\n", this->name);
#endif
	ms = this->nickbks;
	ms2 = ms;
	while(ms != NULL) {
		ms2 = ms->next;
		free(ms);
		ms = ms2;
	}
	this->nickbks = NULL;
}

void initwordbks(struct channel *this)
{
	struct maskstruct *ms;
	struct maskstruct *ms2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "initwordbks(\"%s\")\n", this->name);
#endif
	ms = this->wordbks;
	ms2 = ms;
	while(ms != NULL) {
		ms2 = ms->next;
		free(ms);
		ms = ms2;
	}
	this->wordbks = NULL;
}

void initusers(struct channel *this)
{
	struct botuser *u;
	struct botuser *u2;
	struct maskstruct *ms;
	struct maskstruct *ms2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "initusers(\"%s\")\n", this->name);
#endif
	u = this->users;
	while(u != NULL) {
		u2 = u->next;
		ms = u->firstmask;
		while(ms != NULL) {
			ms2 = ms->next;
			free(ms->mask);
			free(ms);
			ms = ms2;
		}
		free(u);
		u = u2;
	}
	this->users = NULL;
}

struct state *emptystate()
{
	struct state *state;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "emptystate()\n");
#endif
	if((state = (struct state *) malloc(sizeof(struct state))) == NULL) {
		return NULL;
	}
	memset(state->nick, 0, NICKLEN + 1);
	memset(state->altnick, 0, NICKLEN + 1);
	memset(state->user, 0, USERLEN + 1);
	memset(state->host, 0, HOSTLEN + 1);
	memset(state->mode, 0, CHANLEN + 1);
	memset(state->signoff, 0, STRINGLEN + 1);
	memset(state->preferredserver, 0, HOSTLEN + 1);
	memset(state->realname, 0, STRINGLEN + 1);
	memset(state->flags, 0, FLAGLEN + 1);
	memset(state->prefix, 0, MICROSTRINGLEN + 1);
	state->netjoining = 0;
	state->reconnect = 0;
	state->now = time(NULL);
	state->last_actions = 0;
	state->startup = time(NULL);
	state->postnj_checks_at = 0;
	state->last_saved = time(NULL);
	state->sockfd = 0;
	state->loopforever = 0;
	state->customuser = 0;
	state->vhost = 0;
	state->use_alt_nick = 0;
	state->newnick = 0;
	state->newmode = 0;
	state->bailout = 0;
	state->quitting = 0;
	state->logfp = NULL;
	state->logto = NULL;
	state->trusted = NULL;
	state->services = NULL;
	state->admins = NULL;
	state->kickrs = 0;
	state->kickreasons = NULL;
	state->channels = NULL;
	state->timeds = NULL;
	state->current_server = NULL;
	state->servers = NULL;
	return state;
}

struct topic *addnewtopic(struct channel *this, char *setby, char *topic)
{
	int amount;
	struct topic *t;
	struct topic *t2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "addnewtopic(\"%s\", \"%s\", \"%s\")\n", this->name, setby, topic);
#endif
	if(topiccount(this) >= OER_TOPICS) {
		return NULL;
	}
	t = this->topics;
	t2 = t;
	amount = 0;
	while(t != NULL) {
                t2 = t;
                amount++;
                t = t2->next;
        }
	if((t = (struct topic *) malloc(sizeof(struct topic))) == NULL) {
		return NULL;
	}
	if((t->setby = (char *) malloc(strlen(setby) + 1)) == NULL) {
		return NULL;
	}
	if((t->topic = (char *) malloc(strlen(topic) + 1)) == NULL) {
		return NULL;
	}
	strcpy(t->setby, setby);
	strcpy(t->topic, topic);
	t->at = mystate->now;
	t->list = 0;
	t->next = NULL;
	t->prev = NULL;
	this->topic_list = 0;
	if(strcasecmp(setby, "gettopic()")) {
		this->topic_change = 1;
	}
	if(this->topics == NULL) {
		/* first topic, special case */
		this->topics = t;
		return t;
	}
	/* >=1 topics, normal processing */
        t2->next = t;
        t->prev = t2;
        return t;
}

struct topic *insertnewtopic(struct channel *this, char *setby, char *topic, int pos)
{
	int amount;
	struct topic *t;
	struct topic *t2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "insertnewtopic(\"%s\", \"%s\", \"%s\", %d)\n", this->name, setby, topic, pos);
#endif
	if(pos < 1 || pos > OER_TOPICS) {
		return NULL;
	}
	if(topiccount(this) >= OER_TOPICS) {
		return NULL;
	}
	t = this->topics;
	t2 = t;
	amount = 1;
	while((t != NULL) && (amount != pos)) {
                t2 = t;
                amount++;
                t = t2->next;
        }
	if((t = (struct topic *) malloc(sizeof(struct topic))) == NULL) {
		return NULL;
	}
	if((t->setby = (char *) malloc(strlen(setby) + 1)) == NULL) {
		return NULL;
	}
	if((t->topic = (char *) malloc(strlen(topic) + 1)) == NULL) {
		return NULL;
	}
	strcpy(t->setby, setby);
	strcpy(t->topic, topic);
	t->at = mystate->now;
	t->list = 0;
	t->next = NULL;
	t->prev = NULL;
	this->topic_list = 0;
	/* there are 4 possibilities: 1st (new), 1st, last and
	   between 2 other topics */
	if(pos == 1 && this->topics == NULL) {
		/* first */
		this->topic_change = 1;
		this->topics = t;
		return t;
	}
	if(pos == 1 && this->topics != NULL) {
		/* first */
		this->topic_change = 1;
		this->topics = t;
		t->next = t2;
		t2->prev = t;
		return t;
	}
	if(t2->next == NULL) {
		/* last */
		this->topic_change = 1;
		t2->next = t;
		t->prev = t2;
		return t;
	}
	if(t2 != NULL && t2->next != NULL) {
		/* between 2 */
		this->topic_change = 1;
		t->next = t2->next;
		t->prev = t2;
		t2->next->prev = t;
		t2->next = t;
		return t;
	}
	free(t->topic);
	free(t->setby);
	free(t);
	return NULL;
}

struct topic *edittopic(struct channel *this, char *setby, char *topic, int pos)
{
	int amount;
	struct topic *t;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "edittopic(\"%s\", \"%s\", \"%s\", %d)\n", this->name, setby, topic, pos);
#endif
	if(pos < 1 || pos > OER_TOPICS) {
		return NULL;
	}
	amount = 1;
	t = this->topics;
	while((t != NULL) && (amount != pos)) {
		amount++;
		t = t->next;
	}
	if(t == NULL) {
		return NULL;
	}
	free(t->setby);
	free(t->topic);
	if((t->setby = (char *) malloc(strlen(setby) + 1)) == NULL) {
		return NULL;
	}
	if((t->topic = (char *) malloc(strlen(topic) + 1)) == NULL) {
		return NULL;
	}
	strcpy(t->setby, setby);
	strcpy(t->topic, topic);
	t->at = mystate->now;
	t->list = 0;
	this->topic_change = 1;
	this->topic_list = 0;
	return t;
}

void gettopic(struct channel *this, char *topic)
{
	int i;
	int k;
	int length;
	char thistopic[TOPICLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "gettopic(\"%s\", \"%s\")\n", this->name, topic);
#endif
	delalltopics(this);
	for(i = 0, k = 0, length = strlen(topic); i < length && k < TOPICLEN; i++) {
		if(topic[i] == ' ' && topic[i + 1] == '|' && topic[i + 2] == ' ') {
			thistopic[k++] = '\0';
			addnewtopic(this, "gettopic()", thistopic);
			k = 0;
			i += 2;
		} else {
			thistopic[k++] = topic[i];
		}
	}
	if(k > 0) {
		thistopic[k++] = '\0';
		addnewtopic(this, "gettopic()", thistopic);
	}
}

void listtopic(struct channel *this, char *to, int tochan)
{
        int amount;
        struct topic *t;
        char stringbuffer[BIGSTRINGLEN + 1];
        char ts[STRINGLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "listtopic(\"%s\", \"%s\", %d)\n", this->name, to, tochan);
#endif
	t = this->topics;
	amount = 1;
	while(t != NULL) {
		striplf(t->topic);
		strncpy(ts, ctime(&t->at), STRINGLEN);
		striplf(ts);
		if(!strcasecmp(t->setby, "gettopic()")) {
			tzset();
			snprintf(stringbuffer, BIGSTRINGLEN, "Topic #%d: %s (%s %s %s)", amount, t->topic, ts, tzname[0], tzname[1]);
		} else {
			tzset();
			snprintf(stringbuffer, BIGSTRINGLEN, "Topic #%d: %s (%s %s %s, %s)", amount, t->topic, ts, tzname[0], tzname[1], t->setby);
		}
		sendreply(to, tochan, 0, 0, stringbuffer);
		amount++;
		t = t->next;
	}
}

char *deltopic(struct channel *this, int nth, char *deleted)
{
	int amount;
	struct topic *t;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "deltopic(\"%s\", %d)\n", this->name, nth);
#endif
	if(nth < 1 || nth > OER_TOPICS) {
		return NULL;
	}
	t = this->topics;
	amount = 1;
	while((t != NULL) && (amount != nth)) {
		amount++;
		t = t->next;
	}
	if(t == NULL) {
		return NULL;
	}
	if(t->next != NULL) {
		t->next->prev = t->prev;
	}
	if(t->prev != NULL) {
		t->prev->next = t->next;
	} else {
		this->topics = t->next;
	}
	free(t->setby);
	strncpy(deleted, t->topic, TOPICLEN);
	free(t->topic);
	free(t);
	this->topic_change = 1;
	return deleted;
}

void delalltopics(struct channel *this)
{
	struct topic *t;
	struct topic *t2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "delalltopics(\"%s\")\n", this->name);
#endif
	t = this->topics;
	while(t != NULL) {
		t2 = t->next;
		free(t->setby);
		free(t->topic);
		free(t);
		t = t2;
	}
	this->topics = NULL;
}

void swaptopic(struct channel *this, int x, int y)
{
	int amount;
	struct topic *t;
	struct topic *tx;
	struct topic *ty;
	char *ptr;
	time_t at;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "swaptopic(\"%s\", %d, %d)\n", this->name, x, y);
#endif
	if(x > OER_TOPICS || x < 1 || y > OER_TOPICS || y < 1 || x == y) {
		return;
	}
	t = this->topics;
	amount = 1;
	tx = NULL;
	ty = NULL;
	while((t != NULL)) {
		if(amount == x) {
			tx = t;
		}
		if(amount == y) {
			ty = t;
		}
		amount++;
		t = t->next;
	}
	if(tx == NULL || ty == NULL) {
		return;
	}
	ptr = tx->topic;
	tx->topic = ty->topic;
	ty->topic = ptr;
	ptr = tx->setby;
	tx->setby = ty->setby;
	ty->setby = ptr;
	at = tx->at;
	tx->at = ty->at;
	ty->at = at;
	this->topic_change = 1;
}

void parsectcp(char *nick, char *userhost, int ctcp, char *message)
{
	int proceed;
	struct channel *this;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "parsectcp(\"%s\", \"%s\", %d, \"%s\")\n", nick, userhost, ctcp, message);
#endif
	proceed = 0;
	if(index(mystate->flags, (int)'f') != NULL) {
		proceed = 1;
	}
	for(this = mystate->channels; this != NULL && !proceed; this = this->next) {
                if(haschanflags(this, "U") && !isatleastop(this, nick, userhost)) {
                        continue;
                }
		if(isatleastopnow(this, nick, userhost)) {
			proceed = 1;
		}
	}
	if(!proceed) {
		return;
	}
	switch(ctcp) {
	case OER_WHICHCTCP_FINGER:
		snprintf(timed_str, WRITE_BUFFER_LENGTH, "Notice %s :%cFINGER (%s@%s) Idle 0 seconds%c", nick, 1, mystate->user, mystate->host, 1);
		timed_new(NULL, mystate->now, OER_TIMED_TYPE_NORMAL, OER_TIMED_PRIORITY_NOTICE, timed_str);
		break;
	case OER_WHICHCTCP_PING:
		snprintf(timed_str, WRITE_BUFFER_LENGTH, "Notice %s :%s", nick, message);
		timed_new(NULL, mystate->now, OER_TIMED_TYPE_NORMAL, OER_TIMED_PRIORITY_NOTICE, timed_str);
		break;
	case OER_WHICHCTCP_USERINFO:
		snprintf(timed_str, WRITE_BUFFER_LENGTH, "Notice %s :%cUSERINFO oer IRC bot%s%c", nick, 1, (index(mystate->flags, (int)'q') != NULL) ? " (Q friendly)" : "", 1);
		timed_new(NULL, mystate->now, OER_TIMED_TYPE_NORMAL, OER_TIMED_PRIORITY_NOTICE, timed_str);
		break;
	case OER_WHICHCTCP_VERSION:
		snprintf(timed_str, WRITE_BUFFER_LENGTH, "Notice %s :%cVERSION %s%c", nick, 1, OER_COPYRIGHT1, 1);
		timed_new(NULL, mystate->now, OER_TIMED_TYPE_NORMAL, OER_TIMED_PRIORITY_NOTICE, timed_str);
		break;
	}
}

int whichctcp(char *message)
{
	int i;
	int k;
	int length;
	char stringbuffer[HUGESTRINGLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "whichctcp(\"%s\")\n", message);
#endif
	/* We start from the 2nd char (first is ^A) */
	for(i = 1, k = 0, length = strlen(message); i < length && k < HUGESTRINGLEN; i++) {
		if(message[i] == 1) {
			break;
		}
		stringbuffer[k++] = message[i];
	}
	if(k >= HUGESTRINGLEN) {
		return OER_WHICHCTCP_INVALID;
	}
	stringbuffer[k] = '\0';
	if(!strncasecmp(stringbuffer, "ACTION", 6)) {
		return OER_WHICHCTCP_ACTION;
	}
	if(!strncasecmp(stringbuffer, "FINGER", 6)) {
		return OER_WHICHCTCP_FINGER;
	}
	if(!strncasecmp(stringbuffer, "PING", 4)) {
		return OER_WHICHCTCP_PING;
	}
	if(!strncasecmp(stringbuffer, "USERINFO", 8)) {
		return OER_WHICHCTCP_USERINFO;
	}
	if(!strncasecmp(stringbuffer, "VERSION", 7)) {
		return OER_WHICHCTCP_VERSION;
	}
	return OER_WHICHCTCP_INVALID;
}

int whichcommand(char *command, int params)
{
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "whichcommand(\"%s\", %d)\n", command, params);
#endif
	if(!strcasecmp(command, "action")) {
		if(params >= 1) {
			return OER_WHICHCOMMAND_ACTION;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "add")) {
		if(params >= 1) {
			return OER_WHICHCOMMAND_ADD;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "b")) {
		if(params >= 1) {
			return OER_WHICHCOMMAND_BAN;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "ban")) {
		if(params >= 1) {
			return OER_WHICHCOMMAND_BAN;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "bankick")) {
		if(params >= 1) {
			return OER_WHICHCOMMAND_BANKICK;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "bk")) {
		if(params >= 1) {
			return OER_WHICHCOMMAND_BANKICK;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "cc")) {
		if(params == 1) {
			return OER_WHICHCOMMAND_CLONECHANNEL;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "clonechannel")) {
		if(params == 1) {
			return OER_WHICHCOMMAND_CLONECHANNEL;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "del")) {
		if(params >= 1) {
			return OER_WHICHCOMMAND_DEL;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "devoice")) {
		if(params >= 0) {
			return OER_WHICHCOMMAND_DEVOICE;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "dop")) {
		if(params >= 0) {
			return OER_WHICHCOMMAND_DOP;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "edit")) {
		if(params >= 1) {
			return OER_WHICHCOMMAND_EDIT;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "flush")) {
		if(params == 0) {
			return OER_WHICHCOMMAND_FLUSH;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "help")) {
		if(params == 0) {
			return OER_WHICHCOMMAND_HELP;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "info")) {
		if(params == 0) {
			return OER_WHICHCOMMAND_INFO;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "invite")) {
		if(params == 0) {
			return OER_WHICHCOMMAND_INVITE;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "jump")) {
		if(params >= 0) {
			return OER_WHICHCOMMAND_JUMP;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "k")) {
		if(params >= 1) {
			return OER_WHICHCOMMAND_KICK;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "kick")) {
		if(params >= 1) {
			return OER_WHICHCOMMAND_KICK;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "last")) {
		if(params == 0 || params == 1 || params == 2) {
			return OER_WHICHCOMMAND_LAST;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "list")) {
		if(params >= 1) {
			return OER_WHICHCOMMAND_LIST;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "lock")) {
		if(params >= 0) {
			return OER_WHICHCOMMAND_LOCK;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "locku")) {
		if(params >= 0) {
			return OER_WHICHCOMMAND_LOCKU;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "logoff")) {
		if(params == 1) {
			return OER_WHICHCOMMAND_LOGOFF;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "logon")) {
		/* params == 1 when admin logon */
		if(params == 1 || params == 2) {
			return OER_WHICHCOMMAND_LOGON;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "mm")) {
		if(params >= 1) {
			return OER_WHICHCOMMAND_MASSMESSAGE;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "massmessage")) {
		if(params >= 1) {
			return OER_WHICHCOMMAND_MASSMESSAGE;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "m")) {
		if(params >= 1) {
			return OER_WHICHCOMMAND_MODE;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "mode")) {
		if(params >= 1) {
			return OER_WHICHCOMMAND_MODE;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "nstats")) {
		if(params == 1) {
			return OER_WHICHCOMMAND_NSTATS;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "op")) {
		if(params >= 0) {
			return OER_WHICHCOMMAND_OP;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "quit")) {
		if(params >= 0) {
			return OER_WHICHCOMMAND_QUIT;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "quote")) {
		if(params == 1) {
			return OER_WHICHCOMMAND_QUOTE;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "raw")) {
		if(params >= 1) {
			return OER_WHICHCOMMAND_RAW;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "rk")) {
		if(params == 0) {
			return OER_WHICHCOMMAND_RANDOM_KICK;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "rbk")) {
		if(params == 0) {
			return OER_WHICHCOMMAND_RANDOM_BANKICK;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "save")) {
		if(params == 0) {
			return OER_WHICHCOMMAND_SAVE;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "say")) {
		if(params >= 1) {
			return OER_WHICHCOMMAND_SAY;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "seen")) {
		if(params == 0 || params == 1 || params == 2) {
			return OER_WHICHCOMMAND_SEEN;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "sstats")) {
		if(params == 0) {
			return OER_WHICHCOMMAND_SSTATS;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "sync")) {
		if(params == 0) {
			return OER_WHICHCOMMAND_SYNC;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "syncall")) {
		if(params == 0) {
			return OER_WHICHCOMMAND_SYNCALL;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "t")) {
		if(params >= 1) {
			return OER_WHICHCOMMAND_TOPIC_SET;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "topic")) {
		if(params >= 1) {
			return OER_WHICHCOMMAND_TOPIC_SET;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "ta")) {
		if(params >= 1) {
			return OER_WHICHCOMMAND_TOPIC_ADD;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "td")) {
		if(params == 1) {
			return OER_WHICHCOMMAND_TOPIC_DEL;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "te")) {
		if(params >= 2) {
			return OER_WHICHCOMMAND_TOPIC_EDIT;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "ti")) {
		if(params >= 2) {
			return OER_WHICHCOMMAND_TOPIC_INS;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "tl")) {
		if(params == 0) {
			return OER_WHICHCOMMAND_TOPIC_LIST;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "tg")) {
		if(params == 0) {
			return OER_WHICHCOMMAND_TOPIC_GET;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "tr")) {
		if(params == 0) {
			return OER_WHICHCOMMAND_TOPIC_REFRESH;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "ts")) {
		if(params == 2) {
			return OER_WHICHCOMMAND_TOPIC_SWAP;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "uptime")) {
		if(params == 0) {
			return OER_WHICHCOMMAND_UPTIME;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "ub")) {
		if(params >= 1) {
			return OER_WHICHCOMMAND_UNBAN;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "unban")) {
		if(params >= 1) {
			return OER_WHICHCOMMAND_UNBAN;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "unlock")) {
		if(params == 0) {
			return OER_WHICHCOMMAND_UNLOCK;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "v")) {
		if(params >= 0) {
			return OER_WHICHCOMMAND_VOICE;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "voice")) {
		if(params >= 0) {
			return OER_WHICHCOMMAND_VOICE;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	if(!strcasecmp(command, "wall")) {
		if(params > 1) {
			return OER_WHICHCOMMAND_WALL;
		}
		return OER_WHICHCOMMAND_INVALID;
	}
	return OER_WHICHCOMMAND_INVALID;
}

struct maskstruct *addnewadminmask(char *handle, char *mask)
{
	struct botuser *admin;
	struct maskstruct *ms;
	struct maskstruct *ms2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "addnewadminmask(\"%s\", \"%s\")\n", handle, mask);
#endif
	for(admin = mystate->admins; admin != NULL; admin = admin->next) {
		if(!strcasecmp(admin->handle, handle)) {
			break;
		}
	}
	if(admin == NULL) {
		return NULL;
	}
	ms = admin->firstmask;
	ms2 = ms;
	while(ms != NULL) {
		ms2 = ms;
		if(!strcasecmp(ms->mask, mask)) {
			return NULL;
		}
		ms = ms2->next;
	}
	if((ms = (struct maskstruct *) malloc(sizeof(struct maskstruct))) == NULL) {
		return NULL;
	}
	if((ms->mask = (char *) malloc(strlen(mask) + 1)) == NULL) {
		return NULL;
	}
	strcpy(ms->mask, mask);
	ms->next = NULL;
	ms->prev = NULL;
	if(admin->firstmask == NULL) {
		/* first mask, special case */
		admin->firstmask = ms;
		return ms;
	}
	/* >=1 masks, normal processing */
	ms2->next = ms;
	ms->prev = ms2;
	return ms;
}

int deladmin(char *handle)
{
	struct botuser *admin;
        struct maskstruct *ms;
        struct maskstruct *ms2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "deladmin(\"%s\")\n", handle);
#endif
	for(admin = mystate->admins; admin != NULL; admin = admin->next) {
		if(!strcasecmp(admin->handle, handle)) {
			break;
		}
	}
	if(admin == NULL) {
		return 0;
	}
	/* check if admin can be deleted */
	if(!delopa(admin->handle)) {
		return 0;
	}
	/* first free all masks */
	ms = admin->firstmask;
	while(ms != NULL) {
		ms2 = ms->next;
		free(ms->mask);
		free(ms);
		ms = ms2;
	}
	/* then free the admin & adjust lists */
	if(admin->prev == NULL) {
		/* first admin in list */
		mystate->admins = admin->next;
		/* ... check if also last */
		if(admin->next != NULL) {
			admin->next->prev = NULL;
		}
	}
	if(admin->next == NULL) {
		/* last admin in list */
		/* ... check also if the only one */
		if(admin->prev != NULL) {
			admin->prev->next = NULL;
		} else {
			mystate->admins = NULL;
		}
	}
	if(admin->prev != NULL && admin->next != NULL) {
		/* between 2 or more admins */
		admin->prev->next = admin->next;
		admin->next->prev = admin->prev;
	}
	free(admin->handle);
	free(admin->options);
	free(admin);
	return 1;
}

int deladminmask(char *handle, char *mask)
{
	struct botuser *admin;
	struct maskstruct *ms;
	struct maskstruct *ms2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "deladminmask(\"%s\", \"%s\")\n", handle, mask);
#endif
	for(admin = mystate->admins; admin != NULL; admin = admin->next) {
		if(!strcasecmp(admin->handle, handle)) {
			break;
		}
	}
	if(admin == NULL) {
		return 0;
	}
	/* find & remove mask */
	ms = admin->firstmask;
	ms2 = ms;
	while(ms != NULL) {
		ms2 = ms;
		if(!strcasecmp(ms->mask, mask)) {
			break;
		}
		ms = ms2->next;
	}
	if(ms == NULL) {
		return 0;
	}
	/* adjust lists */
	if(ms->prev == NULL) {
		/* first mask in list */
		admin->firstmask = ms->next;
		/* ... check if also last */
		if(ms->next != NULL) {
			ms->next->prev = NULL;
		}
	}
	if(ms->next == NULL) {
		/* last mask in list */
		/* ... check also if the only one */
		if(ms->prev != NULL) {
			ms->prev->next = NULL;
		} else {
			admin->firstmask = NULL;
		}
	}
	if(ms->prev != NULL && ms->next != NULL) {
		/* between 2 or more masks */
		ms->prev->next = ms->next;
		ms->next->prev = ms->prev;
	}
	free(ms->mask);
	free(ms);
	return 1;
}

int deluser(struct channel *this, char *handle)
{
	struct botuser *user;
	struct maskstruct *ms;
	struct maskstruct *ms2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "deluser(\"%s\")\n", handle);
#endif
	for(user = this->users; user != NULL; user = user->next) {
		if(!strcasecmp(user->handle, handle)) {
			break;
		}
	}
	if(user == NULL) {
		return 0;
	}
	/* first free all masks */
	ms = user->firstmask;
	while(ms != NULL) {
		ms2 = ms->next;
		free(ms->mask);
		free(ms);
		ms = ms2;
	}
	/* then free the user & adjust lists */
	if(user->prev == NULL) {
		/* first user in list */
		this->users = user->next;
		/* ... check if also last */
		if(user->next != NULL) {
			user->next->prev = NULL;
		}
	}
	if(user->next == NULL) {
		/* last user in list
		   ... check also if the only one */
		if(user->prev != NULL) {
			user->prev->next = NULL;
		} else {
			this->users = NULL;
		}
	}
	if(user->prev != NULL && user->next != NULL) {
		/* between 2 or more users */
		user->prev->next = user->next;
		user->next->prev = user->prev;
	}
	if(user->password) {
		free(user->password);
	}
	free(user->handle);
	free(user->options);
	free(user);
	return 1;
}

int delusermask(struct channel *this, char *handle, char *mask)
{
	struct botuser *user;
	struct maskstruct *ms;
	struct maskstruct *ms2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "delusermask(\"%s\", \"%s\", \"%s\")\n", this->name, handle, mask);
#endif
	for(user = this->users; user != NULL; user = user->next) {
		if(!strcasecmp(user->handle, handle)) {
			break;
		}
	}
	if(user == NULL) {
		return 0;
	}
	/* find & remove mask */
	ms = user->firstmask;
	ms2 = ms;
	while(ms != NULL) {
		ms2 = ms;
		if(!strcasecmp(ms->mask, mask)) {
			break;
		}
		ms = ms2->next;
	}
	if(ms == NULL) {
		return 0;
	}
	/* adjust lists */
	if(ms->prev == NULL) {
		/* first mask in list */
		user->firstmask = ms->next;
		/* ... check if also last */
		if(ms->next != NULL) {
			ms->next->prev = NULL;
		}
	}
	if(ms->next == NULL) {
		/* last mask in list */
		/* ... check also if the only one */
		if(ms->prev != NULL) {
			ms->prev->next = NULL;
		} else {
			user->firstmask = NULL;
		}
	}
	if(ms->prev != NULL && ms->next != NULL) {
		/* between 2 or more masks */
		ms->prev->next = ms->next;
		ms->next->prev = ms->prev;
	}
	free(ms->mask);
	free(ms);
	return 1;
}

int deladverts(struct channel *this, char *to)
{
	struct advert *a;
	struct advert *a2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "deladverts(\"%s\", \"%s\")\n", this->name, to);
#endif
	/* check for static type adverts & valid userhost */
	if(strcasecmp(to, "admins") && strcasecmp(to, "ops") && strcasecmp(to, "other") && strcasecmp(to, "lamers") && strcasecmp(to, "all") && !isvaliduserhost(to)) {
		return 0;
	}
	a = this->adverts;
	a2 = a;
	while(a != NULL) {
		a2 = a;
		if(!strcmp(a->to, to)) {
			/* adjust lists */
			if(a->prev == NULL) {
				/* first advert in list */
				this->adverts = a->next;
				/* ... check if also last */
				if(a->next != NULL) {
					a->next->prev = NULL;
				}
			}
			if(a->next == NULL) {
				/* last advert in list */
				/* ... check also if the only one */
				if(a->prev != NULL) {
					a->prev->next = NULL;
				} else {
					this->adverts = NULL;
				}
			}
			if(a->prev != NULL && a->next != NULL) {
				/* between 2 or more adverts */
				a->prev->next = a->next;
				a->next->prev = a->prev;
			}
			free(a->to);
			free(a->message);
			free(a);
		}
		a = a2->next;
	}
	return 1;
}

int deltrusted(char *host)
{
	struct maskstruct *ms;
	struct maskstruct *ms2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "deltrusted(\"%s\")\n", host);
#endif
	/* find & remove host */
	ms = mystate->trusted;
	ms2 = ms;
	while(ms != NULL) {
		ms2 = ms;
		if(!strcasecmp(ms->mask, host)) {
			break;
		}
		ms = ms2->next;
	}
	if(ms == NULL) {
		return 0;
	}
	/* adjust lists */
	if(ms->prev == NULL) {
		/* first mask in list */
		mystate->trusted = ms->next;
		/* ... check if also last */
		if(ms->next != NULL) {
			ms->next->prev = NULL;
		}
	}
	if(ms->next == NULL) {
		/* last mask in list */
		/* ... check also if the only one */
		if(ms->prev != NULL) {
			ms->prev->next = NULL;
		}
	}
	if(ms->prev != NULL && ms->next != NULL) {
		/* between 2 or more masks */
		ms->prev->next = ms->next;
		ms->next->prev = ms->prev;
	}
	free(ms->mask);
	free(ms->optstring);
	free(ms);
	return 1;
}

int delservice(char *host)
{
	struct maskstruct *ms;
	struct maskstruct *ms2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "delservice(\"%s\")\n", host);
#endif
	/* find & remove host */
	ms = mystate->services;
	ms2 = ms;
	while(ms != NULL) {
		ms2 = ms;
		if(!strcasecmp(ms->mask, host)) {
			break;
		}
		ms = ms2->next;
	}
	if(ms == NULL) {
		return 0;
	}
	/* adjust lists */
	if(ms->prev == NULL) {
		/* first mask in list */
		mystate->services = ms->next;
		/* ... check if also last */
		if(ms->next != NULL) {
			ms->next->prev = NULL;
		}
	}
	if(ms->next == NULL) {
		/* last mask in list */
		/* ... check also if the only one */
		if(ms->prev != NULL) {
			ms->prev->next = NULL;
		}
	}
	if(ms->prev != NULL && ms->next != NULL) {
		/* between 2 or more masks */
		ms->prev->next = ms->next;
		ms->next->prev = ms->prev;
	}
	free(ms->mask);
	free(ms->optstring);
	free(ms);
	return 1;
}

int delchannel(struct channel *this)
{
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "delchannel(\"%s\")\n", this->name);
#endif
	if(this->joined) {
		/* leave the channel */
		snprintf(timed_str, WRITE_BUFFER_LENGTH, "PART %s", this->name);
		timed_new(NULL, mystate->now, OER_TIMED_TYPE_NORMAL, OER_TIMED_PRIORITY_CHANNEL_HANDLING, timed_str);
	}
	/* do the normal init */
	initchannel(this);
	/* the following differ from normal initchannel() */
	delalltopics(this);
	initpermbans(this);
	initnickbks(this);
	initwordbks(this);
	initusers(this);
	/* adjust lists */
	if(this->prev == NULL) {
		/* first channel in list */
		mystate->channels = this->next;
		/* ... check if also last */
		if(this->next != NULL) {
			this->next->prev = NULL;
		}
	}
	if(this->next == NULL) {
		/* last channel in list */
		/* ... check also if the only one */
		if(this->prev != NULL) {
			this->prev->next = NULL;
		}
	}
	if(this->prev != NULL && this->next != NULL) {
		/* between 2 or more channels */
		this->prev->next = this->next;
		this->next->prev = this->prev;
	}
	free(this);
	return 1;
}

struct maskstruct *editmask(struct maskstruct *first, char *from, char *to)
{
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "editmask(\"%s\", \"%s\", \"%s\")\n", first->mask, from, to);
#endif
	while(first != NULL) {
		if(!strcasecmp(first->mask, from)) {
			break;
		}
		first = first->next;
	}
	if(first == NULL) {
		return NULL;
	}
	free(first->mask);
	if((first->mask = (char *) malloc(strlen(to) + 1)) == NULL) {
		return NULL;
	}
	strcpy(first->mask, to);
	return first;
}

struct maskstruct *addnewusermask(struct channel *this, char *handle, char *mask)
{
	struct botuser *user;
	struct maskstruct *ms;
	struct maskstruct *ms2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "addnewusermask(\"%s\", \"%s\", \"%s\")\n", this->name, handle, mask);
#endif
	for(user = this->users; user != NULL; user = user->next) {
		if(!strcasecmp(user->handle, handle)) {
			break;
		}
	}
	if(user == NULL) {
		return NULL;
	}
	ms = user->firstmask;
	ms2 = ms;
	while(ms != NULL) {
		ms2 = ms;
		if(!strcasecmp(ms->mask, mask)) {
			return NULL;
		}
		ms = ms2->next;
	}
	if((ms = (struct maskstruct *) malloc(sizeof(struct maskstruct))) == NULL) {
		return NULL;
	}
	if((ms->mask = (char *) malloc(strlen(mask) + 1)) == NULL) {
		return NULL;
	}
	strcpy(ms->mask, mask);
	ms->next = NULL;
	ms->prev = NULL;
	if(user->firstmask == NULL) {
		/* first mask, special case */
		user->firstmask = ms;
		return ms;
	}
	/* >=1 masks, normal processing */
	ms2->next = ms;
	ms->prev = ms2;
	return ms;
}

struct advert *addnewadvert(struct channel *this, char *to, char *message)
{
	struct advert *a;
	struct advert *a2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "addnewadvert(\"%s\", \"%s\", \"%s\")\n", this->name, to, message);
#endif
	/* check for static type adverts & valid userhost */
	if(strcasecmp(to, "admins") && strcasecmp(to, "ops") && strcasecmp(to, "other") && strcasecmp(to, "lamers") && strcasecmp(to, "all") && !isvaliduserhost(to)) {
		return 0;
	}
	a = this->adverts;
	a2 = a;
	while(a != NULL) {
		a2 = a;
		if(!strcmp(a->to, to) && !strcasecmp(a->message, message)) {
			return NULL;
		}
		a = a2->next;
	}
	if((a = (struct advert *) malloc(sizeof(struct advert))) == NULL) {
		return NULL;
	}
	if((a->to = (char *) malloc(strlen(to) + 1)) == NULL) {
		return NULL;
	}
	strcpy(a->to, to);
	if((a->message = (char *) malloc(strlen(message) + 1)) == NULL) {
		return NULL;
	}
	strcpy(a->message, message);
	a->next = NULL;
	a->prev = NULL;
	if(this->adverts == NULL) {
		/* first advert, special case */
		this->adverts = a;
		return a;
	}
	/* >=1 adverts, normal processing (append) */
	a2->next = a;
	a->prev = a2;
	return a;
}

struct maskstruct *addnewpermban(struct channel *this, char *mask)
{
	int amount;
	struct maskstruct *ms;
	struct maskstruct *ms2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "addnewpermban(\"%s\", \"%s\")\n", this->name, mask);
#endif
	ms = this->permbans;
	ms2 = ms;
	amount = 0;
	while(ms != NULL) {
		ms2 = ms;
		if(!strcasecmp(ms->mask, mask)) {
			return NULL;
		}
		amount++;
		ms = ms2->next;
	}
	if(amount >= OER_PERMBANS) {
		/* don't fill the channel ban list with our
		   bans, bad things may happen */
		return NULL;
	}
	if((ms = (struct maskstruct *) malloc(sizeof(struct maskstruct))) == NULL) {
		return NULL;
	}
	if((ms->mask = (char *) malloc(strlen(mask) + 1)) == NULL) {
		return NULL;
	}
	strcpy(ms->mask, mask);
	ms->optstring = NULL;
	ms->next = NULL;
	ms->prev = NULL;
	if(this->permbans == NULL) {
		/* first permban, special case */
		this->permbans = ms;
		return ms;
	}
	/* >=1 permbans, normal processing */
	ms2->next = ms;
	ms->prev = ms2;
	return ms;
}

struct maskstruct *addnewnickbk(struct channel *this, char *mask, char *reason)
{
	struct maskstruct *ms;
	struct maskstruct *ms2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "addnewnickbk(\"%s\", \"%s\", \"%s\")\n", this->name, mask, reason);
#endif
	ms = this->nickbks;
	ms2 = ms;
	while(ms != NULL) {
		ms2 = ms;
		if(!strcasecmp(ms->mask, mask)) {
			return NULL;
		}
		ms = ms2->next;
	}
	if((ms = (struct maskstruct *) malloc(sizeof(struct maskstruct))) == NULL) {
		return NULL;
	}
	if((ms->mask = (char *) malloc(strlen(mask) + 1)) == NULL) {
		return NULL;
	}
	if(reason != NULL) {
		if((ms->optstring = (char *) malloc(strlen(reason) + 1)) == NULL) {
			return NULL;
		}
		strcpy(ms->optstring, reason);
	} else {
		ms->optstring = NULL;
	}
	strcpy(ms->mask, mask);
	ms->next = NULL;
	ms->prev = NULL;
	if(this->nickbks == NULL) {
		/* first nickbk, special case */
		this->nickbks = ms;
		return ms;
	}
	/* >=1 nickbks, normal processing */
	ms2->next = ms;
	ms->prev = ms2;
	return ms;
}

struct maskstruct *addnewwordbk(struct channel *this, char *mask)
{
	struct maskstruct *ms;
	struct maskstruct *ms2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "addnewwordbk(\"%s\", \"%s\")\n", this->name, mask);
#endif
	ms = this->wordbks;
	ms2 = ms;
	while(ms != NULL) {
		ms2 = ms;
		if(!strcasecmp(ms->mask, mask)) {
			return NULL;
		}
		ms = ms2->next;
	}
	if((ms = (struct maskstruct *) malloc(sizeof(struct maskstruct))) == NULL) {
		return NULL;
	}
	if((ms->mask = (char *) malloc(strlen(mask) + 1)) == NULL) {
		return NULL;
	}
	strcpy(ms->mask, mask);
	ms->optstring = NULL;
	ms->next = NULL;
	ms->prev = NULL;
	if(this->wordbks == NULL) {
		/* first wordbk, special case */
		this->wordbks = ms;
		return ms;
	}
	/* >=1 wordbks, normal processing */
	ms2->next = ms;
	ms->prev = ms2;
	return ms;
}

struct maskstruct *addnewtrusted(char *host)
{
	struct maskstruct *ms;
	struct maskstruct *ms2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "addnewtrusted(\"%s\")\n", host);
#endif
	ms = mystate->trusted;
	ms2 = ms;
	while(ms != NULL) {
		ms2 = ms;
		if(!strcasecmp(ms->mask, host)) {
			return NULL;
		}
		ms = ms2->next;
	}
	if((ms = (struct maskstruct *) malloc(sizeof(struct maskstruct))) == NULL) {
		return NULL;
	}
	if((ms->mask = (char *) malloc(strlen(host) + 1)) == NULL) {
		return NULL;
	}
	strcpy(ms->mask, host);
	ms->optstring = NULL;
	ms->next = NULL;
	ms->prev = NULL;
	if(mystate->trusted == NULL) {
		/* first trusted, special case */
		mystate->trusted = ms;
		return ms;
	}
	/* >=1 trusted, normal processing */
	ms2->next = ms;
	ms->prev = ms2;
	return ms;
}

struct maskstruct *addnewservice(char *host)
{
	struct maskstruct *ms;
	struct maskstruct *ms2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "addnewservice(\"%s\")\n", host);
#endif
	ms = mystate->services;
	ms2 = ms;
	while(ms != NULL) {
		ms2 = ms;
		if(!strcasecmp(ms->mask, host)) {
			return NULL;
		}
		ms = ms2->next;
	}
	if((ms = (struct maskstruct *) malloc(sizeof(struct maskstruct))) == NULL) {
		return NULL;
	}
	if((ms->mask = (char *) malloc(strlen(host) + 1)) == NULL) {
		return NULL;
	}
	strcpy(ms->mask, host);
	ms->optstring = NULL;
	ms->next = NULL;
	ms->prev = NULL;
	if(mystate->services == NULL) {
		/* first service, special case */
		mystate->services = ms;
		return ms;
	}
	/* >=1 services, normal processing */
	ms2->next = ms;
	ms->prev = ms2;
	return ms;
}

struct botuser *addbotuser(char *handle, char *options)
{
	struct botuser *botuser;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "addbotuser(\"%s\", \"%s\")\n", handle, options);
#endif
	if((botuser = (struct botuser *) malloc(sizeof(struct botuser))) == NULL) {
		return NULL;
	}
	botuser->password = NULL;
	botuser->firstmask = NULL;
	botuser->prev = NULL;
	botuser->next = NULL;
	if((botuser->handle = (char *) malloc(strlen(handle) + 1)) == NULL) {
		return NULL;
	}
	strcpy(botuser->handle, handle);
	if((botuser->options = (char *) malloc(strlen(options) + 1)) == NULL) {
		return NULL;
	}
	strcpy(botuser->options, options);
	return botuser;
}

struct botuser *addnewuser(struct channel *this, char *handle, char *options)
{
	struct botuser *bu;
	struct botuser *bu2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "addnewuser(\"%s\", \"%s\", \"%s\")\n", this->name, handle, options);
#endif
	bu = this->users;
	bu2 = bu;
	while(bu != NULL) {
		bu2 = bu;
		if(!strcasecmp(bu->handle, handle)) {
			return NULL;
		}
		bu = bu2->next;
	}
	if((bu = addbotuser(handle, options)) == NULL) {
		return NULL;
	}
	if(this->users == NULL) {
		this->users = bu;
		return bu;
	}
	/* >=1 users, normal processing */
	bu2->next = bu;
	bu->prev = bu2;
	return bu;
}

struct botuser *addnewadmin(char *handle, char *options)
{
	struct botuser *admin;
	struct botuser *admin2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "addnewadmin(\"%s\", \"%s\")\n", handle, options);
#endif
	admin = mystate->admins;
	admin2 = admin;
	while(admin != NULL) {
		admin2 = admin;
		if(!strcasecmp(admin->handle, handle)) {
			return NULL;
		}
		admin = admin2->next;
	}
	if((admin = addbotuser(handle, options)) == NULL) {
		return NULL;
	}
	if(mystate->admins == NULL) {
		mystate->admins = admin;
		return admin;
	}
	/* >1 admins, normal processing */
	admin2->next = admin;
	admin->prev = admin2;
	return admin;
}

struct maskstruct *addnewkickreason(char *reason)
{
	struct maskstruct *ms;
	struct maskstruct *ms2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "addnewkickreason(\"%s\")\n", reason);
#endif
	ms = mystate->kickreasons;
	ms2 = ms;
	while(ms != NULL) {
		ms2 = ms;
		if(!strcasecmp(ms->mask, reason)) {
			return NULL;
		}
		ms = ms2->next;
	}
	if((ms = (struct maskstruct *) malloc(sizeof(struct maskstruct))) == NULL) {
		return NULL;
	}
	if((ms->mask = (char *) malloc(strlen(reason) + 1)) == NULL) {
		return NULL;
	}
	strcpy(ms->mask, reason);
	ms->optstring = NULL;
	ms->next = NULL;
	ms->prev = NULL;
	mystate->kickrs++;
	if(mystate->kickreasons == NULL) {
		/* first kickreason, special case */
		mystate->kickreasons = ms;
		return ms;
	}
	/* >=1 kick reasons, normal processing */
	ms2->next = ms;
	ms->prev = ms2;
	return ms;
}

struct server *addnewserver(char *serverhost, int serverport, int servermodes, int pingfrequency, int protected_ircops, int linenoise, char *password)
{
	struct server *s;
	struct server *s2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "addnewserver(\"%s\", %d, %d, %d, %d, %d, \"%s\"\n", serverhost, serverport, servermodes, pingfrequency, protected_ircops, linenoise, (password == NULL) ? "(null)" : password);
#endif
	s = mystate->servers;
	s2 = s;
	while(s != NULL) {
		s2 = s;
		if(!strcasecmp(s->serverhost, serverhost) && s->serverport == serverport && s->servermodes == servermodes && s->pingfrequency == pingfrequency && s->protected_ircops == protected_ircops && s->linenoise == linenoise) {
			/* server already exists */
			return NULL;
		}
		s = s2->next;
	}
	if((s = (struct server *) malloc(sizeof(struct server))) == NULL) {
		return NULL;
	}
	strncpy(s->serverhost, serverhost, HOSTLEN);
	s->serverport = serverport;
	s->servermodes = servermodes;
	s->pingfrequency = pingfrequency;
	s->protected_ircops = protected_ircops;
	s->used = 0;
	s->connected = 0;
	s->stoned = 0;
	s->linenoise = linenoise;
	s->rx = 0;
	s->tx = 0;
	s->linkup = 0;
	memset(s->password, 0, TINYSTRINGLEN + 1);
	if(password != NULL) {
		strncpy(s->password, password, TINYSTRINGLEN);
	}
	s->registered = OER_REGISTERCONNECTION_STATUS_BEGIN;
	s->lastping = 0;
	s->prev = NULL;
	s->next = NULL;
	if(mystate->servers == NULL) {
		/* first server, special case */
		mystate->servers = s;
		return s;
	}
	/* >=1 servers, normal case */
	s2->next = s;
	s->prev = s2;
	return s;
}

struct channel *addnewchannel(char *name)
{
	struct channel *this;
	struct channel *this2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "addnewchannel(\"%s\")\n", name);
#endif
	this = mystate->channels;
	this2 = this;
	while(this != NULL) {
		this2 = this;
		if(!strcasecmp(this->name, name)) {
			return NULL;
		}
		this = this2->next;
	}
	if((this = (struct channel *) malloc(sizeof(struct channel))) == NULL) {
		return NULL;
	}
	strncpy(this->name, name, CHANLEN);
	memset(this->mode, 0, CHANLEN + 1);
	memset(this->key, 0, CHANLEN + 1);
	this->haskey = 0;
	this->joined = 0;
	this->joining = 0;
	this->setchanmode = 0;
	this->rejoin_at = 0;
	this->last_quote = 0;
	this->nickcount = 0;
	this->i_am_op = 0;
	this->bantype = OER_BAN_TYPE_HOST;
	memset(&this->chanflags, 0, FLAGLEN + 1);
	this->last = NULL;
	this->seen = NULL;
	this->nicks = NULL;
	this->pubmsgs = NULL;
	this->parts = NULL;
	this->joins = NULL;
	memset(&this->locked, 0, sizeof(struct locked));
	memset(&this->floodvars, 0, sizeof(struct floodvars));
	/* set the flood defaults, run-time changable */
	this->floodvars.repeat_expire = PUBMSG_FLOOD_REPEAT_EXPIRE;
	this->floodvars.repeat_limit = PUBMSG_FLOOD_REPEAT;
	this->floodvars.interval = PUBMSG_FLOOD_INTERVAL;
	this->floodvars.lines = PUBMSG_FLOOD_LINES;
	this->floodvars.chars = PUBMSG_FLOOD_CHARS;
	/* .. */
	memset(&this->banvars, 0, sizeof(struct banvars));
	/* set the ban time defaults, run-time changable */
	this->banvars.auto_rejoin = OER_BANVARS_AUTO_REJOIN;
	this->banvars.part_rejoin = OER_BANVARS_PART_REJOIN;
	this->banvars.public_flood = OER_BANVARS_PUBLIC_FLOOD;
	this->banvars.public_flood_repeat = OER_BANVARS_PUBLIC_FLOOD_REPEAT;
	this->banvars.bad_word = OER_BANVARS_BAD_WORD;
	this->banvars.bad_nick = OER_BANVARS_BAD_NICK;
	this->banvars.normal_ban = OER_BANVARS_NORMAL_BAN;
	/* .. */
	this->permbans = NULL;
	this->nickbks = NULL;
	this->wordbks = NULL;
	this->users = NULL;
	this->autheds = NULL;
	this->topic_change = 0;
	this->topic_list = 0;
	this->synced = 0;
	this->topics = NULL;
	this->adverts = NULL;
	this->mmodes = NULL;
	this->prev = NULL;
	this->next = NULL;
	/* first one is a special case */
	if(mystate->channels == NULL) {
		mystate->channels = this;
		return this;
	}
	/* >=1 channels, normal case */
	this2->next = this;
	this->prev = this2;
	return this;
}

struct channel *getchptr(char *channel)
{
        struct channel *this;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "getchptr(\"%s\")\n", channel);
#endif
        for(this = mystate->channels; this != NULL; this = this->next) {
                if(!strcasecmp(this->name, channel)) {
                        return this;
                }
        }
        return NULL;
}

int loadall(void)
{
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "loadall()\n");
#endif
	/* we only require loadconf() and loadadmins() to succeed */
	if(!loadconf()) {
#ifdef DEBUG
                oer_debug(OER_DEBUG_INFO, "loadall->loadconf() failed\n");
#endif
		return 0;
        }
        if(!loadadmins()) {
#ifdef DEBUG
                oer_debug(OER_DEBUG_INFO, "loadall->loadadmins() failed\n");
#endif
		return 0;
        }
        if(!loadchannels()) {
#ifdef DEBUG
                oer_debug(OER_DEBUG_INFO, "loadall->loadchannels() failed\n");
#endif
        }
        if(!loadtrusted()) {
#ifdef DEBUG
                oer_debug(OER_DEBUG_INFO, "loadall->loadtrusted() failed\n");
#endif
        }
        if(!loadservices()) {
#ifdef DEBUG
                oer_debug(OER_DEBUG_INFO, "loadall->loadservices() failed\n");
#endif
        }
        if(!loadkickreasons()) {
#ifdef DEBUG
                oer_debug(OER_DEBUG_INFO, "loadall->loadkickreasons() failed\n");
#endif
        }
        if(!loadchaninfo()) {
#ifdef DEBUG
                oer_debug(OER_DEBUG_INFO, "loadall->loadchaninfo() failed\n");
#endif
        }
	return 1;
}

int loadconf(void)
{
	FILE *fp;
        char filename[STRINGLEN + 1];
        char stringbuffer[BIGSTRINGLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "loadconf()\n");
#endif
	snprintf(filename, STRINGLEN, "%s%s", mystate->progname, ".conf");
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "loadconf->loading %s\n", filename);
#endif
	if((fp = fopen(filename, "r")) == NULL) {
                return 0;
        }
        while(fgets(stringbuffer, BIGSTRINGLEN, fp)) {
		striplf(stringbuffer);
		if(emptyline(stringbuffer)) {
			continue;
		}
		if(iscomment(stringbuffer)) {
			continue;
		}
                if(!parseconf(stringbuffer)) {
#ifdef DEBUG
			oer_debug(OER_DEBUG_WARNING, "loadconf->non-parsable line: '%s'\n", stringbuffer);
#endif
                        return 0;
                }
        }
        fclose(fp);
        return 1;
}

int saveall(void)
{	
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "saveall()\n");
#endif
	if(!saveconf()) {
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "saveall->saveconf() failed\n");
#endif
		return 0;
	}
	if(!savechannels()) {
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "saveall->savechannels() failed\n");
#endif
		return 0;
	}
	if(!savetrusted()) {
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "saveall->savetrusted() failed\n");
#endif
		return 0;
	}
	if(!saveservices()) {
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "saveall->saveservices() failed\n");
#endif
		return 0;
	}
	if(!saveadmins()) {
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "saveall->saveadmins() failed\n");
#endif
		return 0;
	}
	if(!savekickreasons()) {
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "saveall->savekickreasons() failed\n");
#endif
		return 0;
	}
	if(!savechaninfo()) {
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "saveall->savechaninfo() failed\n");
#endif
		return 0;
	}
	return 1;
}

int saveconf(void)
{
	FILE *fp;
	char *ptr;
        char filename[STRINGLEN + 1];
        char newfilename[STRINGLEN + 1];
        char stringbuffer[BIGSTRINGLEN + 1];
	struct server *server;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "saveconf()\n");
#endif
	snprintf(filename, STRINGLEN, "%s%s", mystate->progname, ".conf.saving");
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "saveconf->saving to %s\n", filename);
#endif
	if((fp = fopen(filename, "w")) == NULL) {
                return 0;
        }
	snprintf(stringbuffer, BIGSTRINGLEN, "nick::%s\n", mystate->nick);
        fputs(stringbuffer, fp);
        snprintf(stringbuffer, BIGSTRINGLEN, "altnick::%s\n", mystate->altnick);
        fputs(stringbuffer, fp);
	ptr = mystate->user;
	while(*ptr == '~') {
		/* server has prefixed our ident with ~
		   strip ~ before saving to avoid double ~ */
		ptr++;
	}
        snprintf(stringbuffer, BIGSTRINGLEN, "user::%s\n", ptr);
        fputs(stringbuffer, fp);
        snprintf(stringbuffer, BIGSTRINGLEN, "usermode::%s\n", mystate->mode);
        fputs(stringbuffer, fp);
        snprintf(stringbuffer, BIGSTRINGLEN, "flags::%s\n", mystate->flags);
        fputs(stringbuffer, fp);
        snprintf(stringbuffer, BIGSTRINGLEN, "prefix::%s\n", mystate->prefix);
        fputs(stringbuffer, fp);
        snprintf(stringbuffer, BIGSTRINGLEN, "realname::%s\n", mystate->realname);
        fputs(stringbuffer, fp);
        if(mystate->vhost) {
                snprintf(stringbuffer, BIGSTRINGLEN, "vhost::%s\n", mystate->host);
                fputs(stringbuffer, fp);
        }
        for(server = mystate->servers; server != NULL; server = server->next) {
                snprintf(stringbuffer, BIGSTRINGLEN, "server::%s::%d::%d::%d::%d::%d::%s\n", server->serverhost, server->serverport, server->servermodes, server->pingfrequency, server->protected_ircops, server->linenoise, (strlen(server->password)) ? server->password : "");
                fputs(stringbuffer, fp);
        }
        fclose(fp);
	snprintf(newfilename, STRINGLEN, "%s%s", mystate->progname, ".conf");
#ifdef DEBUG
        oer_debug(OER_DEBUG_INFO, "saveconf->renaming %s to %s\n", filename, newfilename);
#endif
        if(rename(filename, newfilename) < 0) {
                return 0;
        }
        return 1;
}

int parseconf(char *paramline)
{
	int ppos;
	int ping;
	int linenoise;
	char p1[STRINGLEN + 1];
	char p2[STRINGLEN + 1];
	char p3[STRINGLEN + 1];
	char p4[STRINGLEN + 1];
	char p5[STRINGLEN + 1];
	char p6[STRINGLEN + 1];
	char p7[STRINGLEN + 1];
	char p8[STRINGLEN + 1];
	char *pwptr;
	struct server *server;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "parseconf(\"%s\")\n", paramline);
#endif
	ppos = 0;
	if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p1, STRINGLEN, 1)) < 0) {
		return 0;
	}
	if(!strcasecmp(p1, "nick")) {
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p2, STRINGLEN, 1)) < 0) {
			return 0;
		}
		strncpy(mystate->nick, p2, NICKLEN);
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "parseconf->my nick is %s\n", mystate->nick);
#endif
		return 1;
	}
	if(!strcasecmp(p1, "altnick")) {
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p2, STRINGLEN, 1)) < 0) {
			return 0;
		}
		strncpy(mystate->altnick, p2, NICKLEN);
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "parseconf->my alt nick is %s\n", mystate->altnick);
#endif
		return 1;
	}
	if(!strcasecmp(p1, "realname")) {
		if(!strlen(paramline + ppos)) {
			return 0;
		}
		strncpy(mystate->realname, paramline + ppos, STRINGLEN);
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "parseconf->my IRC REALNAME is %s\n", mystate->realname);
#endif
		return 1;
	}
	if(!strcasecmp(p1, "user")) {
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p2, STRINGLEN, 1)) < 0) {
			return 0;
		}
		strncpy(mystate->user, p2, USERLEN);
		mystate->customuser = 1;
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "parseconf->my user is %s\n", mystate->user);
#endif
		return 1;
	}
	if(!strcasecmp(p1, "usermode")) {
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p2, STRINGLEN, 1)) < 0) {
			return 0;
		}
		strncpy(mystate->mode, p2, CHANLEN);
		mystate->newmode = 1;
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "parseconf->my usermode is %s\n", mystate->mode);
#endif
		return 1;
	}
	if(!strcasecmp(p1, "flags")) {
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p2, STRINGLEN, 1)) < 0) {
			return 0;
		}
		strncpy(mystate->flags, p2, FLAGLEN);
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "parseconf->my flags are %s\n", mystate->flags);
#endif
		return 1;
	}
	if(!strcasecmp(p1, "prefix")) {
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p2, STRINGLEN, 1)) < 0) {
			return 0;
		}
		strncpy(mystate->prefix, p2, MICROSTRINGLEN);
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "parseconf->my prefix is %s\n", mystate->prefix);
#endif
		return 1;
	}
	if(!strcasecmp(p1, "vhost")) {
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p2, STRINGLEN, 1)) < 0) {
			return 0;
		}
		strncpy(mystate->host, p2, HOSTLEN);
		mystate->vhost = 1;
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "parseconf->using virtual host %s\n", mystate->host);
#endif
		return 1;
	}
	if(!strcasecmp(p1, "server")) {
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p2, STRINGLEN, 1)) < 0) {
			return 0;
		}
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p3, STRINGLEN, 1)) < 0) {
			return 0;
		}
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p4, STRINGLEN, 1)) < 0) {
			return 0;
		}
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p5, STRINGLEN, 1)) < 0) {
			return 0;
		}
		ping = atoi(p5);
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p6, STRINGLEN, 1)) < 0) {
			return 0;
		}
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p7, STRINGLEN, 1)) < 0) {
			return 0;
		}
		linenoise = atoi(p7);
		if(linenoise < OER_LINENOISE_INTERVAL_MIN || linenoise > ping) {
#ifdef DEBUG
			oer_debug(OER_DEBUG_WARNING, "parseconf->invalid linenoise value %d, disabled\n", linenoise);
#endif
			linenoise = 0;
		}
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p8, STRINGLEN, 1)) < 0) {
			return 0;
		}
		pwptr = (strlen(p8)) ? p8 : NULL;
		if((server = addnewserver(p2, atoi(p3), atoi(p4), ping, atoi(p6), linenoise, pwptr)) == NULL) {
			return 0;
		}
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "parseconf->added IRC server host: %s port: %d servermodes: %d ping frequency: %d protected ircops: %d linenoise: %d password: %s\n", server->serverhost, server->serverport, server->servermodes, server->pingfrequency, server->protected_ircops, server->linenoise, (pwptr == NULL) ? "(null)" : pwptr);
#endif
		return 1;
	}
#ifdef DEBUG
	oer_debug(OER_DEBUG_WARNING, "parseconf->unknown line: '%s'\n", paramline);
#endif
	return 1;
}

int loadchannels(void)
{
	FILE *fp;
        char filename[STRINGLEN + 1];
        char stringbuffer[BIGSTRINGLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "loadchannels()\n");
#endif
	snprintf(filename, STRINGLEN, "%s%s", mystate->progname, ".channels");
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "loadchannels->loading %s\n", filename);
#endif
	if((fp = fopen(filename, "r")) == NULL) {
                return 0;
        }
	while(fgets(stringbuffer, BIGSTRINGLEN, fp)) {
                striplf(stringbuffer);
                if(emptyline(stringbuffer)) {
                        continue;
                }
                if(iscomment(stringbuffer)) {
                        continue;
                }
                if(!parsechannels(stringbuffer)) {
#ifdef DEBUG
                        oer_debug(OER_DEBUG_WARNING, "loadchannels->non-parsable line: '%s'\n", stringbuffer);
#endif
                        return 0;
                }
        }
        fclose(fp);
        return 1;
}

int savechannels(void)
{
	FILE *fp;
        char filename[STRINGLEN + 1];
        char newfilename[STRINGLEN + 1];
        char stringbuffer[BIGSTRINGLEN + 1];
	struct channel *this;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "savechannels()\n");
#endif
	snprintf(filename, STRINGLEN, "%s%s", mystate->progname, ".channels.saving");
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "savechannels->saving to %s\n", filename);
#endif
	if((fp = fopen(filename, "w")) == NULL) {
                return 0;
        }
	for(this = mystate->channels; this != NULL; this = this->next) {
		if(this->haskey) {
			snprintf(stringbuffer, BIGSTRINGLEN, "channel::%s::%s::%s::%s\n", this->name, this->key, this->mode, this->chanflags);
		} else {
			snprintf(stringbuffer, BIGSTRINGLEN, "channel::%s::::%s::%s\n", this->name, this->mode, this->chanflags);
		}
                fputs(stringbuffer, fp);
        }
        fclose(fp);
	snprintf(newfilename, STRINGLEN, "%s%s", mystate->progname, ".channels");
#ifdef DEBUG
        oer_debug(OER_DEBUG_INFO, "savechannels->renaming %s to %s\n", filename, newfilename);
#endif
        if(rename(filename, newfilename) < 0) {
                return 0;
        }
        return 1;
}

int parsechannels(char *paramline)
{
	int ppos;
	char p1[STRINGLEN + 1];
	char p2[STRINGLEN + 1];
	char p3[STRINGLEN + 1];
	char p4[STRINGLEN + 1];
	char p5[STRINGLEN + 1];
	struct channel *this;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "parsechannels(\"%s\")\n", paramline);
#endif
	ppos = 0;
	if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p1, STRINGLEN, 1)) < 0) {
		return 0;
	}
	if(!strcasecmp(p1, "channel")) {
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p2, STRINGLEN, 1)) < 0) {
			return 0;
		}
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p3, STRINGLEN, 1)) < 0) {
			return 0;
		}
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p4, STRINGLEN, 1)) < 0) {
			return 0;
		}
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p5, STRINGLEN, 1)) < 0) {
			return 0;
		}
		if(!isvalidchannel(p2)) {
			return 0;
		}
		if((this = addnewchannel(p2)) == NULL) {
			return 0;
		}
		if(strlen(p3) > 0) {
			setchankey(this, p3);
		}
		strncpy(this->mode, p4, CHANLEN);
		this->setchanmode = 1;
		sortstring(p5);
		strncpy(this->chanflags, p5, FLAGLEN);
		if(this->haskey) {
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "parsechannels->added channel %s with key %s modes %s and flags %s\n", this->name, this->key, this->mode, this->chanflags);
#endif
		} else {
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "parsechannels->added channel %s with modes %s and flags %s\n", this->name, this->mode, this->chanflags);
#endif
		}
		return 1;
	}
#ifdef DEBUG
        oer_debug(OER_DEBUG_WARNING, "parsechannels->unknown line: '%s'\n", paramline);
#endif
        return 1;
}

int loadtrusted(void)
{
	FILE *fp;
        char filename[STRINGLEN + 1];
        char stringbuffer[BIGSTRINGLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "loadtrusted()\n");
#endif
	snprintf(filename, STRINGLEN, "%s%s", mystate->progname, ".trusted");
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "loadtrusted->loading %s\n", filename);
#endif
	if((fp = fopen(filename, "r")) == NULL) {
                return 0;
        }
	while(fgets(stringbuffer, BIGSTRINGLEN, fp)) {
                striplf(stringbuffer);
                if(emptyline(stringbuffer)) {
                        continue;
                }
                if(iscomment(stringbuffer)) {
                        continue;
                }
                if(!parsetrusted(stringbuffer)) {
#ifdef DEBUG
                        oer_debug(OER_DEBUG_WARNING, "loadtrusted->non-parsable line: '%s'\n", stringbuffer);
#endif
                        return 0;
                }
        }
        fclose(fp);
        return 1;
}

int savetrusted(void)
{
	FILE *fp;
        char filename[STRINGLEN + 1];
        char newfilename[STRINGLEN + 1];
        char stringbuffer[BIGSTRINGLEN + 1];
	struct maskstruct *ms;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "savetrusted()\n");
#endif
	snprintf(filename, STRINGLEN, "%s%s", mystate->progname, ".trusted.saving");
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "savetrusted->saving to %s\n", filename);
#endif
	if((fp = fopen(filename, "w")) == NULL) {
                return 0;
        }
	for(ms = mystate->trusted; ms != NULL; ms = ms->next) {
                snprintf(stringbuffer, BIGSTRINGLEN, "trusted::%s\n", ms->mask);
                fputs(stringbuffer, fp);
        }
        fclose(fp);
	snprintf(newfilename, STRINGLEN, "%s%s", mystate->progname, ".trusted");
#ifdef DEBUG
        oer_debug(OER_DEBUG_INFO, "savetrusted->renaming %s to %s\n", filename, newfilename);
#endif
        if(rename(filename, newfilename) < 0) {
                return 0;
        }
        return 1;
}

int parsetrusted(char *paramline)
{
	int ppos;
	char p1[STRINGLEN + 1];
	char p2[STRINGLEN + 1];
	struct maskstruct *ms;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "parsetrusted(\"%s\")\n", paramline);
#endif
	ppos = 0;
	if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p1, STRINGLEN, 1)) < 0) {
		return 0;
	}
	if(!strcasecmp(p1, "trusted")) {
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p2, STRINGLEN, 1)) < 0) {
			return 0;
		}
		if((ms = addnewtrusted(p2)) == NULL) {
			return 0;
		}
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "parsetrusted->added trusted host %s\n", ms->mask);
#endif
		return 1;
	}
#ifdef DEBUG
        oer_debug(OER_DEBUG_WARNING, "parsetrusted->unknown line: '%s'\n", paramline);
#endif
        return 1;
}

int loadservices(void)
{
	FILE *fp;
        char filename[STRINGLEN + 1];
        char stringbuffer[BIGSTRINGLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "loadservices()\n");
#endif
	snprintf(filename, STRINGLEN, "%s%s", mystate->progname, ".services");
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "loadservices->loading %s\n", filename);
#endif
	if((fp = fopen(filename, "r")) == NULL) {
                return 0;
        }
	while(fgets(stringbuffer, BIGSTRINGLEN, fp)) {
                striplf(stringbuffer);
                if(emptyline(stringbuffer)) {
                        continue;
                }
                if(iscomment(stringbuffer)) {
                        continue;
                }
                if(!parseservices(stringbuffer)) {
#ifdef DEBUG
                        oer_debug(OER_DEBUG_WARNING, "loadservices->non-parsable line: '%s'\n", stringbuffer);
#endif
                        return 0;
                }
        }
        fclose(fp);
        return 1;
}

int saveservices(void)
{
	FILE *fp;
        char filename[STRINGLEN + 1];
        char newfilename[STRINGLEN + 1];
        char stringbuffer[BIGSTRINGLEN + 1];
	struct maskstruct *ms;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "saveservices()\n");
#endif
	snprintf(filename, STRINGLEN, "%s%s", mystate->progname, ".services.saving");
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "saveservices->saving to %s\n", filename);
#endif
	if((fp = fopen(filename, "w")) == NULL) {
                return 0;
        }
	for(ms = mystate->services; ms != NULL; ms = ms->next) {
                snprintf(stringbuffer, BIGSTRINGLEN, "service::%s\n", ms->mask);
                fputs(stringbuffer, fp);
        }
        fclose(fp);
	snprintf(newfilename, STRINGLEN, "%s%s", mystate->progname, ".services");
#ifdef DEBUG
        oer_debug(OER_DEBUG_INFO, "saveservices->renaming %s to %s\n", filename, newfilename);
#endif
        if(rename(filename, newfilename) < 0) {
                return 0;
        }
        return 1;
}

int parseservices(char *paramline)
{
	int ppos;
	char p1[STRINGLEN + 1];
	char p2[STRINGLEN + 1];
	struct maskstruct *ms;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "parseservices(\"%s\")\n", paramline);
#endif
	ppos = 0;
	if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p1, STRINGLEN, 1)) < 0) {
		return 0;
	}
	if(!strcasecmp(p1, "service")) {
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p2, STRINGLEN, 1)) < 0) {
			return 0;
		}
		if((ms = addnewservice(p2)) == NULL) {
			return 0;
		}
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "parseservices->added service %s\n", ms->mask);
#endif
		return 1;
	}
#ifdef DEBUG
        oer_debug(OER_DEBUG_WARNING, "parseservices->unknown line: '%s'\n", paramline);
#endif
        return 1;
}

int loadadmins(void)
{
	FILE *fp;
        char filename[STRINGLEN + 1];
        char stringbuffer[BIGSTRINGLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "loadadmins()\n");
#endif
	snprintf(filename, STRINGLEN, "%s%s", mystate->progname, ".admins");
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "loadadmins->loading %s\n", filename);
#endif
	if((fp = fopen(filename, "r")) == NULL) {
                return 0;
        }
	while(fgets(stringbuffer, BIGSTRINGLEN, fp)) {
                striplf(stringbuffer);
                if(emptyline(stringbuffer)) {
                        continue;
                }
                if(iscomment(stringbuffer)) {
                        continue;
                }
                if(!parseadmins(stringbuffer)) {
#ifdef DEBUG
                        oer_debug(OER_DEBUG_WARNING, "loadadmins->non-parsable line: '%s'\n", stringbuffer);
#endif
                        return 0;
                }
        }
        fclose(fp);
        return 1;
}

int saveadmins(void)
{
	FILE *fp;
        char filename[STRINGLEN + 1];
        char newfilename[STRINGLEN + 1];
        char stringbuffer[BIGSTRINGLEN + 1];
	struct botuser *admin;
	struct maskstruct *ms;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "saveadmins()\n");
#endif
	snprintf(filename, STRINGLEN, "%s%s", mystate->progname, ".admins.saving");
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "saveadmins->saving to %s\n", filename);
#endif
	if((fp = fopen(filename, "w")) == NULL) {
                return 0;
        }
	for(admin = mystate->admins; admin != NULL; admin = admin->next) {
                snprintf(stringbuffer, BIGSTRINGLEN, "admin::%s::%s\n", admin->handle, admin->options);
                fputs(stringbuffer, fp);
                for(ms = admin->firstmask; ms != NULL; ms = ms->next) {
                        snprintf(stringbuffer, BIGSTRINGLEN, "mask::%s::%s\n", admin->handle, ms->mask);
                        fputs(stringbuffer, fp);
                }
        }
        fclose(fp);
	snprintf(newfilename, STRINGLEN, "%s%s", mystate->progname, ".admins");
#ifdef DEBUG
        oer_debug(OER_DEBUG_INFO, "saveadmins->renaming %s to %s\n", filename, newfilename);
#endif
        if(rename(filename, newfilename) < 0) {
                return 0;
        }
        return 1;
}

int parseadmins(char *paramline)
{
	int ppos;
	char p1[STRINGLEN + 1];
	char p2[STRINGLEN + 1];
	char p3[STRINGLEN + 1];
	struct maskstruct *ms;
	struct botuser *admin;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "parseadmins(\"%s\")\n", paramline);
#endif
	ppos = 0;
	if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p1, STRINGLEN, 1)) < 0) {
		return 0;
	}
	if(!strcasecmp(p1, "admin")) {
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p2, STRINGLEN, 1)) < 0) {
			return 0;
		}
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p3, STRINGLEN, 1)) < 0) {
			return 0;
		}
		if((admin = addnewadmin(p2, p3)) == NULL) {
			return 0;
		}
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "parseadmins->added admin %s with options %s\n", admin->handle, admin->options);
#endif
		return 1;
	}
	if(!strcasecmp(p1, "mask")) {
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p2, STRINGLEN, 1)) < 0) {
			return 0;
		}
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p3, STRINGLEN, 1)) < 0) {
			return 0;
		}
		if((ms = addnewadminmask(p2, p3)) == NULL) {
			return 0;
		}
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "parseadmins->added mask %s to admin %s\n", p3, p2);
#endif
		return 1;
	}
#ifdef DEBUG
        oer_debug(OER_DEBUG_WARNING, "parseadmins->unknown line: '%s'\n", paramline);
#endif
        return 1;
}

int loadkickreasons(void)
{
	FILE *fp;
        char filename[STRINGLEN + 1];
        char stringbuffer[BIGSTRINGLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "loadkickreasons()\n");
#endif
	snprintf(filename, STRINGLEN, "%s%s", mystate->progname, ".kickreasons");
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "loadkickreasons->loading %s\n", filename);
#endif
	if((fp = fopen(filename, "r")) == NULL) {
                return 0;
        }
	while(fgets(stringbuffer, BIGSTRINGLEN, fp)) {
                striplf(stringbuffer);
                if(emptyline(stringbuffer)) {
                        continue;
                }
                if(iscomment(stringbuffer)) {
                        continue;
                }
                if(!parsekickreasons(stringbuffer)) {
#ifdef DEBUG
                        oer_debug(OER_DEBUG_WARNING, "loadkickreasons->non-parsable line: '%s'\n", stringbuffer);
#endif
                        return 0;
                }
        }
        fclose(fp);
        return 1;
}

int savekickreasons(void)
{
	FILE *fp;
        char filename[STRINGLEN + 1];
        char newfilename[STRINGLEN + 1];
        char stringbuffer[BIGSTRINGLEN + 1];
	struct maskstruct *ms;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "savekickreasons()\n");
#endif
	snprintf(filename, STRINGLEN, "%s%s", mystate->progname, ".kickreasons.saving");
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "savekickreasons->saving to %s\n", filename);
#endif
	if((fp = fopen(filename, "w")) == NULL) {
                return 0;
        }
	for(ms = mystate->kickreasons; ms != NULL; ms = ms->next) {
                snprintf(stringbuffer, BIGSTRINGLEN, "kickreason::%s\n", ms->mask);
                fputs(stringbuffer, fp);
        }
        fclose(fp);
	snprintf(newfilename, STRINGLEN, "%s%s", mystate->progname, ".kickreasons");
#ifdef DEBUG
        oer_debug(OER_DEBUG_INFO, "savekickreasons->renaming %s to %s\n", filename, newfilename);
#endif
        if(rename(filename, newfilename) < 0) {
                return 0;
        }
        return 1;
}

int parsekickreasons(char *paramline)
{
	int ppos;
	char p1[STRINGLEN + 1];
	char p2[STRINGLEN + 1];
	struct maskstruct *ms;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "parsekickreasons(\"%s\")\n", paramline);
#endif
	ppos = 0;
	if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p1, STRINGLEN, 1)) < 0) {
		return 0;
	}
	if(!strcasecmp(p1, "kickreason")) {
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p2, STRINGLEN, 1)) < 0) {
			return 0;
		}
		if((ms = addnewkickreason(p2)) == NULL) {
			return 0;
		}
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "parsekickreasons->added a new kick reason %s\n", ms->mask);
#endif
		return 1;
	}
#ifdef DEBUG
        oer_debug(OER_DEBUG_WARNING, "parsekickreasons->unknown line: '%s'\n", paramline);
#endif
        return 1;
}

int loadchaninfo(void)
{
	FILE *fp;
        char filename[STRINGLEN + 1];
        char stringbuffer[BIGSTRINGLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "loadchaninfo()\n");
#endif
	snprintf(filename, STRINGLEN, "%s%s", mystate->progname, ".chaninfo");
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "loadchaninfo->loading %s\n", filename);
#endif
	if((fp = fopen(filename, "r")) == NULL) {
                return 0;
        }
	while(fgets(stringbuffer, BIGSTRINGLEN, fp)) {
                striplf(stringbuffer);
                if(emptyline(stringbuffer)) {
                        continue;
                }
                if(iscomment(stringbuffer)) {
                        continue;
                }
                if(!parsechaninfo(stringbuffer)) {
#ifdef DEBUG
                        oer_debug(OER_DEBUG_WARNING, "loadchaninfo->non-parsable line: '%s'\n", stringbuffer);
#endif
                        return 0;
                }
        }
        fclose(fp);
        return 1;
}

int savechaninfo(void)
{
	FILE *fp;
        char filename[STRINGLEN + 1];
        char newfilename[STRINGLEN + 1];
        char stringbuffer[BIGSTRINGLEN + 1];
	struct channel *this;
	struct botuser *user;
	struct maskstruct *ms;
	struct advert *a;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "savechaninfo()\n");
#endif
	snprintf(filename, STRINGLEN, "%s%s", mystate->progname, ".chaninfo.saving");
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "savechaninfo->saving to %s\n", filename);
#endif
	if((fp = fopen(filename, "w")) == NULL) {
                return 0;
        }
	for(this = mystate->channels; this != NULL; this = this->next) {
                for(user = this->users; user != NULL; user = user->next) {
                        snprintf(stringbuffer, BIGSTRINGLEN, "chaninfo::%s::user::%s::%s\n", this->name, user->handle, user->options);
                        fputs(stringbuffer, fp);
                        if(index(user->options, (int)'d') != NULL && user->password != NULL) {
                                snprintf(stringbuffer, BIGSTRINGLEN, "chaninfo::%s::password::%s::%s\n", this->name, user->handle, user->password);
                                fputs(stringbuffer, fp);
                        }
                        for(ms = user->firstmask; ms != NULL; ms = ms->next) {
                                snprintf(stringbuffer, BIGSTRINGLEN, "chaninfo::%s::usermask::%s::%s\n", this->name, user->handle, ms->mask);
                                fputs(stringbuffer, fp);
                        }
                }
                for(ms = this->wordbks; ms != NULL; ms = ms->next) {
                        snprintf(stringbuffer, BIGSTRINGLEN, "chaninfo::%s::wordbk::%s\n", this->name, ms->mask);
                        fputs(stringbuffer, fp);
                }
                for(ms = this->nickbks; ms != NULL; ms = ms->next) {
                        snprintf(stringbuffer, BIGSTRINGLEN, "chaninfo::%s::nickbk::%s::%s\n", this->name, ms->mask, ms->optstring);
                        fputs(stringbuffer, fp);
                }
                for(ms = this->permbans; ms != NULL; ms = ms->next) {
                        snprintf(stringbuffer, BIGSTRINGLEN, "chaninfo::%s::permban::%s\n", this->name, ms->mask);
                        fputs(stringbuffer, fp);
                }
                for(a = this->adverts; a != NULL; a = a->next) {
                        snprintf(stringbuffer, BIGSTRINGLEN, "chaninfo::%s::advert::%s::%s\n", this->name, a->to, a->message);
                        fputs(stringbuffer, fp);
                }
		snprintf(stringbuffer, BIGSTRINGLEN, "chaninfo::%s::floodvars::%d::%d::%d::%d::%d\n", this->name, this->floodvars.repeat_expire, this->floodvars.repeat_limit, this->floodvars.interval, this->floodvars.lines, this->floodvars.chars);
		fputs(stringbuffer, fp);
		snprintf(stringbuffer, BIGSTRINGLEN, "chaninfo::%s::banvars::%d::%d::%d::%d::%d::%d::%d\n", this->name, this->banvars.auto_rejoin, this->banvars.part_rejoin, this->banvars.public_flood, this->banvars.public_flood_repeat, this->banvars.bad_word, this->banvars.bad_nick, this->banvars.normal_ban);
		fputs(stringbuffer, fp);
		snprintf(stringbuffer, BIGSTRINGLEN, "chaninfo::%s::bantype::%d\n", this->name, this->bantype);
		fputs(stringbuffer, fp);
        }
        fclose(fp);
	snprintf(newfilename, STRINGLEN, "%s%s", mystate->progname, ".chaninfo");
#ifdef DEBUG
        oer_debug(OER_DEBUG_INFO, "savechaninfo->renaming %s to %s\n", filename, newfilename);
#endif
        if(rename(filename, newfilename) < 0) {
                return 0;
        }
        return 1;
}

int parsechaninfo(char *paramline)
{
	int ppos;
	int bantype;
	char p0[STRINGLEN + 1];
	char p1[STRINGLEN + 1];
	char p2[STRINGLEN + 1];
	char p3[STRINGLEN + 1];
	char p4[STRINGLEN + 1];
	char p5[STRINGLEN + 1];
	char p6[STRINGLEN + 1];
	char p7[STRINGLEN + 1];
	char p8[STRINGLEN + 1];
	char p9[STRINGLEN + 1];
	struct maskstruct *ms;
	struct botuser *user;
	struct channel *this;
	struct advert *a;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "parsechaninfo(\"%s\")\n", paramline);
#endif
	ppos = 0;
	if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p0, STRINGLEN, 1)) < 0) {
		return 0;
	}
	if(!strcasecmp(p0, "chaninfo")) {
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p1, STRINGLEN, 1)) < 0) {
			return 0;
		}
		if((this = getchptr(p1)) == NULL) {
#ifdef DEBUG
			oer_debug(OER_DEBUG_WARNING, "parsechaninfo->unknown channel %s\n", p1);
#endif
			return 0;
		}
		if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p2, STRINGLEN, 1)) < 0) {
			return 0;
		}
		if(!strcasecmp(p2, "user")) {
			if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p3, STRINGLEN, 1)) < 0) {
				return 0;
			}
			if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p4, STRINGLEN, 1)) < 0) {
				return 0;
			}
			if((user = addnewuser(this, p3, p4)) == NULL) {
				return 0;
			}
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "parsechaninfo->added user %s to channel %s with options %s\n", user->handle, this->name, user->options);
#endif
			return 1;
		}
		if(!strcasecmp(p2, "password")) {
			if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p3, STRINGLEN, 1)) < 0) {
				return 0;
			}
			if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p4, STRINGLEN, 1)) < 0) {
				return 0;
			}
			if(!setpassword(this, p3, p4)) {
#ifdef DEBUG
				oer_debug(OER_DEBUG_INFO, "parsechaninfo->failed to add password %s to %s dyn user %s\n", p4, this->name, p3);
#endif
				return 0;
			}
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "parsechaninfo->added password %s to %s dyn user %s\n", p4, this->name, p3);
#endif
			return 1;
		}
		if(!strcasecmp(p2, "usermask")) {
			if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p3, STRINGLEN, 1)) < 0) {
				return 0;
			}
			if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p4, STRINGLEN, 1)) < 0) {
				return 0;
			}
			if((ms = addnewusermask(this, p3, p4)) == NULL) {
				return 0;
			}
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "parsechaninfo->added mask %s to %s user %s\n", p4, p1, p3);
#endif
			return 1;
		}
		if(!strcasecmp(p2, "wordbk")) {
			if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p3, STRINGLEN, 1)) < 0) {
				return 0;
			}
			addnewwordbk(this, p3);
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "parsechaninfo->added banned word %s to channel %s\n", p3, this->name);
#endif
			return 1;
		}
		if(!strcasecmp(p2, "nickbk")) {
			if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p3, STRINGLEN, 1)) < 0) {
				return 0;
			}
			if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p4, STRINGLEN, 1)) < 0) {
				return 0;
			}
			if(strlen(p4) > 0) {
				addnewnickbk(this, p3, p4);
#ifdef DEBUG
				oer_debug(OER_DEBUG_INFO, "parsechaninfo->added banned nick %s to channel %s (%s)\n", p3, this->name, p4);
#endif
			} else {
				addnewnickbk(this, p3, NULL);
#ifdef DEBUG
				oer_debug(OER_DEBUG_INFO, "parsechaninfo->added banned nick %s to channel %s\n", p3, this->name);
#endif
			}
			return 1;
		}
		if(!strcasecmp(p2, "permban")) {
			if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p3, STRINGLEN, 1)) < 0) {
				return 0;
			}
			addnewpermban(this, p3);
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "parsechaninfo->added permban %s to channel %s\n", p3, p1);
#endif
			return 1;
		}
		if(!strcasecmp(p2, "advert")) {
			if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p3, STRINGLEN, 1)) < 0) {
				return 0;
			}
			if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p4, STRINGLEN, 1)) < 0) {
				return 0;
			}
			if((a = addnewadvert(this, p3, p4)) == NULL) {
				return 0;
			}
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "parsechaninfo->added advert \"%s\" (to: %s) to channel %s\n", a->message, a->to, p1);
#endif
			return 1;
		}
		if(!strcasecmp(p2, "floodvars")) {
			if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p3, STRINGLEN, 1)) < 0) {
				return 0;
			}
			if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p4, STRINGLEN, 1)) < 0) {
				return 0;
			}
			if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p5, STRINGLEN, 1)) < 0) {
				return 0;
			}
			if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p6, STRINGLEN, 1)) < 0) {
				return 0;
			}
			if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p7, STRINGLEN, 1)) < 0) {
				return 0;
			}
			this->floodvars.repeat_expire = atoi(p3);
			this->floodvars.repeat_limit = atoi(p4);
			this->floodvars.interval = atoi(p5);
			this->floodvars.lines = atoi(p6);
			this->floodvars.chars = atoi(p7);
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "parsechaninfo->channel %s floodvars set to %d %d %d %d %d\n", p1, this->floodvars.repeat_expire, this->floodvars.repeat_limit, this->floodvars.interval, this->floodvars.lines, this->floodvars.chars);
#endif
			return 1;
		}
		if(!strcasecmp(p2, "banvars")) {
			if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p3, STRINGLEN, 1)) < 0) {
				return 0;
			}
			if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p4, STRINGLEN, 1)) < 0) {
				return 0;
			}
			if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p5, STRINGLEN, 1)) < 0) {
				return 0;
			}
			if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p6, STRINGLEN, 1)) < 0) {
				return 0;
			}
			if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p7, STRINGLEN, 1)) < 0) {
				return 0;
			}
			if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p8, STRINGLEN, 1)) < 0) {
				return 0;
			}
			if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p9, STRINGLEN, 1)) < 0) {
				return 0;
			}
			this->banvars.auto_rejoin = atoi(p3);
			this->banvars.part_rejoin = atoi(p4);
			this->banvars.public_flood = atoi(p5);
			this->banvars.public_flood_repeat = atoi(p6);
			this->banvars.bad_word = atoi(p7);
			this->banvars.bad_nick = atoi(p8);
			this->banvars.normal_ban = atoi(p9);
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "parsechaninfo->channel %s banvars set to %d %d %d %d %d %d %d\n", p1, this->banvars.auto_rejoin, this->banvars.part_rejoin, this->banvars.public_flood, this->banvars.public_flood_repeat, this->banvars.bad_word, this->banvars.bad_nick, this->banvars.normal_ban);
#endif
			return 1;
		}
		if(!strcasecmp(p2, "bantype")) {
			if((ppos = parse(paramline, ppos, OER_DEFAULT_DELIM, p3, STRINGLEN, 1)) < 0) {
				return 0;
			}
			bantype = atoi(p3);
			if(bantype < OER_BAN_TYPE_HOST || bantype >= OER_BAN_TYPE_INVALID) {
				return 0;
			}
			this->bantype = bantype;
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "parsechaninfo->%s has bantype %d\n", this->name, this->bantype);
#endif
			return 1;
		}
#ifdef DEBUG
		oer_debug(OER_DEBUG_FATAL, "parsechaninfo->bad channel parameter %s\n", p2);
#endif
		return 0;
	}
#ifdef DEBUG
        oer_debug(OER_DEBUG_WARNING, "parsechaninfo->unknown line: '%s'\n", paramline);
#endif
        return 1;
}

int deltachanflags(struct channel *this, char *deltaflags, char polar)
{
	int pos;
        char newflags[FLAGLEN + 1];
        char newflags2[FLAGLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "deltachanflags(\"%s\", \"%s\", \"%c\")\n", this->name, deltaflags, polar);
#endif
	if(polar == '+') {
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "deltachanflags->adding channel flags %s to %s\n", deltaflags, this->name);
#endif
	}
	if(polar == '-') {
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "deltachanflags->removing channel flags %s from %s\n", deltaflags, this->name);
#endif
	}
	/* find out what to add or remove */
	memset(&newflags, 0, FLAGLEN + 1);
	for(pos = strlen(deltaflags) - 1; pos >= 0; pos--) {
		if(index(this->chanflags, (int)deltaflags[pos]) != NULL) {
			if(polar == '-') {
				strncat(newflags, deltaflags + pos, 1);
			}
		} else {
			if(polar == '+') {
				strncat(newflags, deltaflags + pos, 1);
			}
		}
	}
	if(!strlen(newflags)) {
		return 0;
	}
	memset(&newflags2, 0, FLAGLEN + 1);
	for(pos = strlen(this->chanflags) - 1; pos >= 0; pos--) {
		if(index(newflags, (int)this->chanflags[pos]) != NULL) {
			if(polar == '-') {
				continue;
			}
		}
		strncat(newflags2, this->chanflags + pos, 1);
	}
	if(polar == '+') {
		strncat(newflags2, newflags, FLAGLEN - strlen(newflags2));
	}
	sortstring(newflags2);
	strncpy(this->chanflags, newflags2, FLAGLEN);
	return 1;
}

int burstjoins(struct channel *this)
{
	int i;
	time_t delta;
	struct join *j;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "burstjoins(\"%s\")\n", this->name);
#endif
	for(j = this->joins, i = 0; j != NULL; j = j->next) {
		delta = mystate->now - j->at;
		if(delta <= OER_BURST_JOINS_LIMIT) {
			i++;
		}
		if(i >= OER_BURST_JOINS) {
			return 1;
		}
	}
	return 0;
}

int partcount(struct channel *this, char *nick, char *userhost)
{
	int count;
	struct part *p;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "partcount(\"%s\", \"%s\", \"%s\")\n", this->name, nick, userhost);
#endif
	for(count = 0, p = this->parts; p != NULL; p = p->next) {
		if(p->valid && (mystate->now < (p->at + OER_PART_EXPIRE))) {
			if(issamenickandhost(p->nick, nick, p->userhost, userhost)) {
				count++;
			}
		}
	}
	return count;
}

void resetparts(struct channel *this, char *nick, char *userhost)
{
	struct part *p;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "resetparts(\"%s\", \"%s\", \"%s\")\n", this->name, nick, userhost);
#endif
	for(p = this->parts; p != NULL; p = p->next) {
		if(issamenickandhost(p->nick, nick, p->userhost, userhost)) {
			p->valid = 0;
		}
	}
}

time_t lastoff(struct channel *this, char *nick, char *userhost)
{
	time_t delta;
	struct part *p;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "lastoff(\"%s\", \"%s\", \"%s\")\n", this->name, nick, userhost);
#endif
	p = this->parts;
	while(p != NULL) {
		if(issamenickandhost(p->nick, nick, p->userhost, userhost)) {
			delta = (mystate->now - p->at);
			return delta;
		}
		p = p->next;
	}
	return OER_AUTO_REJOIN_TIME + 1;
}

int getjoincount(struct channel *this, char *nick)
{
	int joins;
	struct join *j;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "getjoincount(\"%s\", \"%s\")\n", this->name, nick);
#endif
	joins = 0;
	j = this->joins;
	while(j != NULL) {
		if(!strcasecmp(j->nick, nick)) {
			joins++;
		}
		j = j->next;
	}
	return joins;
}

int isq(char *nick)
{
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "isq(\"%s\")\n", nick);
#endif
	if((index(mystate->flags, (int)'q') != NULL) && !strcasecmp(nick, Q_NICK)) {
                return 1;
        }
	return 0;
}

int isservice(char *host)
{
	struct maskstruct *ms;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "isservice(\"%s\")\n", host);
#endif
        if(index(mystate->flags, (int)'s') == NULL) {
		return 0;
	}
	for(ms = mystate->services; ms != NULL; ms = ms->next) {
                if(wild_match(ms->mask, host)) {
			return 1;
		}
	}
	return 0;
}

int istrusted(char *host)
{
	struct maskstruct *ms;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "istrusted(\"%s\")\n", host);
#endif
	for(ms = mystate->trusted; ms != NULL; ms = ms->next) {
                if(wild_match(ms->mask, host)) {
			return 1;
		}
	}
	return 0;
}

int isme(char *nick)
{
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "isme(\"%s\")\n", nick);
#endif
	if(!strcasecmp((mystate->use_alt_nick) ? mystate->altnick : mystate->nick, nick)) {
		return 1;
	}
	return 0;
}

int ispermban(struct channel *this, char *mask)
{
	struct maskstruct *pb;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "ispermban(\"%s\", \"%s\")\n", this->name, mask);
#endif
	pb = this->permbans;
	while(pb != NULL) {
		if(!strcasecmp(pb->mask, mask)) {
			return 1;
		}
		pb = pb->next;
	}
	return 0;
}

int permbancount(struct channel *this)
{
	int amount;
	struct maskstruct *pb;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "permbancount(\"%s\")\n", this->name);
#endif
	for(amount = 0, pb = this->permbans; pb != NULL; amount++, pb = pb->next)
		;;
	return amount;
}

int topiccount(struct channel *this)
{
	int amount;
	struct topic *t;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "topiccount(\"%s\")\n", this->name);
#endif
	for(amount = 0, t = this->topics; t != NULL; amount++, t = t->next)
		;;
	return amount;
}

char *getkickreason(char *reason)
{
	int counter;
	int nthkr;
	struct maskstruct *kr;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "getkickreason()\n");
#endif
	counter = 0;
	kr = mystate->kickreasons;
	nthkr = getrandom(mystate->kickrs) - 1;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "getkickreason->returning kickreason %d/%d\n", nthkr, mystate->kickrs);
#endif
	while(kr != NULL && counter < nthkr) {
		counter++;
		kr = kr->next;
        }
	if(kr == NULL) {
		return NULL;
	}
	strncpy(reason, kr->mask, STRINGLEN);
	return reason;
}

int isop(struct channel *this, char *userhost)
{
	struct botuser *user;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "isop(\"%s\", \"%s\")\n", (this == NULL) ? "(null)" : this->name, (userhost == NULL) ? "(null)" : userhost);
#endif
	if(this == NULL || userhost == NULL) {
		return 0;
	}
	/* check for static +o user */
	if((user = hasuserflags(this, userhost, "o")) != NULL) {
		if(index(user->options, (int)'d') == NULL) {
			return 1;
		}
	}
	/* check for dynamic +o (logoned) user */
	if(hasuserflags(this, userhost, "do") != NULL && isvalidlogon(this, userhost)) {
		return 1;
	}
	return 0;
}

int isadmin(struct channel *this, char *userhost)
{
	struct botuser *user;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "isadmin(\"%s\", \"%s\")\n", (this == NULL) ? "(null)" : this->name, (userhost == NULL) ? "(null)" : userhost);
#endif
	if(userhost == NULL) {
		return 0;
	}
	/* check for admin */
	if(isopa(userhost)) {
		return 1;
	}
	/* check for static +a user */
	if((user = hasuserflags(this, userhost, "a")) != NULL) {
		if(index(user->options, (int)'d') == NULL) {	
			return 1;
		}
	}
	/* check for dynamic +a (logoned) user */
	if(hasuserflags(this, userhost, "ad") != NULL && isvalidlogon(this, userhost)) {
		return 1;
	}
	return 0;
}

int isvalidlogon(struct channel *this, char *userhost)
{
	struct authed *a;
	struct botuser *user;
	struct maskstruct *ms;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "isvalidlogon(\"%s\", \"%s\")\n", (this == NULL) ? "(null)" : this->name, (userhost == NULL) ? "(null)" : userhost);
#endif
	if(this == NULL || userhost == NULL) {
		return 0;
	}
	/* go through all authed hostmasks that are valid */
	for(a = this->autheds; a != NULL; a = a->next) {
		if(mystate->now > (a->at + OER_LOGON_TIMEOUT)) {
			continue;
		}
		/* check if the auth matches */
		if(strcasecmp(a->userhost, userhost)) {
			continue;
		}
		user = a->user;
		/* check if any of the hosts in the user field match */
		for(ms = user->firstmask; ms != NULL; ms = ms->next) {
			if(wild_match(ms->mask, userhost)) {
				return 1;
			}
		}
		/* this auth didn't match, go to next */
		a = a->next;
	}
	/* no authed for this mask */
	return 0;
}

int isatleastopnow(struct channel *this, char *nick, char *userhost)
{
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "isatleastopnow(\"%s\", \"%s\", \"%s\")\n", (this == NULL) ? "(null)" : this->name, nick, (userhost == NULL) ? "(null)" : userhost);
#endif
	if(isq(nick) || isadmin(this, userhost) || isop(this, userhost) || isopnow(this, nick)) {
		return 1;
	}
	return 0;
}

int isatleastop(struct channel *this, char *nick, char *userhost)
{
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "isatleastop(\"%s\", \"%s\", \"%s\")\n", this->name, nick, userhost);
#endif
	if(isq(nick) || isadmin(this, userhost) || isop(this, userhost)) {
		return 1;
	}
	return 0;
}

int isallowedop(struct channel *this, char *nick)
{
	struct chanuser *cu;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "isallowedop(\"%s\", \"%s\")\n", this->name, nick);
#endif
	for(cu = this->nicks; cu != NULL; cu = cu->next) {
		if(!strcasecmp(cu->nick, nick)) {
			break;
		}
	}
	if(cu == NULL) {
		return 0;
	}
	if(isq(nick)) {
                return 1;
        }
	/* first check for admin */
	if(isadmin(this, cu->userhost)) {
		return 1;
	}
	/* then user */
	if(isop(this, cu->userhost)) {
		return 1;
	}
	/* only dyn left, is dyn user with o flag? */
	if(hasuserflags(this, cu->userhost, "do") == NULL) {
		return 0;
	}
	if(!haschanflags(this, "P")) {
		return 1;
	}
	/* paranoid channel, check for valid logon */
	if(isvalidlogon(this, cu->userhost)) {
		return 1;
	}
	return 0;
}

int isopa(char *userhost)
{
	struct botuser *admin;
	struct maskstruct *ms;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "isopa(\"%s\")\n", (userhost == NULL) ? "(null)" : userhost);
#endif
	if(userhost == NULL) {
		return 0;
	}
	for(admin = mystate->admins; admin != NULL; admin = admin->next) {
		for(ms = admin->firstmask; ms != NULL; ms = ms->next) {
			if(wild_match(ms->mask, userhost)) {
				return 1;
			}
		}
	}
	return 0;
}

int delopa(char *handle)
{
	struct botuser *admin;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "delopa(\"%s\")\n", handle);
#endif
	for(admin = mystate->admins; admin != NULL; admin = admin->next) {
		if(!strcasecmp(admin->handle, handle)) {
			break;
		}
	}
	if(admin == NULL) {
		return 0;
	}
	if(index(admin->options, (int)'n') != NULL) {
		/* we won't delete admins with "n" flag */
		return 0;
	}
	return 1;
}

int isonchan(struct channel *this, char *nick)
{
	struct chanuser *cu;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "isonchan(\"%s\", \"%s\")\n", this->name, nick);
#endif
	for(cu = this->nicks; cu != NULL; cu = cu->next) {
		if(!strcasecmp(cu->nick, nick)) {
			return 1;
		}
	}
	return 0;
}

int issameuser(struct channel *this, char *handle, char *userhost)
{
	struct botuser *user;
	struct maskstruct *ms;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "issameuser(\"%s\", \"%s\", \"%s\")\n", this->name, handle, userhost);
#endif
	for(user = this->users; user != NULL; user = user->next) {
		if(!strcasecmp(user->handle, handle)) {
			break;
		}
	}
	if(user == NULL) {
		return 0;
	}
	for(ms = user->firstmask; ms != NULL; ms = ms->next) {
		if(wild_match(ms->mask, userhost)) {
			return 1;
		}
	}
	return 0;
}

int isopnow(struct channel *this, char *nick)
{
	struct chanuser *cu;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "isopnow(\"%s\", \"%s\")\n", (this == NULL) ? "(null)" : this->name, nick);
#endif
	if(this == NULL) {
		return 0;
	}
	for(cu = this->nicks; cu != NULL; cu = cu->next) {
		if(!strcasecmp(cu->nick, nick)) {
			return cu->chanop;
		}
	}
	return 0;
}

void banuser(struct channel *this, time_t when, char *nick)
{
	struct chanuser *cu;
	char ban[USERHOSTLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "banuser(\"%s\", %lu, \"%s\")\n", this->name, when, nick);
#endif
	for(cu = this->nicks; cu != NULL; cu = cu->next) {
		if(!strcasecmp(cu->nick, nick)) {
			if(cu->userhost == NULL) {
				return;
			}
			strncpy(ban, cu->userhost, USERHOSTLEN);
			if(safeban(this, ban, cu->nick) == NULL) {
				return;
			}
			mmode_new(this, when, "+b", ban);
			mmode_new(this, when + this->banvars.normal_ban, "-b", ban);
			return;
		}
	}
}

void unbanuser(struct channel *this, time_t when, char *nick)
{
	struct chanuser *cu;
	char ban[USERHOSTLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "unbanuser(\"%s\", %lu, \"%s\")\n", this->name, when, nick);
#endif
	for(cu = this->nicks; cu != NULL; cu = cu->next) {
		if(!strcasecmp(cu->nick, nick)) {
			if(cu->userhost == NULL) {
				return;
			}
			if(cu->chanop) {
				continue;
			}
			strncpy(ban, cu->userhost, USERHOSTLEN);
			if(safeban(this, ban, cu->nick) == NULL) {
				return;
			}
			mmode_new(this, when, "-b", ban);
			return;
		}
	}
}

void editcommand(struct channel *this, char *to, int tochan, char *nick, char *userhost, char *commandline)
{
	int proceed;
	int ppos;
	int bantype;
	int tostatic;
	int last_stat;
	int seen_stat;
        char p1[STRINGLEN + 1];
        char p2[STRINGLEN + 1];
        char p3[STRINGLEN + 1];
        char p4[STRINGLEN + 1];
        char p5[STRINGLEN + 1];
        char p6[STRINGLEN + 1];
        char p7[STRINGLEN + 1];
        char p8[STRINGLEN + 1];
	char stringbuffer[BIGSTRINGLEN + 1];
	char cryptsalt[3];
	char *crypted;
	struct botuser *admin;
	struct botuser *user;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "editcommand(\"%s\", \"%s\", %d, \"%s\", \"%s\", \"%s\")\n", (this == NULL) ? "(null)" : this->name, to, tochan, nick, userhost, commandline);
#endif
	/* command line parsing begins here, first parameter = the command */
	ppos = 0;
        if((ppos = parse(commandline, ppos, " ", p1, STRINGLEN, 0)) < 0) {
                return;
        }
	if(!strcasecmp(p1, "flags")) {
		if(!isopa(userhost)) {
			return;
		}
		if(numofparams(commandline) != 1) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		/* check if we have to change some behaviour */
		if(index(p2, (int)'l')) {
			if(index(mystate->flags, (int)'l') == NULL) {
				snprintf(stringbuffer, WRITE_BUFFER_LENGTH, "oer-%d.log", getpid());
				if((mystate->logfp = fopen(stringbuffer, "a+")) == NULL) {
#ifdef DEBUG
					oer_debug(OER_DEBUG_INFO, "editcommand->couldn't open logfile %s: %s\n", stringbuffer, strerror(errno));
#endif
				} else {
#ifdef DEBUG
					oer_debug(OER_DEBUG_INFO, "editcommand->logfile is %s\n", stringbuffer);
#endif
				}
			}
		} else {
			if(index(mystate->flags, (int)'l') != NULL) {
				fclose(mystate->logfp);
				mystate->logfp = NULL;
#ifdef DEBUG
				oer_debug(OER_DEBUG_INFO, "editcommand->logging terminated by request\n");
#endif
			}
		}
		strncpy(mystate->flags, p2, FLAGLEN);
		snprintf(stringbuffer, BIGSTRINGLEN, "my flags are now %s", mystate->flags);
		sendreply(to, tochan, 0, 0, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "floodvars")) {
		if(this == NULL) {
			return;
		}
		if(!isadmin(this, userhost)) {
			return;
		}
		if(numofparams(commandline) != 5) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p3, STRINGLEN, 0)) < 0) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p4, STRINGLEN, 0)) < 0) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p5, STRINGLEN, 0)) < 0) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p6, STRINGLEN, 0)) < 0) {
			return;
		}
		this->floodvars.repeat_expire = atoi(p2);
		this->floodvars.repeat_limit = atoi(p3);
		this->floodvars.interval = atoi(p4);
		this->floodvars.lines = atoi(p5);
		this->floodvars.chars = atoi(p6);
		snprintf(stringbuffer, BIGSTRINGLEN, "%s floodvars are now %d %d %d %d %d", this->name, this->floodvars.repeat_expire, this->floodvars.repeat_limit, this->floodvars.interval, this->floodvars.lines, this->floodvars.chars);
		sendreply(to, tochan, 0, 0, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "banvars")) {
		if(this == NULL) {
			return;
		}
		if(!isadmin(this, userhost)) {
			return;
		}
		if(numofparams(commandline) != 7) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p3, STRINGLEN, 0)) < 0) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p4, STRINGLEN, 0)) < 0) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p5, STRINGLEN, 0)) < 0) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p6, STRINGLEN, 0)) < 0) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p7, STRINGLEN, 0)) < 0) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p8, STRINGLEN, 0)) < 0) {
			return;
		}
		this->banvars.auto_rejoin = atoi(p2);
		this->banvars.part_rejoin = atoi(p3);
		this->banvars.public_flood = atoi(p4);
		this->banvars.public_flood_repeat = atoi(p5);
		this->banvars.bad_word = atoi(p6);
		this->banvars.bad_nick = atoi(p7);
		this->banvars.normal_ban = atoi(p8);
		snprintf(stringbuffer, BIGSTRINGLEN, "%s banvars are now %d %d %d %d %d %d %d", this->name, this->banvars.auto_rejoin, this->banvars.part_rejoin, this->banvars.public_flood, this->banvars.public_flood_repeat, this->banvars.bad_word, this->banvars.bad_nick, this->banvars.normal_ban);
		sendreply(to, tochan, 0, 0, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "bantype")) {
		if(this == NULL) {
			return;
		}
		if(!isadmin(this, userhost)) {
			return;
		}
		if(numofparams(commandline) != 1) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		bantype = atoi(p2);
		if(bantype < OER_BAN_TYPE_HOST || bantype >= OER_BAN_TYPE_INVALID) {
			return;
		}
		this->bantype = bantype;
		snprintf(stringbuffer, BIGSTRINGLEN, "%s ban type is now %d", this->name, this->bantype);
		sendreply(to, tochan, 0, 0, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "chanflags")) {
		if(this == NULL) {
			return;
		}
		if(!isadmin(this, userhost)) {
			return;
		}
		if(numofparams(commandline) != 1) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		last_stat = -1;
		seen_stat = -1;
		if(index(p2, (int)'L') != NULL && index(this->chanflags, (int)'L') == NULL) {
			last_stat = 1;
		}
		if(index(p2, (int)'S') != NULL && index(this->chanflags, (int)'S') == NULL) {
			seen_stat = 1;
		}
		if(index(p2, (int)'L') == NULL && index(this->chanflags, (int)'L') != NULL) {
			last_stat = 0;
		}
		if(index(p2, (int)'S') == NULL && index(this->chanflags, (int)'S') != NULL) {
			seen_stat = 0;
		}
		sortstring(p2);
		strncpy(this->chanflags, p2, FLAGLEN);
		snprintf(stringbuffer, BIGSTRINGLEN, "%s chanflags are now %s", this->name, this->chanflags);
		sendreply(to, tochan, 0, 0, stringbuffer);
		if(last_stat == 1) {
			if(this->last == NULL) {
				snprintf(p3, STRINGLEN, "last-%s", this->name);
				if((this->last = fopen(p3, "a")) == NULL) {
#ifdef DEBUG
					oer_debug(OER_DEBUG_INFO, "editcommand->failed to open %s: %s\n", p3, strerror(errno));
#endif
				} else {
					snprintf(stringbuffer, BIGSTRINGLEN, "%s has been opened for logging", p3);
					sendreply(to, tochan, 0, 0, stringbuffer);
				}
			}
		}
		if(seen_stat == 1) {
			if(this->seen == NULL) {
				snprintf(p3, STRINGLEN, "seen-%s", this->name);
				if((this->seen = fopen(p3, "a")) == NULL) {
#ifdef DEBUG
					oer_debug(OER_DEBUG_INFO, "editcommand->failed to open %s: %s\n", p3, strerror(errno));
#endif
				} else {
					snprintf(stringbuffer, BIGSTRINGLEN, "%s has been opened for logging", p3);
					sendreply(to, tochan, 0, 0, stringbuffer);
				}

			}
		}
		if(last_stat == 0) {
			if(this->last != NULL) {
				fclose(this->last);
				this->last = NULL;
				snprintf(p3, STRINGLEN, "last-%s", this->name);
				snprintf(stringbuffer, BIGSTRINGLEN, "%s has been closed, you will have to delete it manually", p3);
				sendreply(to, tochan, 0, 0, stringbuffer);
			}
		}
		if(seen_stat == 0) {
			if(this->seen != NULL) {
				fclose(this->seen);
				this->seen = NULL;
				snprintf(p3, STRINGLEN, "seen-%s", this->name);
				snprintf(stringbuffer, BIGSTRINGLEN, "%s has been closed, you will have to delete it manually", p3);
				sendreply(to, tochan, 0, 0, stringbuffer);
			}
		}
		return;
	}
	if(!strcasecmp(p1, "chanmode")) {
		if(this == NULL) {
			return;
		}
		if(!isadmin(this, userhost)) {
			return;
		}
		if(numofparams(commandline) != 1) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		strncpy(this->mode, p2, CHANLEN);
		this->setchanmode = 1;
		snprintf(stringbuffer, BIGSTRINGLEN, "%s chanmodes are now %s", this->name, this->mode);
		sendreply(to, tochan, 0, 0, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "chankey")) {
		if(this == NULL) {
			return;
		}
		if(!isadmin(this, userhost)) {
			return;
		}
		if(numofparams(commandline) != 1) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		setchankey(this, p2);
		if(strlen(this->key)) {
			snprintf(stringbuffer, BIGSTRINGLEN, "%s channel key is now %s", this->name, this->key);
			sendreply(to, tochan, 0, 0, stringbuffer);
		}
		return;
	}
	if(!strcasecmp(p1, "nick")) {
		if(!isopa(userhost)) {
			return;
		}
		if(numofparams(commandline) != 1) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		strncpy(mystate->nick, p2, NICKLEN);
		mystate->newnick = 1;
		mystate->use_alt_nick = 0;
		snprintf(stringbuffer, BIGSTRINGLEN, "my nick is now %s", mystate->nick);
		sendreply(to, tochan, 0, 0, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "altnick")) {
		if(!isopa(userhost)) {
			return;
		}
		if(numofparams(commandline) != 1) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		strncpy(mystate->altnick, p2, NICKLEN);
		snprintf(stringbuffer, BIGSTRINGLEN, "my alternative nick is now %s", mystate->altnick);
		sendreply(to, tochan, 0, 0, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "prefix")) {
		if(!isopa(userhost)) {
			return;
		}
		if(numofparams(commandline) != 1) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		strncpy(mystate->prefix, p2, MICROSTRINGLEN);
		snprintf(stringbuffer, BIGSTRINGLEN, "my command prefix is now %s", mystate->prefix);
		sendreply(to, tochan, 0, 0, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "realname")) {
		if(!isopa(userhost)) {
			return;
		}
		if(numofparams(commandline) != 1) {
			return;
		}
		strncpy(mystate->realname, commandline + ppos, STRINGLEN);
		snprintf(stringbuffer, BIGSTRINGLEN, "my IRC REALNAME is now %s", mystate->realname);
		sendreply(to, tochan, 0, 0, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "usermode")) {
		if(!isopa(userhost)) {
			return;
		}
		if(numofparams(commandline) != 1) {
			return;
		}
		strncpy(mystate->mode, commandline + ppos, CHANLEN);
		snprintf(stringbuffer, BIGSTRINGLEN, "my usermodes are now %s", mystate->mode);
		sendreply(to, tochan, 0, 0, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "user")) {
		if(!isopa(userhost)) {
			return;
		}
		if(numofparams(commandline) != 1) {
			return;
		}
		strncpy(mystate->user, commandline + ppos, USERLEN);
		snprintf(stringbuffer, BIGSTRINGLEN, "my user ID (ident) is now %s", mystate->user);
		sendreply(to, tochan, 0, 0, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "vhost")) {
		if(!isopa(userhost)) {
			return;
		}
		if(numofparams(commandline) != 1) {
			return;
		}
		strncpy(mystate->host, commandline + ppos, HOSTLEN);
		mystate->vhost = 1;
		snprintf(stringbuffer, BIGSTRINGLEN, "my virtual host is now %s", mystate->host);
		sendreply(to, tochan, 0, 0, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "password")) {
		if(this == NULL) {
			return;
		}
		if(numofparams(commandline) != 2) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p3, STRINGLEN, 0)) < 0) {
			return;
		}
		for(user = this->users; user != NULL; user = user->next) {
			if(!strcasecmp(user->handle, p2)) {
				break;
			}
		}
		if(user == NULL) {
			return;
		}
		proceed = (isopa(userhost)) ? 1 : 0;
		if(index(user->options, (int)'a') != NULL) {
			/* the user has +a flag */
			if(isadmin(this, userhost) && issameuser(this, p2, userhost)) {
				proceed = 1;
			}
		} else {
			/* a normal user */
			if(isadmin(this, userhost)) {
				proceed = 1;
			}
			if(isop(this, userhost) && issameuser(this, p2, userhost)) {
				proceed = 1;
			}
		}
		if(!proceed) {
			return;
		}
		cryptsalt[0] = salt_chars[getrandom(strlen(salt_chars)) - 1];
		cryptsalt[1] = cryptsalt[0];
		cryptsalt[2] = '\0';
		crypted = crypt(p3, cryptsalt);
		if(!setpassword(this, p2, crypted)) {
			return;
		}
		snprintf(stringbuffer, BIGSTRINGLEN, "password for %s user %s is now %s", this->name, p2, p3);
		sendreply(to, tochan, 0, 0, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "options")) {
		if(numofparams(commandline) != 3) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p3, STRINGLEN, 0)) < 0) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p4, STRINGLEN, 0)) < 0) {
			return;
		}
		if(!strcasecmp(p2, "admin")) {
			if(!isopa(userhost)) {
				return;
			}
			for(admin = mystate->admins; admin != NULL; admin = admin->next) {
				if(!strcasecmp(admin->handle, p3)) {
					break;
				}
			}
			if(admin == NULL) {
				return;
			}
			/* we won't allow removal of the "n" flag for admins */
			if((index(admin->options, (int)'n') != NULL) && (index(p4, (int)'n') == NULL)) {
#ifdef DEBUG
				oer_debug(OER_DEBUG_INFO, "editcommand->%s!%s attempted to unprotect protected admin %s\n", nick, userhost, p3);
#endif
				return;
			}
			free(admin->options);
			if((admin->options = malloc(strlen(p4) + 1)) == NULL) {
				return;
			}
			strcpy(admin->options, p4);
			snprintf(stringbuffer, BIGSTRINGLEN, "options for admin %s are now %s", admin->handle, admin->options);
			sendreply(to, tochan, 0, 0, stringbuffer);
			return;
		}
		if(!strcasecmp(p2, "user")) {
			if(this == NULL) {
				return;
			}
			for(user = this->users; user != NULL; user = user->next) {
				if(!strcasecmp(user->handle, p3)) {
					break;
				}
			}
			if(user == NULL) {
				return;
			}
			proceed = (isopa(userhost)) ? 1 : 0;
			if(index(user->options, (int)'a') != NULL) {
				/* the user has +a flag */
				if(isadmin(this, userhost) && issameuser(this, p3, userhost)) {
					proceed = 1;
				}
			} else {
				/* a normal user, check that no "a" flag is added */
				if(isadmin(this, userhost) && (index(p4, (int)'a') == NULL)) {
					proceed = 1;
				}
			}
			if(!proceed) {
				return;
			}
			tostatic = 0;
			if(index(user->options, (int)'d') != NULL && index(p4, (int)'d') == NULL) {
				tostatic = 1;
			}
			free(user->options);
			if((user->options = malloc(strlen(p4) + 1)) == NULL) {
				return;
			}
			strcpy(user->options, p4);
			if(tostatic && user->password) {
				free(user->password);
				user->password = NULL;
			}
			snprintf(stringbuffer, BIGSTRINGLEN, "options for user %s are now %s", user->handle, user->options);
			sendreply(to, tochan, 0, 0, stringbuffer);
			if(this->i_am_op) {
				syncvoices(this);
				syncops(this);
			}
			return;
		}
		return;
	}
	if(!strcasecmp(p1, "mask")) {
		if(numofparams(commandline) != 4) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p3, STRINGLEN, 0)) < 0) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p4, STRINGLEN, 0)) < 0) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p5, STRINGLEN, 0)) < 0) {
			return;
		}
		if(!strcasecmp(p2, "admin")) {
			if(!isopa(userhost)) {
				return;
			}
			for(admin = mystate->admins; admin != NULL; admin = admin->next) {
				if(!strcasecmp(admin->handle, p3)) {
					break;
				}
			}
			if(admin == NULL) {
				return;
			}
			if(editmask(admin->firstmask, p4, p5) == NULL) {
				return;
			}
			snprintf(stringbuffer, BIGSTRINGLEN, "changed mask for admin %s from %s to %s", p3, p4, p5);
			sendreply(to, tochan, 0, 0, stringbuffer);
			return;
		}
		if(!strcasecmp(p2, "user")) {
			if(this == NULL) {
				return;
			}
			for(user = this->users; user != NULL; user = user->next) {
				if(!strcasecmp(user->handle, p3)) {
					break;
				}
			}
			if(user == NULL) {
				return;
			}
			proceed = (isopa(userhost)) ? 1 : 0;
			if(index(user->options, (int)'a') != NULL) {
				/* the user has +a flag */
				if(isadmin(this, userhost) && issameuser(this, p3, userhost)) {
					proceed = 1;
				}
			} else {
				/* a normal user */
				if(isadmin(this, userhost)) {
					proceed = 1;
				}
				if(isop(this, userhost) && issameuser(this, p3, userhost)) {
					proceed = 1;
				}
			}
			if(!proceed) {
				return;
			}
			if(editmask(user->firstmask, p4, p5) == NULL) {
				return;
			}
			snprintf(stringbuffer, BIGSTRINGLEN, "changed mask for %s channel user %s from %s to %s", this->name, p3, p4, p5);
			sendreply(to, tochan, 0, 0, stringbuffer);
			if(this->i_am_op) {
				syncvoices(this);
				syncops(this);
			}
			return;
		}
		return;
	}
}

void delcommand(struct channel *this, char *to, int tochan, char *nick, char *userhost, char *commandline)
{
	int proceed;
	int port;
	int modes;
	int ping;
	int opers;
	int noise;
	int ppos;
	int nppos;
	int last_stat;
	int seen_stat;
        char p1[STRINGLEN + 1];
        char p2[STRINGLEN + 1];
        char p3[STRINGLEN + 1];
        char p4[STRINGLEN + 1];
        char p5[STRINGLEN + 1];
        char p6[STRINGLEN + 1];
        char p7[STRINGLEN + 1];
	char stringbuffer[BIGSTRINGLEN + 1];
	struct botuser *user;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "delcommand(\"%s\", \"%s\", %d, \"%s\", \"%s\", \"%s\")\n", (this == NULL) ? "(null)" : this->name, to, tochan, nick, userhost, commandline);
#endif
	/* command line parsing begins here, first parameter = the command */
	ppos = 0;
        if((ppos = parse(commandline, ppos, " ", p1, STRINGLEN, 0)) < 0) {
                return;
        }
	if(!strcasecmp(p1, "admin")) {
		if(!isopa(userhost)) {
			return;
		}
		if(numofparams(commandline) < 1) {
			return;
		}
		while(1) {
                        nppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0);
                        if(nppos == ppos || nppos < 0) {
                                break;
                        }
			ppos = nppos;
			if(!deladmin(p2)) {
#ifdef DEBUG
				oer_debug(OER_DEBUG_INFO, "delcommand->%s!%s attempted to delete protected admin %s\n", nick, userhost, p2);
				
#endif
				continue;
			}
			snprintf(stringbuffer, BIGSTRINGLEN, "deleted admin %s", p2);
			sendreply(to, tochan, 0, 1, stringbuffer);
		}
		return;
	}
	if(!strcasecmp(p1, "chankey")) {
		if(this == NULL) {
			return;
		}
		if(!isadmin(this, userhost)) {
			return;
		}
		if((numofparams(commandline) != 0)) {
			return;
		}
		if(delchankey(this)) {
			snprintf(stringbuffer, BIGSTRINGLEN, "deleted channel key from %s", this->name);
			sendreply(to, tochan, 0, 1, stringbuffer);
		}
		return;
	}
	if(!strcasecmp(p1, "channel")) {
		if(!isopa(userhost)) {
			return;
		}
		if(numofparams(commandline) < 1) {
			return;
		}
		while(1) {
                        nppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0);
                        if(nppos == ppos || nppos < 0) {
                                break;
                        }
			ppos = nppos;
			if((this = getchptr(p2)) == NULL) {
				continue;
			}
			last_stat = -1;
			seen_stat = -1;
			if(index(this->chanflags, (int)'L') != NULL) {
				last_stat = 0;
			}
			if(index(this->chanflags, (int)'S') != NULL) {
				seen_stat = 0;
			}
			if(last_stat == 0) {
				if(this->last != NULL) {
					fclose(this->last);
					this->last = NULL;
					snprintf(p3, STRINGLEN, "last-%s", this->name);
					snprintf(stringbuffer, BIGSTRINGLEN, "%s has been closed, you will have to delete it manually", p3);
					sendreply(to, tochan, 0, 0, stringbuffer);
				}
			}
			if(seen_stat == 0) {
				if(this->seen != NULL) {
					fclose(this->seen);
					this->seen = NULL;
					snprintf(p3, STRINGLEN, "seen-%s", this->name);
					snprintf(stringbuffer, BIGSTRINGLEN, "%s has been closed, you will have to delete it manually", p3);
					sendreply(to, tochan, 0, 0, stringbuffer);
				}
			}
			if(!delchannel(this)) {
				return;
			}
			snprintf(stringbuffer, BIGSTRINGLEN, "deleted channel %s", p2);
			sendreply(to, tochan, 0, 1, stringbuffer);
		}
		/* force save here since there is no way to get
		   back .chaninfo for a channel that was deleted */
		savechannels();
		savechaninfo();
		return;
	}
	if(!strcasecmp(p1, "chanflags")) {
		if(this == NULL) {
			return;
		}
		if(!isadmin(this, userhost)) {
			return;
		}
		if(numofparams(commandline) != 1) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		last_stat = -1;
		seen_stat = -1;
		if(index(p2, (int)'L') != NULL && index(this->chanflags, (int)'L') != NULL) {
			last_stat = 0;
		}
		if(index(p2, (int)'S') != NULL && index(this->chanflags, (int)'S') != NULL) {
			seen_stat = 0;
		}
		if(!deltachanflags(this, p2, '-')) {
			return;
		}
		snprintf(stringbuffer, BIGSTRINGLEN, "%s chanflags are now %s", this->name, this->chanflags);
		sendreply(to, tochan, 0, 0, stringbuffer);
		if(last_stat == 0) {
			if(this->last != NULL) {
				fclose(this->last);
				this->last = NULL;
				snprintf(p3, STRINGLEN, "last-%s", this->name);
				snprintf(stringbuffer, BIGSTRINGLEN, "%s has been closed, you will have to delete it manually", p3);
				sendreply(to, tochan, 0, 0, stringbuffer);
			}
		}
		if(seen_stat == 0) {
			if(this->seen != NULL) {
				fclose(this->seen);
				this->seen = NULL;
				snprintf(p3, STRINGLEN, "seen-%s", this->name);
				snprintf(stringbuffer, BIGSTRINGLEN, "%s has been closed, you will have to delete it manually", p3);
				sendreply(to, tochan, 0, 0, stringbuffer);
			}
		}
		return;
	}
	if(!strcasecmp(p1, "user")) {
		if(this == NULL) {
			return;
		}
		if(numofparams(commandline) < 1) {
			return;
		}
		while(1) {
			nppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0);
			if(nppos == ppos || nppos < 0) {
				break;
			}
			ppos = nppos;
			for(user = this->users; user != NULL; user = user->next) {
				if(!strcasecmp(user->handle, p2)) {
					break;
				}
			}
			if(user == NULL) {
				continue;
			}
			proceed = (isopa(userhost)) ? 1 : 0;
			if(index(user->options, (int)'a') == NULL) {
				/* a normal user */
				if(isadmin(this, userhost)) {
					proceed = 1;
				}
			}
			if(!proceed) {
				continue;
			}
			if(!deluser(this, p2)) {
				continue;
			}
			snprintf(stringbuffer, BIGSTRINGLEN, "deleted %s channel user %s", this->name, p2);
			sendreply(to, tochan, 0, 1, stringbuffer);
		}
		if(this->i_am_op) {
			syncops(this);
		}
		return;
	}
	if(!strcasecmp(p1, "mask")) {
		if(numofparams(commandline) < 3) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p3, STRINGLEN, 0)) < 0) {
			return;
		}
		if(!strcasecmp(p2, "admin")) {
			if(!isopa(userhost)) {
				return;
			}
			while(1) {
				nppos = parse(commandline, ppos, " ", p4, STRINGLEN, 0);
				if(nppos == ppos || nppos < 0) {
					break;
				}
				ppos = nppos;
				if(!deladminmask(p3, p4)) {
					continue;
				}
				snprintf(stringbuffer, BIGSTRINGLEN, "deleted mask %s from admin %s", p3, p4);
				sendreply(to, tochan, 0, 1, stringbuffer);
			}
			return;
		}
		if(!strcasecmp(p2, "user")) {
			if(this == NULL) {
				return;
			}
			for(user = this->users; user != NULL; user = user->next) {
				if(!strcasecmp(user->handle, p3)) {
					break;
				}
			}
			if(user == NULL) {
				return;
			}
			proceed = (isopa(userhost)) ? 1 : 0;
			if(index(user->options, (int)'a') != NULL) {
				/* the user has +a flag */
				if(isadmin(this, userhost) && issameuser(this, p3, userhost)) {
					proceed = 1;
				}
			} else {
				/* a normal user */
				if(isadmin(this, userhost)) {
					proceed = 1;
				}
				if(isop(this, userhost) && issameuser(this, p3, userhost)) {
					proceed = 1;
				}
			}
			if(!proceed) {
				return;
			}
			while(1) {
				nppos = parse(commandline, ppos, " ", p4, STRINGLEN, 0);
				if(nppos == ppos || nppos < 0) {
					break;
				}
				ppos = nppos;
				if(!delusermask(this, p3, p4)) {
					continue;
				}
				snprintf(stringbuffer, BIGSTRINGLEN, "deleted mask %s from %s user %s", p4, this->name, p3);
				sendreply(to, tochan, 0, 1, stringbuffer);
			}
			if(this->i_am_op) {
				syncops(this);
			}
			return;
		}
		return;
	}
	if(!strcasecmp(p1, "permban")) {
		if(this == NULL) {
			return;
		}
		if(numofparams(commandline) < 1) {
			return;
		}
		while(1) {
                        nppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0);
                        if(nppos == ppos || nppos < 0) {
                                break;
                        }
			ppos = nppos;
			if(!delpermban(this, p2)) {
				continue;
			}
			snprintf(stringbuffer, BIGSTRINGLEN, "removed permban %s from channel %s", p2, this->name);
			sendreply(to, tochan, 0, 1, stringbuffer);
		}
		if(this->i_am_op) {
			mmode_new(this, mystate->now, "-b", p2);
			syncbans(this);
		}
		return;
	}
	if(!strcasecmp(p1, "adverts")) {
		if(this == NULL) {
			return;
		}
		if(numofparams(commandline) < 1) {
			return;
		}
		while(1) {
                        nppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0);
                        if(nppos == ppos || nppos < 0) {
                                break;
                        }
			ppos = nppos;
			if(!strcasecmp(p2, "admins") && !isadmin(this, userhost)) {
				continue;
			}
			if(!deladverts(this, p2)) {
				continue;
			}
			snprintf(stringbuffer, BIGSTRINGLEN, "removed all adverts directed to \"%s\" from channel %s", p2, this->name);
			sendreply(to, tochan, 0, 1, stringbuffer);
		}
		return;
	}
	if(!strcasecmp(p1, "nickbk")) {
		if(this == NULL) {
			return;
		}
		if(numofparams(commandline) < 1) {
			return;
		}
		while(1) {
                        nppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0);
                        if(nppos == ppos || nppos < 0) {
                                break;
                        }
			ppos = nppos;
			if(!delnickbk(this, p2)) {
				continue;
			}
			snprintf(stringbuffer, BIGSTRINGLEN, "removed nickbk %s from channel %s", p2, this->name);
			sendreply(to, tochan, 0, 1, stringbuffer);
		}
		return;
	}
	if(!strcasecmp(p1, "wordbk")) {
		if(this == NULL) {
			return;
		}
		if(numofparams(commandline) < 1) {
			return;
		}
		while(1) {
                        nppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0);
                        if(nppos == ppos || nppos < 0) {
                                break;
                        }
			ppos = nppos;
			if(!delwordbk(this, p2)) {
				continue;
			}
			snprintf(stringbuffer, BIGSTRINGLEN, "removed wordbk %s from channel %s", p2, this->name);
			sendreply(to, tochan, 0, 1, stringbuffer);
		}
		return;
	}
	if(!strcasecmp(p1, "kickreason")) {
		if(!isopa(userhost)) {
			return;
		}
		if(numofparams(commandline) < 1) {
			return;
		}
		if(delkickreason(commandline + ppos)) {
			snprintf(stringbuffer, BIGSTRINGLEN, "removed kickreason: %s", commandline + ppos);
			sendreply(to, tochan, 0, 1, stringbuffer);
		}
		return;
	}
	if(!strcasecmp(p1, "trusted")) {
		if(!isopa(userhost)) {
			return;
		}
		if(numofparams(commandline) < 1) {
			return;
		}
		while(1) {
                        nppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0);
                        if(nppos == ppos || nppos < 0) {
                                break;
                        }
			ppos = nppos;
			if(!deltrusted(p2)) {
				continue;
			}
			snprintf(stringbuffer, BIGSTRINGLEN, "removed trusted host %s", p2);
			sendreply(to, tochan, 0, 1, stringbuffer);
		}
		return;
	}
	if(!strcasecmp(p1, "service")) {
		if(!isopa(userhost)) {
			return;
		}
		if(numofparams(commandline) < 1) {
			return;
		}
		while(1) {
                        nppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0);
                        if(nppos == ppos || nppos < 0) {
                                break;
                        }
			ppos = nppos;
			if(!delservice(p2)) {
				continue;
			}
			snprintf(stringbuffer, BIGSTRINGLEN, "removed service %s", p2);
			sendreply(to, tochan, 0, 1, stringbuffer);
		}
		return;
	}
	if(!strncasecmp(commandline, "server", 6)) {
		if(!isopa(userhost)) {
			return;
		}
		if(numofparams(commandline) != 6) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p3, STRINGLEN, 0)) < 0) {
			return;
		}
		if(!strlen(p3)) {
			return;
		}
		port = atoi(p3);
		if((ppos = parse(commandline, ppos, " ", p4, STRINGLEN, 0)) < 0) {
			return;
		}
		modes = (strlen(p4) > 0) ? atoi(p4) : 6;
		if((ppos = parse(commandline, ppos, " ", p5, STRINGLEN, 0)) < 0) {
			return;
		}
		ping = (strlen(p5) > 0) ? atoi(p5) : 90;
		if((ppos = parse(commandline, ppos, " ", p6, STRINGLEN, 0)) < 0) {
			return;
		}
		opers = (strlen(p6) > 0) ? atoi(p6) : 1;
		if((ppos = parse(commandline, ppos, " ", p7, STRINGLEN, 0)) < 0) {
			return;
		}
		noise = (strlen(p7) > 0) ? atoi(p7) : 1;
		if(!delserver(p2, port, modes, ping, opers, noise)) {
			return;
		}
                snprintf(stringbuffer, BIGSTRINGLEN, "removed IRC server: %s %d %d %d %d %d", p2, port, modes, ping, opers, noise);
		sendreply(to, tochan, 0, 1, stringbuffer);
		return;
	}
}

void sendadverts(struct channel *this, char *nick, char *userhost)
{
	int send_lamers;
	int send_ops;
	int send_admins;
	int send_other;
	int proceed;
	struct advert *a;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "sendadverts(\"%s\", \"%s\", \"%s\")\n", this->name, nick, userhost);
#endif
	send_lamers = 1;
	send_ops = 0;
	send_admins = 0;
	send_other = 0;
	if(isadmin(this, userhost)) {
		send_lamers = 0;
		send_ops = 1;
		send_admins = 1;
	}
	if(isop(this, userhost)) {
		send_lamers = 0;
		send_ops = 1;
	}
	if(hasuserflags(this, userhost, "d") != NULL || hasuserflags(this, userhost, "f") != NULL) {
		send_lamers = 0;
		send_other = 1;
	}
	for(a = this->adverts; a != NULL; a = a->next) {
		proceed = 0;
		/* check for static types */
		if(!strcmp(a->to, "lamers") && send_lamers) {
			proceed = 1;
		}
		if(!strcmp(a->to, "ops") && send_ops) {
			proceed = 1;
		}
		if(!strcmp(a->to, "admins") && send_admins) {
			proceed = 1;
		}
		if(!strcmp(a->to, "other") && send_other) {
			proceed = 1;
		}
		if(!strcmp(a->to, "all")) {
			proceed = 1;
		}
		/* then check user@host */
		if(wild_match(a->to, userhost)) {
			proceed = 1;
		}
		if(proceed) {
			sendreply(nick, 0, 0, 0, a->message);
		}
	}
}

void addcommand(struct channel *this, char *to, int tochan, char *nick, char *userhost, char *commandline)
{
	int amount;
	int port;
	int modes;
	int ping;
	int opers;
	int noise;
	int proceed;
	int ppos;
	int nppos;
	int last_stat;
	int seen_stat;
        char p1[STRINGLEN + 1];
        char p2[STRINGLEN + 1];
        char p3[STRINGLEN + 1];
        char p4[STRINGLEN + 1];
        char p5[STRINGLEN + 1];
        char p6[STRINGLEN + 1];
        char p7[STRINGLEN + 1];
        char p8[STRINGLEN + 1];
	char stringbuffer[BIGSTRINGLEN + 1];
	char *pwptr;
	struct botuser *user;
	struct maskstruct *ms;
	struct server *server;
	struct advert *a;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "addcommand(\"%s\", \"%s\", %d, \"%s\", \"%s\", \"%s\")\n", (this == NULL) ? "(null)" : this->name, to, tochan, nick, userhost, commandline);
#endif
	/* command line parsing begins here, first parameter = the command */
	ppos = 0;
        if((ppos = parse(commandline, ppos, " ", p1, STRINGLEN, 0)) < 0) {
                return;
        }
	if(!strcasecmp(p1, "channel")) {
		if(!isopa(userhost)) {
			return;
		}
		if(numofparams(commandline) < 1) {
			return;
		}
		while(1) {
                        nppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0);
                        if(nppos == ppos || nppos < 0) {
                                break;
                        }
			ppos = nppos;
			if(!isvalidchannel(p2)) {
				continue;
			}
			if((this = addnewchannel(p2)) == NULL) {
				continue;
			}
			snprintf(stringbuffer, BIGSTRINGLEN, "added new channel %s", this->name);
			sendreply(to, tochan, 0, 0, stringbuffer);
		}
		return;
	}
	if(!strcasecmp(p1, "chanflags")) {
		if(this == NULL) {
			return;
		}
		if(!isadmin(this, userhost)) {
			return;
		}
		if(numofparams(commandline) != 1) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		last_stat = -1;
		seen_stat = -1;
		if(index(p2, (int)'L') != NULL && index(this->chanflags, (int)'L') == NULL) {
			last_stat = 1;
		}
		if(index(p2, (int)'S') != NULL && index(this->chanflags, (int)'S') == NULL) {
			seen_stat = 1;
		}
		if(!deltachanflags(this, p2, '+')) {
			return;
		}
		snprintf(stringbuffer, BIGSTRINGLEN, "%s chanflags are now %s", this->name, this->chanflags);
		sendreply(to, tochan, 0, 0, stringbuffer);
		if(last_stat == 1) {
			if(this->last == NULL) {
				snprintf(p3, STRINGLEN, "last-%s", this->name);
				if((this->last = fopen(p3, "a")) == NULL) {
#ifdef DEBUG
					oer_debug(OER_DEBUG_INFO, "addcommand->failed to open %s: %s\n", p3, strerror(errno));
#endif
				} else {
					snprintf(stringbuffer, BIGSTRINGLEN, "%s has been opened for logging", p3);
					sendreply(to, tochan, 0, 0, stringbuffer);
				}
			}
		}
		if(seen_stat == 1) {
			if(this->seen == NULL) {
				snprintf(p3, STRINGLEN, "seen-%s", this->name);
				if((this->seen = fopen(p3, "a")) == NULL) {
#ifdef DEBUG
					oer_debug(OER_DEBUG_INFO, "addcommand->failed to open %s: %s\n", p3, strerror(errno));
#endif
				} else {
					snprintf(stringbuffer, BIGSTRINGLEN, "%s has been opened for logging", p3);
					sendreply(to, tochan, 0, 0, stringbuffer);
				}
			}
		}
		return;
	}
	if(!strcasecmp(p1, "server")) {
		if(!isopa(userhost)) {
			return;
		}
		if(numofparams(commandline) != 3 && numofparams(commandline) !=6 && numofparams(commandline) != 7) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p3, STRINGLEN, 0)) < 0) {
			return;
		}
		if(!strlen(p3)) {
			return;
		}
		port = atoi(p3);
		if((ppos = parse(commandline, ppos, " ", p4, STRINGLEN, 0)) < 0) {
			return;
		}
		modes = (strlen(p4) > 0) ? atoi(p4) : 6;
		if((ppos = parse(commandline, ppos, " ", p5, STRINGLEN, 0)) < 0) {
			return;
		}
		ping = (strlen(p5) > 0) ? atoi(p5) : 90;
		if((ppos = parse(commandline, ppos, " ", p6, STRINGLEN, 0)) < 0) {
			return;
		}
		opers = (strlen(p6) > 0) ? atoi(p6) : 1;
		if((ppos = parse(commandline, ppos, " ", p7, STRINGLEN, 0)) < 0) {
			return;
		}
		noise = 0;
		if(strlen(p7)) {
			noise = atoi(p7);
			if(noise < OER_LINENOISE_INTERVAL_MIN || noise > ping) {
				snprintf(stringbuffer, BIGSTRINGLEN, "invalid linenoise value %d, disabled", noise);
				sendreply(to, tochan, 0, 0, stringbuffer);
				noise = 0;
			}
		}
		if((ppos = parse(commandline, ppos, " ", p8, STRINGLEN, 0)) < 0) {
			return;
		}
		pwptr = (strlen(p8)) ? p8 : NULL;
		if((server = addnewserver(p2, port, modes, ping, opers, noise, pwptr)) == NULL) {
			return;
		}
                snprintf(stringbuffer, BIGSTRINGLEN, "added new IRC server: %s %d %d %d %d %d %s", server->serverhost, server->serverport, server->servermodes, server->pingfrequency, server->protected_ircops, server->linenoise, (pwptr == NULL) ? "(null)" : pwptr);
		sendreply(to, tochan, 0, 0, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "admin")) {
		if(!isopa(userhost)) {
			return;
		}
		if(numofparams(commandline) != 2) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p3, STRINGLEN, 0)) < 0) {
			return;
		}
		/* only allow +n admins to add other +n admins */
		if((index(p3, (int)'n') != NULL) && !hasadminflags(userhost, "n")) {
			return;
		}
		if((user = addnewadmin(p2, p3)) == NULL) {
			return;
		}
		snprintf(stringbuffer, BIGSTRINGLEN, "added admin %s as %s", p2, p3);
		sendreply(to, tochan, 0, 0, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "user")) {
		if(this == NULL) {
			return;
		}
		if(!isadmin(this, userhost)) {
			return;
		}
		if(numofparams(commandline) != 2) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p3, STRINGLEN, 0)) < 0) {
			return;
		}
		if(index(p3, (int)'a') != NULL) {
			/* only admins can add +a users */
			if(!isopa(userhost)) {
				return;
			}
		}
		if((user = addnewuser(this, p2, p3)) == NULL) {
			return;
		}
		snprintf(stringbuffer, BIGSTRINGLEN, "added user %s to channel %s as %s", p2, this->name, p3);
		sendreply(to, tochan, 0, 0, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "mask")) {
		if(numofparams(commandline) < 3) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p3, STRINGLEN, 0)) < 0) {
			return;
		}
		if(!strcasecmp(p2, "admin")) {
			if(!isopa(userhost)) {
				return;
			}
			while(1) {
				nppos = parse(commandline, ppos, " ", p4, STRINGLEN, 0);
				if(nppos == ppos || nppos < 0) {
					break;
				}
				ppos = nppos;
				if((ms = addnewadminmask(p3, p4)) == NULL) {
					continue;
				}
				snprintf(stringbuffer, BIGSTRINGLEN, "added mask %s to admin %s", p4, p3);
				sendreply(to, tochan, 0, 0, stringbuffer);
			}
			return;
		}
		if(!strcasecmp(p2, "user")) {
			if(this == NULL) {
				return;
			}
			for(user = this->users; user != NULL; user = user->next) {
				if(!strcasecmp(user->handle, p3)) {
					break;
				}
			}
			if(user == NULL) {
				return;
			}
			proceed = (isopa(userhost)) ? 1 : 0;
			if(index(user->options, (int)'a') != NULL) {
				/* the user has +a flag */
				if(isadmin(this, userhost) && issameuser(this, p3, userhost)) {
					proceed = 1;
				}
			} else {
				/* a normal user */
				if(isadmin(this, userhost)) {
					proceed = 1;
				}
				if(isop(this, userhost) && issameuser(this, p3, userhost)) {
					proceed = 1;
				}
			}
			if(!proceed) {
				return;
			}
			while(1) {
				nppos = parse(commandline, ppos, " ", p4, STRINGLEN, 0);
				if(nppos == ppos || nppos < 0) {
					break;
				}
				ppos = nppos;
				if((ms = addnewusermask(this, p3, p4)) == NULL) {
					continue;
				}
				snprintf(stringbuffer, BIGSTRINGLEN, "added mask %s to %s user %s", p4, this->name, p3);
				sendreply(to, tochan, 0, 0, stringbuffer);
			}
			if(this->i_am_op) {
				syncvoices(this);
				syncops(this);
			}
			return;
		}
		return;
	}
	if(!strcasecmp(p1, "permban")) {
		if(this == NULL) {
			return;
		}
		if(numofparams(commandline) < 1) {
			return;
		}
		while(1) {
                        nppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0);
                        if(nppos == ppos || nppos < 0) {
                                break;
                        }
			ppos = nppos;
			amount = permbancount(this);
			if(amount >= OER_PERMBANS) {
				snprintf(stringbuffer, BIGSTRINGLEN, "I won't add permban %s to channel %s (max. %d)", p2, this->name, OER_PERMBANS);
				sendreply(to, tochan, 0, 0, stringbuffer);
				continue;
			}
			if((ms = addnewpermban(this, p2)) == NULL) {
				continue;
			}
			if(this->i_am_op) {
				mmode_new(this, mystate->now, "+b", ms->mask);
			}
			snprintf(stringbuffer, BIGSTRINGLEN, "added new permban %s to channel %s", ms->mask, this->name);
			sendreply(to, tochan, 0, 0, stringbuffer);
		}
		return;
	}
	if(!strcasecmp(p1, "nickbk")) {
		if(this == NULL) {
			return;
		}
		if(numofparams(commandline) < 1) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		if(strlen(commandline + ppos) > 0) {
			snprintf(stringbuffer, BIGSTRINGLEN, "%s (%s)", commandline + ppos, nick);
		} else {
			snprintf(stringbuffer, BIGSTRINGLEN, "unknown (%s)", nick);
		}
		if((ms = addnewnickbk(this, p2, stringbuffer)) == NULL) {
			return;
		}
		if(this->i_am_op) {
			syncnickbks(this);
		}
		snprintf(stringbuffer, BIGSTRINGLEN, "added nick %s as ban & kick for channel %s", ms->mask, this->name);
		sendreply(to, tochan, 0, 0, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "wordbk")) {
		if(this == NULL) {
			return;
		}
		if(numofparams(commandline) < 1) {
			return;
		}
		while(1) {
                        nppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0);
                        if(nppos == ppos || nppos < 0) {
                                break;
                        }
			ppos = nppos;
			if((ms = addnewwordbk(this, p2)) == NULL) {
				continue;
			}
			snprintf(stringbuffer, BIGSTRINGLEN, "added word %s as ban & kick for channel %s", ms->mask, this->name);
			sendreply(to, tochan, 0, 0, stringbuffer);
		}
		return;
	}
	if(!strcasecmp(p1, "kickreason")) {
		if(!isopa(userhost)) {
			return;
		}
		if(numofparams(commandline) < 1) {
			return;
		}
		if(strlen(commandline + ppos) > 0) {
			/* check that the kickreason doesn't exist already */
			for(ms = mystate->kickreasons; ms != NULL; ms = ms->next) {
				if(!strcasecmp(ms->mask, commandline + ppos)) {
					snprintf(stringbuffer, BIGSTRINGLEN, "kickreason %s already exists", commandline + ppos);
					sendreply(to, tochan, 0, 0, stringbuffer);
					return;
				}
			}
			if((ms = addnewkickreason(commandline + ppos)) == NULL) {
				return;
			}
			snprintf(stringbuffer, BIGSTRINGLEN, "added new kickreason: %s", ms->mask);
			sendreply(to, tochan, 0, 0, stringbuffer);
		}
		return;
	}
	if(!strcasecmp(p1, "advert")) {
		if(this == NULL) {
			return;
		}
		if(numofparams(commandline) < 2) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		if(!strcasecmp(p2, "admins") && !isadmin(this, userhost)) {
			return;
		}
		if((a = addnewadvert(this, p2, commandline + ppos)) == NULL) {
			return;
		}
		snprintf(stringbuffer, BIGSTRINGLEN, "added new advert \"%s\" (to: %s) to channel %s", a->message, a->to, this->name);
		sendreply(to, tochan, 0, 0, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "trusted")) {
		if(!isopa(userhost)) {
			return;
		}
		if(numofparams(commandline) < 1) {
			return;
		}
		while(1) {
                        nppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0);
                        if(nppos == ppos || nppos < 0) {
                                break;
                        }
			ppos = nppos;
			if((ms = addnewtrusted(p2)) == NULL) {
				continue;
			}
			snprintf(stringbuffer, BIGSTRINGLEN, "added new trusted host %s", ms->mask);
			sendreply(to, tochan, 0, 0, stringbuffer);
		}
		return;
	}
	if(!strcasecmp(p1, "service")) {
		if(!isopa(userhost)) {
			return;
		}
		if(numofparams(commandline) < 1) {
			return;
		}
		while(1) {
                        nppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0);
                        if(nppos == ppos || nppos < 0) {
                                break;
                        }
			ppos = nppos;
			if((ms = addnewservice(p2)) == NULL) {
				continue;
			}
			snprintf(stringbuffer, BIGSTRINGLEN, "added new service %s", ms->mask);
			sendreply(to, tochan, 0, 0, stringbuffer);
		}
		return;
	}
}

void listcommand(struct channel *this, char *to, int tochan, char *nick, char *userhost, char *commandline)
{
	int lines;
	int startpos;
	int curpos;
	int more;
	int amount;
	int ppos;
	int proceed;
        char p1[STRINGLEN + 1];
        char p2[STRINGLEN + 1];
        char p3[STRINGLEN + 1];
	char outstring[STRINGLEN + 1];
	char stringbuffer[HUGESTRINGLEN + 1];
	char stringbuffer2[HUGESTRINGLEN + 1];
	struct server *server;
	struct botuser *admin;
	struct botuser *user;
	struct maskstruct *ms;
	struct chanuser *cu;
	struct authed *a;
	struct advert *adv;
	struct channel *that;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "listcommand(\"%s\", \"%s\", %d, \"%s\", \"%s\", \"%s\")\n", (this == NULL) ? "(null)" : this->name, to, tochan, nick, userhost, commandline);
#endif
	ppos = 0;
        if((ppos = parse(commandline, ppos, " ", p1, STRINGLEN, 0)) < 0) {
                return;
        }
	if(!strcasecmp(p1, "nick")) {
		snprintf(stringbuffer, HUGESTRINGLEN, "my nick is %s", mystate->nick);
		sendreply(to, tochan, 0, 1, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "altnick")) {
		snprintf(stringbuffer, HUGESTRINGLEN, "my alternative nick is %s", mystate->altnick);
		sendreply(to, tochan, 0, 1, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "user")) {
		snprintf(stringbuffer, HUGESTRINGLEN, "my user ID (ident) is %s", mystate->user);
		sendreply(to, tochan, 0, 1, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "flags")) {
		snprintf(stringbuffer, HUGESTRINGLEN, "my flags are %s", mystate->flags);
		sendreply(to, tochan, 0, 1, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "prefix")) {
		snprintf(stringbuffer, HUGESTRINGLEN, "my command prefix is %s", mystate->prefix);
		sendreply(to, tochan, 0, 1, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "usermode")) {
		snprintf(stringbuffer, HUGESTRINGLEN, "my usermodes are %s", mystate->mode);
		sendreply(to, tochan, 0, 1, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "realname")) {
		snprintf(stringbuffer, HUGESTRINGLEN, "my IRC REALNAME is %s", mystate->realname);
		sendreply(to, tochan, 0, 1, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "vhost")) {
		if(mystate->vhost) {
			snprintf(stringbuffer, HUGESTRINGLEN, "my virtual host is %s", mystate->host);
			sendreply(to, tochan, 0, 1, stringbuffer);
		}
		return;
	}
	if(!strcasecmp(p1, "servers")) {
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		startpos =  (strlen(p2) > 0) ? atoi(p2) : 1;
		for(server = mystate->servers, lines = 0, curpos = 1, more = 0; server != NULL; server = server->next, curpos++) {
			if(lines < OER_MAX_LIST_LINES) {
				if(curpos >= startpos) {
					snprintf(stringbuffer, HUGESTRINGLEN, "%s %d %d %d %d %d %s%s", server->serverhost, server->serverport, server->servermodes, server->pingfrequency, server->protected_ircops, server->linenoise, (strlen(server->password) && isopa(userhost)) ? server->password : "(null)", (server == mystate->current_server) ? " (current)" : "");
					sendreply(to, tochan, 0, 0, stringbuffer);
					lines++;
				}
			} else {
				more = 1;
			}
		}
		if(more) {
			snprintf(stringbuffer, HUGESTRINGLEN, "%d servers, listing limited to %d lines", curpos - 1, OER_MAX_LIST_LINES);
			sendreply(to, tochan, 0, 0, stringbuffer);
		}
		return;
	}
	if(!strcasecmp(p1, "adverts")) {
		if(this == NULL) {
			return;
		}
		if(haschanflags(this, "U") && !isatleastop(this, nick, userhost)) {
			return;
		}
		if(!isatleastopnow(this, nick, userhost)) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		startpos =  (strlen(p2) > 0) ? atoi(p2) : 1;
		for(adv = this->adverts, lines = 0, curpos = 1, more = 0; adv != NULL; adv = adv->next, curpos++) {
			if(!strcmp(adv->to, "admins") && !isadmin(this, userhost)) {
				continue;
			}
			if(lines < OER_MAX_LIST_LINES) {
				if(curpos >= startpos) {
					snprintf(stringbuffer, HUGESTRINGLEN, "%s (to: %s)", adv->message, adv->to);
					sendreply(to, tochan, 0, 0, stringbuffer);
					lines++;
				}
			} else {
				more = 1;
			}
		}
		if(more) {
			snprintf(stringbuffer, HUGESTRINGLEN, "%d adverts, listing limited to %d lines", curpos - 1, OER_MAX_LIST_LINES);
			sendreply(to, tochan, 0, 0, stringbuffer);
		}
		return;
	}
	if(!strcasecmp(p1, "trusted")) {
		if(!isopa(userhost)) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		startpos =  (strlen(p2) > 0) ? atoi(p2) : 1;
		for(ms = mystate->trusted, lines = 0, curpos = 1, more = 0; ms != NULL; ms = ms->next, curpos++) {
			if(lines < OER_MAX_LIST_LINES) {
				if(curpos >= startpos) {
					snprintf(stringbuffer, HUGESTRINGLEN, "trusted: %s", ms->mask);
					sendreply(to, tochan, 0, 0, stringbuffer);
					lines++;
				}
			} else {
				more = 1;
			}
		}
		if(more) {
			snprintf(stringbuffer, HUGESTRINGLEN, "%d trusted hosts, listing limited to %d lines", curpos - 1, OER_MAX_LIST_LINES);
			sendreply(to, tochan, 0, 0, stringbuffer);
		}
		return;
	}
	if(!strcasecmp(p1, "services")) {
		if(!isopa(userhost)) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		startpos =  (strlen(p2) > 0) ? atoi(p2) : 1;
		for(ms = mystate->services, lines = 0, curpos = 1, more = 0; ms != NULL; ms = ms->next, curpos++) {
			if(lines < OER_MAX_LIST_LINES) {
				if(curpos >= startpos) {
					snprintf(stringbuffer, HUGESTRINGLEN, "service: %s", ms->mask);
					sendreply(to, tochan, 0, 0, stringbuffer);
					lines++;
				}
			} else {
				more = 1;
			}
		}
		if(more) {
			snprintf(stringbuffer, HUGESTRINGLEN, "%d services, listing limited to %d lines", curpos - 1, OER_MAX_LIST_LINES);
			sendreply(to, tochan, 0, 0, stringbuffer);
		}
		return;
	}
	if(!strcasecmp(p1, "admins")) {
		if(numofparams(commandline) >= 1) {
			if(!isopa(userhost)) {
				return;
			}
			if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
				return;
			}
			if((ppos = parse(commandline, ppos, " ", p3, STRINGLEN, 0)) < 0) {
				return;
			}
			for(admin = mystate->admins; admin != NULL; admin = admin->next) {
				if(!strcasecmp(admin->handle, p2)) {
					break;
				}
			}
			if(admin == NULL) {
				return;
			}
			if(strlen(p3) > 0) {
				startpos = atoi(p3);
			} else {
				startpos = 1;
				/* only send header info if no startpos */
				snprintf(stringbuffer, HUGESTRINGLEN, "options for admin %s: %s", admin->handle, admin->options);
				sendreply(to, tochan, 0, 0, stringbuffer);
			}
			for(ms = admin->firstmask, lines = 0, curpos = 1, more = 0; ms != NULL; ms = ms->next, curpos++) {
				if(lines < OER_MAX_LIST_LINES) {
					if(curpos >= startpos) {
						snprintf(stringbuffer, HUGESTRINGLEN, "hostmask for admin %s: %s", admin->handle, ms->mask);
						sendreply(to, tochan, 0, 0, stringbuffer);
						lines++;
					}
				} else {
					more = 1;
				}
			}
			if(more) {
				snprintf(stringbuffer, HUGESTRINGLEN, "%d hostmasks in total, listing limited to %d lines", curpos - 1, OER_MAX_LIST_LINES);
				sendreply(to, tochan, 0, 0, stringbuffer);
			}
			return;
		}
		/* no parameters, list all admins */
		memset(stringbuffer, 0, HUGESTRINGLEN + 1);
		strncpy(stringbuffer, "admins: ", HUGESTRINGLEN);
		admin = mystate->admins;
		while(admin != NULL) {
			if(strlen(stringbuffer) + strlen(admin->handle) > HUGESTRINGLEN) {
				sendreply(to, tochan, 0, 0, stringbuffer);
				memset(stringbuffer, 0, HUGESTRINGLEN + 1);
				strncpy(stringbuffer, "admins: ", HUGESTRINGLEN);
			}
			strncat(stringbuffer, admin->handle, HUGESTRINGLEN - strlen(stringbuffer));
			strncat(stringbuffer, " ", HUGESTRINGLEN - strlen(stringbuffer));
			admin = admin->next;
		}
		if(strlen(stringbuffer) > 0) {
			sendreply(to, tochan, 0, 0, stringbuffer);
		}
		return;
	}
	if(!strcasecmp(p1, "kickreasons")) {
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		startpos =  (strlen(p2) > 0) ? atoi(p2) : 1;
		for(ms = mystate->kickreasons, lines = 0, curpos = 1, more = 0; ms != NULL; ms = ms->next, curpos++) {
			if(lines < OER_MAX_LIST_LINES) {
				if(curpos >= startpos) {
					snprintf(stringbuffer, HUGESTRINGLEN, "kickreason: %s", ms->mask);
					sendreply(to, tochan, 0, 0, stringbuffer);
					lines++;
				}
			} else {
				more = 1;
			}
		}
		if(more) {
			snprintf(stringbuffer, HUGESTRINGLEN, "%d kickreasons, listing limited to %d lines", curpos - 1, OER_MAX_LIST_LINES);
			sendreply(to, tochan, 0, 0, stringbuffer);
		}
		return;
	}
	if(!strcasecmp(p1, "channels")) {
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		startpos =  (strlen(p2) > 0) ? atoi(p2) : 1;
		for(that = mystate->channels, lines = 0, curpos = 1, more = 0; that != NULL; that = that->next, curpos++) {
			/* check if channel is +s, if so only display if user on channel */
			if(index(that->mode, (int)'s') != NULL || index(that->mode, (int)'s') != NULL) {
				if(haschanflags(that, "U") && !isatleastop(that, nick, userhost)) {
					continue;
				}
				if(!isatleastopnow(that, nick, userhost)) {
					continue;
				}
			}
			if(lines < OER_MAX_LIST_LINES) {
				if(curpos >= startpos) {
					if(that->haskey) {
						snprintf(stringbuffer, HUGESTRINGLEN, "%s (%d %s) [key: %s]", that->name, that->nickcount, (that->nickcount == 1) ? "user" : "users", that->key);
					} else {
						snprintf(stringbuffer, HUGESTRINGLEN, "%s (%d %s)", that->name, that->nickcount, (that->nickcount == 1) ? "user" : "users");
					}
					sendreply(to, tochan, 0, 0, stringbuffer);
					lines++;
				}
			} else {
				more = 1;
			}
		}
		if(more) {
			snprintf(stringbuffer, HUGESTRINGLEN, "%d channels, listing limited to %d lines", curpos - 1, OER_MAX_LIST_LINES);
			sendreply(to, tochan, 0, 0, stringbuffer);
		}
		return;
	}
	if(!strcasecmp(p1, "chankey")) { 
		if(this == NULL) {
			return;
		}
                if(haschanflags(this, "U") && !isatleastop(this, nick, userhost)) {
                        return;
                }
		if(!isatleastopnow(this, nick, userhost)) {
			return;
		}
		snprintf(stringbuffer, HUGESTRINGLEN, "channel key is %s", this->key);
		sendreply(to, tochan, 0, 1, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "chanmode")) {
		if(this == NULL) {
			return;
		}
                if(haschanflags(this, "U") && !isatleastop(this, nick, userhost)) {
                        return;
                }
		if(!isatleastopnow(this, nick, userhost)) {
			return;
		}
		snprintf(stringbuffer, HUGESTRINGLEN, "channel modes are %s", this->mode);
		sendreply(to, tochan, 0, 1, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "chanflags")) {
		if(this == NULL) {
			return;
		}
                if(haschanflags(this, "U") && !isatleastop(this, nick, userhost)) {
                        return;
                }
		if(!isatleastopnow(this, nick, userhost)) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
                        return;
                }
		sortstring(this->chanflags);
                if(!strlen(p2)) {
                        /* non verbose */
                        snprintf(outstring, STRINGLEN, "channel flags for %s are %s", this->name, this->chanflags);
                        sendreply(to, tochan, 0, 1, outstring);
                        return;
                }
                if(strcasecmp(p2, "verbose")) {
                        return;
                }
		memset(outstring, 0, STRINGLEN + 1);
		memset(stringbuffer, 0, HUGESTRINGLEN + 1);
		strncat(outstring, (haschanflags(this, "!")) ? "!" : "", STRINGLEN - strlen(outstring));
		strncat(stringbuffer, (haschanflags(this, "!")) ? "!/inactive=1 " : "!/inactive=0 ", HUGESTRINGLEN - strlen(stringbuffer));
		strncat(outstring, (haschanflags(this, "a")) ? "a" : "", STRINGLEN - strlen(outstring));
		strncat(stringbuffer, (haschanflags(this, "a")) ? "a/ansi_prot=1 " : "a/ansi_prot=0 ", HUGESTRINGLEN - strlen(stringbuffer));
		strncat(outstring, (haschanflags(this, "A")) ? "A" : "", STRINGLEN - strlen(outstring));
		strncat(stringbuffer, (haschanflags(this, "A")) ? "A/adverts=1 " : "A/adverts=0 ", HUGESTRINGLEN - strlen(stringbuffer));
		strncat(outstring, (haschanflags(this, "b")) ? "b" : "", STRINGLEN - strlen(outstring));
		strncat(stringbuffer, (haschanflags(this, "b")) ? "b/ban_prot=1 " : "b/ban_prot=0 ", HUGESTRINGLEN - strlen(stringbuffer));
		strncat(outstring, (haschanflags(this, "D")) ? "D" : "", STRINGLEN - strlen(outstring));
		strncat(stringbuffer, (haschanflags(this, "D")) ? "D/de-op_protect=1 " : "D/de-op_protect=0 ", HUGESTRINGLEN - strlen(stringbuffer));
		strncat(outstring, (haschanflags(this, "e")) ? "e" : "", STRINGLEN - strlen(outstring));
		strncat(stringbuffer, (haschanflags(this, "e")) ? "e/perm_bans=1 " : "e/perm_bans=0 ", HUGESTRINGLEN - strlen(stringbuffer));
		strncat(outstring, (haschanflags(this, "f")) ? "f" : "", STRINGLEN - strlen(outstring));
		strncat(stringbuffer, (haschanflags(this, "f")) ? "f/pubmsg_floodp=1 " : "f/pubmsg_floodp=0 ", HUGESTRINGLEN - strlen(stringbuffer));
		strncat(outstring, (haschanflags(this, "F")) ? "F" : "", STRINGLEN - strlen(outstring));
		strncat(stringbuffer, (haschanflags(this, "F")) ? "F/friends=1 " : "F/friends=0 ", HUGESTRINGLEN - strlen(stringbuffer));
		strncat(outstring, (haschanflags(this, "G")) ? "G" : "", STRINGLEN - strlen(outstring));
		strncat(stringbuffer, (haschanflags(this, "G")) ? "G/allflood=1 " : "G/allflood=0 ", HUGESTRINGLEN - strlen(stringbuffer));
		strncat(outstring, (haschanflags(this, "l")) ? "l" : "", STRINGLEN - strlen(outstring));
		strncat(stringbuffer, (haschanflags(this, "l")) ? "l/lock_chan=1 " : "l/lock_chan=0 ", HUGESTRINGLEN - strlen(stringbuffer));
		strncat(outstring, (haschanflags(this, "L")) ? "L" : "", STRINGLEN - strlen(outstring));
		strncat(stringbuffer, (haschanflags(this, "L")) ? "L/last=1 " : "L/last=0 ", HUGESTRINGLEN - strlen(stringbuffer));
		strncat(outstring, (haschanflags(this, "m")) ? "m" : "", STRINGLEN - strlen(outstring));
		strncat(stringbuffer, (haschanflags(this, "m")) ? "m/mass_prot=1 " : "m/mass_prot=0 ", HUGESTRINGLEN - strlen(stringbuffer));
		strncat(outstring, (haschanflags(this, "n")) ? "n" : "", STRINGLEN - strlen(outstring));
		strncat(stringbuffer, (haschanflags(this, "n")) ? "n/nick_bk=1 " : "n/nick_bk=0 ", HUGESTRINGLEN - strlen(stringbuffer));
		strncat(outstring, (haschanflags(this, "o")) ? "o" : "", STRINGLEN - strlen(outstring));
		strncat(stringbuffer, (haschanflags(this, "o")) ? "o/auto_op=1 " : "o/auto_op=0 ", HUGESTRINGLEN - strlen(stringbuffer));
		strncat(outstring, (haschanflags(this, "O")) ? "O" : "", STRINGLEN - strlen(outstring));
		strncat(stringbuffer, (haschanflags(this, "O")) ? "O/auto_op_admins=1 " : "O/auto_op_admins=0 ", HUGESTRINGLEN - strlen(stringbuffer));
		strncat(outstring, (haschanflags(this, "p")) ? "p" : "", STRINGLEN - strlen(outstring));
		strncat(stringbuffer, (haschanflags(this, "p")) ? "p/postnj_check=1 " : "p/postnj_check=0 ", HUGESTRINGLEN - strlen(stringbuffer));
		strncat(outstring, (haschanflags(this, "P")) ? "P" : "", STRINGLEN - strlen(outstring));
		strncat(stringbuffer, (haschanflags(this, "P")) ? "P/paranoid=1 " : "P/paranoid=0 ", HUGESTRINGLEN - strlen(stringbuffer));
		strncat(outstring, (haschanflags(this, "q")) ? "q" : "", STRINGLEN - strlen(outstring));
		strncat(stringbuffer, (haschanflags(this, "q")) ? "q/quote=1 " : "q/quote=0 ", HUGESTRINGLEN - strlen(stringbuffer));
		strncat(outstring, (haschanflags(this, "r")) ? "r" : "", STRINGLEN - strlen(outstring));
		strncat(stringbuffer, (haschanflags(this, "r")) ? "r/autorejoin_kb=1 " : "r/autorejoin_kb=0 ", HUGESTRINGLEN - strlen(stringbuffer));
		strncat(outstring, (haschanflags(this, "S")) ? "S" : "", STRINGLEN - strlen(outstring));
		strncat(stringbuffer, (haschanflags(this, "S")) ? "S/seen=1 " : "S/seen=0 ", HUGESTRINGLEN - strlen(stringbuffer));
		strncat(outstring, (haschanflags(this, "u")) ? "u" : "", STRINGLEN - strlen(outstring));
		strncat(stringbuffer, (haschanflags(this, "u")) ? "u/users_op=1 " : "u/users_op=0 ", HUGESTRINGLEN - strlen(stringbuffer));
		strncat(outstring, (haschanflags(this, "U")) ? "U" : "", STRINGLEN - strlen(outstring));
		strncat(stringbuffer, (haschanflags(this, "U")) ? "U/botusersonly=1 " : "U/botusersonly=0 ", HUGESTRINGLEN - strlen(stringbuffer));
		strncat(outstring, (haschanflags(this, "T")) ? "T" : "", STRINGLEN - strlen(outstring));
		strncat(stringbuffer, (haschanflags(this, "T")) ? "T/notopics=1 " : "T/notopics=0 ", HUGESTRINGLEN - strlen(stringbuffer));
		strncat(outstring, (haschanflags(this, "v")) ? "v" : "", STRINGLEN - strlen(outstring));
		strncat(stringbuffer, (haschanflags(this, "v")) ? "v/auto_voice=1 " : "v/auto_voice=0 ", HUGESTRINGLEN - strlen(stringbuffer));
		strncat(outstring, (haschanflags(this, "w")) ? "w" : "", STRINGLEN - strlen(outstring));
		strncat(stringbuffer, (haschanflags(this, "w")) ? "w/word_bk=1 " : "w/word_bk=0 ", HUGESTRINGLEN - strlen(stringbuffer));
		/* strip leading space if any */
		if(strlen(stringbuffer)) {
			stringbuffer[strlen(stringbuffer) - 1] = '\0';
		}
		snprintf(stringbuffer2, HUGESTRINGLEN, "channel flags for %s are [%s] %s", this->name, outstring, stringbuffer);
		sendreply(to, tochan, 0, 1, stringbuffer2);
		return;
	}
	if(!strcasecmp(p1, "users")) {
		if(this == NULL) {
			return;
		}
                if(haschanflags(this, "U") && !isatleastop(this, nick, userhost)) {
                        return;
                }
		if(!isatleastopnow(this, nick, userhost)) {
			return;
		}
		if(numofparams(commandline) >= 1) {
			if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
				return;
			}
			if((ppos = parse(commandline, ppos, " ", p3, STRINGLEN, 0)) < 0) {
				return;
			}
			for(user = this->users; user != NULL; user = user->next) {
				if(!strcasecmp(user->handle, p2)) {
					break;
				}
			}
			if(user == NULL) {
				return;
			}
			proceed = (isopa(userhost)) ? 1 : 0;
			if(index(user->options, (int)'a') != NULL) {
				if(isadmin(this, userhost)) {
					proceed = 1;
				}
			} else {
				proceed = 1;
			}
			if(!proceed) {
				return;
			}
			if(strlen(p3) > 0) {
				startpos = atoi(p3);
			} else {
				startpos = 1;
				/* only send header info if no startpos */
				snprintf(stringbuffer, HUGESTRINGLEN, "options for user %s: %s", user->handle, user->options);
				sendreply(to, tochan, 0, 0, stringbuffer);
			}
			for(ms = user->firstmask, lines = 0, curpos = 1, more = 0; ms != NULL; ms = ms->next, curpos++) {
				if(lines < OER_MAX_LIST_LINES) {
					if(curpos >= startpos) {
						snprintf(stringbuffer, HUGESTRINGLEN, "hostmask for user %s: %s", user->handle, ms->mask);
						sendreply(to, tochan, 0, 0, stringbuffer);
						lines++;
					}
				} else {
					more = 1;
				}
			}
			if(more) {
				snprintf(stringbuffer, HUGESTRINGLEN, "%d hostmasks in total, listing limited to %d lines", curpos - 1, OER_MAX_LIST_LINES);
				sendreply(to, tochan, 0, 0, stringbuffer);
			}
			return;
		}
                /* no parameters, list all channel operators */
		memset(stringbuffer, 0, HUGESTRINGLEN + 1);
		snprintf(stringbuffer, HUGESTRINGLEN, "%s users: ", this->name);
		for(user = this->users; user != NULL; user = user->next) {
			if(strlen(stringbuffer) + strlen(user->handle) > HUGESTRINGLEN) {
				sendreply(to, tochan, 0, 0, stringbuffer);
				memset(stringbuffer, 0, HUGESTRINGLEN + 1);
				snprintf(stringbuffer, HUGESTRINGLEN, "%s users: ", this->name);
			}
			if(index(user->options, (int)'!') != NULL) {
				strncat(stringbuffer, "!", HUGESTRINGLEN - strlen(stringbuffer));
			}
			if(index(user->options, (int)'a') != NULL) {
				strncat(stringbuffer, "*", HUGESTRINGLEN - strlen(stringbuffer));
			}
			strncat(stringbuffer, user->handle, HUGESTRINGLEN - strlen(stringbuffer));
			strncat(stringbuffer, " ", HUGESTRINGLEN - strlen(stringbuffer));
		}
		if(strlen(stringbuffer) > 0) {
			sendreply(to, tochan, 0, 0, stringbuffer);
		}
		return;
	}
	if(!strcasecmp(p1, "nicks")) {
		if(this == NULL) {
			return;
		}
                if(haschanflags(this, "U") && !isatleastop(this, nick, userhost)) {
                        return;
                }
		if(!isatleastopnow(this, nick, userhost)) {
			return;
		}
		if(numofparams(commandline) >= 1) {
			if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
				return;
			}
			for(cu = this->nicks; cu != NULL; cu = cu->next) {
				if(!strcasecmp(cu->nick, p2)) {
					break;
				}
			}
			if(cu == NULL) {
				return;
			}
			snprintf(stringbuffer, HUGESTRINGLEN, "%s!%s (ircop=%d chanop=%d voice=%d friend=%d)", cu->nick, (cu->userhost == NULL) ? "(null)" : cu->userhost, cu->ircop, cu->chanop, cu->voice, cu->friend);
			sendreply(to, tochan, 0, 0, stringbuffer);
			return;
		}
                /* no parameters, list all channel nicks */
		amount = 0;
		memset(stringbuffer, 0, HUGESTRINGLEN + 1);
		cu = this->nicks;
		while(cu != NULL) {
			if((strlen(stringbuffer) + CHANLEN + NICKLEN + 16) > HUGESTRINGLEN) {
				/* send the thing, empty string */
				snprintf(stringbuffer2, HUGESTRINGLEN, "nicks on %s [%d]:", this->name, amount);
				strncat(stringbuffer2, stringbuffer, HUGESTRINGLEN - strlen(stringbuffer2));
				sendreply(to, tochan, 0, 0, stringbuffer2);
				amount = 0;
				memset(stringbuffer, 0, HUGESTRINGLEN + 1);
			}
			strncat(stringbuffer, " ", HUGESTRINGLEN - strlen(stringbuffer));
			if(cu->ircop) {
				strncat(stringbuffer, "*", HUGESTRINGLEN - strlen(stringbuffer));
			}
			if(cu->voice) {
				strncat(stringbuffer, "+", HUGESTRINGLEN - strlen(stringbuffer));
			}
			if(cu->chanop) {
				strncat(stringbuffer, "@", HUGESTRINGLEN - strlen(stringbuffer));
			}
			strncat(stringbuffer, cu->nick, HUGESTRINGLEN - strlen(stringbuffer));
			amount++;
			cu = cu->next;
		}
		if(amount > 0) {
			snprintf(stringbuffer2, HUGESTRINGLEN, "nicks on %s [%d]:", this->name, amount);
			strncat(stringbuffer2, stringbuffer, HUGESTRINGLEN - strlen(stringbuffer2));
			sendreply(to, tochan, 0, 0, stringbuffer2);
		}
		return;
	}
	if(!strcasecmp(p1, "wordbks")) {
		if(this == NULL) {
			return;
		}
                if(haschanflags(this, "U") && !isatleastop(this, nick, userhost)) {
                        return;
                }
		if(!isatleastopnow(this, nick, userhost)) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		startpos =  (strlen(p2) > 0) ? atoi(p2) : 1;
		for(ms = this->wordbks, lines = 0, curpos = 1, more = 0; ms != NULL; ms = ms->next, curpos++) {
			if(lines < OER_MAX_LIST_LINES) {
				if(curpos >= startpos) {
					snprintf(stringbuffer, HUGESTRINGLEN, "%s wordbk: %s", this->name, ms->mask);
					sendreply(to, tochan, 0, 0, stringbuffer);
					lines++;
				}
			} else {
				more = 1;
			}
		}
		if(more) {
			snprintf(stringbuffer, HUGESTRINGLEN, "%d %s wordbks, listing limited to %d lines", curpos - 1, this->name, OER_MAX_LIST_LINES);
			sendreply(to, tochan, 0, 0, stringbuffer);
		}
		return;
	}
	if(!strcasecmp(p1, "nickbks")) {
		if(this == NULL) {
			return;
		}
                if(haschanflags(this, "U") && !isatleastop(this, nick, userhost)) {
                        return;
                }
		if(!isatleastopnow(this, nick, userhost)) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		startpos =  (strlen(p2) > 0) ? atoi(p2) : 1;
		for(ms = this->nickbks, lines = 0, curpos = 1, more = 0; ms != NULL; ms = ms->next, curpos++) {
			if(lines < OER_MAX_LIST_LINES) {
				if(curpos >= startpos) {
					snprintf(stringbuffer, HUGESTRINGLEN, "%s nickbk: %s (%s)", this->name, ms->mask, ms->optstring);
					sendreply(to, tochan, 0, 0, stringbuffer);
					lines++;
				}
			} else {
				more = 1;
			}
		}
		if(more) {
			snprintf(stringbuffer, HUGESTRINGLEN, "%d %s nickbks, listing limited to %d lines", curpos - 1, this->name, OER_MAX_LIST_LINES);
			sendreply(to, tochan, 0, 0, stringbuffer);
		}
		return;
	}
	if(!strcasecmp(p1, "permbans")) {
		if(this == NULL) {
			return;
		}
                if(haschanflags(this, "U") && !isatleastop(this, nick, userhost)) {
                        return;
                }
		if(!isatleastopnow(this, nick, userhost)) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		startpos =  (strlen(p2) > 0) ? atoi(p2) : 1;
		for(ms = this->permbans, lines = 0, curpos = 1, more = 0; ms != NULL; ms = ms->next, curpos++) {
			if(lines < OER_MAX_LIST_LINES) {
				if(curpos >= startpos) {
					snprintf(stringbuffer, HUGESTRINGLEN, "%s permban: %s", this->name, ms->mask);
					sendreply(to, tochan, 0, 0, stringbuffer);
					lines++;
				}
			} else {
				more = 1;
			}
		}
		if(more) {
			snprintf(stringbuffer, HUGESTRINGLEN, "%d %s permbans, listing limited to %d lines", curpos - 1, this->name, OER_MAX_LIST_LINES);
			sendreply(to, tochan, 0, 0, stringbuffer);
		}
		return;
	}
	if(!strcasecmp(p1, "floodvars")) {
		if(this == NULL) {
			return;
		}
                if(haschanflags(this, "U") && !isatleastop(this, nick, userhost)) {
                        return;
                }
		if(!isatleastopnow(this, nick, userhost)) {
			return;
		}
		snprintf(stringbuffer, HUGESTRINGLEN, "%s floodvars are %d %d %d %d %d", this->name, this->floodvars.repeat_expire, this->floodvars.repeat_limit, this->floodvars.interval, this->floodvars.lines, this->floodvars.chars);
		sendreply(to, tochan, 0, 0, stringbuffer);
	}
	if(!strcasecmp(p1, "banvars")) {
		if(this == NULL) {
			return;
		}
                if(haschanflags(this, "U") && !isatleastop(this, nick, userhost)) {
                        return;
                }
		if(!isatleastopnow(this, nick, userhost)) {
			return;
		}
		snprintf(stringbuffer, HUGESTRINGLEN, "%s banvars are %d %d %d %d %d %d %d", this->name, this->banvars.auto_rejoin, this->banvars.part_rejoin, this->banvars.public_flood, this->banvars.public_flood_repeat, this->banvars.bad_word, this->banvars.bad_nick, this->banvars.normal_ban);
		sendreply(to, tochan, 0, 0, stringbuffer);
	}
	if(!strcasecmp(p1, "bantype")) {
		if(this == NULL) {
			return;
		}
                if(haschanflags(this, "U") && !isatleastop(this, nick, userhost)) {
                        return;
                }
		if(!isatleastopnow(this, nick, userhost)) {
			return;
		}
		snprintf(stringbuffer, HUGESTRINGLEN, "%s ban type is %d", this->name, this->bantype);
		sendreply(to, tochan, 0, 0, stringbuffer);
	}
	if(!strcasecmp(p1, "autheds")) {
		if(this == NULL) {
			return;
		}
                if(haschanflags(this, "U") && !isatleastop(this, nick, userhost)) {
                        return;
                }
		if(!isatleastopnow(this, nick, userhost)) {
			return;
		}
		if(numofparams(commandline) >= 1) {
			if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
				return;
			}
			if((ppos = parse(commandline, ppos, " ", p3, STRINGLEN, 0)) < 0) {
				return;
			}
			startpos =  (strlen(p2) > 0) ? atoi(p2) : 1;
			proceed = (isadmin(this, userhost)) ? 1 : 0;
			if(issameuser(this, p2, userhost)) {
				proceed = 1;
			}
			if(!proceed) {
				return;
			}
			for(a = this->autheds, lines = 0, curpos = 1, more = 0; a != NULL; a = a->next, curpos++) {
				if(mystate->now > (a->at + OER_LOGON_TIMEOUT)) {
					continue;
				}
				if(strcasecmp(a->user->handle, p2)) {
					continue;
				}
				if(lines < OER_MAX_LIST_LINES) {
					if(curpos >= startpos) {
						snprintf(stringbuffer, HUGESTRINGLEN, "%s is authed from %s", a->user->handle, a->userhost);
						sendreply(to, tochan, 0, 0, stringbuffer);
						lines++;
					}
				} else {
					more = 1;
				}
			}
			if(more) {
				snprintf(stringbuffer, HUGESTRINGLEN, "%d hostmasks, listing limited to %d lines", curpos - 1, OER_MAX_LIST_LINES);
				sendreply(to, tochan, 0, 0, stringbuffer);
			}
			return;
		}
                /* no parameters, list all autheds for channel */
		amount = 0;
		memset(stringbuffer, 0, HUGESTRINGLEN + 1);
		a = this->autheds;
		while(a != NULL) {
			if((strlen(stringbuffer) + CHANLEN + NICKLEN + 16) > HUGESTRINGLEN) {
				/* send the thing, empty string */
				snprintf(stringbuffer2, HUGESTRINGLEN, "autheds on %s [%d]:", this->name, amount);
				strncat(stringbuffer2, stringbuffer, HUGESTRINGLEN - strlen(stringbuffer2));
				sendreply(to, tochan, 0, 0, stringbuffer2);
				amount = 0;
				memset(stringbuffer, 0, HUGESTRINGLEN + 1);
			}
			strncat(stringbuffer, " ", HUGESTRINGLEN - strlen(stringbuffer));
			if(mystate->now > (a->at + OER_LOGON_TIMEOUT)) {
				strncat(stringbuffer, "!", HUGESTRINGLEN - strlen(stringbuffer));
			}
			strncat(stringbuffer, a->user->handle, HUGESTRINGLEN - strlen(stringbuffer));
			amount++;
			a = a->next;
		}
		if(amount > 0) {
			snprintf(stringbuffer2, HUGESTRINGLEN, "autheds on %s [%d]:", this->name, amount);
			strncat(stringbuffer2, stringbuffer, HUGESTRINGLEN - strlen(stringbuffer2));
			sendreply(to, tochan, 0, 0, stringbuffer2);
		}
		return;
	}
	if(!strcasecmp(p1, "handles")) {
		if(this == NULL) {
			return;
		}
                if(haschanflags(this, "U") && !isatleastop(this, nick, userhost)) {
                        return;
                }
		if(!isatleastopnow(this, nick, userhost)) {
			return;
		}
		if(numofparams(commandline) != 1) {
			return;
		}
		if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
			return;
		}
		for(cu = this->nicks; cu != NULL; cu = cu->next) {
			if(!strcasecmp(cu->nick, p2)) {
				break;
			}
		}
		if(cu == NULL || cu->userhost == NULL) {
			return;
		}
		amount = 0;
		memset(stringbuffer, 0, HUGESTRINGLEN + 1);
		snprintf(stringbuffer, HUGESTRINGLEN, "%s handles matching %s: ", this->name, cu->nick);
		for(user = this->users; user != NULL; user = user->next) {
			for(ms = user->firstmask; ms != NULL; ms = ms->next) {
				if(wild_match(ms->mask, cu->userhost)) {
					/* this one matches, continue with next user */
					if(strlen(stringbuffer) + strlen(user->handle) > HUGESTRINGLEN) {
						sendreply(to, tochan, 0, 0, stringbuffer);
						memset(stringbuffer, 0, HUGESTRINGLEN + 1);
						snprintf(stringbuffer, HUGESTRINGLEN, "%s handles matching %s: ", this->name, cu->nick);
					}
					strncat(stringbuffer, user->handle, HUGESTRINGLEN - strlen(stringbuffer));
					strncat(stringbuffer, " ", HUGESTRINGLEN - strlen(stringbuffer));
					amount++;
					break;
				}
			}
		}
		if((amount > 0) && (strlen(stringbuffer) > 0)) {
			sendreply(to, tochan, 0, 0, stringbuffer);
		}
		return;
	}
}

void logoffcommand(struct channel *this, char *to, int tochan, char *nick, char *userhost, char *commandline)
{
	int ppos;
	int nppos;
	int proceed;
        char p1[STRINGLEN + 1];
	char stringbuffer[HUGESTRINGLEN + 1];
        struct botuser *user;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "logoffcommand(\"%s\", \"%s\", %d, \"%s\", \"%s\", \"%s\")\n", this->name, to, tochan, nick, userhost, commandline);
#endif
	ppos = 0;
	while(1) {
		nppos = parse(commandline, ppos, " ", p1, STRINGLEN, 0);
		if(nppos == ppos || nppos < 0) {
			break;
		}
		ppos = nppos;
		proceed = 0;
		/* check if handle has "d" flag */
                for(user = this->users; user != NULL; user = user->next) {
                        if(!strcasecmp(user->handle, p1)) {
                                if(index(user->options, (int)'d') != NULL) {
                                        proceed = 1;
                                }
                                break;
                        }
                }
		if(!proceed) {
#ifdef DEBUG
                        oer_debug(OER_DEBUG_INFO, "logoffcommand->handle %s has no \"d\" flag\n", p1);
#endif
                        continue;
                }
		if(isopa(userhost)) {
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "logoffcommand->admin logoff for handle %s\n", p1);
#endif
			if(!logoff(this, p1)) {
#ifdef DEBUG
				oer_debug(OER_DEBUG_INFO, "logoffcommand->admin logoff failed for handle %s\n", p1);
#endif
				return;
			}
		} else {
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "logoffcommand->logoff for handle %s\n", p1);
#endif
			if(!issameuser(this, p1, userhost)) {
#ifdef DEBUG
				oer_debug(OER_DEBUG_INFO, "logoffcommand->invalid logoff attempt for handle %s\n", p1);
#endif
				return;
			}
			if(!logoff(this, p1)) {
#ifdef DEBUG
				oer_debug(OER_DEBUG_INFO, "logoffcommand->logoff failed for handle %s\n", p1);
#endif
				return;
			}
		}
		/* logoff shouldn't cause a de-op */
		snprintf(stringbuffer, HUGESTRINGLEN, "%s user %s successfully logged off", this->name, p1);
		sendreply(to, tochan, 0, 1, stringbuffer);
		/* check if user has "f" flag (if not on channel, nothing happens) */
		if(haschanflags(this, "F") && hasuserflags(this, userhost, "f") != NULL) {
			changeuser(this, nick, -1, -1, -1, 0);
		}
	}
}

void logoncommand(struct channel *this, char *to, int tochan, char *nick, char *userhost, char *commandline)
{
	int admin;
	int ppos;
        char p1[STRINGLEN + 1];
        char p2[STRINGLEN + 1];
	char stringbuffer[HUGESTRINGLEN + 1];
        struct botuser *user;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "logoncommand(\"%s\", \"%s\", %d, \"%s\", \"%s\", \"%s\")\n", this->name, to, tochan, nick, userhost, commandline);
#endif
	/* command line parsing begins here, first parameter = the command */
	ppos = 0;
        if((ppos = parse(commandline, ppos, " ", p1, STRINGLEN, 0)) < 0) {
                return;
        }
        if((ppos = parse(commandline, ppos, " ", p2, STRINGLEN, 0)) < 0) {
                return;
        }
	/* check if handle has "d" flag */
        for(user = this->users; user != NULL; user = user->next) {
                if(!strcasecmp(user->handle, p1)) {
                        if(index(user->options, (int)'d') == NULL) {
#ifdef DEBUG
                                oer_debug(OER_DEBUG_INFO, "logoncommand->handle %s has no \"d\" flag\n", p1);
#endif
                                return;
                        }
                        break;
                }
        }
	admin = (isopa(userhost)) ? 1 : 0;
	/* admins can logon anyone without password, they need
	 to specify the hostmask however */
	if(admin) {
		if(!strlen(p2)) {
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "logoncommand->hostmask missing for admin logon\n");
#endif
			return;
		}
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "logoncommand->admin logon for handle %s\n", p1);
#endif
		if(!issameuser(this, p1, p2)) {
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "logoncommand->hostmask %s is invalid for handle %s\n", p2, p1);
#endif
			return;
		}
		if(!logon(this, p1, p2, p1, 1)) {
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "logoncommand->admin logon failed for handle %s\n", p1);
#endif
			return;
		}
	} else {
		/* we require password for normal logon */
		if(!strlen(p2)) {
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "logoncommand->password missing for logon\n");
#endif
			return;
		}
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "logoncommand->logon for handle %s\n", p1);
#endif
		if(!issameuser(this, p1, userhost)) {
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "logoncommand->hostmask %s is invalid for handle %s\n", userhost, p1);
#endif
			return;
		}
		if(!logon(this, p1, userhost, p2, 0)) {
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "logoncommand->logon failed for handle %s password %s\n", p1, p2);
#endif
			return;
		}
	}
	snprintf(stringbuffer, HUGESTRINGLEN, "%s user %s successfully logged on", this->name, p1);
	sendreply(to, tochan, 0, 1, stringbuffer);
	if(admin) {
		/* we don't know who is affected by the handle */
		syncvoices(this);
		syncops(this);
	} else {
		if(this->i_am_op && isonchan(this, nick) && hasuserflags(this, userhost, "do") != NULL) {
			mmode_new(this, mystate->now, "+o", nick);
		}
	}
	/* check if user has "f" flag, set friend (if not on channel, nothing happens) */
	if(haschanflags(this, "F") && hasuserflags(this, userhost, "f") != NULL) {
		changeuser(this, nick, -1, -1, -1, 1);
	}
}

int logoff(struct channel *this, char *handle)
{
	struct authed *a;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "logoff(\"%s\", \"%s\")\n", this->name, handle);
#endif
	for(a = this->autheds; a != NULL; a = a->next) {
		if(strcasecmp(a->user->handle, handle)) {
			continue;
		}
		a->at = 0;
	}
	cleanautheds(this);
	return 1;
}

int logon(struct channel *this, char *handle, char *userhost, char *password, int admin)
{
	struct botuser *user;
	char cryptsalt[3];
	char *crypted;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "logoff(\"%s\", \"%s\", \"%s\", \"%s\", %d)\n", this->name, handle, userhost, password, admin);
#endif
	for(user = this->users; user != NULL; user = user->next) {
		if(!strcasecmp(user->handle, handle)) {
			break;
		}
	}
	if(user == NULL) {
		return 0;
	}
	if(!admin) {
		if(user->password == NULL) {
			/* the user has no password */
			return 0;
		}
		cryptsalt[0] = user->password[0];
		cryptsalt[1] = user->password[1];
		cryptsalt[2] = '\0';
		crypted = crypt(password, cryptsalt);
		if(strcasecmp(user->password, crypted)) {
			return 0;
		}
	}
	if(addnewauthed(this, mystate->now, user, userhost) == NULL) {
		return 0;
	}
	cleanautheds(this);
	return 1;
}

int setpassword(struct channel *this, char *handle, char *password)
{
	struct botuser *user;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "setpassword(\"%s\", \"%s\", \"%s\")\n", this->name, handle, password);
#endif
	for(user = this->users; user != NULL; user = user->next) {
		if(!strcasecmp(user->handle, handle)) {
			break;
		}
	}
	if(user == NULL) {
		return 0;
	}
	if(index(user->options, (int)'d') == NULL) {
		return 0;
	}
	/* password change? */
	if(user->password) {
		/* yes, de-allocate previous */
		free(user->password);
	}
	if((user->password = (char *) malloc(strlen(password) + 1)) == NULL) {
		return 0;
	}
	strcpy(user->password, password);
	return 1;
}

void sendreply(char *target, int tochan, int delay, int randomness, char *message)
{
	time_t when;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "sendreply(\"%s\", %d, %d, %d, \"%s\")\n", target, tochan, delay, randomness, message);
#endif
	if(tochan) {
		snprintf(timed_str, WRITE_BUFFER_LENGTH, "PRIVMSG %s :%s", target, message);
	} else {
		if(index(mystate->flags, (int)'m') != NULL) {
			snprintf(timed_str, WRITE_BUFFER_LENGTH, "PRIVMSG %s :%s", target, message);
		} else {
			snprintf(timed_str, WRITE_BUFFER_LENGTH, "NOTICE %s :%s", target, message);
		}
	}
	/* the getrandom(4) - 1 was added to avoid Excess Floods,
	   a better solution would need to keep count of client->server msgs */
	if(randomness) {
		when = mystate->now + delay + (getrandom(4) - 1);
	} else {
		when = mystate->now + delay;
	}
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "sendreply(%lu)->%s\n", when - mystate->now, timed_str);
#endif
        timed_new(NULL, when, OER_TIMED_TYPE_NORMAL, (tochan) ? OER_TIMED_PRIORITY_PRIVMSG : OER_TIMED_PRIORITY_NOTICE, timed_str);
}

void sendchannelnotice(struct channel *this, int delay, char *message)
{
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "sendchannelnotice(\"%s\", %d, \"%s\")\n", this->name, delay, message);
#endif
	snprintf(timed_str, WRITE_BUFFER_LENGTH, "NOTICE %s :%s", this->name, message);
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "sendchannelnotice->%s", timed_str);
#endif
        timed_new(NULL, mystate->now + delay, OER_TIMED_TYPE_NORMAL, OER_TIMED_PRIORITY_NOTICE, timed_str);
}

void kickuser(struct channel *this, time_t when, char *nick, char *reason)
{
	struct chanuser *cu;
	char kr[STRINGLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "kickuser(\"%s\", %lu, \"%s\", \"%s\")\n", this->name, when, nick, reason);
#endif
	for(cu = this->nicks; cu != NULL; cu = cu->next) {
		if(!strcasecmp(cu->nick, nick)) {
			break;
		}
	}
	if(cu == NULL) {
		return;
	}
	if(cu->userhost == NULL) {
		return;
	}
	if(reason != NULL) {
		striplf(reason);
		snprintf(timed_str, WRITE_BUFFER_LENGTH, "KICK %s %s :%s", this->name, cu->nick, reason);
	} else {
		if(getkickreason(kr) != NULL) {
			snprintf(timed_str, WRITE_BUFFER_LENGTH, "KICK %s %s :%s", this->name, cu->nick, kr);
		} else {
			snprintf(timed_str, WRITE_BUFFER_LENGTH, "KICK %s %s :%s", this->name, cu->nick, (mystate->use_alt_nick) ? mystate->altnick : mystate->nick);
		}
	}
        timed_new(NULL, when, OER_TIMED_TYPE_NORMAL, OER_TIMED_PRIORITY_CHANNEL_PROTECTION, timed_str);
}

int isvoicenow(struct channel *this, char *nick)
{
	struct chanuser *cu;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "isvoicenow(\"%s\", \"%s\")\n", this->name, nick);
#endif
	for(cu = this->nicks; cu != NULL; cu = cu->next) {
		if(!strcasecmp(cu->nick, nick)) {
			return cu->voice;
		}
	}
	return 0;
}

int nickchange(struct channel *this, char *nick, char *newnick)
{
	struct chanuser *cu;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "nickchange(\"%s\", \"%s\", \"%s\")\n", this->name, nick, newnick);
#endif
	for(cu = this->nicks; cu != NULL; cu = cu->next) {
		if(!strcasecmp(cu->nick, nick)) {
			free(cu->nick);
			if((cu->nick = (char *) malloc(strlen(newnick) + 1)) == NULL) {
				return 0;
			}
			strcpy(cu->nick, newnick);
			return 1;
		}
	}
	return 0;
}

char *getlamer(struct channel *this)
{
	struct chanuser *cu;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "getlamer(\"%s\")\n", this->name);
#endif
	for(cu = this->nicks; cu != NULL; cu = cu->next) {
		/* get a random lamer and mark for kicking */
		if(!cu->chanop && !cu->tobek && !isop(this, cu->userhost)) {
			if(cu->ircop && mystate->current_server->protected_ircops) {
				continue;
			}
			if(haschanflags(this, "F") && isfriend(this, cu->nick, cu->userhost)) {
				continue;
			}
			cu->tobek = 1;
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "getlamer->returning %s\n", cu->nick);
#endif
			return cu->nick;
		}
	}
	return NULL;
}

void cleartobeks(struct channel *this)
{
	struct chanuser *cu;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "cleartobeks(\"%s\")\n", this->name);
#endif
	for(cu = this->nicks; cu != NULL; cu = cu->next) {
		cu->tobek = 0;
	}
}

int getrandom(int ceiling)
{
        int thisrandom;
        struct timeval tv;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "getrandom(%d)\n", ceiling);
#endif
        gettimeofday(&tv, 0);
        srand(tv.tv_usec);
        thisrandom = 1 + (int) ((float) ceiling * rand() / (RAND_MAX + 1.0));
        return thisrandom;
}

struct chanuser *getrandomuser(struct channel *this)
{
	int nthuser;
	int counter;
	struct chanuser *cu;
	struct chanuser *cu2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "getrandomuser(\"%s\")\n", this->name);
#endif
	if(!this->joined) {
		return NULL;
	}
	nthuser = getrandom(this->nickcount) - 1;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "getrandomuser->the winner of %s is user in position %d\n", this->name, nthuser);
#endif
	cu = this->nicks;
	counter = 0;
	while(cu != NULL && counter < nthuser) {
		counter++;
		cu = cu->next;
	}
	for(cu2 = cu; cu != NULL; cu = cu->prev) {
		if(cu->chanop) {
			continue;
		}
		if(cu->ircop) {
			if(!mystate->current_server->protected_ircops) {
				return cu;
			}
		}
		if(!haschanflags(this, "F")) {
			return cu;
		}
		if(haschanflags(this, "F") && !isfriend(this, cu->nick, cu->userhost)) {
			return cu;
		}
	}
	for(cu = cu2; cu != NULL; cu = cu->next) {
		if(cu->chanop) {
			continue;
		}
		if(cu->ircop) {
			if(!mystate->current_server->protected_ircops) {
				return cu;
			}
		}
		if(!haschanflags(this, "F")) {
			return cu;
		}
		if(haschanflags(this, "F") && !isfriend(this, cu->nick, cu->userhost)) {
			return cu;
		}
	}
	return NULL;
}

int allhostsknown(struct channel *this)
{
	struct chanuser *cu;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "allhostsknown(\"%s\")\n", this->name);
#endif
	if(!this->joined) {
		return 0;
	}
	for(cu = this->nicks; cu != NULL; cu = cu->next) {
		if(cu->userhost == NULL) {
			return 0;
		}
	}
	return 1;
}

char *getfirsthostless(struct channel *this, char *nick)
{
        struct chanuser *cu;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "getfirsthostless(\"%s\")\n", this->name);
#endif
	if(!this->joined) {
		return NULL;
	}
        for(cu = this->nicks; cu != NULL; cu = cu->next) {
                if(cu->userhost == NULL && !cu->hostquery) {
                        break;
                }
        }
        if(cu == NULL) {
                return NULL;
        }
        strncpy(nick, cu->nick, NICKLEN);
        return nick;
}

void sethostquerystatus(char *nick, int status)
{
	struct chanuser *cu;
	struct channel *this;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "sethostquerystatus(\"%s\", %d)\n", nick, status);
#endif
        for(this = mystate->channels; this != NULL; this = this->next) {
		for(cu = this->nicks; cu != NULL; cu = cu->next) {
			if(!strcasecmp(cu->nick, nick)) {
				cu->hostquery = status;
				break;
			}
		}
	}
}

void setuserhost(char *nick, char *userhost)
{
	struct chanuser *cu;
	struct channel *this;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "setuserhost(\"%s\", \"%s\")\n", nick, userhost);
#endif
        for(this = mystate->channels; this != NULL; this = this->next) {
		if(!this->joined) {
			continue;
		}
		for(cu = this->nicks; cu != NULL; cu = cu->next) {
			if(!strcasecmp(cu->nick, nick)) {
				if(cu->userhost != NULL) {
					free(cu->userhost);
				}
				/* we'll need sometimes to reset the host to
				   force a USERHOST query */
				if(userhost == NULL) {
					cu->userhost = NULL;
					break;
				}
				if((cu->userhost = (char *) malloc(strlen(userhost) + 1)) == NULL) {
					return;
				}
				strcpy(cu->userhost, userhost);
				break;
			}
		}
	}
}

int ishostless(char *nick)
{
	struct chanuser *cu;
	struct channel *this;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "ishostless(\"%s\")\n", nick);
#endif
        for(this = mystate->channels; this != NULL; this = this->next) {
		if(!this->joined) {
			continue;
		}
		for(cu = this->nicks; cu != NULL; cu = cu->next) {
			if(!strcasecmp(cu->nick, nick)) {
				if(cu->userhost == NULL) {
					return 1;
				} else {
					return 0;
				}
			}
		}
	}
	return -1;
}

char *isnickbk(struct channel *this, char *nick)
{
	struct maskstruct *nickbk;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "isnickbk(\"%s\", \"%s\")\n", this->name, nick);
#endif
	for(nickbk = this->nickbks; nickbk != NULL; nickbk = nickbk->next) {
		if(wild_match(nickbk->mask, nick)) {
			if(nickbk->optstring != NULL) {
				return nickbk->optstring;
			}
			return nick;
		}
	}
	return NULL;
}

int iswordbk(struct channel *this, char *message)
{
	struct maskstruct *wordbk;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "iswordbk(\"%s\", \"%s\")\n", this->name, message);
#endif
	for(wordbk = this->wordbks; wordbk != NULL; wordbk = wordbk->next) {
		if(wild_match(wordbk->mask, message)) {
			return 1;
		}
	}
	return 0;
}

int isflood(struct channel *this, char *nick, char *userhost, char *message)
{
	int weight;
	int lines;
	int chars;
	int length;
	int repinterval;
	time_t first;
	time_t firstrep;
	struct pubmsg *pm;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "isflood(\"%s\", \"%s\", \"%s\", \"%s\")\n", this->name, nick, userhost, message);
#endif
	first = mystate->now;
	firstrep = mystate->now;
	lines = 1;
	length = strlen(message);
	chars = length;
	weight = 0;
	pm = this->pubmsgs;
	while(pm != NULL) {
		if(!strcasecmp(pm->nick, nick) && !strcasecmp(pm->userhost, userhost)) {
			if(pm->at < first) {
				first = pm->at;
			}
			if(!strcasecmp(pm->message, message)) {
				if(pm->at < firstrep) {
					firstrep = pm->at;
				}
				weight++;
			}
			lines++;
			chars += strlen(pm->message);
		}
		if((weight >= this->floodvars.repeat_limit - 1) && (length > 5) && (mystate->now - firstrep < this->floodvars.repeat_expire)) {
			/* immediate ban-kick */
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "isflood->REPEAT delta: %lu lines: %d chars: %d weight: %d\n", mystate->now - firstrep, lines, chars, weight);
#endif
			return OER_PUBMSG_FLOOD_REPEAT;
		}
		pm = pm->next;
	}
	/* check for repeat with < 5 long pubmsgs */
	repinterval = (mystate->now - firstrep);
	if(repinterval) {
		repinterval /= weight;
	}
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "isflood->repinterval is %d\n", repinterval);
#endif
	if((weight >= this->floodvars.repeat_limit - 1) && (length <= 5) \
	   && (repinterval < (this->floodvars.interval / this->floodvars.repeat_limit))) {
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "isflood->REPEAT delta: %lu lines: %d chars: %d weight: %d\n", mystate->now - firstrep, lines, chars, weight);
#endif
		return OER_PUBMSG_FLOOD_REPEAT;
	}
	if((chars > this->floodvars.chars || lines > this->floodvars.lines) && mystate->now - first < this->floodvars.interval) {
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "isflood->NORMAL delta: %lu lines: %d chars: %d weight: %d\n", mystate->now - first, lines, chars, weight);
#endif
		return OER_PUBMSG_FLOOD_NORMAL;
	}
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "isflood->NO FLOOD delta: %lu lines: %d chars: %d weight: %d\n", mystate->now - first, lines, chars, weight);
#endif
	return OER_PUBMSG_FLOOD_NONE;
}

int haschanflags(struct channel *this, char *flags)
{
	int i;
	int len;
	int got_flags;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "haschanflags(\"%s\", \"%s\")\n", (this == NULL) ? "(null)" : this->name, flags);
#endif
	if(this == NULL) {
		return 0;
	}
	/* check if the channel has all the required flags set... */
	for(i = 0, got_flags = 0, len = strlen(flags); i < len; i++) {
		if(index(this->chanflags, (int)flags[i]) != NULL) {
			got_flags++;
		}
	}
	if(got_flags == len) {
		return 1;
	}
	return 0;
}

int hasadminflags(char *userhost, char *flags)
{
	int i;
	int len;
	int got_flags;
	struct botuser *admin;
	struct maskstruct *ms;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "hasadminflags(\"%s\", \"%s\")\n", (userhost == NULL) ? "(null)" : userhost, flags);
#endif
	if(userhost == NULL) {
		return 0;
	}
	/* go through all admins */
	for(admin = mystate->admins; admin != NULL; admin = admin->next) {
		/* check if the admin has all the required flags set... */
		for(i = 0, got_flags = 0, len = strlen(flags); i < len; i++) {
			if(index(admin->options, (int)flags[i]) != NULL) {
				got_flags++;
			}
		}
		if(got_flags != len) {
			/* nope, skip to next user */
			continue;
		}
		/* the admin has the right flags, what about hostmask? */
		for(ms = admin->firstmask; ms != NULL; ms = ms->next) {
			if(wild_match(ms->mask, userhost)) {
				return 1;
			}
		}
	}
	return 0;
}

struct botuser *hasuserflags(struct channel *this, char *userhost, char *flags)
{
	int i;
	int len;
	int got_flags;
	struct botuser *user;
	struct maskstruct *ms;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "hasuserflags(\"%s\", \"%s\", \"%s\")\n", (this == NULL) ? "(null)" : this->name, (userhost == NULL) ? "(null)" : userhost, flags);
#endif
	if(this == NULL || userhost == NULL) {
		return NULL;
	}
	for(user = this->users; user != NULL; user = user->next) {
		/* check if the user has all the required flags set... */
		for(i = 0, got_flags = 0, len = strlen(flags); i < len; i++) {
			if(index(user->options, (int)flags[i]) != NULL) {
				got_flags++;
			}
		}
		if(got_flags != len || (index(user->options, (int)'!') != NULL)) {
			/* either not the flags or user is inactive */
			continue;
		}
		/* the user has the right flags, what about hostmask? */
		for(ms = user->firstmask; ms != NULL; ms = ms->next) {
			if(wild_match(ms->mask, userhost)) {
				return user;
			}
		}
        }
	return NULL;
}

void isjoined(struct channel *this)
{
	char stringbuffer[STRINGLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "isjoined(\"%s\")\n", this->name);
#endif
	this->joined = 1;
	this->joining = 0;
	if(haschanflags(this, "S")) {
		snprintf(stringbuffer, STRINGLEN, "seen-%s", this->name);
		if((this->seen = fopen(stringbuffer, "a")) == NULL) {
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "isjoined->failed to open %s: %s\n", stringbuffer, strerror(errno));
#endif
		}
	}
	if(haschanflags(this, "L")) {
		snprintf(stringbuffer, STRINGLEN, "last-%s", this->name);
		if((this->last = fopen(stringbuffer, "a")) == NULL) {
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "isjoined->failed to open %s: %s\n", stringbuffer, strerror(errno));
#endif
		}
	}
}

void setchankey(struct channel *this, char *key)
{
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "setchankey(\"%s\", \"%s\")\n", this->name, key);
#endif
	strncpy(this->key, key, CHANLEN);
	this->haskey = 1;
	this->setchanmode = 1;
}

int delchankey(struct channel *this)
{
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "delchankey(\"%s\")\n", this->name);
#endif
	if(!this->haskey) {
		return 0;
	}
	mmode_new(this, mystate->now, "-k", this->key);
	memset(this->key, 0, CHANLEN);
	this->haskey = 0;
	return 1;
}

char *safeban(struct channel *this, char *safemask, char *nick)
{
	int got_ban;
	int counter;
	int type;
	int is_trusted;
	char user[USERHOSTLEN + 1];
	char host[USERHOSTLEN + 1];
	char ban_mask[USERHOSTLEN + 1];
	char my_mask[USERHOSTLEN + 1];
	char *ptr;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "safeban(\"%s\", \"%s\")\n", this->name, nick);
#endif
	/* safemask contains user@host */
	/* strip user & host */
	strncpy(user, safemask, USERHOSTLEN);
	ptr = index(user, '@');
	memset(ptr, 0, USERHOSTLEN - strlen(user) + 1);
	ptr = index(safemask, '@');
	strncpy(host, ptr + 1, USERHOSTLEN);
	snprintf(my_mask, USERHOSTLEN, "%s!%s@%s", mystate->nick, mystate->user, mystate->host);
	type = this->bantype;
	for(counter = 0, got_ban = 0; !got_ban && counter < OER_BAN_TYPE_INVALID; counter++) {
		is_trusted = 0;
		switch(type) {
		case OER_BAN_TYPE_HOST:
			/* no domain bans for trusteds */
			if(istrusted(host)) {
				is_trusted = 1;
			}
			snprintf(ban_mask, USERHOSTLEN, "*!*@%s", host);
			type = OER_BAN_TYPE_USER_HOST;
			break;
		case OER_BAN_TYPE_USER:
			snprintf(ban_mask, USERHOSTLEN, "*!%s@*", user);
			type = OER_BAN_TYPE_USER_HOST;
			break;
		case OER_BAN_TYPE_USER_HOST:
			snprintf(ban_mask, USERHOSTLEN, "*!%s@%s", user, host);
			type = OER_BAN_TYPE_NICK_USER_HOST;
			break;
		case OER_BAN_TYPE_NICK:
			snprintf(ban_mask, USERHOSTLEN, "%s!*@*", nick);
			type = OER_BAN_TYPE_NICK_HOST;
			break;
		case OER_BAN_TYPE_NICK_HOST:
			snprintf(ban_mask, USERHOSTLEN, "%s!*@%s", nick, host);
			type = OER_BAN_TYPE_NICK_USER;
			break;	
		case OER_BAN_TYPE_NICK_USER:
			snprintf(ban_mask, USERHOSTLEN, "%s!%s@*", nick, user);
			type = OER_BAN_TYPE_NICK_USER_HOST;
			break;
		case OER_BAN_TYPE_NICK_USER_HOST:
			snprintf(ban_mask, USERHOSTLEN, "%s!%s@%s", nick, user, host);
			break;
		default:
			return NULL;
		}
		if(is_trusted) {
			continue;
		}
		/* check that ban is safe */
		if(!wild_match(ban_mask, my_mask)) {
			got_ban = 1;
		}
	}
	if(!got_ban) {
		return NULL;
	}
	strncpy(safemask, ban_mask, USERHOSTLEN);
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "safeban->returning %s\n", safemask);
#endif
	return safemask;
}

int nthmode(char *modeline, int index)
{
	int i;
	int n;
	int length;
	char polar;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "nthmode(\"%s\", %d)\n", modeline, index);
#endif
	length = strlen(modeline);
	for(i = 0, n = 0, polar = 'x'; i < length; i++) {
		switch(modeline[i]) {
		case '+':
			polar = '+';
			break;
		case '-':
			polar = '-';
			break;
		case 'o':
			n++;
			if(n == index) {
				if(polar == '+') {
					return OER_NTHMODE_OP;
				} else {
					return OER_NTHMODE_DOP;
				}
			}
			break;
		case 'v':
			n++;
			if(n == index) {
				if(polar == '+') {
					return OER_NTHMODE_VOICE;
				} else {
					return OER_NTHMODE_DVOICE;
				}
			}
			break;
		case 'b':
			n++;
			if(n == index) {
				if(polar == '+') {
					return OER_NTHMODE_BAN;
				} else {
					return OER_NTHMODE_UNBAN;
				}
			}
			break;
		default:
			return OER_NTHMODE_UNKNOWN;
		}

	}
	return OER_NTHMODE_UNKNOWN;
}

void getnthmode(char *modeline, int index, char *to)
{
	int i;
	int n;
	int length;
	char polar;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "getnthmode(\"%s\", %d)\n", modeline, index);
#endif
	memset(to, 0, CHANLEN + 1);
	length = strlen(modeline);
	for(i = 0, n = 0, polar = 'x'; i < length; i++) {
		switch(modeline[i]) {
		case '+':
		case '-':
			polar = modeline[i];
			break;
                default:
			n++;
			if(n == index) {
				snprintf(to, CHANLEN, "%c%c", polar, modeline[i]);
				return;
			}
			break;
		}
	}
}

struct ls_wrapstruct *addnew_ls(struct ls_wrapstruct *first, int show_amount, time_t at, char *nick, char *userhost, char *message)
{
	int amount;
	struct ls_wrapstruct *ls;
	struct ls_wrapstruct *ls2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "addnew_ls(\"%s\", %d, %lu, \"%s\", \"%s\", \"%s\")\n", first->str3, show_amount, at, nick, userhost, message);
#endif
	ls = first;
	ls2 = ls;
	amount = 0;
	while(ls != NULL) {
		ls2 = ls;
		amount++;
		ls = ls2->next;
	}
	/* add a new entry to the list */
	if((ls = (struct ls_wrapstruct *) malloc(sizeof(struct ls_wrapstruct))) == NULL) {
		return NULL;
	}
	if((ls->str1 = (char *) malloc(strlen(nick) + 1)) == NULL) {
		return NULL;
	}
	strcpy(ls->str1, nick);
	if((ls->str2 = (char *) malloc(strlen(userhost) + 1)) == NULL) {
		return NULL;
	}
	strcpy(ls->str2, userhost);
	if(message != NULL) {
		if((ls->str3 = (char *) malloc(strlen(message) + 1)) == NULL) {
			return NULL;
		}
		strcpy(ls->str3, message);
	} else {
		ls->str3 = NULL;
	}
	ls->at = at;
	ls->prev = NULL;
	ls->next = NULL;
	/* the first one is a special case */
	if(first == NULL) {
		return ls;
	}
	/* >= 1 ls */
	ls->prev = ls2;
	ls2->next = ls;
	if(amount >= show_amount) {
		/* strip first one */
		first->next->prev = NULL;
		ls2 = first->next;
		free(first->str1);
		free(first->str2);
		free(first->str3);
		free(first);
		first = ls2;
	}
	return first;
}

struct pubmsg *addnewpubmsg(struct channel *this, time_t at, char *nick, char *userhost, char *message)
{
	int amount;
	struct pubmsg *pm;
	struct pubmsg *pm2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "addnewpubmsg(\"%s\", %lu, \"%s\", \"%s\", \"%s\")\n", this->name, at, nick, userhost, message);
#endif
	/* add a new entry to the list */
	if((pm = (struct pubmsg *) malloc(sizeof(struct pubmsg))) == NULL) {
		return NULL;
	}
	if((pm->nick = (char *) malloc(strlen(nick) + 1)) == NULL) {
		return NULL;
	}
	strcpy(pm->nick, nick);
	if((pm->userhost = (char *) malloc(strlen(userhost) + 1)) == NULL) {
		return NULL;
	}
	strcpy(pm->userhost, userhost);
	if((pm->message = (char *) malloc(strlen(message) + 1)) == NULL) {
		return NULL;
	}
	strcpy(pm->message, message);
	pm->at = at;
	pm->prev = NULL;
	pm->next = NULL;
	/* the first one is a special case */
	if(this->pubmsgs == NULL) {
		this->pubmsgs = pm;
		return pm;
	}
	/* we got at least 1 pubmsg in the list */
	this->pubmsgs->prev = pm;
	pm->next = this->pubmsgs;
	this->pubmsgs = pm;
	for(pm2 = this->pubmsgs, amount = 0; pm2->next != NULL; pm2 = pm2->next, amount++) {
	}
	if(amount >= OER_PUBMSGS) {
		/* list full, strip last one */
		pm2->prev->next = NULL;
		free(pm2->message);
		free(pm2->userhost);
		free(pm2->nick);
		free(pm2);
	}
	return pm;
}

struct part *addnewpart(struct channel *this, time_t at, char *nick, char *userhost)
{
	int amount;
	struct part *p;
	struct part *p2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "addnewpart(\"%s\", %lu, \"%s\", \"%s\")\n", this->name, at, nick, userhost);
#endif
	if(userhost == NULL) {
#ifdef DEBUG
		oer_debug(OER_DEBUG_INFO, "addnewpart->hostmask = NULL, not adding part for user\n");
#endif
		return NULL;
	}
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "addnewpart->adding part from %s at %lu for %s!%s\n", this->name, at, nick, userhost);
#endif
	/* add a new entry to the list */
	if((p = (struct part *) malloc(sizeof(struct part))) == NULL) {
		return NULL;
	}
	if((p->nick = (char *) malloc(strlen(nick) + 1)) == NULL) {
		return NULL;
	}
	strcpy(p->nick, nick);
	if((p->userhost = (char *) malloc(strlen(userhost) + 1)) == NULL) {
		return NULL;
	}
	strcpy(p->userhost, userhost);
	p->at = at;
	p->valid = 1;
	p->prev = NULL;
	p->next = NULL;
	/* the first one is a special case */
	if(this->parts == NULL) {
		this->parts = p;
		return p;
	}
	/* we got at least 1 part in the list */
	this->parts->prev = p;
	p->next = this->parts;
	this->parts = p;
	for(p2 = this->parts, amount = 0; p2->next != NULL; p2 = p2->next, amount++) {
	}
	if(amount >= OER_PARTS) {
		/* list full, strip last one */
		p2->prev->next = NULL;
		free(p2->nick);
		free(p2->userhost);
		free(p2);
	}
	return p;
}

struct join *addnewjoin(struct channel *this, time_t at, char *nick, char *userhost)
{
	int amount;
	struct join *j;
	struct join *j2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "addnewjoin(\"%s\", %lu, \"%s\", \"%s\")\n", this->name, at, nick, userhost);
#endif
	/* add a new entry to the list */
	if((j = (struct join *) malloc(sizeof(struct join))) == NULL) {
		return NULL;
	}
	if((j->nick = (char *) malloc(strlen(nick) + 1)) == NULL) {
		return NULL;
	}
	strcpy(j->nick, nick);
	if((j->userhost = (char *) malloc(strlen(userhost) + 1)) == NULL) {
		return NULL;
	}
	strcpy(j->userhost, userhost);
	j->at = at;
	j->prev = NULL;
	j->next = NULL;
	/* the first one is a special case */
	if(this->joins == NULL) {
		this->joins = j;
		return j;
	}
	/* we got at least 1 join in the list */
	this->joins->prev = j;
	j->next = this->joins;
	this->joins = j;
	for(j2 = this->joins, amount = 0; j2->next != NULL; j2 = j2->next, amount++) {
	}
	if(amount >= OER_JOINS) {
		/* list full, strip last one */
		j2->prev->next = NULL;
		free(j2->nick);
		free(j2->userhost);
		free(j2);
	}
	return j;
}

struct chanuser *getcuptr(struct channel *this, char *nick)
{
        struct chanuser *cu;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "getcuptr(\"%s\", \"%s\")\n", this->name, nick);
#endif
	for(cu = this->nicks; cu != NULL; cu = cu->next) {
                if(!strcmp(cu->nick, nick)) {
			return cu;
                }
        }
	return NULL;
}

struct chanuser *userjoined(struct channel *this, char *nick, char *userhost, int ircop, int chanop, int voice, int friend)
{
	struct chanuser *cu;
	struct chanuser *cu2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "userjoined(\"%s\", \"%s\", \"%s\", %d, %d, %d, %d)\n", this->name, nick, userhost, ircop, chanop, voice, friend);
#endif
	if((cu = (struct chanuser *) malloc(sizeof(struct chanuser))) == NULL) {
		return NULL;
	}
	cu->hostquery = 0;
	cu->ircop = ircop;
	cu->chanop = chanop;
	cu->voice = voice;
	cu->tobek = 0;
	cu->friend = friend;
	cu->prev = NULL;
	cu->next = NULL;
	if((cu->nick = (char *) malloc(strlen(nick) + 1)) == NULL) {
		return NULL;
	}
	strcpy(cu->nick, nick);
	cu->userhost = NULL;
	if(userhost != NULL) {
		if((cu->userhost = (char *) malloc(strlen(userhost) + 1)) == NULL) {
			return NULL;
		}
		strcpy(cu->userhost, userhost);
	}
	/* if user is logoned and has "f" flag */
	if(haschanflags(this, "F") && isfriend(this, nick, userhost)) {
		cu->friend = 1;
	}
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "userjoined->added join to %s from %s!%s (%d %d %d %d)\n", this->name, cu->nick, (cu->userhost == NULL) ? "(null)" : cu->userhost, cu->ircop, cu->chanop, cu->voice, cu->friend);
#endif
	/* as always, the empty case is special */
	if(this->nicks == NULL) {
		this->nicks = cu;
		this->nickcount++;
		return cu;
	}
	/* there is no need to check for existance, append */
	for(cu2 = this->nicks; cu2->next != NULL; cu2 = cu2->next) {
	}
	cu2->next = cu;
	cu->prev = cu2;
	this->nickcount++;
	return cu;
}

int userleft(struct channel *this, char *nick, char *userhost)
{
	struct chanuser *cu;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "userleft(\"%s\", \"%s\", \"%s\")\n", this->name, nick, userhost);
#endif
	for(cu = this->nicks; cu != NULL; cu = cu->next) {
		/* nick suffices, it is possible that someone leaves
		   before we have stored the host */
		if(!strcasecmp(cu->nick, nick)) {
			break;
		}
	}
	if(cu == NULL) {
		/* a ghost user left?!?! */
		return 0;
	}
	/* free the user & adjust lists */
	if(cu->prev == NULL) {
		/* first user in list */
		this->nicks = cu->next;
		/* ... check if also last */
		if(cu->next != NULL) {
			cu->next->prev = NULL;
		}
	}
	if(cu->next == NULL) {
		/* last user in list
		   ... check also if the only one */
		if(cu->prev != NULL) {
			cu->prev->next = NULL;
		} else {
			this->nicks = NULL;
		}
	}
	if(cu->prev != NULL && cu->next != NULL) {
		/* between 2 or more users */
		cu->prev->next = cu->next;
		cu->next->prev = cu->prev;
	}
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "userleft->removed from %s %s!%s\n", this->name, cu->nick, (cu->userhost == NULL) ? "(null)" : cu->userhost);
#endif
	if(cu->userhost != NULL) {
		free(cu->userhost);
	}
	free(cu->nick);
	free(cu);
	this->nickcount--;
	return 1;
}

void linenoise()
{
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "linenoise()\n");
#endif
	if(!mystate->current_server->linenoise) {
		return;
	}
	if((mystate->now - mystate->last_linenoise) > mystate->current_server->linenoise) {
		snprintf(timed_str, WRITE_BUFFER_LENGTH, "ISON %s", (mystate->use_alt_nick) ? mystate->altnick : mystate->nick);
		timed_new(NULL, mystate->now, OER_TIMED_TYPE_NORMAL, OER_TIMED_PRIORITY_CHANNEL_HANDLING, timed_str);
		mystate->last_linenoise = mystate->now;
	}
}

void checkstoned()
{
	char outstring[STRINGLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "checkstoned()\n");
#endif
	if(!mystate->current_server->lastping) {
		return;
	}
	if(!(mystate->now & 0xF)) {
		snprintf(outstring, STRINGLEN, "checkstoned->last known server activity %s", ctime(&mystate->current_server->lastping));
		striplf(outstring);
		tzset();
		/* this is the only one not enclosed in #ifdef */
		oer_debug(OER_DEBUG_INFO, "%s %s %s\n", outstring, tzname[0], tzname[1]);
	}
	if((mystate->now - mystate->current_server->lastping) > (mystate->current_server->pingfrequency * 3)) {
		mystate->current_server->stoned = mystate->now;
		mystate->reconnect = OER_RECONNECT_STONED;
	}
}
int changeuser(struct channel *this, char *nick, int ircop, int chanop, int voice, int friend)
{
	struct chanuser *cu;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "changeuser(\"%s\", \"%s\", %d, %d, %d, %d)\n", this->name, nick, ircop, chanop, voice, friend);
#endif
	for(cu = this->nicks; cu != NULL; cu = cu->next) {
		if(!strcasecmp(cu->nick, nick)) {
			break;
		}
	}
	if(cu == NULL) {
		return 0;
	}
	cu->ircop = (ircop == -1) ? cu->ircop : ircop;
	cu->chanop = (chanop == -1) ? cu->chanop : chanop;
	cu->voice = (voice == -1) ? cu->voice : voice;
	cu->friend = (friend == -1) ? cu->friend : friend;
	return 1;
}

void changetobek(struct channel *this, char *nick, int tobek)
{
	struct chanuser *cu;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "changetobek(\"%s\", \"%s\", %d)\n", this->name, nick, tobek);
#endif
	for(cu = this->nicks; cu != NULL; cu = cu->next) {
		if(!strcasecmp(cu->nick, nick)) {
			cu->tobek = tobek;
			return;
		}
	}
}

int gettobek(struct channel *this, char *nick)
{
	struct chanuser *cu;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "gettobek(\"%s\", \"%s\")\n", this->name, nick);
#endif
	for(cu = this->nicks; cu != NULL; cu = cu->next) {
		if(!strcasecmp(cu->nick, nick)) {
			return cu->tobek;
		}
	}
	return 0;
}

void freepubmsguser(struct channel *this, char *nick, char *userhost)
{
	struct pubmsg *pm;
	struct pubmsg *pm2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "freepubmsguser(\"%s\", \"%s\", \"%s\")\n", this->name, nick, userhost);
#endif
	pm = this->pubmsgs;
	while(pm != NULL) {
		pm2 = pm->next;
		if(!strcasecmp(pm->nick, nick) && !strcasecmp(pm->userhost, userhost)) {
			if(pm->prev == NULL) {
				/* first pubmsg in list */
				this->pubmsgs = pm->next;
				/* ... check if also last */
				if(pm->next != NULL) {
					pm->next->prev = NULL;
				}
			}
			if(pm->next == NULL) {
				/* last pubmsg in list */
				this->pubmsgs = pm->prev;
				/* ... check also if the only one */
				if(pm->prev != NULL) {
					pm->prev->next = NULL;
				} else {
					this->pubmsgs = NULL;
				}
			}
			if(pm->prev != NULL && pm->next != NULL) {
				/* between 2 or more pubmsgs */
				pm->prev->next = pm->next;
				pm->next->prev = pm->prev;
			}
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "freepubmsguser->removing: %lu %s %s %s\n", pm->at, pm->nick, pm->userhost, pm->message);
#endif
			free(pm->message);
			free(pm->nick);
			free(pm->userhost);
			free(pm);
		}
		pm = pm2;
	}
}

void lockchan(struct channel *this, char *reason, int auto_unlock, char *nick, char *userhost)
{
	char stringbuffer[STRINGLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "lockchan(\"%s\", \"%s\", %d, \"%s\", \"%s\")\n", this->name, reason, auto_unlock, nick, userhost);
#endif
	this->locked.locked = 1;
	this->locked.unlocked = 0;
	this->locked.auto_unlock = auto_unlock;
	memset(this->locked.reason, 0, STRINGLEN + 1);
	if(strlen(reason)) {
		strncpy(this->locked.reason, reason, STRINGLEN);
	}
	strncpy(this->locked.nick, nick, NICKLEN);
	strncpy(this->locked.host, userhost, USERHOSTLEN);
	mmode_new(this, mystate->now, "+i", NULL);
	snprintf(stringbuffer, STRINGLEN, "channel %s is locked by %s!%s", this->name, this->locked.nick, this->locked.host);
	sendchannelnotice(this, 0, stringbuffer);
}

void unlockchan(struct channel *this, char *nick, char *userhost)
{
	char stringbuffer[STRINGLEN + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "unlockchan(\"%s\", \"%s\", \"%s\")\n", this->name, nick, userhost);
#endif
	this->locked.unlocked = 1;
	/* this has to be a timed because of the way mmodes & timeds
	   are processed in the mainloop (we want the -i to appear
           after all lamers have been kicked out, not before) */
	mmode_new(this, mystate->now, "-i", NULL);
	snprintf(stringbuffer, STRINGLEN, "channel %s was unlocked by %s!%s", this->name, nick, userhost);
	sendchannelnotice(this, 0, stringbuffer);
	cleartobeks(this);
}

void wall(struct channel *this, char *nick, char *userhost, char *commandline)
{
	int ppos;
	int counter;
	int delay;
        char p1[STRINGLEN + 1];
	char stringbuffer[HUGESTRINGLEN + 1];
	struct chanuser *cu;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "wall(\"%s\", \"%s\", \"%s\", \"%s\")\n", this->name, nick, userhost, commandline);
#endif
	ppos = 0;
        if((ppos = parse(commandline, ppos, " ", p1, STRINGLEN, 0)) < 0) {
                return;
        }
	if(!strlen(p1) || !strlen(commandline + ppos)) {
		return;
	}
	delay = 0;
	/* all wall messages are being sent with a delay of (counter (0..nicksonchan) * OER_WALL_DELAY) + (counter * (counter >> 2)),
	   this is necessary to avoid 439's */
	if(!strcasecmp(p1, "admins")) {
		if(!isadmin(this, userhost)) {
			return;
		}
		for(cu = this->nicks, counter = 0; cu != NULL; cu = cu->next) {
			if(!isme(cu->nick) && isadmin(this, cu->userhost)) {
				snprintf(stringbuffer, HUGESTRINGLEN, "notice to %s admins from %s: %s", this->name, nick, commandline + ppos);
				delay = (counter < OER_MAXTARGETS) ? (counter * OER_WALL_DELAY) : ((counter - OER_MAXTARGETS + 1) * OER_TARGET_DELAY);
				sendreply(cu->nick, 0, delay, 0, stringbuffer);
				counter++;
			}
		}
		if(!counter) {
			return;
		}
		snprintf(stringbuffer, HUGESTRINGLEN, "notified %d %s on %s (took %d %s)", counter, (counter == 1) ? "admin" : "admins", this->name, delay, (counter == 1) ? "second" : "seconds");
		sendreply(nick, 0, delay, 0, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "channel")) {
		snprintf(stringbuffer, HUGESTRINGLEN, "notice to %s from %s: %s", this->name, nick, commandline + ppos);
		sendchannelnotice(this, 0, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "lamers")) {
		for(cu = this->nicks, counter = 0; cu != NULL; cu = cu->next) {
			if(!isme(cu->nick) && !cu->chanop) {
				snprintf(stringbuffer, HUGESTRINGLEN, "notice to non-opped %s users from %s: %s", this->name, nick, commandline + ppos);
				delay = (counter < OER_MAXTARGETS) ? (counter * OER_WALL_DELAY) : ((counter - OER_MAXTARGETS + 1) * OER_TARGET_DELAY);
				sendreply(cu->nick, 0, delay, 0, stringbuffer);
				counter++;
			}
		}
		if(!counter) {
			return;
		}
		snprintf(stringbuffer, HUGESTRINGLEN, "notified %d %s on %s (took %d %s)", counter, (counter == 1) ? "lamer" : "lamers", this->name, delay, (counter == 1) ? "second" : "seconds");
		sendreply(nick, 0, delay, 0, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "ops")) {
		for(cu = this->nicks, counter = 0; cu != NULL; cu = cu->next) {
			if(!isme(cu->nick) && cu->chanop) {
				snprintf(stringbuffer, HUGESTRINGLEN, "notice to %s channel operators from %s: %s", this->name, nick, commandline + ppos);
				delay = (counter < OER_MAXTARGETS) ? (counter * OER_WALL_DELAY) : ((counter - OER_MAXTARGETS + 1) * OER_TARGET_DELAY);
				sendreply(cu->nick, 0, delay, 0, stringbuffer);
				counter++;
			}
		}
		if(!counter) {
			return;
		}
		snprintf(stringbuffer, HUGESTRINGLEN, "notified %d %s on %s (took %d %s)", counter, (counter == 1) ? "op" : "ops", this->name, delay, (counter == 1) ? "second" : "seconds");
		sendreply(nick, 0, delay, 0, stringbuffer);
		return;
	}
	if(!strcasecmp(p1, "users")) {
		for(cu = this->nicks, counter = 0; cu != NULL; cu = cu->next) {
			if(!isme(cu->nick) && (isadmin(this, cu->userhost) || isop(this, cu->userhost))) {
				snprintf(stringbuffer, HUGESTRINGLEN, "notice to %s bot users from %s: %s", this->name, nick, commandline + ppos);
				delay = (counter < OER_MAXTARGETS) ? (counter * OER_WALL_DELAY) : ((counter - OER_MAXTARGETS + 1) * OER_TARGET_DELAY);
				sendreply(cu->nick, 0, delay, 0, stringbuffer);
				counter++;
			}
		}
		if(!counter) {
			return;
		}
		snprintf(stringbuffer, HUGESTRINGLEN, "notified %d %s on %s (took %d %s)", counter, (counter == 1) ? "bot user" : "bot users", this->name, delay, (counter == 1) ? "second" : "seconds");
		sendreply(nick, 0, delay, 0, stringbuffer);
		return;
	}
}

struct authed *addnewauthed(struct channel *this, time_t at, struct botuser *user, char *userhost)
{
	struct authed *a;
	struct authed *a2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "addnewauthed(\"%s\", %lu, \"%s\", \"%s\")\n", this->name, at, user->handle, userhost);
#endif
	/* add a new entry to the list */
	if((a = (struct authed *) malloc(sizeof(struct authed))) == NULL) {
		return NULL;
	}
	a->at = at;
	a->user = user;
	if((a->userhost = (char *) malloc(strlen(userhost) + 1)) == NULL) {
		return NULL;
	}
	strcpy(a->userhost, userhost);
	a->prev = NULL;
	a->next = NULL;
	/* the first one is a special case */
	if(this->autheds == NULL) {
		this->autheds = a;
		return a;
	}
	/* we got at least 1 authed in the list, append */
	for(a2 = this->autheds; a2->next != NULL; a2 = a2->next) {
		;
	}
	a2->next = a;
	a->prev = a2;
	return a;
}

void cleanautheds(struct channel *this)
{
	struct authed *a;
	struct authed *a2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "cleanautheds(\"%s\")\n", this->name);
#endif
	/* oldest first, stripped from first to last */
	a = this->autheds;
	while(a != NULL) {
		if(mystate->now < (a->at + OER_LOGON_TIMEOUT)) {
			/* since the list is oldest entry first, there
			   are no more entries to go through */
			return;
		}
		/* this entry has expired, remove & adjust lists */
		a2 = a->next;
		free(a->userhost);
		free(a);
		a = a2;
		if(a != NULL) {
			/* the next one will be the first in list */
			a->prev = NULL;
			this->autheds = a;
		} else {
			/* no more autheds for channel */
			this->autheds = NULL;
		}
	}
}

struct timed *timed_new(struct channel *this, time_t at, int type, int prio, char *command)
{
	int rc;
	struct timed *t1;
	struct timed *t2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "timed_new(\"%s\", %lu, %d, %d, \"%s\")\n", (this == NULL) ? "(null)" : this->name, at, type, prio, command);
#endif
	if((t1 = malloc(sizeof(struct timed))) == NULL) {
		return NULL;
	}
	/* channel can be NULL */
	t1->channel = NULL;
	if(this->name != NULL) {
		if((t1->channel = malloc(strlen(this->name) + 1)) == NULL) {
			return NULL;
		}
		strcpy(t1->channel, this->name);
	}
	if((t1->command = malloc(strlen(command) + 1)) == NULL) {
		return NULL;
	}
	strcpy(t1->command, command);
	t1->at = at;
	t1->type = type;
	t1->prio = prio;
	t1->prev = NULL;
	t1->next = NULL;
	if(mystate->timeds == NULL) {
		/* empty list */
		mystate->timeds = t1;
		return t1;
	}
	t2 = mystate->timeds;
	while(1) {
		rc = timed_cmp(t1, t2);
		if(rc == 1) {
			break;
		}
		if(t2->next == NULL) {
			break;
		}
		t2 = t2->next;
	}
	if(t2->prev == NULL) {
		/* first timed in list */
		if(rc == 1) {
			t2->prev = t1;
			t1->next = t2;
			mystate->timeds = t1;
			return t1;
		}
		t2->next = t1;
		t1->prev = t2;
		return t1;
	}
	if(t2->next == NULL) {
		/* last timed in list */
		if(rc == 1) {
			t2->prev->next = t1;
			t2->prev = t1;
			t1->next = t2;
			return t1;
		}
		t2->next = t1;
		t1->prev = t2;
		return t1;
	}
	/* between 2 or more timeds */
	if(rc == 1) {
		t2->prev->next = t1;
		t2->prev = t1;
		t1->next = t2;
		return t1;
	}
	t2->next->prev = t1;
	t2->next = t1;
	t1->prev = t2;
	return t1;
}

void timed_del(struct timed *t1)
{
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "timed_del(%x)\n", t1);
#endif
	if(t1->prev == NULL) {
		/* first timed */
		if(t1->next == NULL) {
			/* only timed */
			if(t1->channel != NULL) {
				free(t1->channel);
			}
			free(t1->command);
			free(t1);
			mystate->timeds = NULL;
			return;
		}
		t1->next->prev = NULL;
		mystate->timeds = t1->next;
		if(t1->channel != NULL) {
			free(t1->channel);
		}
		free(t1->command);
		free(t1);
		return;
	}
	if(t1->next == NULL) {
		/* last timed */
		t1->prev->next = NULL;
		if(t1->channel != NULL) {
			free(t1->channel);
		}
		free(t1->command);
		free(t1);
		return;
	}
	/* between 2 or more timeds */
	t1->prev->next = t1->next;
	t1->next->prev = t1->prev;
	if(t1->channel != NULL) {
		free(t1->channel);
	}
	free(t1->command);
	free(t1);
	return;
}

int timed_cmp(struct timed *t1, struct timed *t2)
{
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "timed_cmp(%x, %x)\n", t1, t2);
#endif
        /* sort timeds based on priority, then time */
        if(t1->prio == t2->prio) {
                /* same priority */
                if(t1->at == t2->at) {
                        /* same time */
                        return 0;
                }
                if(t1->at > t2->at) {
                        /* t1 has bigger time */
                        return -1;
                }
                return 1;
        }
        if(t1->prio > t2->prio) {
                /* t1 has higher priority */
                return 1;
        }
        /* t1 has smaller priority */
        return -1;
}

struct mmode *mmode_new(struct channel *this, time_t at, char *command, char *target)
{
	struct mmode *m1;
	struct mmode *m2;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "mmode_new(\"%s\", %lu, \"%s\", \"%s\")\n", this->name, at, command, target);
#endif
	if((m1 = malloc(sizeof(struct mmode))) == NULL) {
		return NULL;
	}
	if((m1->command = malloc(strlen(command) + 1)) == NULL) {
		return NULL;
	}
	strcpy(m1->command, command);
	/* target can be NULL, because of chanmodes for ex. */
	m1->target = NULL;
	if(target) {
		if((m1->target = malloc(strlen(target) + 1)) == NULL) {
			return NULL;
		}
		strcpy(m1->target, target);
	}
	m1->at = at;
	m1->prev = NULL;
	m1->next = NULL;
	if(this->mmodes == NULL) {
		/* empty list */
		this->mmodes = m1;
		return m1;
	}
	m2 = this->mmodes;
	while(1) {
		if(m1->at < m2->at) {
			break;
		}
		if(m2->next == NULL) {
			break;
		}
		m2 = m2->next;
	}
	if(m2->prev == NULL || m2->next == NULL) {
		/* first or last mmode in list */
		m2->next = m1;
		m1->prev = m2;
		return m1;
	}
	/* between 2 or more mmodes */
	m2->next->prev = m1;
	m2->next = m1;
	m1->prev = m2;
	return m1;
}

void mmode_del(struct channel *this, struct mmode *m1)
{
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "mmode_del(\"%s\", \"%s\")\n", this->name, m1->command);
#endif
	if(m1->prev == NULL) {
		/* first mmode */
		if(m1->next == NULL) {
			/* only mmode */
			free(m1->command);
			if(m1->target) {
				free(m1->target);
			}
			free(m1);
			this->mmodes = NULL;
			return;
		}
		m1->next->prev = NULL;
		this->mmodes = m1->next;
		free(m1->command);
		if(m1->target) {
			free(m1->target);
		}
		free(m1);
		return;
	}
	if(m1->next == NULL) {
		/* last mmode */
		m1->prev->next = NULL;
		free(m1->command);
		if(m1->target) {
			free(m1->target);
		}
		free(m1);
		return;
	}
	/* between 2 or more mmodes */
	m1->prev->next = m1->next;
	m1->next->prev = m1->prev;
	free(m1->command);
	if(m1->target) {
		free(m1->target);
	}
	free(m1);
	return;
}

void mmodes2timeds(struct channel *this)
{
	int modes;
	time_t earliest;
	struct mmode *m1;
	struct mmode *m2;
	char command[CHANLEN + 1];
        char target[WRITE_BUFFER_LENGTH + 1];
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "mmodes2timeds(\"%s\")\n", this->name);
#endif
	if(this->mmodes == NULL) {
		return;
	}
	memset(command, 0, CHANLEN + 1);
	memset(target, 0, WRITE_BUFFER_LENGTH + 1);
	modes = 0;
	m1 = this->mmodes;
	earliest = mystate->now;
	while(m1 != NULL) {
		m2 = m1->next;
		if(m1->at <= mystate->now) {
			earliest = (m1->at < earliest) ? m1->at : earliest;
			strncat(command, m1->command, CHANLEN - strlen(command));
			if(m1->target) {
				strncat(target, m1->target, WRITE_BUFFER_LENGTH - strlen(target));
				strncat(target, " ", WRITE_BUFFER_LENGTH - strlen(target));
			}
			modes++;
			mmode_del(this, m1);
		}
		m1 = m2;
		if(modes >= mystate->current_server->servermodes) {
			snprintf(timed_str, WRITE_BUFFER_LENGTH, "MODE %s %s %s", this->name, command, target);
			timed_new(NULL, earliest, OER_TIMED_TYPE_NORMAL, OER_TIMED_PRIORITY_CHANNEL_PROTECTION, timed_str);
			memset(&command, 0, CHANLEN + 1);
			memset(&target, 0, WRITE_BUFFER_LENGTH + 1);
			modes = 0;
		}
	}
	if(modes) {
		snprintf(timed_str, WRITE_BUFFER_LENGTH, "MODE %s %s %s", this->name, command, target);
		timed_new(NULL, earliest, OER_TIMED_TYPE_NORMAL, OER_TIMED_PRIORITY_CHANNEL_PROTECTION, timed_str);
	}
}

int clonechannel(char *ref, char *commandline)
{
	int ppos;
	char stringbuffer[STRINGLEN + 1];
	char new[STRINGLEN + 1];
	struct channel *that;
	struct channel *this;
	struct maskstruct *ms;
	struct botuser *user;
	struct topic *topic;
	struct advert *advert;
#ifdef DEBUG
	oer_debug(OER_DEBUG_INFO, "clonechannel(\"%s\", \"%s\")\n", ref, commandline);
#endif
	ppos = 0;
        if((ppos = parse(commandline, ppos, " ", new, STRINGLEN, 0)) < 0) {
                return 0;
        }
	if(!strcasecmp(ref, new)) {
		return 0;
	}
	/* does the new channel already exist? */
        if(getchptr(new) != NULL) {
                return 0;
        }
	if((that = getchptr(ref)) == NULL) {
                return 0;
        }
	if(!isvalidchannel(new)) {
		return 0;
	}
	if((this = addnewchannel(new)) == NULL) {
		return 0;
	}
	/* copy data from reference */
	strcpy(this->mode, that->mode);
	if(that->haskey) {
		this->haskey = that->haskey;
		strcpy(this->key, that->key);
	}
	strcpy(this->chanflags, that->chanflags);
	this->bantype = that->bantype;
	/* switch on last & seen if in flags */
	if(!haschanflags(this, "L")) {
		snprintf(stringbuffer, STRINGLEN, "last-%s", this->name);
		if((this->last = fopen(stringbuffer, "a")) == NULL) {
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "clonechannel->failed to open %s: %s\n", stringbuffer, strerror(errno));
#endif
		}
	}
	if(!haschanflags(this, "S")) {
		snprintf(stringbuffer, STRINGLEN, "seen-%s", this->name);
		if((this->seen = fopen(stringbuffer, "a")) == NULL) {
#ifdef DEBUG
			oer_debug(OER_DEBUG_INFO, "clonechannel->failed to open %s: %s\n", stringbuffer, strerror(errno));
#endif
		}
	}
	memcpy(&this->floodvars, &that->floodvars, sizeof(struct floodvars));
	memcpy(&this->banvars, &that->banvars, sizeof(struct banvars));
	/* copy permbans */
	for(ms = that->permbans; ms != NULL; ms = ms->next) {
		if(addnewpermban(this, ms->mask) == NULL) {
			delchannel(this);
			return 0;
		}
	}
	/* copy nickbks */
	for(ms = that->nickbks; ms != NULL; ms = ms->next) {
		if(addnewnickbk(this, ms->mask, ms->optstring) == NULL) {
			delchannel(this);
			return 0;
		}
	}
	/* copy wordbks */
	for(ms = that->wordbks; ms != NULL; ms = ms->next) {
		if(addnewwordbk(this, ms->mask) == NULL) {
			delchannel(this);
			return 0;
		}
	}
	/* copy users, their masks and optionally passwords */
	for(user = that->users; user != NULL; user = user->next) {
		if(addnewuser(this, user->handle, user->options) == NULL) {
			delchannel(this);
			return 0;
		}
		for(ms = user->firstmask; ms != NULL; ms = ms->next) {
			if(addnewusermask(this, user->handle, ms->mask) == NULL) {
				delchannel(this);
				return 0;
			}
		}
		if(user->password != NULL) {
                        if(!setpassword(this, user->handle, user->password)) {
				delchannel(this);
				return 0;
			}
		}
	}
	/* copy topics */
	for(topic = that->topics; topic != NULL; topic = topic->next) {
		if(addnewtopic(this, "clonechannel()", topic->topic) == NULL) {
			delchannel(this);
			return 0;
		}
	}
	/* copy adverts */
	for(advert = that->adverts; advert != NULL; advert = advert->next) {
		if(addnewadvert(this, advert->to, advert->message) == NULL) {
			delchannel(this);
			return 0;
		}
	}
	return 1;
}

int isfriend(struct channel *this, char *nick, char *userhost)
{
        struct chanuser *cu;
	struct botuser *user;
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "isfriend(\"%s\", \"%s\", \"%s\")\n", this->name, nick, userhost);
#endif
	/* first check user records (it's possible the user has logoned
           but isn't on channel yet */
	if((user = hasuserflags(this, userhost, "f")) != NULL) {
		if(index(user->options, (int)'d') == NULL) {
			return 1;
		}
	}
	/* then check if user is logoned and has "f" flag */
	if(hasuserflags(this, userhost, "df") != NULL && isvalidlogon(this, userhost)) {
		return 1;
	}
	/* then check the channel info */
        for(cu = this->nicks; cu != NULL; cu = cu->next) {
		if(!strcasecmp(cu->nick, nick)) {
			return cu->friend;
		}
	}
	return 0;
}

int isvalidchannel(char *name)
{
#ifdef DEBUG
	oer_debug(OER_DEBUG_FLOOD, "isvalidchannel(\"%s\")\n", name);
#endif
	if(strlen(name) > 200) {
		return 0;
	}
	if(name[0] != '#' && name[0] != '&') {
		return 0;
	}
	if(index(name, ' ') != NULL) {
		return 0;
	}
	if(index(name, 7) != NULL) {
		return 0;
	}
	if(index(name, ',') != NULL) {
		return 0;
	}
	return 1;
}
