/*
 * Copyright (c) 2004-2009, Luiz Otavio O Souza <loos.br@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

static const char rcsid[] = "$Id: sql.c 112 2009-03-15 17:30:28Z loos-br $";

#include <errno.h>
#include <stdlib.h>
#include <string.h>

#include "fmt.h"
#include "sql.h"
#include "user.h"
#include "mysql.h"
#include "return.h"
#include "contacts.h"
#include "protocol.h"
#include "array_cmd.h"
#include "msn-proxy.h"

void
sql_data(string *sql, unsigned int len, char *key, char *value) {
 string			buf;

    str_zero(&buf);
    if (len != sql->len)
	if (str_cats(sql, (unsigned char *)", ") == 0) exit(51);
    buf.len = fmt_printf(NULL, "`%S` = '%q'", key, value);
    if (str_ready(&buf, buf.len + 1) == 0) exit(51);
    buf.len = fmt_printf(buf.s, "`%S` = '%q'", key, value);
    if (str_cat(sql, buf.s, buf.len) == 0) exit(51);
    str_free(&buf);
}

void
sql_int_data(string *sql, unsigned int len, char *key, unsigned long value) {
 string                 buf;

    str_zero(&buf);
    if (len != sql->len)
        if (str_cats(sql, (unsigned char *)", ") == 0) exit(51);
    buf.len = fmt_printf(NULL, "`%S` = '%d'", key, value);
    if (str_ready(&buf, buf.len + 1) == 0) exit(51);
    buf.len = fmt_printf(buf.s, "`%S` = '%d'", key, value);
    if (str_cat(sql, buf.s, buf.len) == 0) exit(51);
    str_free(&buf);
}

void
sql_lint_data(string *sql, unsigned int len, char *key, unsigned long long value) {
 string                 buf;

    str_zero(&buf);
    if (len != sql->len)
	if (str_cats(sql, (unsigned char *)", ") == 0) exit(51);
    buf.len = fmt_printf(NULL, "`%S` = '%l'", key, value);
    if (str_ready(&buf, buf.len + 1) == 0) exit(51);
    buf.len = fmt_printf(buf.s, "`%S` = '%l'", key, value);
    if (str_cat(sql, buf.s, buf.len) == 0) exit(51);
    str_free(&buf);
}

int
sql_disconnect_all(void) {
 mysql_		*mysql = &config.mysql;
 string		sql;

    str_zero(&sql);

    /* remove stale sb sessions */
    sql.len = fmt_printf(NULL, "DELETE FROM sb");
    if (str_ready(&sql, sql.len + 1) == 0) die_nomem();
    sql.len = fmt_printf(sql.s, "DELETE FROM sb");

    if (db_query2(mysql, &sql) == RFAIL) {
	str_free(&sql);
	return(RFAIL);
    }

    /* logoff all contacts */
    sql.len = fmt_printf(NULL, "UPDATE contacts SET contact_status = 'OFF', "
				"contact_deny = contact_deny & ~%d",
				CONTACT_BLOCKED);
    if (str_ready(&sql, sql.len + 1) == 0) die_nomem();
    sql.len = fmt_printf(sql.s, "UPDATE contacts SET contact_status = 'OFF', "
				"contact_deny = contact_deny & ~%d",
				CONTACT_BLOCKED);

    if (db_query2(mysql, &sql) == RFAIL) {
	str_free(&sql);
	return(RFAIL);
    }
    str_free(&sql);

    /* logoff everyone */
    sql.len = fmt_printf(NULL, "UPDATE users SET status = 'OFF'");
    if (str_ready(&sql, sql.len + 1) == 0)
	die_nomem();
    sql.len = fmt_printf(sql.s, "UPDATE users SET status = 'OFF'");

    if (db_query2(mysql, &sql) == RFAIL) {
	str_free(&sql);
	return(RFAIL);
    }
    str_free(&sql);

    return(ROK);
}

int
sql_user_disconnect(string *email) {
 mysql_		*mysql = &config.mysql;
 string		sql;
 char		*fmt;

    str_zero(&sql);

    /* logoff user contacts */
    fmt = "UPDATE contacts SET contact_status = 'OFF', "
	  "contact_deny = contact_deny & ~%d WHERE email = '%q'";
    sql.len = fmt_printf(NULL, fmt, CONTACT_BLOCKED, email->s);
    if(str_ready(&sql, sql.len + 1) == 0)
	die_nomem();
    sql.len = fmt_printf(sql.s, fmt, CONTACT_BLOCKED, email->s);

    if (db_query2(mysql, &sql) == RFAIL) {
	str_free(&sql);
	return(RFAIL);
    }

    /* logoff user */
    fmt = "UPDATE users SET status = 'OFF' WHERE email = '%q'";
    sql.len = fmt_printf(NULL, fmt, email->s);
    if(str_ready(&sql, sql.len + 1) == 0)
	die_nomem();
    sql.len = fmt_printf(sql.s, fmt, email->s);

    if (db_query2(mysql, &sql) == RFAIL) {
	str_free(&sql);
	return(RFAIL);
    }

    str_free(&sql);
    return(ROK);
}

int
sql_users_update(struct user_ *user) {
 mysql_		*mysql = &config.mysql;
 string		sql;
 char		*fmt;

    str_zero(&sql);

    /* user exists ? */
    fmt = "UPDATE users SET last_seen = NOW(), last_addr = '%q' "
	  "WHERE email = '%q'";
    sql.len = fmt_printf(NULL, fmt, user->addr.s, user->email.s);
    if(str_ready(&sql, sql.len + 1) == 0)
	die_nomem();
    sql.len = fmt_printf(sql.s, fmt, user->addr.s, user->email.s);

    if (db_query2(mysql, &sql) == RFAIL) {
	str_free(&sql);
	return(RFAIL);
    }
    str_free(&sql);

    if (db_affected_rows(&mysql->mysql) > 0) {

	/* user exist */
	return(ROK);

    } else if (db_affected_rows(&mysql->mysql) == 0) {

	/* user does not exist */
	fmt =	"INSERT INTO users (email, display_name, last_seen, last_addr, "
		"connect, save_msg, save_contacts, commands) VALUES "
		"('%q', '%q', NOW(), '%q', (SELECT connect FROM defaults), "
		"(SELECT save_msg FROM defaults), "
		"(SELECT save_contacts FROM defaults), "
		"(SELECT commands FROM defaults))";

	sql.len = fmt_printf(NULL, fmt, user->email.s,
					user->email.s, user->addr.s);
	if (str_ready(&sql, sql.len + 1) == 0)
	    die_nomem();
	sql.len = fmt_printf(sql.s, fmt, user->email.s,
					 user->email.s, user->addr.s);

	if (db_query2(mysql, &sql) == RFAIL) {
	    str_free(&sql);
	    return(RFAIL);
	}
	str_free(&sql);

	return(ROK);
    }

    /* notreached */
    return(RFAIL);
}

int
sql_set_status(struct user_ *user) {
 mysql_		*mysql = &config.mysql;
 string		sql;
 char		fmt[] = "UPDATE users SET status = '%q' WHERE email = '%q'";

    str_zero(&sql);
    sql.len = fmt_printf(NULL, fmt, user->status.s, user->email.s);
    if (str_ready(&sql, sql.len + 1) == 0)
	die_nomem();
    sql.len = fmt_printf(sql.s, fmt, user->status.s, user->email.s);

    if (db_query2(mysql, &sql) == RFAIL) {
	str_free(&sql);
	return(RFAIL);
    }
    str_free(&sql);

    return(ROK);
}

int
sql_update_user_dn(struct user_ *user) {
 mysql_		*mysql = &config.mysql;
 char		fmt[] = "UPDATE users SET display_name = '%q' "
			"WHERE email = '%q'";
 string		sql;

    str_zero(&sql);
    sql.len = fmt_printf(NULL, fmt, user->dn.s, user->email.s);
    if (str_ready(&sql, sql.len + 1) == 0)
	return(RFAIL);
    sql.len = fmt_printf(sql.s, fmt, user->dn.s, user->email.s);

    if (db_query2(mysql, &sql) == RFAIL) {
	str_free(&sql);
	return(RFAIL);
    }
    str_free(&sql);

    return(ROK);
}

__uint32_t
sql_insert_sb(struct user_ *user) {
 mysql_		*mysql = &config.mysql;
 string		sql;
 char		fmt [] = "INSERT INTO sb (email, since) VALUES ('%q', NOW())";

    str_zero(&sql);
    sql.len = fmt_printf(NULL, fmt, user->email.s);
    if(str_ready(&sql, sql.len + 1) == 0)
	die_nomem();
    sql.len = fmt_printf(sql.s, fmt, user->email.s);

    if (db_query2(mysql, &sql) == RFAIL) {
	str_free(&sql);
	return(RFAIL);
    }
    str_free(&sql);

    return((__uint32_t)db_last_id(&mysql->mysql));
}

int
sql_remove_sb(int id) {
 mysql_		*mysql = &config.mysql;
 string		sql;
 char		fmt[] = "DELETE FROM sb WHERE id = %d";

    str_zero(&sql);
    sql.len = fmt_printf(NULL, fmt, id);
    if(str_ready(&sql, sql.len + 1) == 0)
	die_nomem();
    sql.len = fmt_printf(sql.s, fmt, id);

    if (db_query2(mysql, &sql) == RFAIL) {
	str_free(&sql);
	return(RFAIL);
    }
    str_free(&sql);

    return(ROK);
}

int
sql_log_join(int sb_id, string *email, string *dn) {
 char		fmt[] = "INSERT INTO log (sb_id, `date`, email, display_name, "
			"type) VALUES (%d, NOW(), '%q', '%q', 'join')";
 mysql_		*mysql = &config.mysql;
 string		sql;

    str_zero(&sql);
    sql.len = fmt_printf(NULL, fmt, sb_id, email->s, dn->s);
    if(str_ready(&sql, sql.len + 1) == 0)
	die_nomem();
    sql.len = fmt_printf(sql.s, fmt, sb_id, email->s, dn->s);

    if (db_query2(mysql, &sql) == RFAIL) {
	str_free(&sql);
	return(RFAIL);
    }
    str_free(&sql);

    return(ROK);
}

int
sql_log_stop2(struct sb_ *sb, string *email, string *contact_email) {
 string		sql;
 mysql_		*mysql = &config.mysql;
 char		fmt[] = "INSERT INTO log (sb_id, `date`, email, display_name, "
			"type) VALUES (%d, NOW(), '%q', "
			"(SELECT contact_dn FROM contacts WHERE email = '%q' "
			"AND contact_email = '%q'), 'stop')";

    str_zero(&sql);
    sql.len = fmt_printf(NULL, fmt, sb->id, email->s,
						 email->s, contact_email->s);
    if(str_ready(&sql, sql.len + 1) == 0)
	die_nomem();
    sql.len = fmt_printf(sql.s, fmt, sb->id, email->s,
					     email->s, contact_email->s);

    if (db_query2(mysql, &sql) == RFAIL) {
	str_free(&sql);
	return(RFAIL);
    }
    str_free(&sql);

    return(ROK);
}

int
sql_log_stop(struct sb_ *sb, string *email, string *dn) {
 string		sql;
 mysql_		*mysql = &config.mysql;
 char		fmt[] = "INSERT INTO log (sb_id, `date`, email, display_name, "
			"type) VALUES (%d, NOW(), '%q', '%q', 'stop')";

    str_zero(&sql);
    sql.len = fmt_printf(NULL, fmt, sb->id, email->s, dn->s);
    if(str_ready(&sql, sql.len + 1) == 0)
	die_nomem();
    sql.len = fmt_printf(sql.s, fmt, sb->id, email->s, dn->s);

    if (db_query2(mysql, &sql) == RFAIL) {
	str_free(&sql);
	return(RFAIL);
    }
    str_free(&sql);

    return(ROK);
}

int
sql_log_start(int sb_id, string *email, string *dn) {
 char		fmt[] = "INSERT INTO log (sb_id, `date`, email, display_name, "
			"type) VALUES (%d, NOW(), '%q', '%q', 'start')";
 mysql_		*mysql = &config.mysql;
 string		sql;

    str_zero(&sql);
    sql.len = fmt_printf(NULL, fmt, sb_id, email->s, dn->s);
    if(str_ready(&sql, sql.len + 1) == 0) die_nomem();
    sql.len = fmt_printf(sql.s, fmt, sb_id, email->s, dn->s);

    if (db_query2(mysql, &sql) == RFAIL) {
	str_free(&sql);
	return(RFAIL);
    }
    str_free(&sql);

    return(ROK);
}

int
sql_log_msg(int sb_id, string *email, string *dn, string *to, string *msg) {
 char		fmt[] = "INSERT INTO log (`sb_id`, `date`, `email`, `to`, "
			"`display_name`, `type`, `content`) "
			"VALUES (%d, NOW(), '%q', '%q', '%q', 'msg', "
			"'%q')";
 mysql_		*mysql = &config.mysql;
 string		sql;

    str_zero(&sql);
    sql.len = fmt_printf(NULL, fmt, sb_id,
	email->s ? email->s : (unsigned char *)"",
	to->s ? to->s : (unsigned char *)"",
	dn->s ? dn->s : (unsigned char *)"",
	msg->s ? msg->s : (unsigned char *)"");
    if (str_ready(&sql, sql.len + 1) == 0) die_nomem();
    sql.len = fmt_printf(sql.s, fmt, sb_id,
	email->s ? email->s : (unsigned char *)"",
	to->s ? to->s : (unsigned char *)"",
	dn->s ? dn->s : (unsigned char *)"",
	msg->s ? msg->s : (unsigned char *)"");

    if (db_query2(mysql, &sql) == RFAIL) {
	str_free(&sql);
	return(RFAIL);
    }
    str_free(&sql);

    return(ROK);
}

int
sql_read_defaults(void) {
 char		fmt[] = "SELECT connect, save_msg, save_contacts, "
			"commands, internal_host FROM defaults LIMIT 1";
 defaults_	*defaults = &config.defaults;
 mysql_		*mysql = &config.mysql;
 MYSQL_RES	*res;
 MYSQL_ROW	row;
 string		sql;

    str_zero(&sql);
    sql.len = fmt_printf(NULL, fmt);
    if (str_ready(&sql, sql.len + 1) == 0) die_nomem();
    sql.a = fmt_printf(sql.s, fmt);

    res = db_query(mysql, &sql);
    if (res == (MYSQL_RES *)0) {
	str_free(&sql);
	return(RFAIL);
    }

    str_free(&sql);

    row = db_fetch_row(res);
    if (row == (MYSQL_ROW)0) {
	res = db_free(res);
	return(RFAIL);
    }

    /* connect */
    if (row[0]) {
	if (strcasecmp(row[0], "YES") == 0)
	    defaults->connect = YES;
	else if (strcasecmp(row[0], "NO") == 0)
	    defaults->connect = NO;
    }

    /* save_msg */
    if (row[1]) {
	if (strcasecmp(row[1], "YES") == 0)
	    defaults->save_msg = YES;
	else if (strcasecmp(row[1], "NO") == 0)
	    defaults->save_msg = NO;
    }

    /* save_contacts */
    if (row[2]) {
	if (strcasecmp(row[2], "YES") == 0)
	    defaults->save_contacts = YES;
	else if (strcasecmp(row[2], "NO") == 0)
	    defaults->save_contacts = NO;
    }

    /* commands */
    if (row[3]) {
	defaults->commands = (int)strtol(row[3], (char **)0, 10);
    }

    /* listen_host */
    if (row[4]) {
	if (str_copys(&defaults->internal_host, (unsigned char *)row[4]) == 0) {
	    res = db_free(res);
	    return(RFAIL);
	}
    }

    res = db_free(res);
    return(ROK);
}

int
read_acl(char *fmt, string *email) {
 mysql_		*mysql = &config.mysql;
 MYSQL_RES	*res;
 MYSQL_ROW	row;
 string		sql;

    str_zero(&sql);
    sql.len = fmt_printf(NULL, fmt, email->s);
    if (str_ready(&sql, sql.len + 1) == 0)
        die_nomem();
    sql.len = fmt_printf(sql.s, fmt, email->s);

    res = db_query(mysql, &sql);
    str_free(&sql);

    if (res == (MYSQL_RES *)0)
        return(RFAIL);

    row = db_fetch_row(res);
    if (row == (MYSQL_ROW)0 || row[0] == (char *)0) {
        res = db_free(res);
        return(RFAIL);
    }

    if (strcasecmp(row[0], "YES") == 0) {
	res = db_free(res);
	return(YES);
    }
    else if (strcasecmp(row[0], "NO") == 0) {
	res = db_free(res);
	return(NO);
    }
    return(RFAIL);
}

int
read_commands(struct user_ *user) {
 mysql_         *mysql = &config.mysql;
 MYSQL_RES      *res;
 MYSQL_ROW      row;
 string         sql;
 char		*ep;
 char		fmt[] = "SELECT commands FROM users WHERE email = '%q'";

    str_zero(&sql);
    sql.len = fmt_printf(NULL, fmt, user->email.s);
    if (str_ready(&sql, sql.len + 1) == 0)
        die_nomem();
    sql.len = fmt_printf(sql.s, fmt, user->email.s);

    res = db_query(mysql, &sql);
    str_free(&sql);

    if (res == (MYSQL_RES *)0)
        return(RFAIL);

    row = db_fetch_row(res);
    if (row == (MYSQL_ROW)0 || row[0] == (char *)0) {
        res = db_free(res);
        return(RFAIL);
    }

    errno = 0;
    user->commands |= strtoul(row[0], &ep, 10);
    if (errno != 0 || (ep && *ep != 0)) {
        res = db_free(res);
        return(RFAIL);
    }

    res = db_free(res);
    return(ROK);
}

int
sql_read_user_acl(struct user_ *user) {

    /* reset user->commands */
    user->commands = 0;

    /* read connect */
    switch (read_acl("SELECT connect FROM users WHERE email = '%q'",
	    &user->email)) {

	case YES:
	    break;

	case NO:
	    user->commands |= CONNECT;
	    return(NO);

	case RFAIL:
	default:
	    return(RFAIL);
    }

    /* read save_msg */
    switch (read_acl("SELECT save_msg FROM users WHERE email = '%q'",
	    &user->email)) {

	case YES:
	    break;

	case NO:
	    user->commands |= SAVE_MSG;
	    break;

	case RFAIL:
	default:
	    return(RFAIL);
    }

    /* read save_contacts */
    switch (read_acl("SELECT save_contacts FROM users WHERE email = '%q'",
	    &user->email)) {

	case YES:
	    break;

	case NO:
	    user->commands |= SAVE_CONTACTS;
	    break;

	case RFAIL:
	default:
	    return(RFAIL);
    }

    /* read deny commands */
    if (read_commands(user) == RFAIL)
	return(RFAIL);

    return(ROK);
}

int
sql_connection_denied(string *email) {
 char		fmt[] = "INSERT INTO log (date, email, type) VALUES "
			"(NOW(), '%q', 'connection denied')";
 mysql_		*mysql = &config.mysql;
 string		sql;

    str_zero(&sql);
    sql.len = fmt_printf(NULL, fmt, email->s);
    if (str_ready(&sql, sql.len + 1) == 0) die_nomem();
    sql.len = fmt_printf(sql.s, fmt, email->s);

    if (db_query2(mysql, &sql) == RFAIL) {
	str_free(&sql);
	return(RFAIL);
    }

    str_free(&sql);
    return(ROK);
}

int
sql_version_denied(string *email) {
 char		fmt[] = "INSERT INTO log (date, email, type) VALUES "
			"(NOW(), '%q', 'client version denied')";
 mysql_		*mysql = &config.mysql;
 string		sql;

    str_zero(&sql);
    sql.len = fmt_printf(NULL, fmt, email->s);
    if (str_ready(&sql, sql.len + 1) == 0) die_nomem();
    sql.len = fmt_printf(sql.s, fmt, email->s);

    if (db_query2(mysql, &sql) == RFAIL) {
	str_free(&sql);
	return(RFAIL);
    }

    str_free(&sql);
    return(ROK);
}

int
sql_password_error(string *email) {
 char		fmt[] = "INSERT INTO log (date, email, type) VALUES "
			"(NOW(), '%q', 'password error')";
 mysql_		*mysql = &config.mysql;
 string		sql;

    str_zero(&sql);
    sql.len = fmt_printf(NULL, fmt, email->s);
    if (str_ready(&sql, sql.len + 1) == 0) die_nomem();
    sql.len = fmt_printf(sql.s, fmt, email->s);

    if (db_query2(mysql, &sql) == RFAIL) {
        str_free(&sql);
        return(RFAIL);
    }

    str_free(&sql);
    return(ROK);
}

int
sql_insert_contact(string *email, struct contact_ *contact) {
 char		fmt[] = "INSERT INTO contacts (email, contact_uid, "
			"contact_email, contact_lists, contact_dn, "
			"contact_group, contact_flags, contact_flags2, "
			"contact_deny "
			") VALUES ("
			"'%q', '%q', '%q', %d, '%q', '%q', '%l', '%l', '%d')";
 mysql_		*mysql = &config.mysql;
 string		sql;

    str_zero(&sql);
    sql.len = fmt_printf(NULL, fmt, email->s,
		contact->uid.len > 0 ? contact->uid.s : (unsigned char *)"",
		contact->c.s, contact->lists,
		contact->dn.len > 0 ? contact->dn.s : (unsigned char *)"",
		contact->group.len > 0 ? contact->group.s : (unsigned char *)"",
		contact->flags, contact->flags2, contact->deny);
    if (str_ready(&sql, sql.len + 1) == 0) die_nomem();
    sql.len = fmt_printf(sql.s, fmt, email->s,
		contact->uid.len > 0 ? contact->uid.s : (unsigned char *)"",
		contact->c.s, contact->lists,
		contact->dn.len > 0 ? contact->dn.s : (unsigned char *)"",
		contact->group.len > 0 ? contact->group.s : (unsigned char *)"",
		contact->flags, contact->flags2, contact->deny);

    if (db_query2(mysql, &sql) == RFAIL) {
	str_free(&sql);
	return(RFAIL);
    }

    str_free(&sql);
    return(ROK);
}

int
sql_contact_save(struct user_ *user, struct contact_ *c) {
 mysql_			*mysql = &config.mysql;
 log_			*log = &config.log;
 struct contact_	*contact;
 unsigned int		len;
 string			buf;
 string			sql;
 int			rtrn;

    rtrn = NONE;
    contact = RB_FIND(contacts, &user->contacts, c);
    if (contact == NULL ||
	contact->updated != UPDATE ||
	contact->save == NULL) {
	return(rtrn);
    }

    str_zero(&buf);
    str_zero(&sql);
    if (str_copys(&sql, (unsigned char *)"UPDATE contacts SET ") == 0)
	exit(51);
    len = sql.len;

    if ((contact->dn.len > 0 && contact->save->dn.len > 0 &&
	strcmp((char *)contact->dn.s, (char *)contact->save->dn.s) != 0) ||
	(contact->dn.len == 0 && contact->save->dn.len > 0)) {

	if (contact->c.len > 0 && contact->save->dn.len > 0 &&
	    strcmp((char *)contact->save->dn.s, (char *)contact->c.s) != 0) {

	    if (str_copy(&contact->dn, contact->save->dn.s,
		contact->save->dn.len) == 0)
		die_nomem();
	    sql_data(&sql, len, "contact_dn", (char *)contact->dn.s);
	}
    }
    if ((contact->info.len > 0 && contact->save->info.len > 0 &&
	strcmp((char *)contact->info.s, (char *)contact->save->info.s) != 0) ||
	(contact->info.len == 0 && contact->save->info.len > 0)) {

	if (str_copy(&contact->info, contact->save->info.s,
	    contact->save->info.len) == 0)
	    exit(51);
	sql_data(&sql, len, "contact_info", (char *)contact->info.s);
    }
    if ((contact->status.len > 0 && contact->save->status.len > 0 &&
	strcmp((char *)contact->status.s, (char *)contact->save->status.s) != 0) ||
	(contact->status.len == 0 && contact->save->status.len > 0)) {

	if (str_copy(&contact->status, contact->save->status.s,
	    contact->save->status.len) == 0)
	    exit(51);
	sql_data(&sql, len, "contact_status", (char *)contact->status.s);
    }
    if ((contact->o.len > 0 && contact->save->o.len > 0 &&
	strcmp((char *)contact->o.s, (char *)contact->save->o.s) != 0) ||
	(contact->o.len == 0 &&contact->save->o.len > 0)) {

	if (str_copy(&contact->o, contact->save->o.s,
	    contact->save->o.len) == 0)
	    exit(51);
	sql_data(&sql, len, "contact_obj", (char *)contact->o.s);
    }
    if (contact->deny != contact->save->deny) {

	contact->deny = contact->save->deny;
	sql_int_data(&sql, len, "contact_deny", contact->deny);
    }
    if (contact->save->flags > 0 &&
	contact->flags != contact->save->flags) {

	contact->flags = contact->save->flags;
	sql_lint_data(&sql, len, "contact_flags", contact->flags);
    }
    if (contact->save->flags2 > 0 &&
	contact->flags2 != contact->save->flags2) {

	contact->flags2 = contact->save->flags2;
	sql_lint_data(&sql, len, "contact_flags2", contact->flags2);
    }
    if (contact->save->lists > 0 &&
	contact->lists != contact->save->lists) {

	contact->lists = contact->save->lists;
	sql_int_data(&sql, len, "contact_lists", contact->lists);
    }
    if (contact->chat != YES && contact->save->chat == YES) {

	sql_data(&sql, len, "contact_chat", "YES");
    }

    if (len != sql.len) {
	buf.len = fmt_printf(NULL, " WHERE "
				   "`email` = '%q' AND "
				   "`contact_email` = '%q'", user->email.s,
				   contact->c.s);
	if (str_ready(&buf, buf.len + 1) == 0) exit(51);
	buf.len = fmt_printf(buf.s, " WHERE "
				   "`email` = '%q' AND "
				   "`contact_email` = '%q'", user->email.s,
				   contact->c.s);
	if (buf.len > 0 && str_cat(&sql, buf.s, buf.len) == 0) exit(51);
	str_free(&buf);
    }

    if (len != sql.len && db_query2(mysql, &sql) == RFAIL) {
	log->debug("falha no sql: [%s]\n", &sql);
	rtrn = RFAIL;
    }

    if (len != sql.len && rtrn == NONE)
	rtrn = UPDATE;

    str_free(&sql);

    /* free saving data */
    contact_free(contact->save);
    free(contact->save);

    contact->updated = NONE;
    contact->save = NULL;
    return(rtrn);
}

int
sql_contact_save_all(struct user_ *user) {
 struct contact_	*contact;
 struct contact_	*next;
 struct contact_	*save;
 log_			*log = &config.log;
 int			delete = 0;

    if (user->contact_delete) {
	user->contact_delete = 0;
	delete = 1;
    }

    for (contact = RB_MIN(contacts, &user->contacts); contact != NULL;
	 contact = next) {
	next = RB_NEXT(contacts, &user->contacts, contact);

	if (contact->updated == NEW) {

	    if (user->commands & CONTACT_DENY) {
		contact->deny |= CONTACT_DENY;
	    }
	    if (sql_insert_contact(&user->email, contact) == RFAIL) {
		log->debug("debug: %S: cannot insert contact\n");
		contact_print(contact);
		return(RFAIL);
	    }

	    contact->updated = NONE;
	} else if (contact->updated == UPDATE) {

	    if (sql_contact_save(user, contact) == RFAIL) {
		log->debug("debug: %S: cannot save contact\n");
		contact_print(contact);
		return(RFAIL);
	    }
	} else if (contact->updated == NONE && delete) {

	    if ((contact->deny & CONTACT_REMOVED) == 0) {

		save = contact_update(user, &contact->c);
		if (save == NULL)
		    continue;

		save->deny |= CONTACT_REMOVED;

		if (sql_contact_save(user, save) == RFAIL) {
		    log->debug("debug: %S: cannot save contact (removed)\n");
		    contact_print(contact);
		    return(RFAIL);
		}
	    }

//	    RB_REMOVE(contacts, &user->contacts, contact);
//	    contact_free(contact);
//	    free(contact);

	}
    }

    return(ROK);
}

int sql_check_contactgroup(string *email, string *name, string *id) {
 char           fmt[] = "SELECT name, id FROM contact_groups WHERE "
			"email = '%q' AND name = '%q'";
 mysql_		*mysql = &config.mysql;
 MYSQL_RES	*res;
 MYSQL_ROW	row;
 string		sql;

    str_zero(&sql);
    sql.len = fmt_printf(NULL, fmt, email->s, name->s);
    if (str_ready(&sql, sql.len + 1) == 0) die_nomem();
    sql.len = fmt_printf(sql.s, fmt, email->s, name->s);

    res = db_query(mysql, &sql);
    if (!res) {
	str_free(&sql);
	return(RFAIL);
    }

    str_free(&sql);

    if (db_count(res) == 0) {
	/* contact not found */
	res = db_free(res);
	return(NO);
    }

    /* check for update */
    row = db_fetch_row(res);
    if (!row || !row[0] || !row[1]) {
	res = db_free(res);
	return(RFAIL);
    }

    if (strcasecmp(row[0], (char *)name->s) != 0 ||
	strcasecmp(row[1], (char *)id->s) != 0) {

	/* contact need to be updated */
	res = db_free(res);
	return(UPDATE);
    }

    res = db_free(res);
    return(YES);
}

int
sql_update_contactgroup(string *email, string *name, string *id) {
 char           fmt[] = "UPDATE contact_groups SET name = '%q', id = '%q' "
			"WHERE email = '%q' AND name = '%q'";
 mysql_         *mysql = &config.mysql;
 string         sql;

    str_zero(&sql);
    sql.len = fmt_printf(NULL, fmt, name->s, id->s, email->s, name->s);
    if (str_ready(&sql, sql.len + 1) == 0) die_nomem();
    sql.len = fmt_printf(sql.s, fmt, name->s, id->s, email->s, name->s);

    if (db_query2(mysql, &sql) == RFAIL) {
        str_free(&sql);
        return(RFAIL);
    }

    str_free(&sql);
    return(ROK);
}

int
sql_insert_contactgroup(string *email, string *name, string *id) {
 char           fmt[] = "INSERT INTO contact_groups (email, name, id) "
                        "VALUES ('%q', '%q', '%q')";
 mysql_         *mysql = &config.mysql;
 string         sql;

    str_zero(&sql);
    sql.len = fmt_printf(NULL, fmt, email->s, name->s, id->s);
    if (str_ready(&sql, sql.len + 1) == 0) die_nomem();
    sql.len = fmt_printf(sql.s, fmt, email->s, name->s, id->s);

    if (db_query2(mysql, &sql) == RFAIL) {
        str_free(&sql);
        return(RFAIL);
    }

    str_free(&sql);
    return(ROK);
}

int
sql_save_contactgroup(string *email, string *name, string *id) {
    switch (sql_check_contactgroup(email, name, id)) {

	case UPDATE:
	    /* contact changed - update */
	    return(sql_update_contactgroup(email, name, id));

	case NO:
	    /* contact not in list - add */
	    return(sql_insert_contactgroup(email, name, id));

	case YES:
	    /* contact in list - do nothing */
	    break;

	case RFAIL:
	default:
	    return(RFAIL);
    }
    return(ROK);
}

int
sql_contact_load(struct user_ *user, struct contact_ *contact) {
 char			fmt[] = "SELECT contact_email, contact_dn, contact_uid, "
				"contact_status, contact_info, contact_flags, "
				"contact_flags2, contact_lists, contact_deny "
				"contact_chat "
				"FROM contacts WHERE email = '%q' AND "
				"contact_email = '%q'";
 MYSQL_RES		*res;
 MYSQL_ROW		row;
 mysql_         	*mysql = &config.mysql;
 string         	sql;

    str_zero(&sql);
    sql.len = fmt_printf(NULL, fmt, user->email.s, contact->c.s);
    if (str_ready(&sql, sql.len + 1) == 0) die_nomem();
    sql.len = fmt_printf(sql.s, fmt, user->email.s, contact->c.s);

    res = db_query(mysql, &sql);
    if (res == NULL) {
	str_free(&sql);
	return(RFAIL);
    }
    str_free(&sql);

    row = db_fetch_row(res);
    if (row == NULL) {
	res = db_free(res);
	return(RFAIL);
    }

    if (row[0] && strlen(row[0]) > 0)			/* contact_email */
	if (str_copys(&contact->c, (unsigned char *)row[0]) == 0) exit(51);
    if (row[1] && strlen(row[1]) > 0)			/* contact_dn */
	if (str_copys(&contact->dn, (unsigned char *)row[1]) == 0) exit(51);
    if (row[2] && strlen(row[2]) > 0)			/* contact_uid */
	if (str_copys(&contact->uid, (unsigned char *)row[2]) == 0) exit(51);
    if (row[3] && strlen(row[3]) > 0)			/* contact_status */
	if (str_copys(&contact->status, (unsigned char *)row[3]) == 0) exit(51);
    if (row[4] && strlen(row[4]) > 0)			/* contact_info */
	if (str_copys(&contact->info, (unsigned char *)row[4]) == 0) exit(51);

    contact->flags  = row[5] ? atoll(row[5]) : 0;	/* flags */
    contact->flags2 = row[6] ? atoll(row[6]) : 0;	/* flags2 */
    contact->lists  = row[7] ? atol(row[7]) : 0;	/* lists */
    contact->deny   = row[8] ? atol(row[8]) : 0;	/* deny */

    if (row[9] && strcasecmp(row[9], "YES") == 0)	/* contact_chat */
	contact->chat = YES;

    res = db_free(res);
    return(ROK);
}

int
sql_contact_load_all(struct user_ *user) {
 char			fmt[] = "SELECT contact_email, contact_dn, contact_uid, "
				"contact_status, contact_info, contact_flags, "
				"contact_flags2, contact_lists, contact_deny "
				"contact_chat "
				"FROM contacts WHERE email = '%q'";
 struct contact_	*contact;
 MYSQL_RES		*res;
 MYSQL_ROW		row;
 mysql_         	*mysql = &config.mysql;
 string         	sql;
 string         	c;

    str_zero(&sql);
    sql.len = fmt_printf(NULL, fmt, user->email.s);
    if (str_ready(&sql, sql.len + 1) == 0) die_nomem();
    sql.len = fmt_printf(sql.s, fmt, user->email.s);

    res = db_query(mysql, &sql);
    if (res == NULL) {
	str_free(&sql);
	return(RFAIL);
    }
    str_free(&sql);

    str_zero(&c);
    while ((row = db_fetch_row(res))) {

	/* skip nulls and zero lines */
	if (row[0] == NULL || strlen(row[0]) == 0)
	    continue;

	if (str_copys(&c, (unsigned char *)row[0]) == 0) exit(51);
	contact = contact_add(user, &c);
	str_free(&c);

	if (row[1] && strlen(row[1]) > 0)	/* contact_dn */
	    if (str_copys(&contact->dn, (unsigned char *)row[1]) == 0) exit(51);
	if (row[2] && strlen(row[2]) > 0)	/* contact_uid */
	    if (str_copys(&contact->uid, (unsigned char *)row[2]) == 0) exit(51);
	if (row[3] && strlen(row[3]) > 0)	/* contact_status */
	    if (str_copys(&contact->status, (unsigned char *)row[3]) == 0) exit(51);
	if (row[4] && strlen(row[4]) > 0)	/* contact_info */
	    if (str_copys(&contact->info, (unsigned char *)row[4]) == 0) exit(51);

	contact->flags  = row[5] ? atoll(row[5]) : 0;		/* flags */
	contact->flags2 = row[6] ? atoll(row[6]) : 0;		/* flags2 */
	contact->lists  = row[7] ? atol(row[7]) : 0;		/* lists */
	contact->deny   = row[8] ? atol(row[8]) : 0;		/* deny */

	if (row[9] && strcasecmp(row[9], "YES") == 0)	/* contact_chat */
	   contact->chat = YES;
    }
    res = db_free(res);

    return(ROK);
}

int
sql_zero_ubx(string *email, string *contact) {
 char		fmt[] = "UPDATE contacts SET "
			"contact_info = '', contact_media = '' "
			"WHERE email = '%q' AND contact_email = '%q'";
 mysql_		*mysql = &config.mysql;
 string		sql;

    str_zero(&sql);
    sql.len = fmt_printf(NULL, fmt, email->s, contact->s);
    if (str_ready(&sql, sql.len + 1) == 0) die_nomem();
    sql.len = fmt_printf(sql.s, fmt, email->s, contact->s);

    if (db_query2(mysql, &sql) == RFAIL) {
        str_free(&sql);
        return(RFAIL);
    }

    str_free(&sql);
    return(ROK);
}

int
sql_read_warning(string *warndn, string *warnmsg, string *warnemail,
		 string *msgfont, string *msgcolor) {
 char		fmt[] = "SELECT warndn, warnmsg, warnemail, msgfont, msgcolor "
			"FROM defaults LIMIT 1";
 mysql_		*mysql = &config.mysql;
 string		tmp;
 string		sql;
 MYSQL_RES	*res;
 MYSQL_ROW	row;

    str_zero(&sql);
    sql.len = fmt_printf(NULL, fmt);
    if (str_ready(&sql, sql.len + 1) == 0) die_nomem();
    sql.len = fmt_printf(sql.s, fmt);

    res = db_query(mysql, &sql);
    if (res == NULL) {
	str_free(&sql);
	return(RFAIL);
    }

    str_free(&sql);
    row = db_fetch_row(res);
    if (row == NULL)
	goto error;

    /* warndn */
    str_zero(&tmp);
    if (row[0]) {
	tmp.s = (unsigned char *)row[0];
	tmp.len = strlen(row[0]);
	if (msn_encode(&tmp, warndn, 0) == RFAIL) goto error;
    }

    /* warnmsg */
    str_zero(&tmp);
    if (row[1]) {
	tmp.s = (unsigned char *)row[1];
	tmp.len = strlen(row[1]);
	if (msn_encode(&tmp, warnmsg, PAYLOAD) == RFAIL) goto error;
    }

    /* warnemail */
    str_zero(&tmp);
    if (row[2]) {
	tmp.s = (unsigned char *)row[2];
	tmp.len = strlen(row[2]);
	if (msn_encode(&tmp, warnemail, 0) == RFAIL) goto error;
    }

    /* msgfont */
    str_zero(&tmp);
    if (row[3]) {
	tmp.s = (unsigned char *)row[3];
	tmp.len = strlen(row[3]);
	if (msn_encode(&tmp, msgfont, 0) == RFAIL) goto error;
    }

    /* msgcolor */
    str_zero(&tmp);
    if (row[4]) {
	tmp.s = (unsigned char *)row[4];
	tmp.len = strlen(row[4]);
	if (msn_encode(&tmp, msgcolor, 0) == RFAIL) goto error;
    }

    res = db_free(res);
    return(ROK);

error:
    res = db_free(res);
    return(RFAIL);
}

