
/* MySQL functions.
 *
 * (C) 2003-2008 Anope Team
 * Contact us at info@anope.org
 *
 * Please read COPYING and README for further details.
 *
 * Based on the original code of Epona by Lara.
 * Based on the original code of Services by Andy Church. 
 * 
 * $Id: mysql.c 1462 2008-10-01 22:01:27Z viper $ 
 *
 */
#include "services.h"

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

/* Database Global Variables */
MYSQL *mysql;                   /* MySQL Handler */
MYSQL_RES *mysql_res;           /* MySQL Result  */
MYSQL_FIELD *mysql_fields;      /* MySQL Fields  */
MYSQL_ROW mysql_row;            /* MySQL Row     */

int mysql_is_connected = 0;     /* Are we currently connected? */

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

/* Throw a mysql error into the logs. If severity is MYSQL_ERROR, we
 * also exit Anope...
 */
void db_mysql_error(int severity, char *msg)
{
    static char buf[512];

    if (mysql_error(mysql)) {
        snprintf(buf, sizeof(buf), "MySQL %s %s: %s", msg,
                 severity == MYSQL_WARNING ? "warning" : "error",
                 mysql_error(mysql));
    } else {
        snprintf(buf, sizeof(buf), "MySQL %s %s", msg,
                 severity == MYSQL_WARNING ? "warning" : "error");
    }

    log_perror(buf);

    if (severity == MYSQL_ERROR) {
        log_perror("MySQL FATAL error... aborting.");
        exit(0);
    }

}

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

/* Initialize the MySQL code */
int db_mysql_init()
{

    /* If the host is not defined, assume we don't want MySQL */
    if (!MysqlHost) {
        do_mysql = 0;
        alog("MySQL: has been disabled.");
        return 0;
    } else {
        do_mysql = 1;
        alog("MySQL: has been enabled.");
        alog("MySQL: client version %s.", mysql_get_client_info());
    }

    /* The following configuration options are required.
     * If missing disable MySQL to avoid any problems.   
     */

    if ((do_mysql) && (!MysqlName || !MysqlUser)) {
        do_mysql = 0;
        alog("MySQL Error: Set all required configuration options.");
        return 0;
    }

    if (!db_mysql_open()) {
        do_mysql = 0;
        return 0;
    }

    return 1;
}

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

/* Open a connection to the mysql database. Return 0 on failure, or
 * 1 on success. If this succeeds, we're guaranteed of a working
 * mysql connection (unless something unexpected happens ofcourse...)
 */
int db_mysql_open()
{
    /* If MySQL is disabled, return 0 */
    if (!do_mysql)
        return 0;

    /* If we are reported to be connected, ping MySQL to see if we really are
     * still connected. (yes mysql_ping() returns 0 on success)
     */
    if (mysql_is_connected && !mysql_ping(mysql))
        return 1;
    
    mysql_is_connected = 0;
    
    mysql = mysql_init(NULL);
    if (mysql == NULL) {
        db_mysql_error(MYSQL_WARNING, "Unable to create mysql object");
        return 0;
    }

    if (!MysqlPort)
        MysqlPort = MYSQL_DEFAULT_PORT;
	
    if (!mysql_real_connect(mysql, MysqlHost, MysqlUser, MysqlPass, MysqlName, MysqlPort, MysqlSock, 0)) {
        log_perror("MySQL Error: Cant connect to MySQL: %s\n", mysql_error(mysql));
        return 0;
    }

    mysql_is_connected = 1;

    return 1;

}


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

/* Perform a MySQL query. Return 1 if the query succeeded and 0 if the
 * query failed. Before returning failure, re-try the query a few times
 * and die if it still fails.
 */
int db_mysql_query(char *sql)
{
    int lcv;

    if (!do_mysql)
        return 0;

    if (debug)
        alog("debug: MySQL: %s", sql);

    /* Try as many times as configured in MysqlRetries */
    for (lcv = 0; lcv < MysqlRetries; lcv++) {
        if (db_mysql_open() && (mysql_query(mysql, sql) == 0))
            return 1;

        /* If we get here, we could not run the query */
        log_perror("Unable to run query: %s\n", mysql_error(mysql));

        /* Wait for MysqlRetryGap seconds and try again */
        sleep(MysqlRetryGap);
    }

    /* Unable to run the query even after MysqlRetries tries */
    db_mysql_error(MYSQL_WARNING, "query");

    return 0;

}

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

/* Quote a string to be safely included in a query. The result of this
 * function is allocated; it MUST be freed by the caller.
 */
char *db_mysql_quote(char *sql)
{
    int slen;
    char *quoted;


    if (!sql)
        return sstrdup("");

    slen = strlen(sql);
    quoted = malloc((1 + (slen * 2)) * sizeof(char));

    mysql_real_escape_string(mysql, quoted, sql, slen);
    
    return quoted;

}

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

/* Close the MySQL database connection. */
int db_mysql_close()
{
    mysql_close(mysql);

    mysql_is_connected = 0;

    return 1;
}

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

/* Try to execute a query and issue a warning when failed. Return 1 on
 * success and 0 on failure.
 */
int db_mysql_try(const char *fmt, ...)
{
    va_list args;
    static char sql[MAX_SQL_BUF];

    va_start(args, fmt);
    vsnprintf(sql, MAX_SQL_BUF, fmt, args);
    va_end(args);

    if (!db_mysql_query(sql)) {
        log_perror("Can't create sql query: %s", sql);
        db_mysql_error(MYSQL_WARNING, "query");
        return 0;
    }
    
    return 1;
}

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

/* Return a string to insert into a SQL query. The string will, once
 * evaluated by MySQL, result in a the given pass encoded in the encryption
 * type selected for MysqlSecure
 *
 * This should be removed since Rob properly did encryption modules... -GD
 */
char *db_mysql_secure(char *pass)
{
    char epass[BUFSIZE];
    char tmp_pass[PASSMAX];

    /* Initialize the buffer. Bug #86 */
    memset(epass, '\0', BUFSIZE);
    memset(tmp_pass, '\0', PASSMAX);

    /* We couldnt decrypt the pass... */
    if (enc_decrypt(pass, tmp_pass, PASSMAX - 1) != 1) {
        snprintf(epass, sizeof(epass), "'%s'", pass);
    } else {                    /* if we could decrypt the pass */
        if (!pass) {
            snprintf(epass, sizeof(epass), "''");
        } else if ((!MysqlSecure) || (strcmp(MysqlSecure, "") == 0)) {
            snprintf(epass, sizeof(epass), "'%s'", tmp_pass);
        } else if (strcmp(MysqlSecure, "des") == 0) {
            snprintf(epass, sizeof(epass), "ENCRYPT('%s')", tmp_pass);
        } else if (strcmp(MysqlSecure, "md5") == 0) {
            snprintf(epass, sizeof(epass), "MD5('%s')", tmp_pass);
        } else if (strcmp(MysqlSecure, "sha") == 0) {
            snprintf(epass, sizeof(epass), "SHA('%s')", tmp_pass);
        } else {
            snprintf(epass, sizeof(epass), "ENCODE('%s','%s')", tmp_pass,
                     MysqlSecure);
        }
    }
    return sstrdup(epass);
}

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

/*
 * NickServ Specific Secion
 */

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

/* Save the given NickRequest into the database
 * Return 1 on success, 0 on failure
 * These tables are tagged and will be cleaned:
 * - anope_ns_request
 */
int db_mysql_save_ns_req(NickRequest * nr)
{
    int ret;
    char *q_nick, *q_passcode, *q_password, *q_email;

    q_nick = db_mysql_quote(nr->nick);
    q_passcode = db_mysql_quote(nr->passcode);
    q_password = db_mysql_quote(nr->password);
    q_email = db_mysql_quote(nr->email);

    ret = db_mysql_try("UPDATE anope_ns_request "
                       "SET passcode = '%s', password = '%s', email = '%s', requested = %d, active = 1 "
                       "WHERE nick = '%s'",
                       q_passcode, q_password, q_email, (int) nr->requested,
                       q_nick);

    if (ret && (mysql_affected_rows(mysql) == 0)) {
    	ret = db_mysql_try("INSERT DELAYED INTO anope_ns_request "
    	                   "(nick, passcode, password, email, requested, active) "
                           "VALUES ('%s', '%s', '%s', '%s', %d, 1)",
                           q_nick, q_passcode, q_password, q_email, 
                           (int) nr->requested);
	}

    free(q_nick);
    free(q_passcode);
    free(q_password);
    free(q_email);
    
    return ret;
}

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

/* Save the given NickCore into the database
 * Also save the access list and memo's for this user
 * Return 1 on success, 0 on failure
 * These tables are tagged and will be cleaned:
 * - anope_ns_core
 * - anope_ns_alias
 * - anope_ns_access
 * - anope_ms_info (serv='NICK')
 */
int db_mysql_save_ns_core(NickCore * nc)
{
    int ret;
    int i;
    char *q_display, *q_pass, *epass, *q_email, *q_greet, *q_url,
        *q_access, *q_sender, *q_text;

    q_display = db_mysql_quote(nc->display);
    q_pass = db_mysql_quote(nc->pass);
    q_email = db_mysql_quote(nc->email);
    q_greet = db_mysql_quote(nc->greet);
    q_url = db_mysql_quote(nc->url);

    epass = db_mysql_secure(q_pass);
    free(q_pass);

    /* Let's take care of the core itself */
    /* Update the existing records */
    ret = db_mysql_try("UPDATE anope_ns_core "
                       "SET pass = %s, email = '%s', greet = '%s', icq = %d, url = '%s', flags = %d, language = %d, accesscount = %d, memocount = %d, "
                       "    memomax = %d, channelcount = %d, channelmax = %d, active = 1 "
                       "WHERE display = '%s'",
                       epass, q_email, q_greet, nc->icq, q_url, nc->flags,
                       nc->language, nc->accesscount, nc->memos.memocount,
                       nc->memos.memomax, nc->channelcount, nc->channelmax,
                       q_display);

    /* Our previous UPDATE affected no rows, therefore this is a new record */
    if (ret && (mysql_affected_rows(mysql) == 0)) {
        ret = db_mysql_try("INSERT DELAYED INTO anope_ns_core "
                           "(display, pass, email, greet, icq, url, flags, language, accesscount, memocount, memomax, channelcount, channelmax, active) "
                           "VALUES ('%s', %s, '%s', '%s', %d, '%s', %d, %d, %d, %d, %d, %d, %d, 1)",
                           q_display, epass, q_email, q_greet, nc->icq, q_url,
                           nc->flags, nc->language, nc->accesscount,
                           nc->memos.memocount, nc->memos.memomax,
                           nc->channelcount, nc->channelmax);
    }

    /* Now let's do the access */
    for (i = 0; ret && (i < nc->accesscount); i++) {
        q_access = db_mysql_quote(nc->access[i]);

        ret = db_mysql_try("UPDATE anope_ns_access "
                           "SET access = '%s' "
                           "WHERE display = '%s'",
                           q_access, q_display);

        if (ret && (mysql_affected_rows(mysql) == 0)) {
            ret = db_mysql_try("INSERT DELAYED INTO anope_ns_access "
                               "(display, access) "
                               "VALUES ('%s','%s')",
                               q_display, q_access);
        }

        free(q_access);
    }

    /* Memos */
    for (i = 0; ret && (i < nc->memos.memocount); i++) {
        q_sender = db_mysql_quote(nc->memos.memos[i].sender);
        q_text = db_mysql_quote(nc->memos.memos[i].text);

        ret = db_mysql_try("UPDATE anope_ms_info "
                           "SET receiver = '%s', number = %d, flags = %d, time = %d, sender = '%s', text = '%s', active = 1 "
                           "WHERE  nm_id = %d AND serv = 'NICK'",
                           q_display, nc->memos.memos[i].number,
                           nc->memos.memos[i].flags,
                           (int) nc->memos.memos[i].time, q_sender, q_text,
                           nc->memos.memos[i].id);
        
        if (ret && (mysql_affected_rows(mysql) == 0)) {
            ret = db_mysql_try("INSERT INTO anope_ms_info "
                               "(receiver, number, flags, time, sender, text, serv, active) "
                               "VALUES ('%s', %d, %d, %d, '%s', '%s', 'NICK', 1)",
                               q_display, nc->memos.memos[i].number,
                               nc->memos.memos[i].flags,
                               (int) nc->memos.memos[i].time, q_sender,
                               q_text);

            /* This is to make sure we can UPDATE memos instead of TRUNCATE
             * the table each time and then INSERT them all again. Ideally
             * everything in core would have it's dbase-id stored, but that's
             * something for phase 3. -GD
             */
            if (ret)
                nc->memos.memos[i].id = mysql_insert_id(mysql);
        }

        free(q_sender);
        free(q_text);
    }

    free(q_display);
    free(epass);
    free(q_email);
    free(q_greet);
    free(q_url);
    
    return ret;
}


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

/* Save the given NickAlias into the database
 * Return 1 on success, 0 on failure
 * These tables are tagged and will be cleaned:
 * - anope_ns_core
 * - anope_ns_alias
 * - anope_ns_access
 * - anope_ms_info (serv='NICK')
 */
int db_mysql_save_ns_alias(NickAlias * na)
{
    int ret;
    char *q_nick, *q_lastmask, *q_lastrname, *q_lastquit, *q_display;

    q_nick = db_mysql_quote(na->nick);
    q_lastmask = db_mysql_quote(na->last_usermask);
    q_lastrname = db_mysql_quote(na->last_realname);
    q_lastquit = db_mysql_quote(na->last_quit);
    q_display = db_mysql_quote(na->nc->display);

    ret = db_mysql_try("UPDATE anope_ns_alias "
                       "SET last_usermask = '%s', last_realname = '%s', last_quit = '%s', time_registered = %d, last_seen = %d, status = %d, "
                       "    display = '%s', active = 1 "
                       "WHERE nick = '%s'",
                       q_lastmask, q_lastrname, q_lastquit,
                       (int) na->time_registered, (int) na->last_seen,
                       (int) na->status, q_display, q_nick);

    /* Our previous UPDATE affected no rows, therefore this is a new record */
    if (ret && (mysql_affected_rows(mysql) == 0)) {
        ret = db_mysql_try("INSERT DELAYED INTO anope_ns_alias "
                           "(nick, last_usermask, last_realname, last_quit, time_registered, last_seen, status, display, active) "
                           "VALUES ('%s', '%s', '%s', '%s', %d, %d, %d, '%s', 1)",
                           q_nick, q_lastmask, q_lastrname, q_lastquit,
                           (int) na->time_registered, (int) na->last_seen,
                           (int) na->status, q_display);
    }

    free(q_nick);
    free(q_lastmask);
    free(q_lastrname);
    free(q_lastquit);
    free(q_display);
    
    return ret;
}

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

/*
 * ChanServ Specific Secion
 */

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

/* Save the given ChannelInfo into the database
 * Also save the access list, levels, akicks, badwords, ttb, and memo's for this channel
 * Return 1 on success, 0 on failure
 * These tables are tagged and will be cleaned:
 * - anope_cs_info
 * - anope_cs_access
 * - anope_cs_levels
 * - anope_cs_akicks
 * - anope_cs_badwords
 * - anope_cs_ttb
 * - anope_ms_info (serv='CHAN')
 */
int db_mysql_save_cs_info(ChannelInfo * ci)
{
    int ret;
    int i;
    char *q_name;
    char *q_founder;
    char *q_successor;
    char *q_pass;
    char *e_pass;
    char *q_desc;
    char *q_url;
    char *q_email;
    char *q_lasttopic;
    char *q_lasttopicsetter;
    char *q_forbidby;
    char *q_forbidreason;
    char *q_mlock_key;
    char *q_mlock_flood;
    char *q_mlock_redirect;
    char *q_entrymsg;
    char *q_botnick;
    char *q_sender;
    char *q_text;
    char *q_accessdisp;
    char *q_akickdisp;
    char *q_akickreason;
    char *q_akickcreator;
    char *q_badwords;

    q_name = db_mysql_quote(ci->name);
    if (ci->founder) {
        q_founder = db_mysql_quote(ci->founder->display);
    } else {
        q_founder = db_mysql_quote("");
    }
    if (ci->successor) {
        q_successor = db_mysql_quote(ci->successor->display);
    } else {
        q_successor = db_mysql_quote("");
    }
    q_pass = db_mysql_quote(ci->founderpass);
    q_desc = db_mysql_quote(ci->desc);
    q_url = db_mysql_quote(ci->url);
    q_email = db_mysql_quote(ci->email);
    q_lasttopic = db_mysql_quote(ci->last_topic);
    q_lasttopicsetter = db_mysql_quote(ci->last_topic_setter);
    q_forbidby = db_mysql_quote(ci->forbidby);
    q_forbidreason = db_mysql_quote(ci->forbidreason);
    q_mlock_key = db_mysql_quote(ci->mlock_key);
    q_mlock_flood = db_mysql_quote(ci->mlock_flood);
    q_mlock_redirect = db_mysql_quote(ci->mlock_redirect);
    q_entrymsg = db_mysql_quote(ci->entry_message);
    if (ci->bi) {
        q_botnick = db_mysql_quote(ci->bi->nick);
    } else {
        q_botnick = db_mysql_quote("");
    }

    e_pass = db_mysql_secure(q_pass);
    free(q_pass);

    /* Let's take care of the core itself */
    ret = db_mysql_try("UPDATE anope_cs_info "
                       "SET founder = '%s', successor = '%s', founderpass = %s, descr = '%s', url = '%s', email = '%s', time_registered = %d, "
                       "    last_used = %d, last_topic = '%s', last_topic_setter = '%s', last_topic_time = %d, flags = %d, forbidby = '%s', "
                       "    forbidreason = '%s', bantype = %d, accesscount = %d, akickcount = %d, mlock_on = %d,  mlock_off = %d, mlock_limit = %d, "
                       "    mlock_key = '%s', mlock_flood = '%s', mlock_redirect = '%s', entry_message = '%s', memomax = %d, botnick = '%s', botflags = %d, "
                       "    bwcount = %d, capsmin = %d, capspercent = %d, floodlines = %d, floodsecs = %d, repeattimes = %d, active = 1 "
                       "WHERE name = '%s'",
                       q_founder, q_successor, e_pass, q_desc, q_url, q_email,
                       (int) ci->time_registered, (int) ci->last_used,
                       q_lasttopic, q_lasttopicsetter,
                       (int) ci->last_topic_time, (int) ci->flags, q_forbidby,
                       q_forbidreason, (int) ci->bantype,
                       (int) ci->accesscount, (int) ci->akickcount,
                       (int) ci->mlock_on, (int) ci->mlock_off,
                       (int) ci->mlock_limit, q_mlock_key, q_mlock_flood,
                       q_mlock_redirect, q_entrymsg, (int) ci->memos.memomax,
                       q_botnick, (int) ci->botflags, (int) ci->bwcount,
                       (int) ci->capsmin, (int) ci->capspercent,
                       (int) ci->floodlines, (int) ci->floodsecs,
                       (int) ci->repeattimes, q_name);

    /* Our previous UPDATE affected no rows, therefore this is a new record */
    if (ret && (mysql_affected_rows(mysql) == 0)) {
        ret = db_mysql_try("INSERT DELAYED INTO anope_cs_info "
                           "(name, founder, successor, founderpass, descr, url, email, time_registered, last_used,  last_topic, last_topic_setter, "
                           "    last_topic_time, flags, forbidby, forbidreason, bantype, accesscount, akickcount, mlock_on, mlock_off, mlock_limit, "
                           "    mlock_key, mlock_flood, mlock_redirect, entry_message, botnick, botflags, bwcount, capsmin, capspercent, floodlines, "
                           "    floodsecs, repeattimes, active) "
                           "VALUES ('%s', '%s', '%s', %s, '%s', '%s', '%s', %d, %d, '%s', '%s', %d, %d, '%s', '%s', %d, %d, %d, %d, %d, %d, '%s', '%s', "
                           "        '%s', '%s', '%s', %d, %d, %d, %d, %d, %d, %d, 1)",
                           q_name, q_founder, q_successor, e_pass, q_desc,
                           q_url, q_email, (int) ci->time_registered,
                           (int) ci->last_used, q_lasttopic,
                           q_lasttopicsetter, (int) ci->last_topic_time,
                           (int) ci->flags, q_forbidby, q_forbidreason,
                           (int) ci->bantype, (int) ci->accesscount,
                           (int) ci->akickcount, (int) ci->mlock_on,
                           (int) ci->mlock_off, (int) ci->mlock_limit,
                           q_mlock_key, q_mlock_flood, q_mlock_redirect,
                           q_entrymsg, q_botnick, (int) ci->botflags,
                           (int) ci->bwcount, (int) ci->capsmin,
                           (int) ci->capspercent, (int) ci->floodlines,
                           (int) ci->floodsecs, (int) ci->repeattimes);
    }

    /* Memos */
    for (i = 0; ret && (i < ci->memos.memocount); i++) {
        q_sender = db_mysql_quote(ci->memos.memos[i].sender);
        q_text = db_mysql_quote(ci->memos.memos[i].text);

        ret = db_mysql_try("UPDATE anope_ms_info "
                           "SET receiver = '%s', number = %d, flags = %d, time = %d, sender = '%s', text = '%s', active = 1 "
                           "WHERE nm_id = %d AND serv = 'CHAN'",
                           q_name, ci->memos.memos[i].number,
                           ci->memos.memos[i].flags,
                           (int) ci->memos.memos[i].time, q_sender, q_text,
                           ci->memos.memos[i].id);
        
        if (ret && (mysql_affected_rows(mysql) == 0)) {
            ret = db_mysql_try("INSERT INTO anope_ms_info "
                               "(receiver, number,flags, time, sender, text, serv, active) "
                               "VALUES ('%s', %d, %d, %d, '%s', '%s', 'CHAN', 1)",
                               q_name, ci->memos.memos[i].number,
                               ci->memos.memos[i].flags,
                               (int) ci->memos.memos[i].time, q_sender,
                               q_text);

            /* See comment at db_mysql_save_ns_core */
            if (ret)
            	ci->memos.memos[i].id = mysql_insert_id(mysql);
        }

        free(q_sender);
        free(q_text);
    }

    /* Access */
    for (i = 0; ret && (i < ci->accesscount); i++) {
        if (ci->access[i].in_use) {
            q_accessdisp = db_mysql_quote(ci->access[i].nc->display);
            
            ret = db_mysql_try("UPDATE anope_cs_access "
                               "SET in_use = %d, level = %d, last_seen = %d, active = 1 "
                               "WHERE channel = '%s' AND display = '%s'",
                               (int) ci->access[i].in_use,
                               (int) ci->access[i].level,
                               (int) ci->access[i].last_seen,
                               q_name, q_accessdisp);
            
            if (ret && (mysql_affected_rows(mysql) == 0)) {
                ret = db_mysql_try("INSERT DELAYED INTO anope_cs_access "
                                   "(channel, display, in_use, level, last_seen, active) "
                                   "VALUES ('%s', '%s', %d, %d, %d, 1)",
                                   q_name, q_accessdisp,
                                   (int) ci->access[i].in_use,
                                   (int) ci->access[i].level,
                                   (int) ci->access[i].last_seen);
            }
            
            free(q_accessdisp);
        }
    }

    /* Levels */
    for (i = 0; ret && (i < CA_SIZE); i++) {
        ret = db_mysql_try("UPDATE anope_cs_levels "
                           "SET level = %d, active = 1 "
                           "WHERE channel = '%s' AND position = %d",
                           (int) ci->levels[i], q_name, i);
        
        if (ret && (mysql_affected_rows(mysql) == 0)) {
            ret = db_mysql_try("INSERT DELAYED INTO anope_cs_levels "
                               "(channel, position, level, active) "
                               "VALUES ('%s', %d, %d, 1)",
                               q_name, i, (int) ci->levels[i]);
        }
    }

    /* Akicks */
    for (i = 0; ret && (i < ci->akickcount); i++) {
        if (ci->akick[i].flags & AK_USED) {
            if (ci->akick[i].flags & AK_ISNICK)
                q_akickdisp = db_mysql_quote(ci->akick[i].u.nc->display);
            else
                q_akickdisp = db_mysql_quote(ci->akick[i].u.mask);

            q_akickreason = db_mysql_quote(ci->akick[i].reason);
            q_akickcreator = db_mysql_quote(ci->akick[i].creator);
        } else {
            q_akickdisp = "";
            q_akickreason = "";
            q_akickcreator = "";
        }

        ret = db_mysql_try("UPDATE anope_cs_akicks "
                           "SET flags = %d, reason = '%s', creator = '%s', addtime = %d, active = 1 "
                           "WHERE channel = '%s' AND dmask = '%s'",
                           (int) ci->akick[i].flags, q_akickreason,
                           q_akickcreator, (ci->akick[i].flags & AK_USED ?
                                             (int) ci->akick[i].addtime : 0),
                           q_name, q_akickdisp);
        
        if (ret && (mysql_affected_rows(mysql) == 0)) {
            ret = db_mysql_try("INSERT DELAYED INTO anope_cs_akicks "
                               "(channel, dmask, flags, reason, creator, addtime, active) "
                               "VALUES ('%s', '%s', %d, '%s', '%s', %d, 1)",
                               q_name, q_akickdisp, (int) ci->akick[i].flags,
                               q_akickreason, q_akickcreator,
                               (ci->akick[i].flags & AK_USED ?
                                 (int) ci->akick[i].addtime : 0));
        }

        if (ci->akick[i].flags & AK_USED) {
            free(q_akickdisp);
            free(q_akickreason);
            free(q_akickcreator);
        }
    }

    /* Bad Words */
    for (i = 0; ret && (i < ci->bwcount); i++) {
        if (ci->badwords[i].in_use) {
            q_badwords = db_mysql_quote(ci->badwords[i].word);

            ret = db_mysql_try("UPDATE anope_cs_badwords "
                               "SET type = %d, active = 1 "
                               "WHERE channel = '%s' AND word = '%s'",
                               (int) ci->badwords[i].type, q_name,
                               q_badwords);
            
            if (ret && (mysql_affected_rows(mysql) == 0)) {
                ret = db_mysql_try("INSERT DELAYED INTO anope_cs_badwords "
                                   "(channel, word, type, active) "
                                   "VALUES ('%s', '%s', %d, 1)",
                                   q_name, q_badwords,
                                   (int) ci->badwords[i].type);
            }

            free(q_badwords);
        }
    }

    /* TTB's */
    for (i = 0; ret && (i < TTB_SIZE); i++) {
        ret = db_mysql_try("UPDATE anope_cs_ttb "
                           "SET value = %d, active = 1 "
                           "WHERE channel = '%s' AND ttb_id = %d",
                           ci->ttb[i], q_name, i);
        
        if (ret && (mysql_affected_rows(mysql) == 0)) {
            ret = db_mysql_try("INSERT DELAYED INTO anope_cs_ttb "
                               "(channel, ttb_id, value, active) "
                               "VALUES ('%s', %d, %d, 1)",
                               q_name, i, ci->ttb[i]);
        }
    }

    free(q_name);
    free(q_founder);
    free(q_successor);
    free(e_pass);
    free(q_desc);
    free(q_url);
    free(q_email);
    free(q_lasttopic);
    free(q_lasttopicsetter);
    free(q_mlock_key);
    free(q_mlock_flood);
    free(q_mlock_redirect);
    free(q_entrymsg);
    free(q_botnick);
    free(q_forbidby);
    free(q_forbidreason);
    
    return ret;
}

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


/*
 * OperServ Specific Section
 */

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

/* Save the OperServ database into MySQL
 * Return 1 on success, 0 on failure
 * These tables are tagged and will be cleaned:
 * - anope_os_akills
 * - anope_os_sglines
 * - anope_os_sqlines
 * - anope_os_szlines
 * These tables are emptied:
 * - anope_os_core
 */

int db_mysql_save_os_db(unsigned int maxucnt, unsigned int maxutime,
                         SList * ak, SList * sgl, SList * sql, SList * szl)
{
	int ret;
    int i;
    Akill *akl;
    SXLine *sl;
    char *q_user;
    char *q_host;
    char *q_mask;
    char *q_by;
    char *q_reason;


    /* First save the core info */
    ret = db_mysql_try("INSERT DELAYED INTO anope_os_core "
                       "(maxusercnt, maxusertime, akills_count, sglines_count, sqlines_count, szlines_count) "
                       "VALUES (%d, %d, %d, %d, %d, %d)",
                       maxucnt, maxutime, ak->count, sgl->count, sql->count,
                       szl->count);

    /* Next save all AKILLs */
    for (i = 0; ret && (i < ak->count); i++) {
        akl = ak->list[i];
        q_user = db_mysql_quote(akl->user);
        q_host = db_mysql_quote(akl->host);
        q_by = db_mysql_quote(akl->by);
        q_reason = db_mysql_quote(akl->reason);

        ret = db_mysql_try("UPDATE anope_os_akills "
                           "SET xby = '%s', reason = '%s', seton = %d, expire = %d, active = 1 "
                           "WHERE user = '%s' AND host = '%s'",
                           q_by, q_reason, (int) akl->seton,
                           (int) akl->expires, q_user, q_host);
        
        if (ret && (mysql_affected_rows(mysql) == 0)) {
            ret = db_mysql_try("INSERT DELAYED INTO anope_os_akills "
                               "(user, host, xby, reason, seton, expire, active) "
                               "VALUES ('%s', '%s', '%s', '%s', %d, %d, 1)",
                                q_user, q_host, q_by, q_reason,
                                (int) akl->seton, (int) akl->expires);
        }

        free(q_user);
        free(q_host);
        free(q_by);
        free(q_reason);
    }

    /* Time to save the SGLINEs */
    for (i = 0; ret && (i < sgl->count); i++) {
        sl = sgl->list[i];
        q_mask = db_mysql_quote(sl->mask);
        q_by = db_mysql_quote(sl->by);
        q_reason = db_mysql_quote(sl->reason);

        ret = db_mysql_try("UPDATE anope_os_sglines "
                           "SET xby = '%s', reason = '%s', seton = %d, expire = %d, active = 1 "
                           "WHERE mask = '%s'",
                           q_by, q_reason, (int) sl->seton, (int) sl->expires,
                           q_mask);
        
        if (ret && (mysql_affected_rows(mysql) == 0)) {
            ret = db_mysql_try("INSERT DELAYED INTO anope_os_sglines "
                               "(mask, xby, reason, seton, expire, active) "
                               "VALUES ('%s', '%s', '%s', %d, %d, 1)",
                               q_mask, q_by, q_reason, (int) sl->seton,
                               (int) sl->expires);
        }

        free(q_mask);
        free(q_by);
        free(q_reason);
    }

    /* Save the SQLINEs */
    for (i = 0; ret && (i < sql->count); i++) {
        sl = sql->list[i];

        q_mask = db_mysql_quote(sl->mask);
        q_by = db_mysql_quote(sl->by);
        q_reason = db_mysql_quote(sl->reason);

        ret = db_mysql_try("UPDATE anope_os_sqlines "
                           "SET xby = '%s', reason = '%s',  seton = %d, expire = %d, active = 1 "
                           "WHERE mask = '%s'",
                           q_by, q_reason, (int) sl->seton, (int) sl->expires,
                           q_mask);
        
        if (ret && (mysql_affected_rows(mysql) == 0)) {
            ret = db_mysql_try("INSERT DELAYED INTO anope_os_sqlines "
                               "(mask, xby, reason, seton, expire, active) "
                               "VALUES ('%s', '%s', '%s', %d, %d, 1)",
                               q_mask, q_by, q_reason, (int) sl->seton,
                               (int) sl->expires);
        }

        free(q_mask);
        free(q_by);
        free(q_reason);
    }

    /* Now save the SZLINEs */
    for (i = 0; ret && (i < szl->count); i++) {
        sl = szl->list[i];

        q_mask = db_mysql_quote(sl->mask);
        q_by = db_mysql_quote(sl->by);
        q_reason = db_mysql_quote(sl->reason);

        ret = db_mysql_try("UPDATE anope_os_szlines "
                           "SET xby = '%s', reason = '%s', seton = %d, expire = %d, active = 1 "
                           "WHERE mask = '%s'",
                           q_by, q_reason, (int) sl->seton, (int) sl->expires,
                           q_mask);
        
        if (ret && (mysql_affected_rows(mysql) == 0)) {
            ret = db_mysql_try("INSERT DELAYED INTO anope_os_szlines "
                               "(mask, xby, reason, seton, expire, active) "
                               "VALUES ('%s', '%s', '%s', %d, %d, 1)",
                               q_mask, q_by, q_reason, (int) sl->seton,
                               (int) sl->expires);
        }

        free(q_mask);
        free(q_by);
        free(q_reason);
    }
    
    return ret;
}

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

/* Save the given NewsItem
 * These tables are tagged and will be cleaned:
 * - anope_os_news
 */
int db_mysql_save_news(NewsItem * ni)
{
	int ret;
    char *q_text;
    char *q_who;

    q_text = db_mysql_quote(ni->text);
    q_who = db_mysql_quote(ni->who);

    ret = db_mysql_try("UPDATE anope_os_news "
                       "SET ntext = '%s', who = '%s', active = 1 "
                       "WHERE type = %d AND num = %d AND `time` = %d",
                       q_text, q_who, ni->type, ni->num, (int) ni->time);
    
    if (ret && (mysql_affected_rows(mysql) == 0)) {
        ret = db_mysql_try("INSERT DELAYED INTO anope_os_news "
                           "(type, num, ntext, who, `time`, active) "
                           "VALUES (%d, %d, '%s', '%s', %d, 1)",
                           ni->type, ni->num, q_text, q_who, (int) ni->time);
    }

    free(q_text);
    free(q_who);
    
    return ret;
}

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

/* Save the given Exception
 * These tables are tagged and will be cleaned:
 * - anope_os_exceptions
 */

int db_mysql_save_exceptions(Exception * e)
{
	int ret;
    char *q_mask;
    char *q_who;
    char *q_reason;

    q_mask = db_mysql_quote(e->mask);
    q_who = db_mysql_quote(e->who);
    q_reason = db_mysql_quote(e->reason);

    ret = db_mysql_try("UPDATE anope_os_exceptions "
                       "SET lim = %d, who = '%s', reason = '%s', `time` = %d, expires = %d, active = 1 "
                       "WHERE mask = '%s'",
                       e->limit, q_who, q_reason, (int) e->time,
                       (int) e->expires, q_mask);
    
    if (ret && (mysql_affected_rows(mysql)) == 0) {
        ret = db_mysql_try("INSERT DELAYED INTO anope_os_exceptions "
                           "(mask, lim, who, reason, `time`, expires, active) "
                           "VALUES ('%s', %d, '%s', '%s', %d, %d, 1)",
                           q_mask, e->limit, q_who, q_reason, (int) e->time,
                           (int) e->expires);
    }

    free(q_mask);
    free(q_who);
    free(q_reason);
    
    return ret;
}

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


/*
 * HostServ Specific Section
 */

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

/* Save the given HostCore
 * These tables are tagged and will be cleaned:
 * - anope_hs_core
 */

int db_mysql_save_hs_core(HostCore * hc)
{
	int ret;
    char *q_nick;
    char *q_ident;
    char *q_host;
    char *q_creator;

    q_nick = db_mysql_quote(hc->nick);
    q_ident = db_mysql_quote(hc->vIdent);
    q_host = db_mysql_quote(hc->vHost);
    q_creator = db_mysql_quote(hc->creator);

    ret = db_mysql_try("UPDATE anope_hs_core "
                       "SET vident = '%s', vhost = '%s', creator = '%s', `time` = %d, active = 1 "
                       "WHERE nick = '%s'",
                       q_ident, q_host, q_creator, (int) hc->time, q_nick);
    
    if (ret && (mysql_affected_rows(mysql) == 0)) {
        ret = db_mysql_try("INSERT DELAYED INTO anope_hs_core "
                           "(nick, vident, vhost, creator, `time`, active) "
                           "VALUES ('%s', '%s', '%s', '%s', %d, 1)",
                           q_nick, q_ident, q_host, q_creator,
                           (int) hc->time);
    }

    free(q_nick);
    free(q_ident);
    free(q_host);
    free(q_creator);
    
    return ret;
}

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

/*
 * BotServ Specific Section
 */

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

int db_mysql_save_bs_core(BotInfo * bi)
{
	int ret;
    char *q_nick;
    char *q_user;
    char *q_host;
    char *q_real;

    q_nick = db_mysql_quote(bi->nick);
    q_user = db_mysql_quote(bi->user);
    q_host = db_mysql_quote(bi->host);
    q_real = db_mysql_quote(bi->real);

    ret = db_mysql_try("UPDATE anope_bs_core "
                       "SET user = '%s', host = '%s', rname = '%s', flags = %d, created = %d, chancount = %d, active = 1 "
                       "WHERE nick = '%s'",
                       q_user, q_host, q_real, bi->flags, (int) bi->created,
                       bi->chancount, q_nick);
    
    if (ret && (mysql_affected_rows(mysql) == 0)) {
        ret = db_mysql_try("INSERT DELAYED INTO anope_bs_core "
                           "(nick, user, host, rname, flags, created, chancount, active) "
                           "VALUES ('%s', '%s', '%s', '%s', %d, %d, %d, 1)",
                           q_nick, q_user, q_host, q_real, bi->flags,
                           (int) bi->created, bi->chancount);
    }

    free(q_nick);
    free(q_user);
    free(q_host);
    free(q_real);
    
    return ret;
}

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

/* Some loading code! */

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

int db_mysql_load_bs_dbase(void)
{
	int ret;
    BotInfo *bi;

    if (!do_mysql)
        return 0;

    ret = db_mysql_try("SELECT nick, user, host, rname, flags, created "
                       "FROM anope_bs_core "
                       "WHERE active = 1");
    
    if (!ret)
        return 0;
    
    mysql_res = mysql_use_result(mysql);
        
    while ((mysql_row = mysql_fetch_row(mysql_res))) {
        bi = makebot(mysql_row[0]);
        bi->user = sstrdup(mysql_row[1]);
        bi->host = sstrdup(mysql_row[2]);
        bi->real = sstrdup(mysql_row[3]);
        bi->flags = strtol(mysql_row[4], (char **) NULL, 10);
        bi->created = strtol(mysql_row[5], (char **) NULL, 10);
        bi->chancount = strtol(mysql_row[6], (char **) NULL, 10);
    }

    mysql_free_result(mysql_res);
    
    return 1;
}

int db_mysql_load_hs_dbase(void)
{
	int ret;
    int32 time;

    if (!do_mysql)
        return 0;

    ret = db_mysql_try("SELECT nick, vident, vhost, creator, `time` "
                       "FROM anope_hs_core "
                       "WHERE active = 1");

    if (!ret)
        return 0;
    
    mysql_res = mysql_use_result(mysql);

    while ((mysql_row = mysql_fetch_row(mysql_res))) {
        time = strtol(mysql_row[4], (char **) NULL, 10);
        addHostCore(mysql_row[0], mysql_row[1], mysql_row[2], mysql_row[3],
                    time);
    }

    mysql_free_result(mysql_res);
    
    return 1;
}

int db_mysql_load_news(void)
{
	int ret;
    int i;

    if (!do_mysql)
        return 0;

    ret = db_mysql_try("SELECT type, num, ntext, who, `time` "
                       "FROM anope_os_news "
                       "WHERE active = 1");

    if (!ret)
        return 0;
    
    mysql_res = mysql_store_result(mysql);

    nnews = mysql_num_rows(mysql_res);
    if (nnews < 8)              /* 2^3 */
        news_size = 16;         /* 2^4 */
    else if (nnews >= 16384)    /* 2^14 */
        news_size = 32767;      /* 2^15 - 1 */
    else
        news_size = 2 * nnews;

    news = scalloc(news_size, sizeof(*news));

    i = 0;
    while ((mysql_row = mysql_fetch_row(mysql_res))) {
        news[i].type = strtol(mysql_row[0], (char **) NULL, 10);
        news[i].num = strtol(mysql_row[1], (char **) NULL, 10);
        news[i].text = sstrdup(mysql_row[2]);
        snprintf(news[i].who, NICKMAX, "%s", mysql_row[3]);
        news[i].time = strtol(mysql_row[4], (char **) NULL, 10);
        i++;
    }

    mysql_free_result(mysql_res);
    
    return 1;
}

int db_mysql_load_exceptions(void)
{
	int ret;
    int i;

    if (!do_mysql)
        return 0;

    ret = db_mysql_try("SELECT mask, lim, who, reason, `time`, expires "
                       "FROM anope_os_exceptions "
                       "WHERE active = 1");

    if (!ret)
        return 0;
    
    mysql_res = mysql_store_result(mysql);
    nexceptions = mysql_num_rows(mysql_res);
    exceptions = scalloc(nexceptions, sizeof(Exception));

    i = 0;
    while ((mysql_row = mysql_fetch_row(mysql_res))) {
        exceptions[i].mask = sstrdup(mysql_row[0]);
        exceptions[i].limit = strtol(mysql_row[1], (char **) NULL, 10);
        snprintf(exceptions[i].who, NICKMAX, "%s", mysql_row[2]);
        exceptions[i].reason = sstrdup(mysql_row[3]);
        exceptions[i].time = strtol(mysql_row[4], (char **) NULL, 10);
        exceptions[i].expires = strtol(mysql_row[5], (char **) NULL, 10);
        i++;
    }

    mysql_free_result(mysql_res);
    
    return 1;
}

int db_mysql_load_os_dbase(void)
{
	int ret;
    Akill *ak;
    SXLine *sl;
    int akc, sglc, sqlc, szlc;

    if (!do_mysql)
        return 0;

    ret = db_mysql_try("SELECT maxusercnt, maxusertime, akills_count, sglines_count, sqlines_count, szlines_count "
                       "FROM anope_os_core");
    
    if (!ret)
        return 0;
    
    mysql_res = mysql_use_result(mysql);

    if ((mysql_row = mysql_fetch_row(mysql_res))) {
        maxusercnt = strtol(mysql_row[0], (char **) NULL, 10);
        maxusertime = strtol(mysql_row[1], (char **) NULL, 10);
        /* I'm not too happy with the idea of storing thse counts in a field
         * instead of just using mysql_num_rows on the actual tables when
         * filling the data. For now this will do, but it's bound to give
         * problems sooner or later... (it probably does if you are looking
         * at this) -GD
         */
        akc = strtol(mysql_row[2], (char **) NULL, 10);
        sglc = strtol(mysql_row[3], (char **) NULL, 10);
        sqlc = strtol(mysql_row[4], (char **) NULL, 10);
        szlc = strtol(mysql_row[5], (char **) NULL, 10);
    } else {
        maxusercnt = 0;
        maxusertime = time(NULL);
        akc = 0;
        sglc = 0;
        sqlc = 0;
        szlc = 0;
    }

    mysql_free_result(mysql_res);


    /* Load the AKILLs */

    ret = db_mysql_try("SELECT user, host, xby, reason, seton, expire "
                       "FROM anope_os_akills "
                       "WHERE active = 1");

    if (!ret)
        return 0;
    
    mysql_res = mysql_use_result(mysql);
    slist_setcapacity(&akills, akc);

    while ((mysql_row = mysql_fetch_row(mysql_res))) {
        ak = scalloc(1, sizeof(Akill));
        ak->user = sstrdup(mysql_row[0]);
        ak->host = sstrdup(mysql_row[1]);
        ak->by = sstrdup(mysql_row[2]);
        ak->reason = sstrdup(mysql_row[3]);
        ak->seton = strtol(mysql_row[4], (char **) NULL, 10);
        ak->expires = strtol(mysql_row[5], (char **) NULL, 10);
        slist_add(&akills, ak);
    }

    mysql_free_result(mysql_res);


    /* Load the SGLINEs */

    ret = db_mysql_try("SELECT mask, xby, reason, seton, expire "
                       "FROM anope_os_sglines "
                       "WHERE active = 1");

    if (!ret)
        return 0;
    
    mysql_res = mysql_use_result(mysql);
    slist_setcapacity(&sglines, sglc);

    while ((mysql_row = mysql_fetch_row(mysql_res))) {
        sl = scalloc(1, sizeof(SXLine));
        sl->mask = sstrdup(mysql_row[0]);
        sl->by = sstrdup(mysql_row[1]);
        sl->reason = sstrdup(mysql_row[2]);
        sl->seton = strtol(mysql_row[3], (char **) NULL, 10);
        sl->expires = strtol(mysql_row[4], (char **) NULL, 10);
        slist_add(&sglines, sl);
    }

    mysql_free_result(mysql_res);


    /* Load the SQLINEs */

    ret = db_mysql_try("SELECT mask, xby, reason, seton, expire "
                       "FROM anope_os_sqlines "
                       "WHERE active = 1");
    
    if (!ret)
        return 0;

    mysql_res = mysql_use_result(mysql);
    slist_setcapacity(&sqlines, sqlc);

    while ((mysql_row = mysql_fetch_row(mysql_res))) {
        sl = scalloc(1, sizeof(SXLine));
        sl->mask = sstrdup(mysql_row[0]);
        sl->by = sstrdup(mysql_row[1]);
        sl->reason = sstrdup(mysql_row[2]);
        sl->seton = strtol(mysql_row[3], (char **) NULL, 10);
        sl->expires = strtol(mysql_row[4], (char **) NULL, 10);
        slist_add(&sqlines, sl);
    }

    mysql_free_result(mysql_res);


    /* Load the SZLINEs */

    ret = db_mysql_try("SELECT mask, xby, reason, seton, expire "
                       "FROM anope_os_szlines "
                       "WHERE active = 1");

    if (!ret)
        return 0;
    
    mysql_res = mysql_use_result(mysql);
    slist_setcapacity(&szlines, szlc);

    while ((mysql_row = mysql_fetch_row(mysql_res))) {
        sl = scalloc(1, sizeof(SXLine));
        sl->mask = sstrdup(mysql_row[0]);
        sl->by = sstrdup(mysql_row[1]);
        sl->reason = sstrdup(mysql_row[2]);
        sl->seton = strtol(mysql_row[3], (char **) NULL, 10);
        sl->expires = strtol(mysql_row[4], (char **) NULL, 10);
        slist_add(&szlines, sl);
    }

    mysql_free_result(mysql_res);
    
    return 1;
}

int db_mysql_load_cs_dbase(void)
{
    int ret;
    char *q_name;
    ChannelInfo *ci;
    int i;
    MYSQL_RES *res;
    MYSQL_ROW row;

    if (!do_mysql)
        return 0;

    ret = db_mysql_try("SELECT name, founder, successor, founderpass, descr, url, email, time_registered, last_used, last_topic, last_topic_setter, "
                       "       last_topic_time, flags, forbidby, forbidreason, bantype, accesscount, akickcount, mlock_on, mlock_off, mlock_limit, "
                       "       mlock_key, mlock_flood, mlock_redirect, entry_message, memomax, botnick, botflags, bwcount, capsmin, capspercent, floodlines, "
                       "       floodsecs, repeattimes "
                       "FROM anope_cs_info "
                       "WHERE active = 1");

    if (!ret)
        return 0;
    
    /* I'd really like to use mysql_use_result here, but it'd tie up with
     * all the queries being run inside each iteration... -GD
     */
    mysql_res = mysql_store_result(mysql);

    while (ret && (mysql_row = mysql_fetch_row(mysql_res))) {
        ci = scalloc(1, sizeof(ChannelInfo));

        /* Name, founder, successor, password */
        snprintf(ci->name, CHANMAX, "%s", mysql_row[0]);
        ci->founder = findcore(mysql_row[1]);
        if (mysql_row[2] && *(mysql_row[2]))
            ci->successor = findcore(mysql_row[2]);
        else
            ci->successor = NULL;
        snprintf(ci->founderpass, PASSMAX, "%s", mysql_row[3]);

        /* Description, URL, email -- scalloc() initializes to 0/NULL */
        ci->desc = sstrdup(mysql_row[4]);
        if (mysql_row[5] && *(mysql_row[5]))
            ci->url = sstrdup(mysql_row[5]);
        if (mysql_row[6] && *(mysql_row[6]))
            ci->email = sstrdup(mysql_row[6]);

        /* Time registered, last used, last topic, last topic setter + time */
        ci->time_registered = strtol(mysql_row[7], (char **) NULL, 10);
        ci->last_used = strtol(mysql_row[8], (char **) NULL, 10);
        ci->last_topic = sstrdup(mysql_row[9]);
        snprintf(ci->last_topic_setter, NICKMAX, "%s", mysql_row[10]);
        ci->last_topic_time = strtol(mysql_row[11], (char **) NULL, 10);

        /* Flags, forbidden by, forbid reason, bantype
         * NOTE: CI_INHABIT will be disabled in flags!!
         */
        ci->flags =
            strtol(mysql_row[12], (char **) NULL, 10) & ~CI_INHABIT;
        ci->forbidby = sstrdup(mysql_row[13]);
        ci->forbidreason = sstrdup(mysql_row[14]);
        ci->bantype = strtol(mysql_row[15], (char **) NULL, 10);

        /* Accesscount, akickcount */
        ci->accesscount = strtol(mysql_row[16], (char **) NULL, 10);
        ci->akickcount = strtol(mysql_row[17], (char **) NULL, 10);

        /* Mlock: on, off, limit, key, flood, redirect */
        ci->mlock_on = strtol(mysql_row[18], (char **) NULL, 10);
        ci->mlock_off = strtol(mysql_row[19], (char **) NULL, 10);
        ci->mlock_limit = strtol(mysql_row[20], (char **) NULL, 10);
        ci->mlock_key = sstrdup(mysql_row[21]);
        ci->mlock_flood = sstrdup(mysql_row[22]);
        ci->mlock_redirect = sstrdup(mysql_row[23]);

        /* MemoMax, entrymessage, botinfo, botflags, badwordcount */
        ci->memos.memomax = strtol(mysql_row[25], (char **) NULL, 10);
        if (mysql_row[24] && *(mysql_row[24]))
            ci->entry_message = sstrdup(mysql_row[24]);
        ci->bi = findbot(mysql_row[26]);
        ci->botflags = strtol(mysql_row[27], (char **) NULL, 10);
        ci->bwcount = strtol(mysql_row[28], (char **) NULL, 10);

        /* Capsmin, capspercent, floodlines, floodsecs, repeattimes */
        ci->capsmin = strtol(mysql_row[29], (char **) NULL, 10);
        ci->capspercent = strtol(mysql_row[30], (char **) NULL, 10);
        ci->floodlines = strtol(mysql_row[31], (char **) NULL, 10);
        ci->floodsecs = strtol(mysql_row[32], (char **) NULL, 10);
        ci->repeattimes = strtol(mysql_row[33], (char **) NULL, 10);


        /* Get info from other tables; we'll need the channel name */
        q_name = db_mysql_quote(ci->name);

        /* Get the LEVELS list */
        ret = db_mysql_try("SELECT position, level "
                           "FROM anope_cs_levels "
                           "WHERE channel = '%s' AND active = 1",
                           q_name);

        if (ret) {
            res = mysql_use_result(mysql);
            ci->levels = scalloc(CA_SIZE, sizeof(*ci->levels));
            reset_levels(ci);

            while ((row = mysql_fetch_row(res))) {
                i = strtol(row[0], (char **) NULL, 10);
                ci->levels[i] = strtol(row[1], (char **) NULL, 10);
            }

            mysql_free_result(res);
        }

        /* Get the channel ACCESS list */
        if (ret && (ci->accesscount > 0)) {
            ci->access = scalloc(ci->accesscount, sizeof(ChanAccess));

            ret = db_mysql_try("SELECT level, display, last_seen "
                               "FROM anope_cs_access "
                               "WHERE channel = '%s' AND in_use = 1 AND active = 1",
                               q_name);
            
            if (ret) {
                res = mysql_store_result(mysql);

                i = 0;
                while ((row = mysql_fetch_row(res))) {
                    ci->access[i].in_use = 1;
                    ci->access[i].level = strtol(row[0], (char **) NULL, 10);
                    ci->access[i].nc = findcore(row[1]);
                    if (!(ci->access[i].nc))
                        ci->access[i].in_use = 0;
                    ci->access[i].last_seen =
                        strtol(row[2], (char **) NULL, 10);
                    i++;
                }
                
                mysql_free_result(res);
            }
        }

        /* Get the channel AKICK list */
        if (ret && (ci->akickcount > 0)) {
            ci->akick = scalloc(ci->akickcount, sizeof(AutoKick));

            ret = db_mysql_try("SELECT flags, dmask, reason, creator, addtime "
                               "FROM anope_cs_akicks "
                               "WHERE channel = '%s' AND active = 1 AND (flags & %d) <> 0",
                               q_name, AK_USED);
            
            if (ret) {
                res = mysql_use_result(mysql);

                i = 0;
                while ((row = mysql_fetch_row(res))) {
                    ci->akick[i].flags = strtol(row[0], (char **) NULL, 10);
                    if (ci->akick[i].flags & AK_ISNICK) {
                        ci->akick[i].u.nc = findcore(row[1]);
                        if (!(ci->akick[i].u.nc))
                            ci->akick[i].flags &= ~AK_USED;
                    } else {
                        ci->akick[i].u.mask = sstrdup(row[1]);
                    }
                    ci->akick[i].reason = sstrdup(row[2]);
                    ci->akick[i].creator = sstrdup(row[3]);
                    ci->akick[i].addtime = strtol(row[4], (char **) NULL, 10);
                    i++;
                }

                mysql_free_result(res);
            }
        }

        if (ret) {
            /* Get the channel memos */
            ret = db_mysql_try("SELECT nm_id, number, flags, time, sender, text "
                               "FROM anope_ms_info "
                               "WHERE receiver = '%s' AND serv = 'CHAN' AND active = 1",
                               q_name);
            
            if (ret) {
                res = mysql_store_result(mysql);
                ci->memos.memocount = mysql_num_rows(res);
                
                if (ci->memos.memocount > 0) {
                    Memo *memos;

                    memos = scalloc(ci->memos.memocount, sizeof(Memo));
                    ci->memos.memos = memos;

                    i = 0;
                    while ((row = mysql_fetch_row(res))) {
                        memos[i].id = strtol(row[0], (char **) NULL, 10);
                        memos[i].number = strtol(row[1], (char **) NULL, 10);
                        memos[i].flags = strtol(row[2], (char **) NULL, 10);
                        memos[i].time = strtol(row[3], (char **) NULL, 10);
                        snprintf(memos[i].sender, NICKMAX, "%s", row[4]);
                        memos[i].text = sstrdup(row[5]);
                        i++;
                    }
                }
                
                mysql_free_result(res);
            }
        }

        /* Get the TTB data */
        if (ret) {
            ci->ttb = scalloc(TTB_SIZE, sizeof(*ci->ttb));

            ret = db_mysql_try("SELECT ttb_id, value "
                               "FROM anope_cs_ttb "
                               "WHERE channel = '%s' AND active = 1",
                               q_name);
        
            if (ret) {
                res = mysql_use_result(mysql);

                while ((row = mysql_fetch_row(res))) {
                    i = strtol(row[0], (char **) NULL, 10);
                    /* Should we do a sanity check on the value of i? -GD */
                    ci->ttb[i] = strtol(row[1], (char **) NULL, 10);
                }

                mysql_free_result(res);
            }
        }

        /* Get the badwords */
        if (ret && (ci->bwcount > 0)) {
            ci->badwords = scalloc(ci->bwcount, sizeof(BadWord));

            ret = db_mysql_try("SELECT word, type "
                               "FROM anope_cs_badwords "
                               "WHERE channel = '%s' AND active = 1",
                               q_name);

            if (ret) {
                res = mysql_use_result(mysql);

                i = 0;
                while ((row = mysql_fetch_row(res))) {
                    ci->badwords[i].in_use = 1;
                    ci->badwords[i].word = sstrdup(row[0]);
                    ci->badwords[i].type = strtol(row[1], (char **) NULL, 10);
                    i++;
                }

                mysql_free_result(res);
            }
        }

        /* YAY! all done; free q_name and insert the channel */
        free(q_name);
        alpha_insert_chan(ci);
    }

    mysql_free_result(mysql_res);

    /* Check to be sure that all channels still have a founder. If not,
     * delete them. This code seems to be required in the old mysql code
     * so i'll leave it in just to be sure. I also wonder why they didn't
     * do that check up above immediately when it was known there was no
     * founder... -GD
     */
    for (i = 0; i < 256; i++) {
        ChannelInfo *next;
        for (ci = chanlists[i]; ci; ci = next) {
            next = ci->next;
            if (!(ci->flags & CI_VERBOTEN) && !ci->founder) {
                alog("%s: database load: Deleting founderless channel %s",
                     s_ChanServ, ci->name);
                delchan(ci);
            }
        }
    }
    
    return ret;
}

int db_mysql_load_ns_req_dbase(void)
{
    int ret;
    NickRequest *nr;

    if (!do_mysql)
        return 0;

    ret = db_mysql_try("SELECT nick, passcode, password, email, requested "
                       "FROM anope_ns_request "
                       "WHERE active = 1");

    if (ret) {
        mysql_res = mysql_use_result(mysql);

        while ((mysql_row = mysql_fetch_row(mysql_res))) {
            nr = scalloc(1, sizeof(NickRequest));

            nr->nick = sstrdup(mysql_row[0]);
            nr->passcode = sstrdup(mysql_row[1]);
            snprintf(nr->password, PASSMAX, "%s", mysql_row[2]);
            nr->email = sstrdup(mysql_row[3]);
            nr->requested = strtol(mysql_row[4], (char **) NULL, 10);

            insert_requestnick(nr);
        }

        mysql_free_result(mysql_res);
    }
    
    return ret;
}

int db_mysql_load_ns_dbase(void)
{
    int ret;
    char *q_display;
    NickCore *nc;
    NickAlias *na;
    MYSQL_RES *res;
    MYSQL_ROW row;
    int i;

    if (!do_mysql)
        return 0;

    ret = db_mysql_try("SELECT display, pass, email, icq, url, flags, language, accesscount, memocount, memomax, channelcount, channelmax, greet "
                       "FROM anope_ns_core "
                       "WHERE active = 1");

    if (!ret)
        return 0;
    
    /* I'd really like to use mysql_use_result here, but it'd tie up with
     * all the queries being run inside each iteration... -GD
     */
    mysql_res = mysql_store_result(mysql);

    while (ret && (mysql_row = mysql_fetch_row(mysql_res))) {
        nc = scalloc(1, sizeof(NickCore));

        /* Display, password, email, ICQ, URL, flags */
        nc->display = sstrdup(mysql_row[0]);
        snprintf(nc->pass, PASSMAX, "%s", mysql_row[1]);
        nc->email = sstrdup(mysql_row[2]);
        nc->icq = strtol(mysql_row[3], (char **) NULL, 10);
        nc->url = sstrdup(mysql_row[4]);
        nc->flags = strtol(mysql_row[5], (char **) NULL, 10);

        /* Language, accesscount, memocount, memomax */
        nc->language = strtol(mysql_row[6], (char **) NULL, 10);
        nc->accesscount = strtol(mysql_row[7], (char **) NULL, 10);
        nc->memos.memocount = strtol(mysql_row[8], (char **) NULL, 10);
        nc->memos.memomax = strtol(mysql_row[9], (char **) NULL, 10);

        /* Channelcount, channelmax, greet */
        nc->channelcount = strtol(mysql_row[10], (char **) NULL, 10);
        nc->channelmax = strtol(mysql_row[11], (char **) NULL, 10);
        if (mysql_row[12] && *(mysql_row[12]))
            nc->greet = sstrdup(mysql_row[12]);

        /* Don't allow KILL_IMMED if the config doesn't allow it */
        if (!NSAllowKillImmed)
            nc->flags &= ~NI_KILL_IMMED;

        /* Check if the current user is important enough to be added to
         * services admin or services oper lists
         */
        if (nc->flags & NI_SERVICES_ADMIN)
            slist_add(&servadmins, nc);
        if (nc->flags & NI_SERVICES_OPER)
            slist_add(&servopers, nc);

        /* Unset the SERVICES_ROOT flag; we will set it again later if this
         * user is really a services root (checked per NickAlias) -GD
         */
        nc->flags &= ~NI_SERVICES_ROOT;

        /* Get info from other tables; we'll need the display */
        q_display = db_mysql_quote(nc->display);

        /* Fill the accesslist */
        if (ret && (nc->accesscount > 0)) {
            nc->access = scalloc(nc->accesscount, sizeof(char *));

            ret = db_mysql_try("SELECT access "
                               "FROM anope_ns_access "
                               "WHERE display = '%s' AND active = 1",
                               q_display);

            if (ret) {
                res = mysql_use_result(mysql);

                i = 0;
                while ((row = mysql_fetch_row(res))) {
                    if (row[0] && *(row[0])) {
                        nc->access[i] = sstrdup(row[0]);
                        i++;
                    }
                }

                mysql_free_result(res);
            }
        }

        /* Load the memos */
        if (ret && (nc->memos.memocount > 0)) {
            nc->memos.memos = scalloc(nc->memos.memocount, sizeof(Memo));

            ret = db_mysql_try("SELECT nm_id, number, flags, time, sender, text "
                               "FROM anope_ms_info "
                               "WHERE receiver = '%s' AND active = 1 AND serv = 'NICK' "
                               "ORDER BY number ASC",
                               q_display);

            if (ret) {
                res = mysql_use_result(mysql);

                i = 0;
                while ((row = mysql_fetch_row(res))) {
                    nc->memos.memos[i].id = strtol(row[0], (char **) NULL, 10);
                    nc->memos.memos[i].number = strtol(row[1], (char **) NULL, 10);
                    nc->memos.memos[i].flags = strtol(row[2], (char **) NULL, 10);
                    nc->memos.memos[i].time = strtol(row[3], (char **) NULL, 10);
                    snprintf(nc->memos.memos[i].sender, NICKMAX, "%s", row[4]);
                    nc->memos.memos[i].text = sstrdup(row[5]);

                    i++;
                }

                mysql_free_result(res);
            }
        }

        /* Done with the core; insert it */
        insert_core(nc);
    }

    mysql_free_result(mysql_res);

    if (!ret)
    	return 0;

    /* Load the nickaliases */
    ret = db_mysql_try("SELECT nick, display, time_registered, last_seen, status, last_usermask, last_realname, last_quit "
                       "FROM anope_ns_alias "
                       "WHERE active = 1");

    if (!ret)
        return 0;
    
    mysql_res = mysql_use_result(mysql);

    while ((mysql_row = mysql_fetch_row(mysql_res))) {
        /* First make sure this NickAlias has a NickCore; else we don't even
         * bother adding it to the aliases at all...
         */
        NickCore *nc;

        if (!(nc = findcore(mysql_row[1])))
            continue;

        na = scalloc(1, sizeof(NickAlias));

        /* nick, time_registered, last_seen, status
         * NOTE: remove NS_TEMPORARY from status on load
         */
        na->nick = sstrdup(mysql_row[0]);
        na->nc = nc;
        na->time_registered = strtol(mysql_row[2], (char **) NULL, 10);
        na->last_seen = strtol(mysql_row[3], (char **) NULL, 10);
        na->status =
            strtol(mysql_row[4], (char **) NULL, 10) & ~NS_TEMPORARY;

        /* last_usermask, last_realname, last_quit */
        na->last_usermask = sstrdup(mysql_row[5]);
        na->last_realname = sstrdup(mysql_row[6]);
        na->last_quit = sstrdup(mysql_row[7]);

        /* Assign to the nickcore aliases */
        slist_add(&na->nc->aliases, na);

        /* Check if this user is a services root */
        for (i = 0; i < RootNumber; i++) {
            if (stricmp(ServicesRoots[i], na->nick) == 0)
                na->nc->flags |= NI_SERVICES_ROOT;
        }

        /* Last, but not least: insert the alias! */
        alpha_insert_alias(na);
    }

    mysql_free_result(mysql_res);
    
    return ret;
}

/* get random mysql number for the generator */
unsigned int mysql_rand(void)
{
    unsigned int num = 0;

    if (!do_mysql)
        return 0;

    db_mysql_try("SELECT RAND()");

    mysql_res = mysql_store_result(mysql);

    if (!(mysql_row = mysql_fetch_row(mysql_res))) {
        mysql_free_result(mysql_res);
        return 0;
    }

    num = UserKey3 * strtol(mysql_row[0], (char **) NULL, 10);

    mysql_free_result(mysql_res);

    return num;
}
