/* DBTool

   Cygnus IRC Services - Copyright (c) 2001-2002 Darcy Grexton
   Contact: skold@habber.net, skold @ HabberNet

   See doc/LICENSE for licensing details.

   IRCServices 4.5.x DB Converter courtesy of Ron885
*/

#include "../inc/dbtool.h"
#include "../inc/sysconf.h"

static FILE *log_file;
static char opt[256];

/* Wrecked */
NickInfo_Wrecked *nicklists_wrecked[256];
MemoList_Wrecked *memolists_wrecked[256];
ChannelInfo_Wrecked *chanlists_wrecked[256];
SOP_Wrecked *sops_wrecked = NULL;
AutoKill_Wrecked *akill_wrecked = NULL;
Allow_Wrecked *allow_wrecked = NULL;
int nakill_wrecked = 0, akill_size_wrecked = 0;
int nsop_wrecked = 0, sop_size_wrecked = 0;
int nallow_wrecked = 0, allow_size_wrecked = 0;

/* IRCServices */
NickInfo_IRCServices45 *nicklists_ircservices45[256];
ChannelInfo_IRCServices45 *chanlists_ircservices45[256];
SOP_IRCServices45 *sops_ircservices45 = NULL;
SOP_IRCServices45 *sadmins_ircservices45 = NULL;
Akill_IRCServices45 *akill_ircservices45 = NULL;
Exception_IRCServices45 *exception_ircservices45 = NULL;
int nakill_ircservices45 = 0, akill_size_ircservices45 = 0;
int nsop_ircservices45 = 0, sop_size_ircservices45 = 0;
int nsadmin_ircservices45 = 0, sadmin_size_ircservices45 = 0;
int nexception_ircservices45 = 0, exception_size_ircservices45 = 0;

/* SirvNET */
NickInfo_Sirv *nicklists_sirv[256];
MemoList_Sirv *memolists_sirv[256];
ChannelInfo_Sirv *chanlists_sirv[256];
AutoKill_Sirv *akill_sirv = NULL;
Trigger_Sirv *trigger_sirv = NULL;
char *sadmin_sirv[32];
char *sop_sirv[32];
int nakill_sirv = 0, akill_size_sirv = 0;
int ntrigger_sirv = 0, trigger_size_sirv = 0;

/* Statistics stuff */
int nin = 0, nout = 0, lin = 0, lout = 0;
int min = 0, mout = 0, cin = 0, cout = 0;
int ain = 0, aout = 0, tin = 0, tout = 0, eout = 0;
int soin = 0, sout = 0, adrop = 0, ndrop = 0, cdrop = 0;
int mdrop = 0, notes = 0;
struct timeval start;

/* Command-line help message */
void do_help (char *prog)
{
printf(
"DBTool - Copyright (c) 2001-2002 Darcy Grexton\n\n"
"Syntax: %s options\n\n"
"-help                This message\n"
"-notes               Display conversion notes\n"
"-wrecked             Convert WreckedNet Services DBs\n"
"-ircservices45       Convert IRCServices 4.5.x Services DBs\n"
"-sirv                Convert SirvNET Services 2.9.0 DBs\n\n"
"You can stack options, ie, -notes -wrecked, to display notes about\n"
"converting WreckedNet databases. Be sure to give the services you\n"
"want to work with (in this case, wrecked) last, or the other options\n"
"will have no effect.\n"
, prog);
}

void log (const char *fmt,...)
{
    va_list args;

    va_start (args, fmt);

    if (log_file)
    {
        vfprintf (log_file, fmt, args);
        fputc ('\n', log_file);
        fflush (log_file);
    }
}

int tv2ms (struct timeval *tv)
{
    return (tv->tv_sec * 1000) + (int) (tv->tv_usec / 1000);
}

long string_to_flags (char *modes)
{
    long flags = 0;

    while (*modes)
    {
	int i;

	for (i = 0; cmodes[i].mode != 0 && cmodes[i].mode != *modes; i++);

	if (!cmodes[i].mode)
	    return 0;

	flags |= cmodes[i].flag;
	*modes++ = 0;
    }

    return flags;
}

long is45flags_to_cygflags (long is45flags)
{
    long cygflags = 0;
    int i;

    for (i = 0; cmodes[i].flag != 0; i++)
	if (is45flags & cmodes[i].is45flag)
	    cygflags |= cmodes[i].flag;

    return cygflags;
}

long sirvflags_to_cygflags (long sirvflags)
{
    long cygflags = 0;
    int i;

    for (i = 0; cmodes_sirv[i].flag != 0; i++)
	if (sirvflags & cmodes_sirv[i].sirvflag)
            cygflags |= cmodes_sirv[i].flag;

    return cygflags;
}

int get_file_version_wrecked (FILE * f, const char *filename)
{
    int version = fgetc (f) << 24 | fgetc (f) << 16 | fgetc (f) << 8 | fgetc (f);

    if (ferror (f) || feof (f))
	return 0;

    else if (version > 6 || version < 1)
	return 0;

    return version;
}

NickInfo_Wrecked *findnick_wrecked (const char *nick)
{
    NickInfo_Wrecked *ni;

    for (ni = nicklists_wrecked[tolower (*nick)]; ni; ni = ni->next)
	if (strcasecmp (ni->nick, nick) == 0)
	    return ni;

    return NULL;
}

ChannelInfo_Wrecked *cs_findchan_wrecked (const char *chan)
{
    ChannelInfo_Wrecked *ci;

    for (ci = chanlists_wrecked[tolower (*chan)]; ci; ci = ci->next)
	if (strcasecmp (ci->name, chan) == 0)
		return ci;

    return NULL;
}

MemoList_Wrecked *find_memolist_wrecked (const char *nick)
{
    MemoList_Wrecked *ml = malloc (sizeof (MemoList_Wrecked));
    int i = -1;
    NickInfo_Wrecked *ni = findnick_wrecked (nick);

    if (ni)
    {
	for (ml = memolists_wrecked[tolower (*(ni->nick))];
	    ml && (i = strcasecmp (ml->nick, ni->nick)) < 0;
	    ml = ml->next);
    }

    return i == 0 ? ml : NULL;
}

MemoList_Wrecked *find_dupelist (const char *nick)
{
    MemoList_Wrecked *ml;
    int i = -1;

    for (ml = memolists_wrecked[tolower (*(nick))];
        ml && (i = strcasecmp (ml->nick, nick)) < 0;
            ml = ml->next);

    return i == 0 ? ml : NULL;
}

void del_memolist_wrecked (MemoList_Wrecked *ml)
{
    if (!ml) return;
    if (ml->next)
	ml->next->prev = ml->prev;
    if (ml->prev)
	ml->prev->next = ml->next;
    else
	memolists_wrecked[tolower (*ml->nick)] = ml->next;
    if (ml->memos)
	free (ml->memos);
    free (ml);
}

int delchan_wrecked (ChannelInfo_Wrecked *ci)
{
    int i;

    if (ci->next)
	ci->next->prev = ci->prev;
    if (ci->prev)
	ci->prev->next = ci->next;
    else
	chanlists_wrecked[tolower (*ci->name)] = ci->next;
    if (ci->desc)
	free (ci->desc);
    if (ci->url)
	free (ci->url);
    if (ci->mlock_key)
	free (ci->mlock_key);
    if (ci->last_topic)
	free (ci->last_topic);
    for (i = 0; i < ci->accesscount; ++i)
	if (ci->access[i].name)
	    free (ci->access[i].name);
    if (ci->access)
	free (ci->access);
    for (i = 0; i < ci->akickcount; ++i)
    {
	if (ci->akick[i].name)
	    free (ci->akick[i].name);
	if (ci->akick[i].reason)
	    free (ci->akick[i].reason);
    }
    if (ci->akick)
	free (ci->akick);
    free (ci);
    ci = NULL;
    return 1;
}

void alpha_insert_nick_wrecked (NickInfo_Wrecked *ni)
{
    NickInfo_Wrecked *ni2, *ni3;
    char *nick = ni->nick;

    for (ni3 = NULL, ni2 = nicklists_wrecked[tolower (*nick)];
	ni2 && strcasecmp (ni2->nick, nick) < 0;
	ni3 = ni2, ni2 = ni2->next);

    ni->prev = ni3;
    ni->next = ni2;

    if (!ni3)
	nicklists_wrecked[tolower (*nick)] = ni;
    else
	ni3->next = ni;

    if (ni2)
	ni2->prev = ni;
}

void alpha_insert_chan_wrecked (ChannelInfo_Wrecked *ci)
{
    ChannelInfo_Wrecked *ci2, *ci3;
    char *chan = ci->name;

    for (ci3 = NULL, ci2 = chanlists_wrecked[tolower (*chan)];
	ci2 && strcasecmp (ci2->name, chan) < 0;
	ci3 = ci2, ci2 = ci2->next)
	;

    ci->prev = ci3;
    ci->next = ci2;

    if (!ci3)
	chanlists_wrecked[tolower (*chan)] = ci;
    else
	ci3->next = ci;
    if (ci2)
	ci2->prev = ci;
}

void alpha_insert_memolist_wrecked (MemoList_Wrecked *ml)
{
    MemoList_Wrecked *ml2, *ml3;
    char *nick = ml->nick;

    for (ml3 = NULL, ml2 = memolists_wrecked[tolower (*nick)];
	ml2 && strcasecmp (ml2->nick, nick) < 0;
	ml3 = ml2, ml2 = ml2->next);

    ml->prev = ml3;
    ml->next = ml2;

    if (!ml3)
	memolists_wrecked[tolower (*nick)] = ml;
    else
	ml3->next = ml;
    if (ml2)
	ml2->prev = ml;
}

char *read_string (FILE * f, const char *filename)
{
    char *s;
    int len;

    len = fgetc (f) * 256 + fgetc (f);
    s = malloc (len);

    if (len != fread (s, 1, len, f))
	return 0;

    return s;
}

int get_file_version_ircservices45 (FILE * f, const char *filename)
{
    int version = fgetc (f) << 24 | fgetc (f) << 16 | fgetc (f) << 8 | fgetc (f);

    if (ferror (f) || feof (f))
	return 0;

    else if (version > 11 || version < 1)
	return 0;

    return version;
}

void alpha_insert_nick_ircservices45 (NickInfo_IRCServices45 *ni)
{
    NickInfo_IRCServices45 *ni2, *ni3;
    char *nick = ni->nick;

    for (ni3 = NULL, ni2 = nicklists_ircservices45[tolower (*nick)];
	ni2 && strcasecmp (ni2->nick, nick) < 0; ni3 = ni2, ni2 = ni2->next);

    ni->prev = ni3;
    ni->next = ni2;

    if (!ni3)
	nicklists_ircservices45[tolower (*nick)] = ni;
    else
	ni3->next = ni;

    if (ni2)
	ni2->prev = ni;
}

void alpha_insert_chan_ircservices45 (ChannelInfo_IRCServices45 *ci)
{
    ChannelInfo_IRCServices45 *ci2, *ci3;
    char *chan = ci->name;

    for (ci3 = NULL, ci2 = chanlists_ircservices45[tolower (*chan)];
	ci2 && strcasecmp (ci2->name, chan) < 0; ci3 = ci2, ci2 = ci2->next);

    ci->prev = ci3;
    ci->next = ci2;

    if (!ci3)
	chanlists_ircservices45[tolower (*chan)] = ci;
    else
	ci3->next = ci;
    if (ci2)
	ci2->prev = ci;
}

#define SAFE(x) do { if ((x) < 0) return 3; } while (0)
#define read_buffer_ircservices45(buf,f) (fread ((buf),1,sizeof (buf), (f)) == sizeof (buf))

int read_ptr_ircservices45 (void **ret, FILE *f)
{
    int c;

    c = fgetc (f);
    if (c == EOF)
	return -1;
    *ret = (c ? (void *)1 : (void *)0);
    return 0;
}

int read_int16_ircservices45 (int *ret, FILE *f)
{
    int c1, c2;

    c1 = fgetc (f);
    c2 = fgetc (f);
    if (c2 == EOF)
	return -1;
    *ret = c1<<8 | c2;
    return 0;
}

int read_int32_ircservices45 (long *ret, FILE *f)
{
    int c1, c2, c3, c4;

    c1 = fgetc (f);
    c2 = fgetc (f);
    c3 = fgetc (f);
    c4 = fgetc (f);
    if (c4 == EOF)
	return -1;
    *ret = c1<<24 | c2<<16 | c3<<8 | c4;
    return 0;
}

int read_string_ircservices45 (char **ret, FILE *f)
{
    char *s;
    int len;

    if (read_int16_ircservices45 (&len, f) < 0)
	return -1;
    if (len == 0) {
	*ret = NULL;
	return 0;
    }
    s = malloc (len);
    if (len != fread (s, 1, len, f)) {
	free (s);
	return -1;
    }
    *ret = s;
    return 0;
}

int get_file_version_sirv (FILE *f, const char *filename)
{
    int version = fgetc(f)<<24 | fgetc(f)<<16 | fgetc(f)<<8 | fgetc(f);

    if (ferror(f))
	return 0;
    else if (ferror(f))
	return 0;
    else if (version > 8 || version < 1)
	return 0;
    return version;
}

NickInfo_Sirv *findnick_sirv (const char *nick)
{
    NickInfo_Sirv *ni;

    for (ni = nicklists_sirv[tolower(*nick)]; ni; ni = ni->next)
        if (strcasecmp(ni->nick, nick) == 0)
            return ni;

    return NULL;
}

ChannelInfo_Sirv *cs_findchan_sirv (const char *chan)
{
    ChannelInfo_Sirv *ci;

    for (ci = chanlists_sirv[tolower(chan[1])]; ci; ci = ci->next)
        if (strcasecmp(ci->name, chan) == 0)
            return ci;

    return NULL;
}

MemoList_Sirv *find_memolist_sirv (const char *nick)
{
    MemoList_Sirv *ml;
    int i = 0;

    for (ml = memolists_sirv[tolower(*nick)];
	 ml && (i = strcasecmp (ml->nick, nick)) < 0;
	 ml = ml->next);
	return i == 0 ? ml : NULL;
}

void alpha_insert_nick_sirv (NickInfo_Sirv *ni)
{
    NickInfo_Sirv *ni2, *ni3;
    char *nick = ni->nick;

    for (ni3 = NULL, ni2 = nicklists_sirv[tolower(*nick)];
	 ni2 && strcasecmp(ni2->nick, nick) < 0;  
	 ni3 = ni2, ni2 = ni2->next);

    ni->prev = ni3;
    ni->next = ni2;

    if (!ni3)
        nicklists_sirv[tolower(*nick)] = ni;
    else
        ni3->next = ni;
    if (ni2)
        ni2->prev = ni;
}

void alpha_insert_chan_sirv (ChannelInfo_Sirv *ci)
{
    ChannelInfo_Sirv *ci2, *ci3;
    char *chan = ci->name;

    for (ci3 = NULL, ci2 = chanlists_sirv[tolower(chan[1])];
	 ci2 && strcasecmp(ci2->name, chan) < 0;
	 ci3 = ci2, ci2 = ci2->next);

    ci->prev = ci3;
    ci->next = ci2;

    if (!ci3)
        chanlists_sirv[tolower(chan[1])] = ci;
    else
        ci3->next = ci;
    if (ci2)
        ci2->prev = ci;
}

void alpha_insert_memolist_sirv (MemoList_Sirv *ml)
{
    MemoList_Sirv *ml2, *ml3;
    char *nick = ml->nick;

    for (ml3 = NULL, ml2 = memolists_sirv[tolower(*nick)];
	 ml2 && strcasecmp(ml2->nick, nick) < 0;
	 ml3 = ml2, ml2 = ml2->next);

    ml->prev = ml3;
    ml->next = ml2;

    if (!ml3)
        memolists_sirv[tolower(*nick)] = ml;
    else
        ml3->next = ml;
    if (ml2)
        ml2->prev = ml;
}

void print_stats ()
{
    printf ("\n\nConversion statistics:\n");
    log ("\nConversion statistics:");

    printf ("     %d nicknames in, %d nicknames out\n", nin, nout);
    log ("     %d nicknames in, %d nicknames out", nin, nout);

    if (lin)
    {
	printf ("     %d links in, %d links out\n", lin, lout);
	log ("     %d links in, %d links out", lin, lout);
    }

    printf ("     %d channels in, %d channels out\n", cin, cout);
    log ("     %d channels in, %d channels out", cin, cout);

    printf ("     %d memos in, %d memos out\n", min, mout);
    log ("     %d memos in, %d memos out", min, mout);

    if (ain)
    {
	printf ("     %d akills in, %d akills out\n", ain, aout);
	log ("     %d akills in, %d akills out", ain, aout);
    }

    if (tin)
    {
	printf ("     %d triggers in, %d triggers out\n", tin, tout);
	log ("     %d triggers in, %d triggers out", tin, tout);
    }

    if (soin)
    {
	printf ("     %d csops in, %d csops out\n", soin, sout);
	log ("     %d csops in, %d csops out", soin, sout);
    }

    if (ndrop)
    {
	printf ("     %d lost nicknames (Probably forbidden, see log)\n", ndrop);
	log ("     %d lost nicknames (Probably forbidden)", ndrop);
    }

    if (mdrop)
    {
	printf ("     %d lost memos (Probably marked deleted, see log)\n", mdrop);
	log ("     %d lost memos (Probably marked deleted)", mdrop);
    }

    if (cdrop)
    {
	printf ("     %d lost channels (Probably forbidden, see log)\n", cdrop);
	log ("     %d lost channels (Probably forbidden)", cdrop);
    }

    if (adrop)
    {
	printf ("     %d lost channel access list entries (Probably non-nickname, see log)\n", adrop);
	log ("     %d lost channel access list entries (Probably non-nickname)", adrop);
    }
}

int main (int ac, char **av, char **envp)
{
    struct rlimit rlim;
    int i;
    char *s;

    if (!getrlimit (RLIMIT_CORE, &rlim))
    {
	rlim.rlim_cur = rlim.rlim_max;
	setrlimit (RLIMIT_CORE, &rlim);
    }

    if (chdir (SERVICES_DIR) < 0)
    {
	perror (SERVICES_DIR);
	return 20;
    }

#if HAVE_UMASK
    umask (077);
#endif

    log_file = fopen ("dbtool.log", "w");

    printf ("DBTool - Copyright (c) 2001-2002 Darcy Grexton\n");

    for (i = 1; i < ac; ++i)
    {
	s = av[i];

	if (*s == '-' || *s == '/')
	{
	    ++s;
	    if (!strcasecmp (s, "-help") || !strcasecmp (s, "?") ||
		  !strcasecmp (s, "help"))
	    {
		do_help (av[0]);
		return 0;
	    }
	    else if (!strcasecmp (s, "notes"))
		notes = 1;
	    else if (!strcasecmp (s, "wrecked"))
	    {
		if (!convert_wrecked_db ())
		    return 0;
	    }
	    else if (!strcasecmp (s, "ircservices45"))
	    {
		if (!convert_ircservices45_db ())
		    return 0;
	    }
	    else if (!strcasecmp (s, "sirv"))
	    {
		if (!convert_sirv_db ())
		    return 0;
	    }
	}
    }

    i = 0;

    printf ("\nPlease select the type of services databases you want to convert. If\n");
    printf ("the services you want are not listed below, you cannot convert them\n");
    printf ("at this time. Feel free to contact the author to see if support for\n");
    printf ("them can be added. Read section 4.0.0 of the README for contact info.\n\n");
    printf ("  1) WreckedNet IRC Services v1.2.0\n");
    printf ("  2) IRCServices v4.5.x\n");
    printf ("  3) SirvNET Services v2.9.0\n");
    printf ("  4) You don't have my services!\n\n");
    printf ("Option: ");

    scanf ("%d", &i);

    if (i == 1)
    {
	printf ("\nDo you want to read the WreckedNet database conversion notes?\n\n");
	printf ("[Y/N] ");

	scanf ("%s", opt);

	if (strchr (opt, 'y') || strchr (opt, 'Y'))
	    notes = 1;

	if (!convert_wrecked_db ())
	    return 0;
    }

    if (i == 2)
    {
	printf ("\nDo you want to read the IRCServices database conversion notes?\n\n");
	printf ("[Y/N] ");

	scanf ("%s", opt);

	if (strchr (opt, 'y') || strchr (opt, 'Y'))
	    notes = 1;

	if (!convert_ircservices45_db ())
	    return 0;
    }

    if (i == 3)
    {
        printf ("\nDo you want to read the SirvNET database conversion notes?\n\n");
        printf ("[Y/N] ");

        scanf ("%s", opt);

        if (strchr (opt, 'y') || strchr (opt, 'Y'))
            notes = 1;

        if (!convert_sirv_db ())
            return 0;
    } 

    printf ("\nI guess I can't help you.. Sorry.\n\n");

    return 0;
}

int convert_wrecked_db ()
{
    int problem = 0;
    struct timeval now, tmp;

    /* Conversion notes? Ok, tell them, then exit. */
    if (notes)
    {
	printf ("\nConversion notes for WreckedNet 1.2.0 -> Cygnus:\n\n");
	printf ("  * Channel AKick setters will all be stored as ChanServ, since\n");
	printf ("    this information is not present in the WreckedNet database.\n\n");
	printf ("  * Channel AKick timestamps will be the time of the conversion,\n");
	printf ("    since this information is not present in the WreckedNet database.\n\n");
	printf ("  * Channel descriptions will be lost, as this is not in Cygnus.\n\n");
	printf ("  * KEEPTOPIC, PRIVATE and JOIN channel flags will be lost, as these\n");
	printf ("    are not in Cygnus.\n\n");
	printf ("  * Channels with topiclock will have topiclock aop after conversion,\n");
	printf ("    as WreckedNet did not have multiple topiclock levels.\n\n");
	printf ("  * Non-nickname channel access list entries will be ignored.\n\n");
	printf ("  * Forbidden channels will be lost in the conversion.\n\n");
	printf ("  * Forbidden nicknames will be lost in the conversion.\n\n");
	printf ("  * Nickname Ignores will be lost, as this is not in Cygnus.\n\n");
	printf ("  * The IRCOP nick flag will be lost, as this is not in Cygnus.\n\n");
	printf ("  * LogonMSGs will not be converted, as Cygnus does not use them.\n\n");
	printf ("Do you want to continue with conversion?\n\n");
	printf ("[Y/N] ");

	scanf ("%s", opt);

	if (strchr (opt, 'n') || strchr (opt, 'N'))
	{
	    printf ("\nOk.\n\n");
	    return 0;
	}
    }

    printf ("\nPlease enter the path to your current services databases. For example,\n");
    printf ("/home/bob/services\n\n");
    printf ("If you're not sure, type \"QUIT\" below, go to the directory with your\n");
    printf ("databases, and type pwd. Re run the db converter (\"./dbtool\") and type\n");
    printf ("what pwd said here.\n\n");
    printf ("Option: ");

    scanf ("%s", opt);

    if (!strcasecmp (opt, "QUIT"))
    {
	printf ("\nOk.\n\n");
	return 0;
    }

    log ("Converting WreckedNet 1.2.0 databases...");

    printf ("\nConverting WreckedNet Services 1.2.0 databases.\n\n");
    printf ("Loading databases... ");

    gettimeofday (&(start), NULL);

    /* Load all of the DBs from disk. If the load function returns anything but 0,
       it's an error.
     */
    if (!(problem = load_wrecked_ns ()))
	printf ("nick.db... ");
    else
    {
	printf ("\n\nProblem loading nick.db! %s.. skipping...\n",
	    problem == 1 ? "Incorrect version." :
	    problem == 2 ? "File not found." :
	    problem == 3 ? "Possible database corruption." : "Unknown problem... ;/");
    }

    if (!(problem = load_wrecked_cs ()))
	printf ("chan.db... ");
    else
    {
	printf ("\n\nProblem loading chan.db! %s.. skipping...\n",
	    problem == 1 ? "Incorrect version." :
	    problem == 2 ? "File not found." :
	    problem == 3 ? "Possible database corruption." : "Unknown problem... ;/");
    }

    if (!(problem = load_wrecked_ms ()))
	printf ("memo.db... ");
    else
    {
	printf ("\n\nProblem loading memo.db! %s.. skipping...\n",
	    problem == 1 ? "Incorrect version." :
	    problem == 2 ? "File not found." :
	    problem == 3 ? "Possible database corruption." : "Unknown problem... ;/");
    }

    if (!(problem = load_wrecked_rs ()))
	printf ("sop.db... akill.db... clone.db... ");
    else
    {
	printf ("\n\nProblem loading one of the OperServ dbs! %s.. skipping...\n",
	    problem == 1 ? "Incorrect version." :
	    problem == 2 ? "File not found." :
	    problem == 3 ? "Possible database corruption." : "Unknown problem... ;/");
    }

    printf ("\nSaving databases... ");

    if (!(problem = save_wrecked_ns ()))
	printf ("nickserv.db... ");
    else
    {
	printf ("\n\nProblem saving nickserv.db! %s\n\n",
	    problem == 1 ? "Can't write to file." :
	    "Unknown problem... ;/");
	return 0;
    }

    if (!(problem = save_wrecked_cs ()))
	printf ("chanserv.db... ");
    else
    {
	printf ("\n\nProblem saving chanserv.db! %s\n\n",
	    problem == 1 ? "Can't write to file." :
	    "Unknown problem... ;/");
	return 0;
    }

    if (!(problem = save_wrecked_rs ()))
	printf ("rootserv.db... ");
    else
    {
	printf ("\n\nProblem saving rootserv.db! %s\n\n",
	    problem == 1 ? "Can't write to file." :
	    "Unknown problem... ;/");
	return 0;
    }

    gettimeofday (&now, NULL);
    timersub (&now, &start, &tmp);

    print_stats ();

    printf ("     Conversion took %d ms\n", tv2ms (&tmp));
    log ("     Conversion took %d ms", tv2ms (&tmp));

    printf ("\nAll done.\n\n");
    log ("\nAll done.");

    return 0;
}

int load_wrecked_ns ()
{
    FILE *f;
    int i, j, dupenick;
    NickInfo_Wrecked *ni;
    char tmp[264];

    snprintf (tmp, sizeof (tmp), "%s/nick.db", opt);

    f = fopen (tmp, "r");

    while (!f)
    {
	printf ("\n\nUnable to locate %s/nick.db. Please specify the full path and\n", opt);
	printf ("filename of your NickServ database, or QUIT to quit.\n\n");
        printf ("Option: ");

	scanf ("%s", tmp);

	if (!strcasecmp (tmp, "QUIT"))
	    exit;

	f = fopen (tmp, "r");
    }

    if (!f)
	return 2;

    switch (i = get_file_version_wrecked (f, tmp))
    {
	case 6:
	    for (i = 33; i < 256; ++i)
		while (fgetc (f) == 1)
		{
		    dupenick = 0;
		    ni = malloc (sizeof (NickInfo_Wrecked));

		    if (1 != fread (ni, sizeof (NickInfo_Wrecked), 1, f))
			return 3;

		    ni->flags &= ~(WNI_IDENTIFIED | WNI_RECOGNIZED);

		    if (findnick_wrecked (ni->nick))
			dupenick = 1;

		    alpha_insert_nick_wrecked (ni);
		    nin++;

		    if (ni->flags & WNI_SLAVE)
			lin++;

		    if (ni->email)
		    {
			ni->email = read_string (f, tmp);

			if (!strlen (ni->email))
			{
			    free (ni->email);
			    ni->email = NULL;
			}
		    }
		    else
			ni->email = NULL;

		    if (ni->url)
		    {
			ni->url = read_string (f, tmp);

			if (!strlen (ni->url))
			{
			    free (ni->url);
			    ni->url = NULL;
			}
		    }
		    else
			ni->url = NULL;

		    ni->last_usermask = read_string (f, tmp);
		    ni->last_realname = read_string (f, tmp);

		    if (ni->accesscount)
		    {
			char **tmpaccess;

			tmpaccess = malloc (sizeof (char *) * ni->accesscount);
			ni->access = tmpaccess;

			for (j = 0; j < ni->accesscount; ++j, ++tmpaccess)
			    *tmpaccess = read_string (f, tmp);
		    }

		    if (ni->ignorecount)
		    {
			char **ignore;

			ignore = malloc (sizeof (char *) * ni->ignorecount);
			ni->ignore = ignore;

			for (j = 0; j < ni->ignorecount; ++j, ++ignore)
			    *ignore = read_string (f, tmp);
		    }

		    if (dupenick)
		    {
			if (ni->next)
			    ni->next->prev = ni->prev;
			if (ni->prev)
			    ni->prev->next = ni->next;
			else
			    nicklists_wrecked[tolower (*ni->nick)] = ni->next;

			if (ni->email)
			    free (ni->email);
			if (ni->url)
			    free (ni->url);
			if (ni->last_usermask)
			    free (ni->last_usermask);
			if (ni->last_realname)
			    free (ni->last_realname);
			if (ni->access)
			{
			    for (i = 0; i < ni->accesscount; ++i)
				if (ni->access[i])
				    free (ni->access[i]);

			    if (ni->access)
				free (ni->access);
			}

			if (ni->ignore)
			{
			    for (i = 0; i < ni->ignorecount; ++i)
				if (ni->ignore[i])
				    free (ni->ignore[i]);

			    if (ni->ignore)
				free (ni->ignore);
			}

			free (ni);
			ni = NULL;
		    }
		}
	    break;
	case 5:
	case 4:
	case 3:
	case 2:
	case 1:
	default:
	    return 1;
    }

    fclose (f);

    return 0;
}

int load_wrecked_ms ()
{
    FILE *f;
    int i, j, dupelist;
    MemoList_Wrecked *ml;
    Memo_Wrecked *memos;
    char tmp[264];

    snprintf (tmp, sizeof (tmp), "%s/memo.db", opt);

    f = fopen (tmp, "r");

    while (!f)
    {
	printf ("\n\nUnable to locate %s/memo.db. Please specify the full path and\n", opt);
	printf ("filename of your MemoServ database, or QUIT to quit.\n\n");
        printf ("Option: ");

	scanf ("%s", tmp);

	if (!strcasecmp (tmp, "QUIT"))
	    exit;

	f = fopen (tmp, "r");
    }

    if (!f)
	return 2;

    switch (i = get_file_version_wrecked (f, tmp))
    {
	case 6:
	case 5:
	case 4:
	case 3:
	case 2:
	case 1:
	    for (i = 33; i < 256; ++i)
		while (fgetc (f) == 1)
		{
		    dupelist = 0;
		    ml = malloc (sizeof (MemoList_Wrecked));

		    if (1 != fread (ml, sizeof (MemoList_Wrecked), 1, f))
			return 3;

		    if (find_dupelist (ml->nick))
			dupelist = 1;

		    alpha_insert_memolist_wrecked (ml);
		    ml->memos = memos = malloc (sizeof (Memo_Wrecked) * ml->n_memos);
		    fread (memos, sizeof (Memo_Wrecked), ml->n_memos, f);

		    for (j = 0; j < ml->n_memos; ++j, ++memos)
			memos->text = read_string (f, tmp);

		    if (!dupelist && findnick_wrecked (ml->nick))
			min = min + ml->n_memos;

		    if (dupelist == 1)
			del_memolist_wrecked (ml);

		    if (!findnick_wrecked (ml->nick))
			del_memolist_wrecked (ml);
		}
	    break;
	default:
	    return 1;
    }

    fclose (f);

    return 0;
}

int load_wrecked_cs ()
{
    FILE *f;
    int i, j, dupechan;
    ChannelInfo_Wrecked *ci;
    char tmp[264];

    snprintf (tmp, sizeof (tmp), "%s/chan.db", opt);

    f = fopen (tmp, "r");

    while (!f)
    {
	printf ("\n\nUnable to locate %s/chan.db. Please specify the full path and\n", opt);
	printf ("filename of your ChanServ database, or QUIT to quit.\n\n");
	printf ("Option: ");

	scanf ("%s", tmp);

	if (!strcasecmp (tmp, "QUIT"))
	    exit;

	f = fopen (tmp, "r");
    }

    if (!f)
	return 2;

    switch (i = get_file_version_wrecked (f, tmp))
    {
	case 6:
	    for (i = 33; i < 256; ++i)
		while (fgetc (f) == 1)
		{
		    dupechan = 0;
		    ci = malloc (sizeof (ChannelInfo_Wrecked));

		    if (1 != fread (ci, sizeof (ChannelInfo_Wrecked), 1, f))
			return 3;

		    if (cs_findchan_wrecked (ci->name))
			dupechan = 1;

		    alpha_insert_chan_wrecked (ci);

		    ci->desc = read_string (f, tmp);

		    if (ci->url)
			ci->url = read_string (f, tmp);
		    if (ci->mlock_key)
			ci->mlock_key = read_string (f, tmp);
		    if (ci->last_topic)
			ci->last_topic = read_string (f, tmp);

		    if (ci->accesscount)
		    {
			ChanAccess_Wrecked *tmpaccess;
			tmpaccess = malloc (sizeof (ChanAccess_Wrecked) * ci->accesscount);
			ci->access = tmpaccess;

			if (ci->accesscount != fread (tmpaccess, sizeof (ChanAccess_Wrecked),
			    ci->accesscount, f))
			    return 3;

			for (j = 0; j < ci->accesscount; ++j, ++tmpaccess)
			    tmpaccess->name = read_string (f, tmp);

			j = 0;
			tmpaccess = ci->access;

			while (j < ci->accesscount)
			{
			    if (tmpaccess->is_nick < 0)
			    {
				--ci->accesscount;
				free (tmpaccess->name);
				if (j < ci->accesscount)
				    bcopy (tmpaccess + 1, tmpaccess, sizeof (*tmpaccess) *
					(ci->accesscount - j));
			    }
			    else
			    {
				++j;
				++tmpaccess;
			    }
			}

			if (ci->accesscount)
			    ci->access = realloc (ci->access,
				sizeof (ChanAccess_Wrecked) * ci->accesscount);
			else
			{
			    free (ci->access);
			    ci->access = NULL;
			}
		    }

		    if (ci->akickcount)
		    {
			AutoKick *akick;
			akick = malloc (sizeof (AutoKick) * ci->akickcount);
			ci->akick = akick;
			if (ci->akickcount != fread (akick, sizeof (AutoKick), ci->akickcount, f))
			    return 3;

			for (j = 0; j < ci->akickcount; ++j, ++akick)
			{
			    akick->name = read_string (f, tmp);
			    if (akick->reason)
				akick->reason = read_string (f, tmp);
			}

			j = 0;
			akick = ci->akick;

			while (j < ci->akickcount)
			{
			    if (akick->is_nick < 0)
			    {
				--ci->akickcount;
				free (akick->name);
				if (akick->reason)
				    free (akick->reason);
				if (j < ci->akickcount)
				    bcopy (akick + 1, akick, sizeof (*akick) *
					(ci->akickcount - j));
			    }
			    else
			    {
				++j;
				++akick;
			    }
			}

			if (ci->akickcount)
			{
			    ci->akick = realloc (ci->akick,
				sizeof (AutoKick) * ci->akickcount);
			}
			else
			{
			    free (ci->akick);
			    ci->akick = NULL;
			}
		    }

		    if (ci->cmd_access)
		    {
			int n_entries;
			ci->cmd_access = malloc (sizeof (short) * 8);

			n_entries = fgetc (f) << 8 | fgetc (f);

			if (n_entries < 0)
			    return 3;

			if (n_entries <= 8)
			    fread (ci->cmd_access, sizeof (short), n_entries, f);
			else
			{
			    fread (ci->cmd_access, sizeof (short), 8, f);
			    fseek (f, sizeof (short) * (n_entries - 8),
				SEEK_CUR);
			}
		    }

		    if (dupechan == 1)
			delchan_wrecked (ci);
		    else
			cin++;
		}
	    break;
	case 5:
	case 4:
	case 3:
	case 2:
	case 1:
	default:
	    return 3;
    }

    fclose (f);

    return 0;
}

int load_wrecked_rs ()
{
    FILE *f;
    int i;
    char tmp[264];

    snprintf (tmp, sizeof (tmp), "%s/sop.db", opt);

    f = fopen (tmp, "r");

    while (!f)
    {
	printf ("\n\nUnable to locate %s/sop.db. Please specify the full path and\n", opt);
	printf ("filename of your CSOp database, or QUIT to quit.\n\n");
	printf ("Option: ");

	scanf ("%s", tmp);

	if (!strcasecmp (tmp, "QUIT"))
	    exit;

	f = fopen (tmp, "r");
    }

    if (!f)
	return 2;

   /* Since we don't HAVE an sop.db in Cygnus, we'll just apply
      the CSOP flag to each of these nicknames as we load them.
    */
   switch (i = get_file_version_wrecked (f, tmp))
   {
	case 6:
	case 5:
	case 4:
	case 3:
	case 2:
	case 1:
	    nsop_wrecked = fgetc (f) * 256 + fgetc (f);

	    if (nsop_wrecked < 8)
		sop_size_wrecked = 16;
	    else
		sop_size_wrecked = 2 * nsop_wrecked;

	    sops_wrecked = realloc (sops_wrecked, sizeof (SOP_Wrecked) * sop_size_wrecked);

	    if (!nsop_wrecked)
		break;

	    soin = nsop_wrecked;

	    if (nsop_wrecked != fread (sops_wrecked, sizeof (SOP_Wrecked), nsop_wrecked, f))
		return 3;

	    break;

	default:
	    return 1;
    }

    fclose (f);

    snprintf (tmp, sizeof (tmp), "%s/akill.db", opt);

    f = fopen (tmp, "r");

    if (!f)
	return 2;

    switch (i = get_file_version_wrecked (f, tmp))
    {
	case 6:
	case 5:
	case 4:
	case 3:
	case 2:
	    nakill_wrecked = fgetc (f) * 256 + fgetc (f);

	    if (nakill_wrecked < 8)
		akill_size_wrecked = 16;
	    else
		akill_size_wrecked = 2 * nakill_wrecked;

	    akill_wrecked = malloc (sizeof (*akill_wrecked) * akill_size_wrecked);

	    if (!nakill_wrecked || nakill_wrecked == 1)
		break;

	    if (nakill_wrecked != fread (akill_wrecked, sizeof (*akill_wrecked), nakill_wrecked, f))
		return 3;

	    ain = nakill_wrecked;

	    for (i = 0; i < nakill_wrecked; ++i)
	    {
		akill_wrecked[i].mask = read_string (f, tmp);
		akill_wrecked[i].reason = read_string (f, tmp);
	    }

	    break;

	case 1:
	default:
	    return 1;
    }

    fclose (f);

    snprintf (tmp, sizeof (tmp), "%s/clone.db", opt);

    f = fopen (tmp, "r");

    if (!f)
	return 2;

    switch (i = get_file_version_wrecked (f, tmp))
    {
	case 6:
	case 5:
	case 4:
	case 3:
	case 2:
	case 1:
	    nallow_wrecked = fgetc (f) * 256 + fgetc (f);

	    if (nallow_wrecked < 8)
		allow_size_wrecked = 16;
	    else
		allow_size_wrecked = 2 * nallow_wrecked;

	    allow_wrecked = malloc (sizeof (*allow_wrecked) * allow_size_wrecked);

	    if (!nallow_wrecked || nallow_wrecked == 1)
		break;

	    if (nallow_wrecked != fread (allow_wrecked, sizeof (*allow_wrecked), nallow_wrecked, f))
		return 3;

	    tin = nallow_wrecked;

	    for (i = 0; i < nallow_wrecked; ++i)
	    {
		allow_wrecked[i].host = read_string (f, tmp);
		allow_wrecked[i].reason = read_string (f, tmp);
	    }

	    break;

	default:
	    return 1;
    }

    fclose (f);

    return 0;
}

int save_wrecked_ns ()
{
    FILE *f;
    NickInfo_Wrecked *ni, *tni;
    Memo_Wrecked *m;
    MemoList_Wrecked *ml;
    int i, j, haslinks = 0;
    char **tmpaccess;

    f = fopen ("nickserv.db", "w");

    if (!f)
	return 1;

    fprintf (f, "NV 3\n");

    for (i = 33; i < 256; ++i)
    {
	for (ni = nicklists_wrecked[i]; ni; ni = ni->next)
	{
	    long outflags = 0;

	    /* We don't do forbids. Skip to the next nick. */
	    if (ni->flags & WNI_VERBOTEN)
	    {
		log ("Dropping forbidden nickname %s", ni->nick);
		ndrop++;
		continue;
	    }

	    /* Check for applicable nickflags */
	    if (ni->flags & WNI_SUSPENDED)
		outflags |= CNF_FROZEN;

	    if (ni->flags & WNI_KILLPROTECT)
		outflags |= CNF_ENFORCE;

	    if (ni->flags & WNI_SECURE)
		outflags |= CNF_SECURE;

	    if (ni->flags & WNI_PRIVATE)
		outflags |= CNF_PRIVATE;

	    if (ni->flags & WNI_PRIVMSG)
		outflags |= CNF_PRIVMSG;

	    if (ni->flags & WNI_SLAVE)
		outflags |= CNF_LINKED;

	    if (ni->flags & WNI_NOMEMO)
		outflags |= CNF_NOMEMO;

	    if (ni->flags & WNI_HIDEMAIL)
		outflags |= CNF_HIDEMAIL;

	    if (ni->flags & WNI_NEVEROP)
		outflags |= CNF_NEVEROP;

	    if (ni->flags & WNI_NOOP)
		outflags |= CNF_NOOP;

	    if (ni->flags & WNI_MARKED)
		outflags |= CNF_MARKED;

	    if (ni->flags & WNI_HOLD)
		outflags |= CNF_HELD;

	    if (ni->flags & WNI_MEMOMAIL)
		outflags |= CNF_MEMOMAIL;

	    /* Set flags for CSOps */
	    for (j = 0; j < nsop_wrecked; ++j)
		if (!strcasecmp (ni->nick, sops_wrecked[j].nick))
		{
		    outflags |= CNF_CSOP;
		    sout++; 
		}

	    if (!(ni->flags & WNI_SLAVE))
		fprintf (f, "NI %s %s %lu %lu %lu 0 %li 25 0 0 %s %s\n",
		    ni->nick, ni->pass, ni->time_registered, ni->last_seen,
		    ni->last_signon, outflags, ni->last_usermask,
		    strlen (ni->last_realname) ? ni->last_realname : "unknown");

	    nout++;

	    for (tmpaccess = ni->access, j = 0; j < ni->accesscount; ++tmpaccess, ++j)
		fprintf (f, "AC %s\n", *tmpaccess);

	    /* Since we store linked nicks differently, we'll go through every nickname,
	       checking if each one is linked to this one.. Bit cpu intensive, but we're
	       not going to be running that long anyhow so who cares?
	     */
	    haslinks = 0;

	    for (j = 33; j < 256; ++j)
		for (tni = nicklists_wrecked[j]; tni; tni = tni->next)
		    if (!strcasecmp (tni->last_usermask, ni->nick))
		    {
			haslinks = 1;
			break;
		    }

	    if (haslinks)
	    {
		fprintf (f, "LN");

		for (j = 33; j < 256; ++j)
		{
		    for (tni = nicklists_wrecked[j]; tni; tni = tni->next)
			if (!strcasecmp (tni->last_usermask, ni->nick))
			    fprintf (f, " %s", tni->nick);
		    lout++;
		}

		fprintf (f, "\n");
	    }

	    ml = find_memolist_wrecked (ni->nick);

	    if (ml)
	    {
		for (j = 0, m = ml->memos; j < ml->n_memos; ++j, ++m)
		{
		    if (m->flags & WMF_EMAILED)
		    {
			m->flags &= WMF_EMAILED;
			m->flags |= CMF_EMAILED;
		    }

		    fprintf (f, "MO %li %li %lu %s %s\n", m->number, m->flags,
			m->time, m->sender, m->text);

		    mout++;
		}
	    }

	    if (ni->email)
		fprintf (f, "EM %s\n", ni->email);

	    if (ni->url)
		fprintf (f, "UR %s\n", ni->url);

	    if (ni->uin)
		fprintf (f, "UN %li\n", ni->uin);
	}
    }

    fprintf (f, "DE %d %d %d\n", nout, lout, mout);

    fclose (f);

    return 0;
}

int save_wrecked_cs ()
{
    FILE *f;
    int i, j;
    ChannelInfo_Wrecked *ci;
    ChanAccess_Wrecked *ca;
    AutoKick *ak;

    f = fopen ("chanserv.db", "w");

    if (!f)
	return 1;

    fprintf (f, "CV 2\n");

    for (i = 33; i < 256; ++i)
    {
	for (ci = chanlists_wrecked[i]; ci; ci = ci->next)
	{
	    long outflags = 0;

	    /* We don't do forbids. Skip to the next channel. */
	    if (ci->flags & WCI_VERBOTEN)
	    {
		log ("Dropping forbidden channel %s", ci->name);
		cdrop++;
		continue;
	    }

	    /* Check for applicable channel flags */
	    if (ci->flags & WCI_VERBOSE)
		outflags |= CCF_VERBOSE;

	    if (ci->flags & WCI_VOPALL)
		outflags |= CCF_VOPALL;

	    /* These are the same thing in Cygnus */
	    if ((ci->flags & WCI_SECURE) || (ci->flags & WCI_SECUREOPS))
		outflags |= CCF_SECURE;

	    if (ci->flags & WCI_RESTRICTED)
		outflags |= CCF_RESTRICTED;

	    if (ci->flags & WCI_SUSPENDED)
		outflags |= CCF_FROZEN;

	    if (ci->flags & WCI_HOLD)
		outflags |= CCF_HELD;

	    if (ci->flags & WCI_MARKED)
		outflags |= CCF_MARKED;

	    fprintf (f, "CI %s %s %s %lu %lu %li %li %li %d 0 0\n", ci->name,
		ci->founder, ci->founderpass, ci->time_registered, ci->last_used,
		outflags, strlen (ci->mlock_on) ? string_to_flags (ci->mlock_on) : 0,
		strlen (ci->mlock_off) ? string_to_flags (ci->mlock_off) : 0,
		(ci->flags & WCI_TOPICLOCK) ? 3 : 0);

	    /* The founder isn't in the access list, but we need them in there on Cygnus */
	    fprintf (f, "CA %s 5\n", ci->founder);

	    for (ca = ci->access, j = 0; j < ci->accesscount; ++ca, ++j)
	    {
		int newlevel = 0;

		if (!ca->is_nick)
		    continue;

		if (!findnick_wrecked (ca->name))
		{
		    log ("Dropping access list entry of %s on %s", ca->name, ci->name);
		    adrop++;
		    continue;
		}

		/* Convert the Wrecked-style db levels to Cygnus-style. */
		if (ca->level <= 9)
		    newlevel = 1;

		if (ca->level > 9 && ca->level <= 14)
		    newlevel = 2;

		if (ca->level > 14 && ca->level <= 24)
		    newlevel = 3;

		if (ca->level > 24 && ca->level <= 49)
		    newlevel = 4;

		if (ca->level >= 50)
		{
		    newlevel = 5;

		    /* No one should have this level.. Warn of it */
		    log ("*** Warning: Founder-level access for %s on %s!", ca->name, ci->name);
		}

		/* If by some miracle we get here and still don't have a level assigned,
		   give them VOP and warn about it in the log..
		 */
		if (!newlevel)
		{
		    newlevel = 1;
		    log ("*** Warning: Couldn't get access level for %s on %s! Giving VOP.", ca->name, ci->name);
		}

		fprintf (f, "CA %s %d\n", ca->name, newlevel);
	    }

	    for (ak = ci->akick, j = 0; j < ci->akickcount; ++ak, ++j)
		fprintf (f, "AK %s ChanServ %d %lu %s\n", ak->name,
		    ak->is_nick ? 2 : 1, time (NULL), ak->reason ? ak->reason : "");

	    if (ci->url)
		fprintf (f, "UR %s\n", ci->url);

	    if (ci->last_topic && strlen (ci->last_topic))
		fprintf (f, "CT %s %lu %s\n", ci->last_topic_setter ?
		    ci->last_topic_setter : "ChanServ", ci->last_topic_time,
		    ci->last_topic);

	    if (ci->mlock_key)
		fprintf (f, "KY %s\n", ci->mlock_key);

	    if (ci->mlock_limit)
		fprintf (f, "LM %li\n", ci->mlock_limit);

	    cout++;
	}
    }

    fprintf (f, "DE %d\n", cout);

    fclose (f);

    return 0;
}

int save_wrecked_rs ()
{
    FILE *f;
    int i;

    f = fopen ("rootserv.db", "w");

    if (!f)
	return 1;

    fprintf (f, "RV 2\n");

    /* Wrecked doesn't store expire times.. Additionally, there may
       be some clock drift, so we can't do time(NULL)-akill->time..
       So we'll just set it to the Wrecked default of one week.
     */
    if (nakill_wrecked)
	for (i = 0; i < nakill_wrecked; ++i)
	{
	    fprintf (f, "AK %s %s 0 %lu %d %s\n", akill_wrecked[i].mask,
		akill_wrecked[i].who, akill_wrecked[i].time, akill_wrecked[i].time == 0 ?
		0 : 604800, akill_wrecked[i].reason);
	    aout++;
	}

    if (nallow_wrecked)
	for (i = 0; i < nallow_wrecked; ++i)
	{
	    fprintf (f, "TR %s %d\n", allow_wrecked[i].host, allow_wrecked[i].amount);
	    tout++;
	}

    fprintf (f, "DE %d %d 0\n", aout, tout);

    fclose (f);

    return 0;
}

int convert_ircservices45_db ()
{
    int problem = 0;
    struct timeval now, tmp;

    if (notes)
    {
	printf ("\nConversion notes for IRCServices 4.5.x -> Cygnus:\n\n");
	printf ("  * Channel AKick setters will all be stored as ChanServ, since\n");
	printf ("    this information is not present in the IRCServices database.\n\n");
	printf ("  * Channel AKick timestamps will be the time of the conversion,\n");
	printf ("    since this information is not present in the IRCServices database.\n\n");
	printf ("  * Channel descriptions will be lost, as these are not in Cygnus.\n\n");
	printf ("  * KEEPTOPIC and PRIVATE flags will be lost, as these are not in Cygnus.\n\n");
	printf ("  * Channels with Topiclock will have Topiclock AOP after conversion,\n");
	printf ("    as IRCServices does not have multiple Topiclock levels.\n\n");
	printf ("  * Channel memos will be lost, as they are done differently in Cygnus.\n\n");
	printf ("  * Forbidden channels will be converted into Frozen channels.\n\n");
	printf ("  * Forbidden nicknames will be converted into Frozen nicknames.\n\n");
	printf ("  * AKill Exceptions with a limit will be converted into triggers.\n\n");
	printf ("  * CSOPs and SADMINs will both be added into the CSOP list.\n\n");
	printf ("  * LogonMSGs will not be converted, as Cygnus does not use them.\n\n");
	printf ("  * If you use encrypted passwords, you will not be able to\n");
	printf ("    convert or use the databases.\n\n");
	printf ("Do you want to continue with conversion?\n\n");
	printf ("[Y/N] ");

	scanf ("%s", opt);

	if (strchr (opt, 'n') || strchr (opt, 'N'))
	{
	    printf ("\nOk.\n\n");
	    return 0;
	}
    }

    printf ("\nPlease enter the path to your current services databases. For example,\n");
    printf ("/home/bob/services\n\n");
    printf ("If you're not sure, type \"QUIT\" below, go to the directory with your\n");
    printf ("databases, and type pwd. Re run the db converter (\"./dbtool\") and type\n");
    printf ("what pwd said here.\n\n");
    printf ("Option: ");

    scanf ("%s", opt);

    if (!strcasecmp (opt, "QUIT"))
    {
	printf ("\nOk.\n\n");
	return 0;
    }

    log ("Converting IRCServices 4.5.x databases...");

    printf ("\nConverting IRCServices 4.5.x databases.\n\n");
    printf ("Loading databases... ");

    gettimeofday (&(start), NULL);

    /* Load all of the DBs from disk. If the load function returns anything but 0,
       it's an error.
     */
    if (!(problem = load_ircservices45_ns ()))
	printf ("nick.db... ");
    else
    {
	printf ("\n\nProblem loading nick.db! %s.. skipping...\n",
	    problem == 1 ? "Incorrect version." :
	    problem == 2 ? "File not found." :
	    problem == 3 ? "Possible database corruption." :
	    problem == 4 ? "Encrypted passwords used, unable to convert!" : "Unknown problem... ;/");
    }

    if (!(problem = load_ircservices45_cs ()))
	printf ("chan.db... ");
    else
    {
	printf ("\n\nProblem loading chan.db! %s.. skipping...\n",
	    problem == 1 ? "Incorrect version." :
	    problem == 2 ? "File not found." :
	    problem == 3 ? "Possible database corruption." :
	    problem == 4 ? "Encrypted passwords used, unable to convert!" : "Unknown problem... ;/");
    }

    if (!(problem = load_ircservices45_rs ()))
	printf ("akill.db... exception.db... oper.db... ");
    else
    {
	printf ("\n\nProblem loading one of the OperServ dbs! %s.. skipping...\n",
	    problem == 1 ? "Incorrect version." :
	    problem == 2 ? "File not found." :
	    problem == 3 ? "Possible database corruption." : "Unknown problem... ;/");
    }

    printf ("\nSaving databases... ");

    if (!(problem = save_ircservices45_ns ()))
	printf ("nickserv.db... ");
    else
    {
	printf ("\n\nProblem saving nickserv.db! %s\n\n",
	    problem == 1 ? "Can't write to file." :
	    "Unknown problem... ;/");
	return 0;
    }

    if (!(problem = save_ircservices45_cs ()))
	printf ("chanserv.db... ");
    else
    {
	printf ("\n\nProblem saving chanserv.db! %s\n\n",
	    problem == 1 ? "Can't write to file." :
	    "Unknown problem... ;/");
	return 0;
    }

    if (!(problem = save_ircservices45_rs ()))
	printf ("rootserv.db... ");
    else
    {
	printf ("\n\nProblem saving rootserv.db! %s\n\n",
	    problem == 1 ? "Can't write to file." :
	    "Unknown problem... ;/");
	return 0;
    }

    gettimeofday (&now, NULL);
    timersub (&now, &start, &tmp);

    print_stats ();

    printf ("     Conversion took %d ms\n", tv2ms (&tmp));
    log ("     Conversion took %d ms", tv2ms (&tmp));

    printf ("\nAll done.\n\n");
    log ("\nAll done.");

    return 0;
}

int load_ircservices45_ns ()
{
    FILE *f;
    int i, j, unusedint;
    NickInfo_IRCServices45 *ni;
    char tmp[264], *unusedchar;
    long unusedlong;

    snprintf (tmp, sizeof (tmp), "%s/nick.db", opt);

    f = fopen (tmp, "r");

    while (!f)
    {
	printf ("\n\nUnable to locate %s/nick.db. Please specify the full path and\n", opt);
	printf ("filename of your NickServ database, or QUIT to quit.\n\n");
	printf ("Option: ");

	scanf ("%s", tmp);

	if (!strcasecmp (tmp, "QUIT"))
	    exit;

	f = fopen (tmp, "r");
    }

    if (!f)
	return 2;

    switch (i = get_file_version_ircservices45 (f, tmp))
    {
	case 11:
	    for (i = 0; i < 256; ++i)
		while (fgetc (f) == 1)
		{
		    ni = malloc (sizeof (NickInfo_IRCServices45));
		    SAFE (read_buffer_ircservices45 (ni->nick, f));
		    SAFE (read_buffer_ircservices45 (ni->pass, f));
		    alpha_insert_nick_ircservices45 (ni);
		    SAFE (read_string_ircservices45 (&ni->url, f));
		    SAFE (read_string_ircservices45 (&ni->email, f));
		    SAFE (read_string_ircservices45 (&ni->last_usermask, f));
		    SAFE (read_string_ircservices45 (&ni->last_realname, f));
		    SAFE (read_string_ircservices45 (&unusedchar, f)); /* lastquit */
		    free (unusedchar);
		    SAFE (read_int32_ircservices45 (&ni->time_registered, f));
		    SAFE (read_int32_ircservices45 (&ni->last_seen, f));
		    SAFE (read_int16_ircservices45 (&ni->status, f));
		    ni->status &= ~0xFF00;
		    if (ni->status & IS45NS_ENCRYPTEDPW)
			return 4;
		    SAFE (read_string_ircservices45 (&ni->link, f));
		    SAFE (read_int16_ircservices45 (&unusedint, f));
		    if (ni->link)
		    {
			lin++;
			SAFE (read_int16_ircservices45 (&unusedint, f));
		    }
		    else
		    {
			nin++;
			SAFE (read_int32_ircservices45 (&ni->flags, f));
			SAFE (read_ptr_ircservices45 ((void **)&ni->suspendinfo, f));
			if (ni->suspendinfo)
			{
			    SuspendInfo_IRCServices45 *si;
			    si = malloc (sizeof (*si));
			    SAFE (read_buffer_ircservices45 (si->who, f));
			    SAFE (read_string_ircservices45 (&si->reason, f));
			    SAFE (read_int32_ircservices45 (&si->suspended, f));
			    SAFE (read_int32_ircservices45 (&unusedlong, f));
			    ni->suspendinfo = si;
			}
			SAFE (read_int16_ircservices45 (&ni->accesscount, f));
			if (ni->accesscount)
			{
			    char **tmpaccess;
			    tmpaccess = malloc (sizeof (char *) * ni->accesscount);
			    ni->access = tmpaccess;
			    for (j = 0; j < ni->accesscount; j++, tmpaccess++)
				SAFE (read_string_ircservices45 (tmpaccess, f));
			}
			SAFE (read_int16_ircservices45 (&ni->memocount, f));
			SAFE (read_int16_ircservices45 (&ni->memomax, f));
			if (ni->memocount)
			{
			    Memo_IRCServices45 *memos;
			    memos = malloc (sizeof (Memo_IRCServices45) * ni->memocount);
			    ni->memos = memos;
			    for (j = 0; j < ni->memocount; j++, memos++)
			    {
				SAFE (read_int32_ircservices45 (&memos->number, f));
				SAFE (read_int16_ircservices45 (&memos->flags, f));
				SAFE (read_int32_ircservices45 (&memos->time, f));
				SAFE (read_buffer_ircservices45 (memos->sender, f));
				SAFE (read_string_ircservices45 (&memos->text, f));
			    }
			}
			SAFE (read_int16_ircservices45 (&unusedint, f));
			SAFE (read_int16_ircservices45 (&unusedint, f));
			SAFE (read_int16_ircservices45 (&unusedint, f));
		    }
		}
	    break;
	default:
	    return 1;
    }

    fclose (f);

    return 0;
}

int load_ircservices45_cs ()
{
    FILE *f;
    int i, j, n_levels, unusedint;
    ChannelInfo_IRCServices45 *ci;
    char tmp[264], *unusedchar, unusedbuf[NICKMAX];
    long unusedlong;

    snprintf (tmp, sizeof (tmp), "%s/chan.db", opt);

    f = fopen (tmp, "r");

    while (!f)
    {
	printf ("\n\nUnable to locate %s/chan.db. Please specify the full path and\n", opt);
	printf ("filename of your ChanServ database, or QUIT to quit.\n\n");
	printf ("Option: ");

	scanf ("%s", tmp);

	if (!strcasecmp (tmp, "QUIT"))
	    exit;

	f = fopen (tmp, "r");
    }

    if (!f)
	return 2;

    switch (i = get_file_version_ircservices45 (f, tmp))
    {
	case 11:
	    for (i = 0; i < 256; ++i)
		while (fgetc (f) == 1)
		{
		    ci = malloc (sizeof (ChannelInfo_IRCServices45));
		    SAFE (read_buffer_ircservices45 (ci->name, f));
		    alpha_insert_chan_ircservices45 (ci);
		    cin++;
		    SAFE (read_string_ircservices45 (&ci->founder, f));
		    SAFE (read_string_ircservices45 (&ci->successor, f));
		    SAFE (read_buffer_ircservices45 (ci->founderpass, f));
		    SAFE (read_string_ircservices45 (&ci->desc, f));
		    SAFE (read_string_ircservices45 (&ci->url, f));
		    SAFE (read_string_ircservices45 (&unusedchar, f));
		    free (unusedchar);
		    SAFE (read_int32_ircservices45 (&ci->time_registered, f));
		    SAFE (read_int32_ircservices45 (&ci->last_used, f));
		    SAFE (read_string_ircservices45 (&ci->last_topic, f));
		    SAFE (read_buffer_ircservices45 (ci->last_topic_setter, f));
		    SAFE (read_int32_ircservices45 (&ci->last_topic_time, f));
		    SAFE (read_int32_ircservices45 (&ci->flags, f));
		    if (ci->flags & IS45CI_ENCRYPTEDPW)
			return 4;
		    SAFE (read_ptr_ircservices45 ((void **)&ci->suspendinfo, f));
		    if (ci->suspendinfo)
		    {
			SuspendInfo_IRCServices45 *si;
			si = malloc (sizeof (*si));
			SAFE (read_buffer_ircservices45 (si->who, f));
			SAFE (read_string_ircservices45 (&si->reason, f));
			SAFE (read_int32_ircservices45 (&si->suspended, f));
			SAFE (read_int32_ircservices45 (&unusedlong, f));
			ci->suspendinfo = si;
		    }
		    SAFE (read_int16_ircservices45 (&n_levels, f));
		    ci->levels = malloc (18 * sizeof(*ci->levels));
		    for (j = 0; j < 18; j++)
			ci->levels[j] = 0;
		    for (j = 0; j < n_levels; j++)
		    {
			if (j < 18)
			    SAFE (read_int16_ircservices45 (&ci->levels[j], f));
			else
			    SAFE (read_int16_ircservices45 (&unusedint, f));
		    }
		    SAFE (read_int16_ircservices45 (&ci->accesscount, f));
		    if (ci->accesscount)
		    {
			ChanAccess_IRCServices45 *tmpaccess;
			tmpaccess = malloc (sizeof (ChanAccess_IRCServices45) * ci->accesscount);
			ci->access = tmpaccess;
			for (j = 0; j < ci->accesscount; j++)
			{
			    SAFE (read_int16_ircservices45 (&ci->access[j].in_use, f));
			    if (ci->access[j].in_use)
			    {
				SAFE (read_int16_ircservices45 (&ci->access[j].level, f));
				SAFE(read_string_ircservices45 (&ci->access[j].name, f));
				if (ci->access[j].name == NULL)
				    ci->access[j].in_use = 0;
			    }
			}
		    }
		    SAFE (read_int16_ircservices45 (&ci->akickcount, f));
		    if (ci->akickcount)
		    {
			AutoKick_IRCServices45 *akick;
			akick = malloc (sizeof (AutoKick_IRCServices45) * ci->akickcount);
			ci->akick = akick;
			for (j = 0; j < ci->akickcount; j++)
			{
			    SAFE (read_int16_ircservices45 (&ci->akick[j].in_use, f));
			    if (ci->akick[j].in_use)
			    {
				SAFE (read_int16_ircservices45 (&ci->akick[j].is_nick, f));
				SAFE (read_string_ircservices45 (&ci->akick[j].mask, f));
				SAFE (read_string_ircservices45 (&ci->akick[j].reason, f));
				SAFE (read_buffer_ircservices45 (ci->akick[j].who, f));
			    }
			}
		    }
		    SAFE (read_int32_ircservices45 (&ci->mlock_on, f));
		    SAFE (read_int32_ircservices45 (&ci->mlock_off, f));
		    SAFE (read_int32_ircservices45 (&ci->mlock_limit, f));
		    SAFE (read_string_ircservices45 (&ci->mlock_key, f));
		    SAFE (read_int16_ircservices45 (&ci->memocount, f));
		    SAFE (read_int16_ircservices45 (&unusedint, f)); /* memomax */
		    if (ci->memocount)
		    {
			for (j = 0; j < ci->memocount; j++)
			{
			    SAFE (read_int32_ircservices45 (&unusedlong, f));
			    SAFE (read_int16_ircservices45 (&unusedint, f));
			    SAFE (read_int32_ircservices45 (&unusedlong, f));
			    SAFE (read_buffer_ircservices45 (unusedbuf, f));
			    SAFE (read_string_ircservices45 (&unusedchar, f));
			    free (unusedchar);
			}
		    }
		    SAFE (read_string_ircservices45 (&ci->entry_message, f));
		}
	    break;
	default:
	    return 3;
    }

    fclose (f);

    return 0;
}

int load_ircservices45_rs ()
{
    FILE *f;
    int i;
    char tmp[264], unusedbuf[NICKMAX], *unusedchar;
    long unusedlong;

    snprintf (tmp, sizeof (tmp), "%s/akill.db", opt);

    f = fopen (tmp, "r");

    while (!f)
    {
	printf ("\n\nUnable to locate %s/akill.db. Please specify the full path and\n", opt);
	printf ("filename of your AutoKill database, or QUIT to quit.\n\n");
	printf ("Option: ");

	scanf ("%s", tmp);

	if (!strcasecmp (tmp, "QUIT"))
	    exit;

	f = fopen (tmp, "r");
    }

    if (!f)
	return 2;

    switch (i = get_file_version_ircservices45 (f, tmp))
    {
	case 11:
	    SAFE (read_int16_ircservices45 (&nakill_ircservices45, f));
	    if (nakill_ircservices45 < 8)
		akill_size_ircservices45 = 16;
	    else
		akill_size_ircservices45 = 2 * nakill_ircservices45;
	    akill_ircservices45 = malloc (sizeof (*akill_ircservices45) * akill_size_ircservices45);
	    if (!nakill_ircservices45 || nakill_ircservices45 == 1)
		break;
	    ain = nakill_ircservices45;
	    for (i = 0; i < nakill_ircservices45; ++i)
	    {
		SAFE (read_string_ircservices45 (&akill_ircservices45[i].mask, f));
		SAFE (read_string_ircservices45 (&akill_ircservices45[i].reason, f));
		SAFE (read_buffer_ircservices45 (akill_ircservices45[i].who, f));
		SAFE (read_int32_ircservices45 (&akill_ircservices45[i].time, f));
		SAFE (read_int32_ircservices45 (&akill_ircservices45[i].expires, f));
	    }

	    break;

	default:
	    return 1;
    }

    fclose (f);

    snprintf (tmp, sizeof (tmp), "%s/exception.db", opt);

    f = fopen (tmp, "r");

    while (!f)
    {
	printf ("\n\nUnable to locate %s/exception.db. Please specify the full path and\n", opt);
	printf ("filename of your Exception database, or QUIT to quit.\n\n");
	printf ("Option: ");

	scanf ("%s", tmp);

	if (!strcasecmp (tmp, "QUIT"))
	    exit;

	f = fopen (tmp, "r");
    }

    if (!f)
	return 2;

    switch (i = get_file_version_ircservices45 (f, tmp))
    {
	case 11:
	    SAFE (read_int16_ircservices45 (&nexception_ircservices45, f));
	    if (nexception_ircservices45 < 8)
		exception_size_ircservices45 = 16;
	    else
		exception_size_ircservices45 = 2 * nexception_ircservices45;
	    exception_ircservices45 = malloc (sizeof (*exception_ircservices45) * exception_size_ircservices45);
	    if (!nexception_ircservices45 || nexception_ircservices45 == 1)
		break;
	    tin = nexception_ircservices45;
	    for (i = 0; i < nexception_ircservices45; ++i)
	    {
		SAFE (read_string_ircservices45 (&exception_ircservices45[i].mask, f));
		SAFE (read_int16_ircservices45 (&exception_ircservices45[i].limit, f));
		SAFE (read_buffer_ircservices45 (unusedbuf, f));
		SAFE (read_string_ircservices45 (&unusedchar, f));
		free (unusedchar);
		SAFE (read_int32_ircservices45 (&unusedlong, f));
		SAFE (read_int32_ircservices45 (&unusedlong, f));
	    }

	    break;

	default:
	    return 1;
    }

    fclose (f);

    snprintf (tmp, sizeof (tmp), "%s/oper.db", opt);

    f = fopen (tmp, "r");

    while (!f)
    {
	printf ("\n\nUnable to locate %s/oper.db. Please specify the full path and\n", opt);
	printf ("filename of your Services Operator database, or QUIT to quit.\n\n");
	printf ("Option: ");

	scanf ("%s", tmp);

	if (!strcasecmp (tmp, "QUIT"))
	    exit;

	f = fopen (tmp, "r");
    }

    if (!f)
	return 2;

    switch (i = get_file_version_ircservices45 (f, tmp))
    {
	case 11:
	    SAFE (read_int16_ircservices45 (&nsadmin_ircservices45, f));
	    if (nsadmin_ircservices45 < 8)
		sadmin_size_ircservices45 = 16;
	    else
		sadmin_size_ircservices45 = 2 * nsadmin_ircservices45;
	    sadmins_ircservices45 = realloc (sadmins_ircservices45, sizeof (SOP_IRCServices45) * sadmin_size_ircservices45);
	    if (!nsadmin_ircservices45)
		break;
	    soin = nsadmin_ircservices45;
	    for (i = 0; i < nsadmin_ircservices45; ++i)
	    {
		SAFE (read_string_ircservices45 (&sadmins_ircservices45[i].nick, f));
	    }
	    SAFE (read_int16_ircservices45 (&nsop_ircservices45, f));
	    if (nsop_ircservices45 < 8)
		sop_size_ircservices45 = 16;
	    else
		sop_size_ircservices45 = 2 * nsop_ircservices45;
	    sops_ircservices45 = realloc (sops_ircservices45, sizeof (SOP_IRCServices45) * sop_size_ircservices45);
	    if (!nsop_ircservices45)
		break;
	    soin += nsop_ircservices45;
	    for (i = 0; i < nsop_ircservices45; ++i)
	    {
		SAFE (read_string_ircservices45 (&sops_ircservices45[i].nick, f));
	    }

	    break;

	default:
	    return 1;
    }

    fclose (f);

    return 0;
}

int save_ircservices45_ns ()
{
    FILE *f;
    NickInfo_IRCServices45 *ni, *tni;
    Memo_IRCServices45 *m;
    int i, j, haslinks = 0;
    char **tmpaccess;

    f = fopen ("nickserv.db", "w");

    if (!f)
	return 1;

    fprintf (f, "NV 3\n");

    for (i = 0; i < 256; ++i)
    {
	for (ni = nicklists_ircservices45[i]; ni; ni = ni->next)
	{
	    long outflags = 0;

	    /* Don't need to do anything if its a link */
	    if (ni->link)
	    {
		lout++;
		continue;
	    }

	    /* Check for applicable nickflags */
	    if (ni->status & IS45NS_NOEXPIRE)
		outflags |= CNF_HELD;

	    if ((ni->status & IS45NS_VERBOTEN) || (ni->suspendinfo))
		outflags |= CNF_FROZEN;

	    if (ni->flags & IS45NI_KILLPROTECT)
		outflags |= CNF_ENFORCE;

	    if (ni->flags & IS45NI_SECURE)
		outflags |= CNF_SECURE;

	    if (ni->flags & IS45NI_HIDE_EMAIL)
		outflags |= CNF_HIDEMAIL;

	    if (ni->flags & IS45NI_HIDE_MASK)
		outflags |= CNF_PRIVATE;

	    /* Set flags for CSOps */
	    for (j = 0; j < nsop_ircservices45; ++j)
	    {
		if (!strcasecmp (ni->nick, sops_ircservices45[j].nick))
		{
		    outflags |= CNF_CSOP;
		    sout++;
		    break;
		}
	    }

	    /* Set flags for SADMINs */
	    for (j = 0; j < nsadmin_ircservices45; ++j)
	    {
		if (!strcasecmp (ni->nick, sadmins_ircservices45[j].nick))
		{
		    outflags |= CNF_CSOP;
		    sout++;
		    break;
		}
	    }

	    fprintf (f, "NI %s %s %lu %lu 0 0 %li %d 0 0 %s %s\n", ni->nick,
		ni->pass, ni->time_registered, ni->last_seen, outflags,
		ni->memomax, ni->last_usermask,
		strlen (ni->last_realname) ? ni->last_realname : "unknown");

	    nout++;

	    for (tmpaccess = ni->access, j = 0; j < ni->accesscount; tmpaccess++, j++)
		fprintf (f, "AC %s\n", *tmpaccess);

	    /* Since we store linked nicks differently, we'll go through every nickname,
	       checking if each one is linked to this one.. Bit cpu intensive, but we're
	       not going to be running that long anyhow so who cares?
	     */
	    haslinks = 0;

	    for (j = 0; j < 256; ++j)
		for (tni = nicklists_ircservices45[j]; tni; tni = tni->next)
		    if (tni->link && !strcasecmp (tni->link, ni->nick))
		    {
			haslinks = 1;
			break;
		    }

	    if (haslinks)
	    {
		fprintf (f, "LN");

		for (j = 0; j < 256; ++j)
		    for (tni = nicklists_ircservices45[j]; tni; tni = tni->next)
			if (tni->link && !strcasecmp (tni->link, ni->nick))
			    fprintf (f, " %s", tni->nick);

		fprintf (f, "\n");
	    }

	    m = ni->memos;
	    for (j = 0; j < ni->memocount; ++j, ++m)
	    {
		fprintf (f, "MO %li %d %lu %s %s\n", m->number, m->flags,
		    m->time, m->sender, m->text);
		mout++;
	    }

	    if (ni->email)
		fprintf (f, "EM %s\n", ni->email);

	    if (ni->url)
		fprintf (f, "UR %s\n", ni->url);
	}
    }

    fprintf (f, "DE %d %d %d\n", nout, lout, mout);

    fclose (f);

    return 0;
}

int save_ircservices45_cs ()
{
    FILE *f;
    int i, j;
    ChannelInfo_IRCServices45 *ci;
    ChanAccess_IRCServices45 *ca;
    AutoKick_IRCServices45 *ak;

    f = fopen ("chanserv.db", "w");

    if (!f)
	return 1;

    fprintf (f, "CV 2\n");

    for (i = 0; i < 256; ++i)
    {
	for (ci = chanlists_ircservices45[i]; ci; ci = ci->next)
	{
	    long outflags = 0;

	    /* Check for applicable channel flags */
	    if (ci->flags & IS45CI_OPNOTICE)
		outflags |= CCF_VERBOSE;

	    if (ci->flags & IS45CI_NOEXPIRE)
		outflags |= CCF_HELD;

	    if (ci->levels[1] == 0)
		outflags |= CCF_VOPALL;

	    if ((ci->flags & IS45CI_VERBOTEN) || (ci->suspendinfo))
		outflags |= CCF_FROZEN;

	    /* These are the same thing in Cygnus */
	    if ((ci->flags & IS45CI_SECURE) || (ci->flags & IS45CI_SECUREOPS))
		outflags |= CCF_SECURE;

	    if (ci->flags & IS45CI_RESTRICTED)
		outflags |= CCF_RESTRICTED;

	    fprintf (f, "CI %s %s %s %lu %lu %li %li %li %d 0 0\n", ci->name,
		ci->founder, ci->founderpass, ci->time_registered, ci->last_used,
		outflags, ci->mlock_on ? is45flags_to_cygflags (ci->mlock_on) : 0,
		ci->mlock_off ? is45flags_to_cygflags (ci->mlock_off) : 0,
		(ci->flags & IS45CI_TOPICLOCK) ? 3 : 0);

	    /* The founder isn't in the access list, but we need them in there */
	    fprintf (f, "CA %s 5\n", ci->founder);

	    for (ca = ci->access, j = 0; j < ci->accesscount; ++ca, ++j)
	    {
		int newlevel = 0;

		if (!ca->in_use)
		    continue;

		/* Convert the levels to Cygnus-style
		    6 == AUTOVOICE
		   14 == AUTOHALFOP
		    4 == AUTOOP
		   16 == AUTOPROTECT (SOP)
		 */
		if (ca->level >= ci->levels[6])		/* VOP */
		    newlevel = 1;
		if (ca->level >= ci->levels[14])	/* HOP */
		    newlevel = 2;
		if (ca->level >= ci->levels[4])		/* AOP */
		    newlevel = 3;
		if (ca->level >= ci->levels[16])	/* SOP */
		    newlevel = 4;

		fprintf (f, "CA %s %d\n", ca->name, newlevel);
	    }

	    for (ak = ci->akick, j = 0; j < ci->akickcount; ++ak, ++j)
		fprintf (f, "AK %s %s %d %lu %s\n", ak->mask, ak->who,
		    ak->is_nick ? 2 : 1, time (NULL), ak->reason ? ak->reason : "");

	    if (ci->successor)
		fprintf (f, "SU %s\n", ci->successor);

	    if (ci->entry_message)
		fprintf (f, "GR %s\n", ci->entry_message);

	    if (ci->url)
		fprintf (f, "UR %s\n", ci->url);

	    if (ci->last_topic)
		fprintf (f, "CT %s %lu %s\n", ci->last_topic_setter ?
		    ci->last_topic_setter : "ChanServ", ci->last_topic_time,
		    ci->last_topic);

	    if (ci->mlock_key)
		fprintf (f, "KY %s\n", ci->mlock_key);

	    if (ci->mlock_limit)
		fprintf (f, "LM %li\n", ci->mlock_limit);

	    cout++;
	}
    }

    fprintf (f, "DE %d\n", cout);

    fclose (f);

    return 0;
}

int save_ircservices45_rs ()
{
    FILE *f;
    int i;

    f = fopen ("rootserv.db", "w");

    if (!f)
	return 1;

    fprintf (f, "RV 2\n");

    if (nakill_ircservices45)
	for (i = 0; i < nakill_ircservices45; ++i)
	{
	    fprintf (f, "AK %s %s 0 %lu %lu %s\n", akill_ircservices45[i].mask,
		akill_ircservices45[i].who, akill_ircservices45[i].time,
		(akill_ircservices45[i].expires == 0) ? 0 :
		akill_ircservices45[i].expires - akill_ircservices45[i].time,
		akill_ircservices45[i].reason);
	    aout++;
	}

    if (nexception_ircservices45)
	for (i = 0; i < nexception_ircservices45; ++i)
	{
	    if (exception_ircservices45[i].limit == 0)
	    {
		fprintf (f, "EX *@%s\n", exception_ircservices45[i].mask);
		eout++;
	    }
	    else
	    {
		fprintf (f, "TR *@%s %d\n", exception_ircservices45[i].mask,
		    exception_ircservices45[i].limit);
		tout++;
	    }
	}

    fprintf (f, "DE %d %d %d\n", aout, tout, eout);

    fclose (f);

    return 0;
}

int convert_sirv_db ()
{
    int problem = 0;
    struct timeval now, tmp;

    if (notes)
    {
	printf ("\nConversion notes for SirvNET 2.9.0 -> Cygnus:\n\n");
	printf ("  * Channel AKick setters will all be stored as ChanServ, since\n");
	printf ("    this information is not present in the SirvNET database.\n\n");
	printf ("  * Channel access list supporters will be lost, as this feature\n");
	printf ("    is not present in Cygnus.\n\n");
	printf ("  * Channel AKick timestamps will be the time of the conversion,\n");
	printf ("    since this information is not present in the SirvNET database.\n\n");
	printf ("  * Channel descriptions will be lost, as this is not in Cygnus.\n\n");
	printf ("  * KEEPTOPIC, PRIVATE, LEAVEOPS, OPERONLY, SOPONLY, SAONLY\n");
	printf ("    SRAONLY, CODERONLY, ABUSEONLY, UNSECURE, REMIND and PROTECTED\n");
	printf ("    channel flags will be lost, as these are not in Cygnus.\n\n");
	printf ("  * Forbidden channels will be lost in the conversion.\n\n");
	printf ("  * Forbidden nicknames will be lost in the conversion.\n\n");
	printf ("  * Memos marked as deleted will be dropped.\n\n");
	printf ("  * Channel CoFounders will be given SOP access.\n\n");
	printf ("  * Ignore, Q:Line, G:Line, Z:Line, AbuseServ and Log DBs will\n");
	printf ("    be lost.\n\n");
	printf ("  * If you use encrypted passwords, you will not be able to\n");
	printf ("    convert the databases.\n\n");
	printf ("Do you want to continue with conversion?\n\n");
	printf ("[Y/N] ");

	scanf ("%s", opt);

	if (strchr (opt, 'n') || strchr (opt, 'N'))
	{
	    printf ("\nOk.\n\n");
	    return 0;
	}
    }

    printf ("\nPlease enter the path to your current services databases. For example,\n");
    printf ("/home/bob/services\n\n");
    printf ("If you're not sure, type \"QUIT\" below, go to the directory with your\n");
    printf ("databases, and type pwd. Re run the db converter (\"./dbtool\") and type\n");
    printf ("what pwd said here.\n\n");
    printf ("Option: ");

    scanf ("%s", opt);

    if (!strcasecmp (opt, "QUIT"))
    {
	printf ("\nOk.\n\n");
	return 0;
    }

    log ("Converting SirvNET 2.9.0 databases...");

    printf ("\nConverting SirvNET 2.9.0 databases.\n\n");
    printf ("Loading databases... ");

    gettimeofday (&(start), NULL);

    /* Load all of the DBs from disk. If the load function returns anything but 0,
       it's an error.
     */
    if (!(problem = load_sirv_ns ()))
	printf ("nick.db... ");
    else
    {
	printf ("\n\nProblem loading nick.db! %s.. skipping...\n",
	    problem == 1 ? "Incorrect version." :
	    problem == 2 ? "File not found." :
	    problem == 3 ? "Possible database corruption." :
	    problem == 4 ? "Encrypted passwords used, unable to convert!" : "Unknown problem... ;/");
    }

    if (!(problem = load_sirv_cs ()))
	printf ("chan.db... ");
    else
    {
	printf ("\n\nProblem loading chan.db! %s.. skipping...\n",
	    problem == 1 ? "Incorrect version." :
	    problem == 2 ? "File not found." :
	    problem == 3 ? "Possible database corruption." :
	    problem == 4 ? "Encrypted passwords used, unable to convert!" : "Unknown problem... ;/");
    }

    if (!(problem = load_sirv_ms ()))
	printf ("memo.db... ");
    else
    {
	printf ("\n\nProblem loading memo.db! %s.. skipping...\n",
	    problem == 1 ? "Incorrect version." :
	    problem == 2 ? "File not found." :
	    problem == 3 ? "Possible database corruption." :
	    problem == 4 ? "Encrypted passwords used, unable to convert!" : "Unknown problem... ;/");
    }

    if (!(problem = load_sirv_rs ()))
	printf ("os_sop.db... os_sa.db... trigger.db... akill.db... ");
    else
    {
	printf ("\n\nProblem loading one of the OperServ dbs! %s.. skipping...\n",
	    problem == 1 ? "Incorrect version." :
	    problem == 2 ? "File not found." :
	    problem == 3 ? "Possible database corruption." :
	    problem == 4 ? "Encrypted passwords used, unable to convert!" : "Unknown problem... ;/");
    }

    printf ("\nSaving databases... ");

    if (!(problem = save_sirv_ns ()))
	printf ("nickserv.db... ");
    else
    {
	printf ("\n\nProblem saving nickserv.db! %s\n\n",
	    problem == 1 ? "Can't write to file." :
	    "Unknown problem... ;/");
	return 0;
    }

    if (!(problem = save_sirv_cs ()))
	printf ("chanserv.db... ");
    else
    {
	printf ("\n\nProblem saving chanserv.db! %s\n\n",
	    problem == 1 ? "Can't write to file." :
	    "Unknown problem... ;/");
	return 0;
    }

    if (!(problem = save_sirv_rs ()))
	printf ("rootserv.db... ");
    else
    {
	printf ("\n\nProblem saving rootserv.db! %s\n\n",
	    problem == 1 ? "Can't write to file." :
	    "Unknown problem... ;/");
	return 0;
    }

    gettimeofday (&now, NULL);
    timersub (&now, &start, &tmp);

    print_stats ();

    printf ("     Conversion took %d ms\n", tv2ms (&tmp));
    log ("     Conversion took %d ms", tv2ms (&tmp));

    printf ("\nAll done.\n\n");
    log ("\nAll done.");

    return 0;
}

int load_sirv_ns ()
{
    FILE *f;
    int ver, i, j;
    NickInfo_Sirv *ni;
    char tmp[264];

    snprintf (tmp, sizeof (tmp), "%s/nick.db", opt);

    f = fopen (tmp, "r");

    while (!f)
    {
        printf ("\n\nUnable to locate %s/nick.db. Please specify the full path and\n", opt);
        printf ("filename of your NickServ database, or QUIT to quit.\n\n");
	printf ("Option: ");
                    
        scanf ("%s", tmp);
    
        if (!strcasecmp (tmp, "QUIT"))
            exit;

        f = fopen (tmp, "r");
    }

    if (!f)
	return 2;

    switch (ver = get_file_version_sirv (f, tmp))
    {
      case 8:
        for (i = 33; i < 256; ++i)
	{
            while (fgetc(f) == 1)
	    {
                ni = malloc (sizeof (NickInfo_Sirv));
                   if (1 != fread (ni, sizeof (NickInfo_Sirv), 1, f))
                       return 3;

                if (ni->flags & SNI_ENCRYPTEDPW)
		    return 4;

                ni->flags &= ~(SNI_IDENTIFIED | SNI_RECOGNIZED);

                alpha_insert_nick_sirv (ni);
		nin++;

                if (ni->url)
                    ni->url = read_string (f, tmp);
                if (ni->email)
                    ni->email = read_string (f, tmp);
		if (ni->forward)
		    ni->forward = read_string (f, tmp);
                if (ni->hold)
                    ni->hold = read_string (f, tmp);
                if (ni->mark)
                    ni->mark = read_string (f, tmp);
                if (ni->forbid)
                    ni->forbid = read_string (f, tmp);
                if (ni->regemail)
                    ni->regemail = read_string (f, tmp);
                ni->last_usermask = read_string (f, tmp);
                ni->last_realname = read_string (f, tmp);

                if (ni->accesscount)
		{
                    char **tmpaccess;

                    tmpaccess = malloc (sizeof (char *) *ni->accesscount);
                    ni->access = tmpaccess;

                    for (j = 0; j < ni->accesscount; j++, tmpaccess++)
                        *tmpaccess = read_string (f, tmp);
                }

                ni->id_timestamp = 0;
            }
        }
        break;

      case 7:
      case 6:
      case 5:
      case 4:
      case 3:
      case 2:
      case 1:
      default:
	return 1;
    }

    fclose (f);

    return 0;
}

int load_sirv_ms ()
{
    FILE *f;
    int ver, i, j;
    MemoList_Sirv *ml;
    Memo_Sirv *memos;
    char tmp[264];

    snprintf (tmp, sizeof (tmp), "%s/memo.db", opt);

    f = fopen (tmp, "r");

    while (!f)
    {
        printf ("\n\nUnable to locate %s/memo.db. Please specify the full path and\n", opt);
        printf ("filename of your MemoServ database, or QUIT to quit.\n\n");
	printf ("Option: ");
                    
        scanf ("%s", tmp);
    
        if (!strcasecmp (tmp, "QUIT"))
            exit;
       
        f = fopen (tmp, "r");
    }

    if (!f)
	return 2;

    switch (ver = get_file_version_sirv (f, tmp))
    {
      case 8:
	for (i = 33; i < 256; ++i)
	{
	    while (fgetc(f) == 1)
	    {
		ml = malloc (sizeof (MemoList_Sirv));
		if (1 != fread (ml, sizeof (MemoList_Sirv), 1, f))
		    return 3;

		alpha_insert_memolist_sirv (ml);
		ml->memos = memos = malloc (sizeof (Memo_Sirv) * ml->n_memos);
		fread (memos, sizeof (Memo_Sirv), ml->n_memos, f);

                for (j = 0; j < ml->n_memos; ++j, ++memos)
		{
		    memos->text = read_string (f, tmp);
                    if (memos->chan)
                        memos->chan = read_string (f, tmp);
                }

		min = min + ml->n_memos;
	    }
	}
	break;

      case 7:
      case 6:
      case 5:
      case 4:
      case 3:
      case 2:
      case 1:
      default:
	return 1;
    }

    fclose (f);

    return 0;
}

int load_sirv_cs ()
{
    FILE *f;
    int ver, i, j;
    ChannelInfo_Sirv *ci;
    NickInfo_Sirv *ni;
    char tmp[264];

    snprintf (tmp, sizeof (tmp), "%s/chan.db", opt);

    f = fopen (tmp, "r");

    while (!f)
    {
        printf ("\n\nUnable to locate %s/chan.db. Please specify the full path and\n", opt);
        printf ("filename of your ChanServ database, or QUIT to quit.\n\n");
	printf ("Option: ");
                    
        scanf ("%s", tmp);
    
        if (!strcasecmp (tmp, "QUIT"))
            exit;
       
        f = fopen (tmp, "r");
    }

    if (!f)
	return 2;

    switch (ver = get_file_version_sirv (f, tmp))
    {
      case 8:
        for (i = 33; i < 256; ++i)
	{
            while (fgetc (f) == 1)
	    {
		ci = malloc (sizeof (ChannelInfo_Sirv));

                if (1 != fread (ci, sizeof (ChannelInfo_Sirv), 1, f))
                    return 3;

                if (ci->flags & SCI_ENCRYPTEDPW)
		    return 4;

                alpha_insert_chan_sirv (ci);

		cin++;

                ni = findnick_sirv (ci->founder);
                if (ni && ni->channelcount + 1 > ni->channelcount)
		    ni->channelcount++;

                ci->desc = read_string (f, tmp);
                if (ci->url)
                    ci->url = read_string (f, tmp);
                if (ci->email)
                    ci->email = read_string (f, tmp);
                if (ci->mlock_key)
                    ci->mlock_key = read_string (f, tmp);
                if (ci->last_topic)
                    ci->last_topic = read_string (f, tmp);
		if (ci->welcome)
		    ci->welcome = read_string (f, tmp);
                if (ci->hold)
                    ci->hold = read_string (f, tmp);
                if (ci->mark)
                    ci->mark = read_string (f, tmp);
                if (ci->freeze)
                    ci->freeze = read_string (f, tmp);
                if (ci->forbid)
                    ci->forbid = read_string (f, tmp);

                if (ci->accesscount)
		{
                    ChanAccess_Sirv *tmpaccess;
                    tmpaccess = malloc (sizeof (ChanAccess_Sirv) * ci->accesscount);
                    ci->access = tmpaccess;

                    if (ci->accesscount != fread (tmpaccess, sizeof (ChanAccess_Sirv), ci->accesscount, f))
			return 3;

                    for (j = 0; j < ci->accesscount; ++j, ++tmpaccess)
		    {
                        tmpaccess->name = read_string (f, tmp);
                        tmpaccess->sponser = read_string (f, tmp);
                    }

                    j = 0;
		    tmpaccess = ci->access;

                    while (j < ci->accesscount)
		    {
                        if (tmpaccess->is_nick < 0)
                        {
                            --ci->accesscount;
                            free (tmpaccess->name);
                            if (tmpaccess->sponser)
                                 free (tmpaccess->sponser);
                            if (j < ci->accesscount)
                                bcopy (tmpaccess + 1, tmpaccess, sizeof (*tmpaccess) * (ci->accesscount - j));
                        }
			else
			{
                            ++j;
			    ++tmpaccess;
                        }
                    }

                    if (ci->accesscount)
                        ci->access = realloc (ci->access, sizeof (ChanAccess_Sirv) * ci->accesscount);
                    else
		    {
                        free (ci->access);
                        ci->access = NULL;
                    }
                }

                if (ci->akickcount)
		{
                    AutoKick *akick;

                    akick = malloc (sizeof (AutoKick) * ci->akickcount);
                    ci->akick = akick;
                    if (ci->akickcount != fread (akick, sizeof (AutoKick), ci->akickcount, f))
			return 3;

                    for (j = 0; j < ci->akickcount; ++j, ++akick)
		    {
                        akick->name = read_string (f, tmp);

                        if (akick->reason)
                            akick->reason = read_string (f, tmp);
                    }

                    j = 0;
		    akick = ci->akick;

                    while (j < ci->akickcount)
		    {
                        if (akick->is_nick < 0)
			{
                            --ci->akickcount;
                            free (akick->name);
                            if (akick->reason)
                                free (akick->reason);
                            if (j < ci->akickcount)
                                bcopy (akick + 1, akick, sizeof (*akick) * (ci->akickcount - j));
                        }
			else
			{
                            ++j;
			    ++akick;
                        }
                    }

                    if (ci->akickcount)
                        ci->akick = realloc (ci->akick, sizeof (AutoKick) * ci->akickcount);
                    else
		    {
                        free (ci->akick);
                        ci->akick = NULL;
                    }
                }

                if (ci->levels)
		{
                    int n_entries;
		    unsigned int k;

                    ci->levels = NULL;
 
		    if (ci->levels)
			free (ci->levels);

		    ci->levels = malloc (12 * sizeof (*ci->levels));

		    for (k = 0; def_levels_sirv[k][0] >= 0; k++)
			ci->levels[def_levels_sirv[k][0]] = def_levels_sirv[k][1];

                    n_entries = fgetc (f) << 8 | fgetc (f);

                    if (n_entries < 0)
			return 3;

                    if (n_entries <= 12)
                        fread (ci->levels, sizeof (short), n_entries, f);
                    else
		    {
                        fread (ci->levels, sizeof (short), 12, f);
                        fseek (f, sizeof (short) * (n_entries - 12), SEEK_CUR);
                    }
                }
            }
        }

        break;

      case 7:
      case 6:
      case 5:
      case 4:
      case 3:
      case 2:
      case 1:
      default:
	return 1;

    }

    fclose (f);

    return 0;
}

int load_sirv_rs ()
{
    FILE *f;
    int i;
    char tmp[264];

    snprintf (tmp, sizeof (tmp), "%s/os_sop.db", opt);

    f = fopen (tmp, "r");

    while (!f)
    {
        printf ("\n\nUnable to locate %s/os_sop.db. Please specify the full path and\n", opt);
        printf ("filename of your Services Operator database, or QUIT to quit.\n\n");
	printf ("Option: ");
                    
        scanf ("%s", tmp);
    
        if (!strcasecmp (tmp, "QUIT"))
            exit;
       
        f = fopen (tmp, "r");
    }

    if (!f)
	return 2;

    switch (i = get_file_version_sirv (f, tmp))
    {
      case 8:
      case 7:
      case 6:
      case 5:
        i = fgetc (f) << 8 | fgetc (f);
        while (--i >= 0)
	{
	    sop_sirv[i] = read_string (f, tmp);
	    soin++;
	}
        break;

      default:
	return 1;
    }

    fclose (f);

    snprintf (tmp, sizeof (tmp), "%s/os_sa.db", opt);

    f = fopen (tmp, "r");

    while (!f)
    {
        printf ("\n\nUnable to locate %s/os_sa.db. Please specify the full path and\n", opt);
        printf ("filename of your Services Admin database, or QUIT to quit.\n\n");
	printf ("Option: ");
                    
        scanf ("%s", tmp);
    
        if (!strcasecmp (tmp, "QUIT"))
            exit;
       
        f = fopen (tmp, "r");
    }

    if (!f)
	return 2;

    switch (i = get_file_version_sirv (f, tmp))
    {
      case 8:
      case 7:
      case 6:
      case 5:
        i = fgetc (f) << 8 | fgetc (f);
        while (--i >= 0)
	{
	    sadmin_sirv[i] = read_string (f, tmp);
	    soin++;
	}
        break;

      default:
	return 1;
    }

    fclose (f);

    snprintf (tmp, sizeof (tmp), "%s/trigger.db", opt);

    f = fopen (tmp, "r");

    while (!f)
    {
        printf ("\n\nUnable to locate %s/trigger.db. Please specify the full path and\n", opt);
        printf ("filename of your Trigger database, or QUIT to quit.\n\n");
	printf ("Option: ");
                    
        scanf ("%s", tmp);
    
        if (!strcasecmp (tmp, "QUIT"))
            exit;
       
        f = fopen (tmp, "r");
    }

    if (!f)
	return 2;

    switch (i = get_file_version_sirv (f, tmp))
    {
      case 5:
      case 6:
      case 7:
      case 8:
        ntrigger_sirv = fgetc (f) * 256 + fgetc (f);

        if (ntrigger_sirv < 8)
            trigger_size_sirv = 16;
        else
            trigger_size_sirv = 2 * ntrigger_sirv;

        trigger_sirv = malloc (sizeof (*trigger_sirv) * trigger_size_sirv);

        if (!ntrigger_sirv)
	{
            fclose (f);
            break;
        }

        if (ntrigger_sirv != fread (trigger_sirv, sizeof (*trigger_sirv), ntrigger_sirv, f))
            return 3;

	tin = ntrigger_sirv;

        for (i = 0; i < ntrigger_sirv; i++)
            trigger_sirv[i].mask = read_string (f, tmp);

        break;

      default:
        return 1;
    }

    snprintf (tmp, sizeof (tmp), "%s/akill.db", opt);

    f = fopen (tmp, "r");

    while (!f)
    {
        printf ("\n\nUnable to locate %s/akill.db. Please specify the full path and\n", opt);
        printf ("filename of your AutoKill database, or QUIT to quit.\n\n");
	printf ("Option: ");
                    
        scanf ("%s", tmp);
    
        if (!strcasecmp (tmp, "QUIT"))
            exit;
       
        f = fopen (tmp, "r");
    }

    if (!f)
	return 2;

    switch (i = get_file_version_sirv (f, tmp))
    {
      case 5:
      case 6:
      case 7:
      case 8:
	nakill_sirv = fgetc (f) * 256 + fgetc (f);

	if (nakill_sirv < 8)
	    akill_size_sirv = 16;
	else
	    akill_size_sirv = 2 * nakill_sirv;

	akill_sirv = malloc (sizeof (*akill_sirv) * akill_size_sirv);

	if (!nakill_sirv)
	{
	    fclose (f);
	    break;
	}

	if (nakill_sirv != fread (akill_sirv, sizeof (*akill_sirv), nakill_sirv, f))
	    return 3;

	ain = nakill_sirv;

	for (i = 0; i < nakill_sirv; i++)
	{
	    akill_sirv[i].mask = read_string (f, tmp);
	    akill_sirv[i].reason = read_string (f, tmp);
	}

	break;

      default:
	return 1;
    }

    return 0;
}

int save_sirv_ns ()
{
    FILE *f;
    NickInfo_Sirv *ni;
    Memo_Sirv *m;
    MemoList_Sirv *ml;
    int i, j;
    char **tmpaccess;

    f = fopen ("nickserv.db", "w");

    if (!f)
	return 1;

    fprintf (f, "NV 3\n");

    for (i = 33; i < 256; ++i)
    {
	for (ni = nicklists_sirv[i]; ni; ni = ni->next)
	{
	    long outflags = 0;

	    /* We don't do forbids. Skip to the next nick. */
	    if (ni->flags & SNI_VERBOTEN)
	    {
		log ("Dropping forbidden nickname %s", ni->nick);
		ndrop++;
		continue;
	    }

	    /* Sirv's auth isn't compatible with ours. */
	    if (ni->flags & SNI_AUTH)
	    {
		log ("Dropping unauthed nickname %s", ni->nick);
		ndrop++;
		continue;
	    }

	    /* Check for applicable nickflags */
	    if (ni->flags & SNI_KILLPROTECT)
		outflags |= CNF_ENFORCE;

	    if (ni->flags & SNI_SECURE)
		outflags |= CNF_SECURE;

	    if (ni->flags & SNI_PRIVATE)
		outflags |= CNF_PRIVATE;

	    if (ni->flags & SNI_NOMEMO)
		outflags |= CNF_NOMEMO;

	    if (ni->flags & SNI_NEVEROP)
		outflags |= CNF_NEVEROP;

	    if (ni->flags & SNI_NOOP)
		outflags |= CNF_NOOP;

	    if (ni->flags & SNI_MARK)
		outflags |= CNF_MARKED;

	    if (ni->flags & SNI_HIDE_EMAIL)
		outflags |= CNF_HIDEMAIL;

	    if (ni->flags & SNI_HOLD)
		outflags |= CNF_HELD;

	    if (ni->flags & SNI_EMAILMEMOS)
		outflags |= CNF_MEMOMAIL;

	    /* Set flags for CSOps. We combine Sirv's SOP and SADMINs into CSOps. */
	    for (j = 0; j < 32; j++)
		if (sadmin_sirv[j] && !strcasecmp (ni->nick, sadmin_sirv[j]))
		{
		    outflags |= CNF_CSOP;
		    sout++; 
		}

	    for (j = 0; j < 32; j++)
		if (sop_sirv[j] && !strcasecmp (ni->nick, sop_sirv[j]))
		{
		    outflags |= CNF_CSOP;
		    sout++; 
		}

	    fprintf (f, "NI %s %s %lu %lu %lu 0 %li 25 0 0 %s %s\n",
		ni->nick, ni->pass, ni->time_registered, ni->last_seen,
		ni->id_timestamp, outflags, ni->last_usermask,
		strlen (ni->last_realname) ? ni->last_realname : "unknown");

	    nout++;

	    for (tmpaccess = ni->access, j = 0; j < ni->accesscount; ++tmpaccess, ++j)
		fprintf (f, "AC %s\n", *tmpaccess);

	    ml = find_memolist_sirv (ni->nick);

	    if (ml)
	    {
		for (j = 0, m = ml->memos; j < ml->n_memos; ++j, ++m)
		{
		    if (m->flags & SMF_UNREAD)
			m->flags &= CMF_UNREAD;

		    if (!(m->flags & SMF_DEL))
		    {
			fprintf (f, "MO %li %d %lu %s %s\n", m->number, m->flags,
			    m->time, m->sender, m->text);

			mout++;
		    }
		    else
		    {
			log ("Dropping marked-deleted memo for %s", ni->nick);
			mdrop++;
		    }
		}
	    }

	    if (ni->email)
		fprintf (f, "EM %s\n", ni->email);

	    if (ni->url)
		fprintf (f, "UR %s\n", ni->url);

	    if (ni->icq)
		fprintf (f, "UN %li\n", ni->icq);

	    if (ni->forward)
		fprintf (f, "FW %s\n", ni->forward);
	}
    }

    fprintf (f, "DE %d 0 %d\n", nout, mout);

    fclose (f);

    return 0;
}

int save_sirv_cs ()
{
    FILE *f;
    int i, j;
    ChannelInfo_Sirv *ci;
    ChanAccess_Sirv *ca;
    AutoKick *ak;

    f = fopen ("chanserv.db", "w");

    if (!f)
	return 1;

    fprintf (f, "CV 2\n");

    for (i = 33; i < 256; ++i)
    {
	for (ci = chanlists_sirv[i]; ci; ci = ci->next)
	{
	    long outflags = 0;
	    int topiclock = 0;

	    /* We don't do forbids. Skip to the next channel. */
	    if (ci->flags & SCI_VERBOTEN)
	    {
		log ("Dropping forbidden channel %s", ci->name);
		cdrop++;
		continue;
	    }

	    /* Check for applicable channel flags */
	    if (ci->flags & SCI_RESTRICTED)
		outflags |= CCF_RESTRICTED;

	    if (ci->flags & SCI_CLOSED || ci->flags & SCI_FREEZECHAN)
		outflags |= CCF_FROZEN;

	    if (ci->flags & SCI_OPGUARD || ci->flags & SCI_IDENT)
		outflags |= CCF_SECURE;

	    if (ci->flags & SCI_HELDCHAN)
		outflags |= CCF_HELD;

	    if (ci->flags & SCI_MARKCHAN)
		outflags |= CCF_MARKED;

	    if (ci->topic_allow == 3)
		topiclock = 1;

	    if (ci->topic_allow == 5)
		topiclock = 3;

	    if (ci->topic_allow == 10)
		topiclock = 4;

	    if (ci->topic_allow == 15)
		topiclock = 5;

	    fprintf (f, "CI %s %s %s %lu %lu %li %li %li %d 0 0\n", ci->name,
		ci->founder, ci->founderpass, ci->time_registered, ci->last_used,
		outflags, ci->mlock_on ? sirvflags_to_cygflags (ci->mlock_on) : 0,
		ci->mlock_off ? sirvflags_to_cygflags (ci->mlock_off) : 0,
		topiclock);

	    /* The founder isn't in the access list, but we need them in there on Cygnus */
	    fprintf (f, "CA %s 5\n", ci->founder);

	    for (ca = ci->access, j = 0; j < ci->accesscount; ++ca, ++j)
	    {
		int newlevel = 0;

		if (!ca->is_nick)
		    continue;

		if (!findnick_sirv (ca->name))
		{
		    log ("Dropping access list entry of %s on %s", ca->name, ci->name);
		    adrop++;
		    continue;
		}

		/* Convert the Sirv-style db levels to Cygnus-style. */
		if (ca->level == 3)
		    newlevel = 1;

		if (ca->level == 5)
		    newlevel = 3;

		if (ca->level == 10 || ca->level == 13)
		    newlevel = 4;

		if (ca->level == 15)
		{
		    newlevel = 5;

		    /* No one should have this level.. Warn of it */
		    log ("*** Warning: Founder-level access for %s on %s!", ca->name, ci->name);
		}

		/* If by some miracle we get here and still don't have a level assigned,
		   give them VOP and warn about it in the log..
		 */
		if (!newlevel)
		{
		    newlevel = 1;
		    log ("*** Warning: Couldn't get access level for %s on %s! Giving VOP.", ca->name, ci->name);
		}

		fprintf (f, "CA %s %d\n", ca->name, newlevel);
	    }

	    for (ak = ci->akick, j = 0; j < ci->akickcount; ++ak, ++j)
		fprintf (f, "AK %s ChanServ %d %lu %s\n", ak->name,
		    ak->is_nick ? 2 : 1, time (NULL), ak->reason ? ak->reason : "");

	    if (ci->url)
		fprintf (f, "UR %s\n", ci->url);

	    if (ci->welcome)
		fprintf (f, "GR %s\n", ci->welcome);

	    if (ci->last_topic && strlen (ci->last_topic))
		fprintf (f, "CT %s %lu %s\n", ci->last_topic_setter ?
		    ci->last_topic_setter : "ChanServ", ci->last_topic_time,
		    ci->last_topic);

	    if (ci->mlock_key)
		fprintf (f, "KY %s\n", ci->mlock_key);

	    if (ci->mlock_limit)
		fprintf (f, "LM %li\n", ci->mlock_limit);

	    cout++;
	}
    }

    fprintf (f, "DE %d\n", cout);

    fclose (f);

    return 0;
}

int save_sirv_rs ()
{
    FILE *f;
    int i;

    f = fopen ("rootserv.db", "w");

    if (!f)
	return 1;

    fprintf (f, "RV 2\n");

    /* Sirv doesn't store expire times.. Additionally, there may
       be some clock drift, so we can't do time(NULL)-akill->time..
       So we'll just set it to the Sirv default of one week.
     */
    if (nakill_sirv)
	for (i = 0; i < nakill_sirv; ++i)
	{
	    fprintf (f, "AK %s %s 0 %lu %lu %s\n", akill_sirv[i].mask,
		akill_sirv[i].who, akill_sirv[i].time, akill_sirv[i].expires,
		akill_sirv[i].reason);
	    aout++;
	}

    if (ntrigger_sirv)
	for (i = 0; i < ntrigger_sirv; ++i)
	{
	    fprintf (f, "TR %s %li\n", trigger_sirv[i].mask, trigger_sirv[i].tvalue);
	    tout++;
	}

    fprintf (f, "DE %d %d 0\n", aout, tout);

    fclose (f);

    return 0;
}
