/* Conversion routines for SirvNET databases (any version), Auspice
 * databases (versions 2.5-2.7), and Bolivia databases (version 1.2.0).
 *
 * IRC Services is copyright (c) 1996-2009 Andrew Church.
 *     E-mail: <achurch@achurch.org>
 * Parts written by Andrew Kempe and others.
 * This program is free but copyrighted software; see the file COPYING for
 * details.
 */

#include "convert-db.h"

#define TYPE_SIRV	0
#define TYPE_AUSPICE	1
#define TYPE_BOLIVIA	2

#if NICKMAX < 32
# error NICKMAX too small (must be >=32)
#elif CHANMAX < 64
# error CHANMAX too small (must be >=64)
#elif PASSMAX < 32
# error PASSMAX too small (must be >=32)
#endif

/*************************************************************************/

static void sirv_load_nick(const char *sourcedir, int type)
{
    char *s;
    dbFILE *f;
    long i, j;
    int c;
    NickInfo *ni;
    NickGroupInfo *ngi;
    struct oldni_ {
	struct oldni_ *next, *prev;
	char nick[32];
	char pass[32];
	char *usermask;
	char *realname;
	time_t reg;
	time_t seen;
	long naccess;
	char **access;
	long flags;
	time_t idstamp;
	unsigned short memomax;
	unsigned short channelcount;
	char *url;
	char *email;
	char *forward;
	char *hold;
	char *mark;
	char *forbid;	/* Bolivia calls it "banish" instead */
    } oldni;
    struct {
	int news;
	char *regemail;
	long icq;
	long auth;	/* 2.9 only */
	long resv[2];
    } oldni_sirv;
    struct {
	int news;
	char *uin;
	char *age;
	char *info;
	char *sex;
	char *mlock;
	char *last_quit;
	long eflags;
	long ajoincount;
	char **autojoin;
	long comline;
	char **comment;
	long noteline;
	char **note;
    } oldni_auspice;

    if (type == TYPE_SIRV) {
	f = open_db_ver(sourcedir, "nick.db", 5, 8, NULL);
    } else if (type == TYPE_AUSPICE) {
	f = open_db_ver(sourcedir, "nick.db", 6, 6, NULL);
    } else if (type == TYPE_BOLIVIA) {
	f = open_db_ver(sourcedir, "nick.db", 5, 5, NULL);
    } else {
	fprintf(stderr, "BUG: invalid type %d to sirv_load_nick()\n", type);
	exit(1);
    }
    for (i = 33; i < 256; i++) {
	while ((c = getc_db(f)) == 1) {
	    SAFE(read_variable(oldni, f));
	    if (type == TYPE_AUSPICE) {
		SAFE(read_variable(oldni_auspice, f));
	    } else if (type == TYPE_BOLIVIA) {
		/* Bolivia should have a long resv[6]; at the end, but a
		 * missing comment terminator in services.h cuts it out */
		/*SAFE(read_variable(oldni_bolivia, f));*/
	    } else {
		SAFE(read_variable(oldni_sirv, f));
	    }
	    if (oldni.url)
		SAFE(read_string(&oldni.url, f));
	    if (oldni.email)
		SAFE(read_string(&oldni.email, f));
	    if (type == TYPE_AUSPICE) {
		if (oldni_auspice.uin)
		    SAFE(read_string(&oldni_auspice.uin, f));
		if (oldni_auspice.age)
		    SAFE(read_string(&oldni_auspice.age, f));
	    }
	    if (oldni.forward)
		SAFE(read_string(&oldni.forward, f));
	    if (oldni.hold)
		SAFE(read_string(&oldni.hold, f));
	    if (oldni.mark)
		SAFE(read_string(&oldni.mark, f));
	    if (oldni.forbid)
		SAFE(read_string(&oldni.forbid, f));
	    if (type == TYPE_AUSPICE) {
		if (oldni_auspice.info)
		    SAFE(read_string(&oldni_auspice.info, f));
		if (oldni_auspice.sex)
		    SAFE(read_string(&oldni_auspice.sex, f));
		if (oldni_auspice.mlock)
		    SAFE(read_string(&oldni_auspice.mlock, f));
		if (oldni_auspice.last_quit)
		    SAFE(read_string(&oldni_auspice.last_quit, f));
	    } else if (type == TYPE_SIRV) {
		if (oldni_sirv.regemail)
		    SAFE(read_string(&oldni_sirv.regemail, f));
	    }
	    if (type != TYPE_AUSPICE || oldni.usermask)
		SAFE(read_string(&oldni.usermask, f));
	    if (type != TYPE_AUSPICE || oldni.realname)
		SAFE(read_string(&oldni.realname, f));
	    ni = makenick(oldni.nick, &ngi);
	    ni->last_usermask = oldni.usermask;
	    ni->last_realname = oldni.realname;
	    if (type == TYPE_AUSPICE)
		ni->last_quit = oldni_auspice.last_quit;
	    ni->time_registered = oldni.reg;
	    ni->last_seen = oldni.seen;
	    memcpy(ngi->pass, oldni.pass, sizeof(oldni.pass));
	    ngi->url = oldni.url;
	    ngi->email = oldni.email;
	    ngi->access_count = oldni.naccess;
	    ngi->memos.memomax = oldni.memomax;
	    ngi->memos.memos = NULL;
	    if (oldni.flags & 0x00000001)
		ngi->flags |= NF_KILLPROTECT;
	    if (oldni.flags & 0x00000002)
		ngi->flags |= NF_SECURE;
	    if (oldni.flags & 0x00000004) {
		ni->status |= NS_VERBOTEN;
		del_nickgroupinfo(ngi);
		ni->nickgroup = 0;
		/* Just in case--to differentiate from linked nicks */
		ni->last_usermask = NULL;
	    }
	    if (oldni.flags & 0x00000008) {
		static int warned = 0;
		if (!warned) {
		    fprintf(stderr,
			    "Warning: passwords are encrypted; be sure to"
			    " be sure to enable the MD5\n"
			    "         encryption module in your Services"
			    " configuration.\n");
		    warned = 1;
		}
	    }
	    if (oldni.flags & 0x00000010)
		ngi->flags |= NF_MEMO_SIGNON;
	    if (oldni.flags & 0x00000020)
		ngi->flags |= NF_MEMO_RECEIVE;
	    if (oldni.flags & 0x00000040)
		ngi->flags |= NF_PRIVATE;
	    if (oldni.flags & 0x00000080)
		ngi->flags |= NF_HIDE_EMAIL;
	    if (oldni.flags & 0x00000200)
		ni->status |= NS_NOEXPIRE;
	    if (oldni.flags & 0x00001000)
		ngi->memos.memomax = 0;
	    if (oldni.flags & 0x00002000)
		ngi->flags |= NF_HIDE_MASK;
	    if (type == TYPE_BOLIVIA && (oldni.flags & 0x00020000)) {
		ngi->suspendinfo = new_suspendinfo(
			"<unknown>",
			"Unknown (imported from Bolivia IRC Services)",
			0
		);
	    }
	    if (type == TYPE_AUSPICE && (oldni.flags & 0x00800000)) {
		/* Linked ("slave") nick; parent is in last_usermask */
		ni->nickgroup = 0;
	    }
	    ngi->access = scalloc(ngi->access_count, sizeof(char *));
	    for (j = 0; j < ngi->access_count; j++)
		SAFE(read_string(&ngi->access[j], f));
	    if (type == TYPE_AUSPICE) {
		if (oldni_auspice.ajoincount) {
		    ngi->ajoin_count = oldni_auspice.ajoincount;
		    ngi->ajoin =
			smalloc(sizeof(char *) * oldni_auspice.ajoincount);
		    for (j = 0; j < oldni_auspice.ajoincount; j++)
			SAFE(read_string(&ngi->ajoin[j], f));
		}
		for (j = 0; j < oldni_auspice.comline; j++)
		    SAFE(read_string(&s, f));
		if (oldni_auspice.noteline) {
		    ngi->memos.memos_count = oldni_auspice.noteline;
		    ngi->memos.memos =
			scalloc(sizeof(Memo), oldni_auspice.noteline);
		    for (j = 0; j < oldni_auspice.noteline; j++) {
			ngi->memos.memos[j].number = j+1;
			ngi->memos.memos[j].flags = 0;
			ngi->memos.memos[j].time = time(NULL);
			strscpy(ngi->memos.memos[j].sender, oldni.nick,
				NICKMAX);
			SAFE(read_string(&ngi->memos.memos[j].text, f));
		    }
		}
	    }
	} /* while ((c = getc_db(f)) == 1) */
	if (c != 0) {
	    fprintf(stderr, "%s is corrupt, aborting.\n", f->filename);
	    exit(1);
	}
    } /* for (i = 33..256) */
    close_db(f);

    /* Resolve links */
    for (ni = first_nickinfo(); ni; ni = next_nickinfo()) {
	if (!ni->nickgroup && ni->last_usermask) {
	    NickInfo *ni2;
	    /* Find parent nick */
	    ni2 = get_nickinfo(ni->last_usermask);
	    /* Set nickgroup, or delete nick if an error occurred */
	    if (ni2 == ni) {
		fprintf(stderr,
			"Warning: dropping nick %s with circular link\n",
			ni->nick);
		del_nickinfo(ni);
	    } else if (!ni2) {
		fprintf(stderr, "Warning: dropping nick %s linked to"
			" nonexistent nick %s\n", ni->nick, ni->last_usermask);
		del_nickinfo(ni);
	    } else if (ni2->status & NS_VERBOTEN) {
		/* Auspice allows links to forbidden nicks; we don't
		 * (because forbidden nicks don't have nickgroups).
		 * Just make the nick forbidden. */
		ni->status = NS_VERBOTEN;
		ni->last_usermask = NULL;
	    } else if (ni2->nickgroup == 0) {
		fprintf(stderr, "BUG: link target %s of nick %s has no"
			" no nickgroup, dropping both nicks\n",
			ni2->nick, ni->nick);
		del_nickinfo(ni);
		del_nickinfo(ni2);
	    } else {
		ni->last_usermask = ni2->last_usermask;
		ni->nickgroup = ni2->nickgroup;
		ngi = get_nickgroupinfo(ni->nickgroup);
		if (ngi) {
		    ARRAY_EXTEND(ngi->nicks);
		    strscpy(ngi->nicks[ngi->nicks_count-1], ni->nick, NICKMAX);
		} else if (ni->nickgroup != 0) {
		    fprintf(stderr, "Warning: Nick group %d for nick %s not"
			    " found -- program bug?  Output may be corrupt.",
			    ni->nickgroup, ni->nick);
		}
	    }
	}
    }
}

/*************************************************************************/

struct flagmode {
    short flag;
    char mode;
};
static struct flagmode sirv_cmodes[] = {
    { 0x0001, 'i' },
    { 0x0002, 'm' },
    { 0x0004, 'n' },
    { 0x0008, 'p' },
    { 0x0010, 's' },
    { 0x0020, 't' },
    { 0x0040, 'k' },
    { 0x0080, 'l' },
    { 0x0100, 0   }, /* CMODE_r, never set in mlock */
    { 0x0200, 'J' },
    { 0x0400, 'R' },
    { 0x0800, 'c' },
    { 0x1000, 'O' },
    { 0, 0 }
};
static struct flagmode bolivia_cmodes[] = {
    { 0x0001, 'i' },
    { 0x0002, 'm' },
    { 0x0004, 'n' },
    { 0x0008, 'p' },
    { 0x0010, 's' },
    { 0x0020, 't' },
    { 0x0040, 'k' },
    { 0x0080, 'l' },
    { 0x0100, 0   }, /* CMODE_r, never set in mlock */
    { 0x0200, 'R' },
    { 0x0400, 'c' },
    { 0x0800, 'M' },
    { 0, 0 }
};

static void sirv_load_chan(const char *sourcedir, int type)
{
    char *s;
    dbFILE *f;
    int32 ver;
    long i, j;
    int c;
    int16 tmp16;
    int tmpint;
    ChannelInfo *ci;
    NickInfo *ni;
    char **adders;  /* for remembering which access entries had an adder */
    int has_adders; /* is this a database with access entry adder info? */
    struct access_ {
	int16 level;
	short in_use;
	char *name;
    } access;
    struct akick_ {
	short is_nick;
	short pad;
	char *name;
	char *reason;
    } akick;
    struct oldci_ {
	struct oldci_ *next, *prev;
	char name[64];
	char founder[32];
	char pass[32];
	char *desc;
	time_t reg;
	time_t used;
	long naccess;
	struct access_ *access;
	long nakick;
	struct akick_ *akick;
    } oldci;
    /* Sirv/Bolivia use shorts for mlock_{on,off}; Auspice uses char[64] */
    short mlock_on_sirv, mlock_off_sirv;
    char mlock_on_auspice[64], mlock_off_auspice[64];
    struct {
	long mlock_limit;
	char *mlock_key;
	char *topic;
	char topic_setter[32];
	time_t topic_time;
	long flags;
	short *levels;
	char *url;
	char *email;
	char *welcome;
	char *hold;
	char *mark;
	char *freeze;
	char *forbid;
	int topic_allow;
    } oldci2;
    struct {
	long auth;	/* 2.9 only */
	long resv[4];
    } oldci_sirv;
    struct {
	char *successor;
	char *mlock_link;
	char *mlock_flood;
	char *bot;
	long botflag;
	long newsline;
	char **news;
	long badwline;
	char **badwords;
	long resv[3];
    } oldci_auspice;
    struct {
	char *successor;
	char *mlock_link;
	char *mlock_flood;
	long newsline;
	char **news;
	long badwline;
	char **badwords;
	char *markreason;
	char *freezereason;
	char *holdreason;
	char *lastgetpass;
	char *bot;
	long botflag;
	int ttb;
	int capsmin, capspercent;
	int floodlines, floodsecs;
	int repeattimes;
    } oldci_auspice8;
    struct {
	long resv[5];
    } oldci_bolivia;

#ifdef CLEAN_COMPILE
    /* To avoid "possibly uninitialized" warnings: */
    adders = NULL;
#endif

    if (type == TYPE_SIRV) {
	f = open_db_ver(sourcedir, "chan.db", 5, 8, &ver);
    } else if (type == TYPE_AUSPICE) {
	f = open_db_ver(sourcedir, "chan.db", 7, 8, &ver);
    } else if (type == TYPE_BOLIVIA) {
	f = open_db_ver(sourcedir, "chan.db", 5, 5, &ver);
    } else {
	fprintf(stderr, "BUG: invalid type %d to sirv_load_chan()\n", type);
	exit(1);
    }
    has_adders = (type == TYPE_AUSPICE || ver >= 8);
    for (i = 33; i < 256; i++) {
	while ((c = getc_db(f)) == 1) {
	    SAFE(read_variable(oldci, f));
	    if (type == TYPE_AUSPICE) {
		SAFE(read_buffer(mlock_on_auspice, f));
		SAFE(read_buffer(mlock_off_auspice, f));
		/* An apparent bug in (at least) Auspice 2.7 causes CRs to
		 * sometimes appear in mode locks. */
		s = mlock_on_auspice;
		while (*s) {
		    if (*s == 13)
			memmove(s, s+1, strlen(s+1)+1);
		    else
			s++;
		}
		s = mlock_off_auspice;
		while (*s) {
		    if (*s == 13)
			memmove(s, s+1, strlen(s+1)+1);
		    else
			s++;
		}
	    } else {
		SAFE(read_variable(mlock_on_sirv, f));
		SAFE(read_variable(mlock_off_sirv, f));
	    }
	    SAFE(read_variable(oldci2, f));
	    if (type == TYPE_AUSPICE) {
		if (ver == 8) {
		    SAFE(read_variable(oldci_auspice8, f));
		    oldci_auspice.successor   = oldci_auspice8.successor;
		    oldci_auspice.mlock_link  = oldci_auspice8.mlock_link;
		    oldci_auspice.mlock_flood = oldci_auspice8.mlock_flood;
		    oldci_auspice.bot         = oldci_auspice8.bot;
		    oldci_auspice.botflag     = oldci_auspice8.botflag;
		    oldci_auspice.newsline    = oldci_auspice8.newsline;
		    oldci_auspice.news        = oldci_auspice8.news;
		    oldci_auspice.badwline    = oldci_auspice8.badwline;
		    oldci_auspice.badwords    = oldci_auspice8.badwords;
		} else {
		    SAFE(read_variable(oldci_auspice, f));
		}
	    } else if (type == TYPE_BOLIVIA) {
		SAFE(read_variable(oldci_bolivia, f));
	    } else {
		SAFE(read_variable(oldci_sirv, f));
	    }
	    SAFE(read_string(&oldci.desc, f));
	    if (oldci2.url)
		SAFE(read_string(&oldci2.url, f));
	    if (oldci2.email)
		SAFE(read_string(&oldci2.email, f));
	    if (oldci2.mlock_key)
		SAFE(read_string(&oldci2.mlock_key, f));
	    if (oldci2.topic)
		SAFE(read_string(&oldci2.topic, f));
	    if (oldci2.welcome)
		SAFE(read_string(&oldci2.welcome, f));
	    if (oldci2.hold)
		SAFE(read_string(&oldci2.hold, f));
	    if (oldci2.mark)
		SAFE(read_string(&oldci2.mark, f));
	    if (oldci2.freeze)
		SAFE(read_string(&oldci2.freeze, f));
	    if (oldci2.forbid)
		SAFE(read_string(&oldci2.forbid, f));
	    if (type == TYPE_AUSPICE) {
		if (oldci_auspice.successor)
		    SAFE(read_string(&oldci_auspice.successor, f));
		if (oldci_auspice.mlock_link)
		    SAFE(read_string(&oldci_auspice.mlock_link, f));
		if (oldci_auspice.mlock_flood)
		    SAFE(read_string(&oldci_auspice.mlock_flood, f));
		if (oldci_auspice.bot)
		    SAFE(read_string(&oldci_auspice.bot, f));
		if (ver >= 8 && oldci_auspice8.markreason)
		    SAFE(read_string(&oldci_auspice8.markreason, f));
		if (ver >= 8 && oldci_auspice8.freezereason)
		    SAFE(read_string(&oldci_auspice8.freezereason, f));
		if (ver >= 8 && oldci_auspice8.holdreason)
		    SAFE(read_string(&oldci_auspice8.holdreason, f));
		if (ver >= 8 && oldci_auspice8.lastgetpass)
		    SAFE(read_string(&oldci_auspice8.lastgetpass, f));
	    }
	    ci = makechan(oldci.name);
	    if (*oldci.founder) {
		ni = get_nickinfo(oldci.founder);
		if (!ni) {
		    fprintf(stderr,
			    "Warning: Founder %s for channel %s not found\n",
			    oldci.founder, oldci.name);
		} else if (!ni->nickgroup) {
		    fprintf(stderr, "Warning: Founder %s for channel %s is a"
			    " forbidden nick\n", oldci.founder, oldci.name);
		} else {
		    ci->founder = ni->nickgroup;
		}
	    }
	    if (type == TYPE_AUSPICE && oldci_auspice.successor) {
		ni = get_nickinfo(oldci_auspice.successor);
		if (!ni) {
		    fprintf(stderr,
			    "Warning: Successor %s for channel %s not found,"
			    " clearing\n", oldci_auspice.successor,
			    oldci.name);
		} else if (!ni->nickgroup) {
		    fprintf(stderr, "Warning: Successor %s for channel %s is a"
			    " forbidden nick, clearing\n",
			    oldci_auspice.successor, oldci.name);
		} else if (ni->nickgroup == ci->founder) {
		    fprintf(stderr, "Warning: Successor %s for channel %s is"
			    " the same as the founder, clearing\n",
			    oldci_auspice.successor, oldci.name);
		} else {
		    ci->successor = ni->nickgroup;
		}
	    }
	    strscpy(ci->founderpass, oldci.pass, sizeof(oldci.pass));
	    ci->desc = oldci.desc;
	    ci->url = oldci2.url;
	    ci->email = oldci2.email;
	    ci->time_registered = oldci.reg;
	    ci->last_used = oldci.used;
	    ci->access_count = oldci.naccess;
	    ci->akick_count = oldci.nakick;
	    ci->mlock_limit = oldci2.mlock_limit;
	    ci->mlock_key = oldci2.mlock_key;
	    if (type == TYPE_AUSPICE) {
		ci->mlock_link = oldci_auspice.mlock_link;
		ci->mlock_flood = oldci_auspice.mlock_flood;
	    }
	    ci->last_topic = oldci2.topic;
	    strscpy(ci->last_topic_setter, oldci2.topic_setter, NICKMAX);
	    ci->last_topic_time = oldci2.topic_time;
	    ci->memos.memomax = MEMOMAX_DEFAULT;
	    if (oldci2.flags & 0x00000001)
		ci->flags |= CI_KEEPTOPIC;
	    if (oldci2.flags & 0x00000002)
		ci->flags |= CI_SECUREOPS;
	    if (oldci2.flags & 0x00000004) {
		if (type == TYPE_BOLIVIA)
		    ci->flags |= CI_OPNOTICE;
		else
		    ci->flags |= CI_PRIVATE;
	    }
	    if (oldci2.flags & 0x00000008)
		ci->flags |= CI_TOPICLOCK;
	    if (oldci2.flags & 0x00000010)
		ci->flags |= CI_RESTRICTED;
	    if (oldci2.flags & 0x00000020)
		ci->flags |= CI_LEAVEOPS;
	    if (oldci2.flags & 0x00000040)
		ci->flags |= CI_SECURE;
	    if (oldci2.flags & 0x00000080)
		ci->flags |= CI_VERBOTEN;
	    if (oldci2.flags & 0x00000200)
		ci->flags |= CI_NOEXPIRE;
	    if (!(oldci2.flags & 0x01000000))
		ci->flags |= CI_SECURE;
	    if (type == TYPE_SIRV && (oldci2.flags & 0x04000000))
		ci->flags |= CI_ENFORCE;
	    if (type != TYPE_BOLIVIA && (oldci2.flags & 0x08000000)) {
		ci->suspendinfo = new_suspendinfo("<unknown>", NULL, 0);
		if (type == TYPE_AUSPICE) {
		    ci->suspendinfo->reason =
			(char *)"Unknown (imported from Auspice Services)";
		} else {
		    ci->suspendinfo->reason =
			(char *)"Unknown (imported from SirvNET IRC Services)";
		}
	    }

	    ci->mlock_on = scalloc(64, 1);
	    ci->mlock_off = scalloc(64, 1);
	    if (type == TYPE_AUSPICE) {
		strscpy(ci->mlock_on, mlock_on_auspice, 64);
		strscpy(ci->mlock_off, mlock_off_auspice, 64);
	    } else {
		char *on = ci->mlock_on;
		char *off = ci->mlock_off;
		struct flagmode *cmodes;
		if (type == TYPE_BOLIVIA)
		    cmodes = bolivia_cmodes;
		else
		    cmodes = sirv_cmodes;
		for (j = 0; cmodes[j].flag != 0; j++) {
		    if (mlock_on_sirv & cmodes[j].flag)
			*on++ = cmodes[j].mode;
		    if (mlock_off_sirv & cmodes[j].flag)
			*off++ = cmodes[j].mode;
		}
		*on = 0;
		*off = 0;
	    }

	    ci->access = scalloc(sizeof(ChanAccess), oldci.naccess);
	    if (has_adders)
		adders = scalloc(sizeof(char *), oldci.naccess);
	    for (j = 0; j < oldci.naccess; j++) {
		SAFE(read_variable(access, f));
		if (has_adders)
		    SAFE(read_variable(adders[j], f));
		ci->access[j].nickgroup = access.in_use;
		ci->access[j].level = access.level;
		if (type == TYPE_AUSPICE && ver >= 8) {
		    SAFE(read_variable(tmpint, f));  /* accflag */
		    if (tmpint)  /* discard TIMEOP entries */
			ci->access[j].nickgroup = 0;
		}
		ci->access[j].level = convert_acclev(ci->access[j].level);
	    }
	    for (j = 0; j < oldci.naccess; j++) {
		SAFE(read_string(&s, f));
		if (has_adders && adders[j])
		    SAFE(read_string(&adders[j], f));
		if (!s) {
		    ci->access[j].nickgroup = 0;
		    continue;
		}
		if (ci->access[j].nickgroup) {
		    ni = get_nickinfo(s);
		    ci->access[j].nickgroup = ni ? ni->nickgroup : 0;
		}
	    }
	    ci->akick = scalloc(sizeof(AutoKick), oldci.nakick);
	    for (j = 0; j < oldci.nakick; j++) {
		SAFE(read_variable(akick, f));
		/* `lastused' field temporarily used to hold `is_nick' */
		ci->akick[j].lastused = akick.is_nick;
		ci->akick[j].reason = akick.reason;
		strscpy(ci->akick[j].who, "<unknown>", NICKMAX);
		ci->akick[j].set = time(NULL);
	    }
	    for (j = 0; j < oldci.nakick; j++) {
		SAFE(read_string(&ci->akick[j].mask, f));
		if (ci->akick[j].lastused) {  /* was `is_nick' */
		    char *s = smalloc(strlen(ci->akick[j].mask)+5);
		    sprintf(s, "%s!*@*", ci->akick[j].mask);
		    free(ci->akick[j].mask);
		    ci->akick[j].mask = s;
		}
		ci->akick[j].lastused = 0;
		if (ci->akick[j].reason) {
		    SAFE(read_string(&ci->akick[j].reason, f));
		    if (!ci->akick[j].mask && ci->akick[j].reason)
			ci->akick[j].reason = NULL;
		}
	    }

	    SAFE(read_int16(&tmp16, f));  /* levels */
	    for (j = tmp16; j > 0; j--)
		SAFE(read_int16(&tmp16, f));

	    if (type == TYPE_AUSPICE) {
		for (j = 0; j < oldci_auspice.newsline; j++)
		    SAFE(read_string(&s, f));
		for (j = 0; j < oldci_auspice.badwline; j++)
		    SAFE(read_string(&s, f));
	    }

	    /* Only insert in list if founder is found or channel is
	     * forbidden */
	    if (ci->founder || (ci->flags & CI_VERBOTEN))
		add_channelinfo(ci);
	} /* while more entries */
	if (c != 0) {
	    fprintf(stderr, "%s is corrupt, aborting.\n", f->filename);
	    exit(1);
	}
    } /* for 33..256 */

    close_db(f);
}

/*************************************************************************/

static void sirv_load_memo(const char *sourcedir, int type)
{
    char *s;
    dbFILE *f;
    struct memo_ {
	char sender[32];
	long number;
	time_t time;
	char *text;
	char *chan;
	short flags;
	short pad;
	long resv[3];
    } memo;
    struct memolist_ {
	struct memolist_ *next, *prev;
	char nick[32];
	long n_memos;
	Memo *memos;
	long resv[4];
    } memolist;
    NickGroupInfo *ngi;
    Memo *m = NULL;
    long i, j, chancount;
    int c;

    if (type == TYPE_SIRV) {
	f = open_db_ver(sourcedir, "memo.db", 5, 8, NULL);
    } else if (type == TYPE_AUSPICE) {
	f = open_db_ver(sourcedir, "memo.db", 6, 6, NULL);
    } else if (type == TYPE_BOLIVIA) {
	f = open_db_ver(sourcedir, "memo.db", 5, 5, NULL);
    } else {
	fprintf(stderr, "BUG: invalid type %d to sirv_load_memo()\n", type);
	exit(1);
    }
    for (i = 33; i < 256; i++) {
	while ((c = getc_db(f)) == 1) {
	    SAFE(read_variable(memolist, f));
	    ngi = get_nickgroupinfo_by_nick(memolist.nick);
	    if (ngi) {
		ngi->memos.memos_count = memolist.n_memos;
		m = scalloc(sizeof(*m), ngi->memos.memos_count);
		ngi->memos.memos = m;
	    }
	    chancount = 0;
	    for (j = 0; j < memolist.n_memos; j++) {
		SAFE(read_variable(memo, f));
		if (ngi) {
		    m[j].number = memo.number;
		    if (memo.flags & 0x0001)
			m[j].flags |= MF_UNREAD;
		    if (type == TYPE_AUSPICE && !(memo.flags & 0x0008))
			m[j].flags |= MF_EXPIREOK;
		    m[j].time = memo.time;
		    strscpy(m[j].sender, memo.sender, NICKMAX);
		    if (memo.chan)
			m[j].flags |= 0x8000;
		} else if (memo.chan) {
		    chancount++;
		}
	    }
	    for (j = 0; j < memolist.n_memos; j++) {
		SAFE(read_string(&s, f));
		if (ngi) {
		    m[j].text = s;
		    if (m[j].flags & 0x8000) {
			m[j].flags &= ~0x8000;
			SAFE(read_string(&s, f));
		    }
		}
	    }
	    for (j = 0; j < chancount; j++)
		SAFE(read_string(&s, f));
	}
	if (c != 0) {
	    fprintf(stderr, "%s is corrupt, aborting.\n", f->filename);
	    exit(1);
	}
    }
    close_db(f);
}

/*************************************************************************/

static void sirv_load_os_sop(const char *sourcedir)
{
    char *s;
    dbFILE *f;
    int16 n, i;

    f = open_db_ver(sourcedir, "os_sop.db", 5, 8, NULL);
    SAFE(read_int16(&n, f));
    for (i = 0; i < n; i++) {
	SAFE(read_string(&s, f));
	set_os_priv(s, NP_SERVADMIN);
    }
    close_db(f);
}

/*************************************************************************/

static void sirv_load_os_sa(const char *sourcedir)
{
    char *s;
    dbFILE *f;
    int16 n, i;

    f = open_db_ver(sourcedir, "os_sa.db", 5, 8, NULL);
    SAFE(read_int16(&n, f));
    for (i = 0; i < n; i++) {
	SAFE(read_string(&s, f));
	set_os_priv(s, NP_SERVADMIN);
    }
    close_db(f);
}

/*************************************************************************/

static void auspice_load_admin(const char *sourcedir)
{
    char *s;
    dbFILE *f;
    long i, j;
    int c;
    struct admin_ {
	struct admin_ *next, *prev;
	char *nick;
	char *host;	/* fake host to set */
	char *who;	/* added by who */
	char *server;
	char **mark;
	long markline;
	long adflags;
	long flags;
	time_t added;
    } admin;

    f = open_db_ver(sourcedir, "admin.db", 1, 1, NULL);
    for (i = 33; i < 256; i++) {
	while ((c = getc_db(f)) == 1) {
	    SAFE(read_variable(admin, f));
	    if (admin.nick)
		SAFE(read_string(&admin.nick, f));
	    if (admin.host)
		SAFE(read_string(&admin.host, f));
	    if (admin.who)
		SAFE(read_string(&admin.who, f));
	    if (admin.server)
		SAFE(read_string(&admin.server, f));
	    for (j = 0; j < admin.markline; j++)
		SAFE(read_string(&s, f));
	    if (admin.adflags & 4)
		set_os_priv(admin.nick, NP_SERVADMIN);
	    else if (admin.adflags & 2)
		set_os_priv(admin.nick, NP_SERVOPER);
	}
	if (c != 0) {
	    fprintf(stderr, "%s is corrupt, aborting.\n", f->filename);
	    exit(1);
	}
    }
    close_db(f);
}

/*************************************************************************/

static void sirv_load_akill(const char *sourcedir, int type)
{
    dbFILE *f;
    int16 i, n;
    MaskData *md;
    struct akill_ {
	char *mask;
	char *reason;
	char who[32];
	time_t time;
	time_t expires;
	long resv[4];
    } akill;

    if (type == TYPE_SIRV) {
	f = open_db_ver(sourcedir, "akill.db", 5, 8, NULL);
    } else if (type == TYPE_AUSPICE) {
	f = open_db_ver(sourcedir, "akill.db", 6, 6, NULL);
    } else if (type == TYPE_BOLIVIA) {
	f = open_db_ver(sourcedir, "akill.db", 5, 5, NULL);
    } else {
	fprintf(stderr, "BUG: invalid type %d to sirv_load_akill()\n", type);
	exit(1);
    }
    SAFE(read_int16(&n, f));
    md = scalloc(sizeof(*md), n);
    for (i = 0; i < n; i++) {
	SAFE(read_variable(akill, f));
	strscpy(md[i].who, akill.who, NICKMAX);
	md[i].time = akill.time;
	md[i].expires = akill.expires;
    }
    for (i = 0; i < n; i++) {
	SAFE(read_string(&md[i].mask, f));
	SAFE(read_string(&md[i].reason, f));
	add_maskdata(MD_AKILL, &md[i]);
    }
    close_db(f);
}

/*************************************************************************/

static void sirv_load_trigger(const char *sourcedir, int type)
{
    dbFILE *f;
    int16 i, n;
    MaskData *md;
    struct trigger_ {
	char *mask;
	long tvalue;
	char who[32];
	long resv[4];
    } trigger;

    if (type == TYPE_SIRV) {
	f = open_db_ver(sourcedir, "trigger.db", 5, 8, NULL);
    } else if (type == TYPE_AUSPICE) {
	f = open_db_ver(sourcedir, "trigger.db", 6, 6, NULL);
    } else if (type == TYPE_BOLIVIA) {
	f = open_db_ver(sourcedir, "trigger.db", 5, 5, NULL);
    } else {
	fprintf(stderr, "BUG: invalid type %d to sirv_load_trigger()\n", type);
	exit(1);
    }
    SAFE(read_int16(&n, f));
    md = scalloc(sizeof(*md), n);
    for (i = 0; i < n; i++) {
	SAFE(read_variable(trigger, f));
	if (trigger.tvalue > SESSION_MAXLIMIT)
	    trigger.tvalue = SESSION_MAXLIMIT;
	md[i].limit = trigger.tvalue;
	strscpy(md[i].who, trigger.who, NICKMAX);
	md[i].reason = (char *)"(unknown)";
	md[i].time = time(NULL);
	md[i].expires = 0;
    }
    for (i = 0; i < n; i++) {
	SAFE(read_string(&md[i].mask, f));
	add_maskdata(MD_EXCEPTION, &md[i]);
    }
    close_db(f);
}

/*************************************************************************/

static void bolivia_load_gline(const char *sourcedir)
{
    dbFILE *f;
    int16 i, n;
    MaskData *md;
    char *s;

    f = open_db_ver(sourcedir, "gline.db", 5, 5, NULL);
    SAFE(read_int16(&n, f));
    md = scalloc(sizeof(*md), n);
    for (i = 0; i < n; i++) {
	SAFE(read_string(&md[i].mask, f));
	SAFE(read_string(&md[i].reason, f));
	SAFE(read_string(&s, f));
	if (!s)
	    s = (char *)"<unknown>";
	strscpy(md[i].who, s, NICKMAX);
	add_maskdata(MD_SGLINE, &md[i]);
    }
    close_db(f);
}

/*************************************************************************/

static void bolivia_load_qline(const char *sourcedir)
{
    dbFILE *f;
    int16 i, n;
    MaskData *md;

    f = open_db_ver(sourcedir, "qline.db", 5, 5, NULL);
    SAFE(read_int16(&n, f));
    md = scalloc(sizeof(*md), n);
    for (i = 0; i < n; i++) {
	SAFE(read_string(&md[i].mask, f));
	md[i].reason = (char *)"Unknown (imported from Bolivia IRC Services)";
	strscpy(md[i].who, "<unknown>", NICKMAX);
	add_maskdata(MD_SQLINE, &md[i]);
    }
    close_db(f);
}

/*************************************************************************/

static void bolivia_load_zline(const char *sourcedir)
{
    dbFILE *f;
    int16 i, n;
    MaskData *md;
    char *s;

    f = open_db_ver(sourcedir, "zline.db", 5, 5, NULL);
    SAFE(read_int16(&n, f));
    md = scalloc(sizeof(*md), n);
    for (i = 0; i < n; i++) {
	SAFE(read_string(&md[i].mask, f));
	SAFE(read_string(&md[i].reason, f));
	SAFE(read_string(&s, f));
	if (!s)
	    s = (char *)"<unknown>";
	strscpy(md[i].who, s, NICKMAX);
	add_maskdata(MD_SZLINE, &md[i]);
    }
    close_db(f);
}

/*************************************************************************/
/*************************************************************************/

static const char *check_auspice(const char *sourcedir)
{
    FILE *f;
    char buf[PATH_MAX+1];

    snprintf(buf, sizeof(buf), "%s/admin.db", sourcedir);
    if (access(buf, R_OK) == 0) {
	snprintf(buf, sizeof(buf), "%s/chan.db", sourcedir);
	f = fopen(buf, "rb");
	if (f) {
	    int ver;
	    ver  = fgetc(f)<<24;
	    ver |= fgetc(f)<<16;
	    ver |= fgetc(f)<<8;
	    ver |= fgetc(f);
	    fclose(f);
	    if (ver == 7)
		return "Auspice 2.5.x";
	    else if (ver == 8)
		return "Auspice 2.6/2.7";
	}
    }
    return NULL;
}

static void load_auspice(const char *sourcedir, int verbose, int ac, char **av)
{
    if (ac > 1) {
	fprintf(stderr, "Unrecognized option %s\n", av[1]);
	usage(av[0]);
    }
    if (verbose)
	fprintf(stderr, "Loading nick.db...\n");
    sirv_load_nick(sourcedir, TYPE_AUSPICE);
    if (verbose)
	fprintf(stderr, "Loading chan.db...\n");
    sirv_load_chan(sourcedir, TYPE_AUSPICE);
    if (verbose)
	fprintf(stderr, "Loading memo.db...\n");
    sirv_load_memo(sourcedir, TYPE_AUSPICE);
    if (verbose)
	fprintf(stderr, "Loading admin.db...\n");
    auspice_load_admin(sourcedir);
    if (verbose)
	fprintf(stderr, "Loading akill.db...\n");
    sirv_load_akill(sourcedir, TYPE_AUSPICE);
    if (verbose)
	fprintf(stderr, "Loading trigger.db...\n");
    sirv_load_trigger(sourcedir, TYPE_AUSPICE);
}

/*************************************************************************/
/*************************************************************************/

static const char *check_bolivia(const char *sourcedir)
{
    char buf[PATH_MAX+1];

    snprintf(buf, sizeof(buf), "%s/jupenick.db", sourcedir);
    if (access(buf, R_OK) == 0)
	return "Bolivia";
    return NULL;
}

static void load_bolivia(const char *sourcedir, int verbose, int ac, char **av)
{
    if (ac > 1) {
	fprintf(stderr, "Unrecognized option %s\n", av[1]);
	usage(av[0]);
    }
    if (verbose)
	fprintf(stderr, "Loading nick.db...\n");
    sirv_load_nick(sourcedir, TYPE_BOLIVIA);
    if (verbose)
	fprintf(stderr, "Loading chan.db...\n");
    sirv_load_chan(sourcedir, TYPE_BOLIVIA);
    if (verbose)
	fprintf(stderr, "Loading memo.db...\n");
    sirv_load_memo(sourcedir, TYPE_BOLIVIA);
    if (verbose)
	fprintf(stderr, "Loading akill.db...\n");
    sirv_load_akill(sourcedir, TYPE_BOLIVIA);
    if (verbose)
	fprintf(stderr, "Loading trigger.db...\n");
    sirv_load_trigger(sourcedir, TYPE_BOLIVIA);
    if (verbose)
	fprintf(stderr, "Loading gline.db...\n");
    bolivia_load_gline(sourcedir);
    if (verbose)
	fprintf(stderr, "Loading qline.db...\n");
    bolivia_load_qline(sourcedir);
    if (verbose)
	fprintf(stderr, "Loading zline.db...\n");
    bolivia_load_zline(sourcedir);
}

/*************************************************************************/
/*************************************************************************/

static const char *check_sirv(const char *sourcedir)
{
    FILE *f;
    char buf[PATH_MAX+1];

    if (check_auspice(sourcedir) || check_bolivia(sourcedir))
	return NULL;
    snprintf(buf, sizeof(buf), "%s/trigger.db", sourcedir);
    f = fopen(buf, "rb");
    if (f) {
	int ver;
	ver  = fgetc(f)<<24;
	ver |= fgetc(f)<<16;
	ver |= fgetc(f)<<8;
	ver |= fgetc(f);
	fclose(f);
	if (ver == 5)
	    return "SirvNET 1.x";
	else if (ver == 6)
	    return "SirvNET 2.0.0-2.2.0";
	else if (ver == 7)
	    return "SirvNET 2.2.1-2.8.0";
	else if (ver == 8)
	    return "SirvNET 2.9.0";
    }
    return NULL;
}

static void load_sirv(const char *sourcedir, int verbose, int ac, char **av)
{
    if (ac > 1) {
	fprintf(stderr, "Unrecognized option %s\n", av[1]);
	usage(av[0]);
    }
    if (verbose)
	fprintf(stderr, "Loading nick.db...\n");
    sirv_load_nick(sourcedir, TYPE_SIRV);
    if (verbose)
	fprintf(stderr, "Loading chan.db...\n");
    sirv_load_chan(sourcedir, TYPE_SIRV);
    if (verbose)
	fprintf(stderr, "Loading memo.db...\n");
    sirv_load_memo(sourcedir, TYPE_SIRV);
    if (verbose)
	fprintf(stderr, "Loading os_sa.db...\n");
    sirv_load_os_sa(sourcedir);
    if (verbose)
	fprintf(stderr, "Loading os_sop.db...\n");
    sirv_load_os_sop(sourcedir);
    if (verbose)
	fprintf(stderr, "Loading akill.db...\n");
    sirv_load_akill(sourcedir, TYPE_SIRV);
    if (verbose)
	fprintf(stderr, "Loading trigger.db...\n");
    sirv_load_trigger(sourcedir, TYPE_SIRV);
}

/*************************************************************************/
/*************************************************************************/

DBTypeInfo dbtype_sirv = {
    "sirv",
    check_sirv,
    load_sirv
};

DBTypeInfo dbtype_auspice = {
    "auspice",
    check_auspice,
    load_auspice
};

DBTypeInfo dbtype_bolivia = {
    "bolivia",
    check_bolivia,
    load_bolivia
};

/*************************************************************************/
