/*
 * 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: msn-proxy.c 112 2009-03-15 17:30:28Z loos-br $";

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>

#include "ns.h"
#include "io.h"
#include "ctl.h"
#include "sql.h"
#include "sig.h"
#include "user.h"
#include "net-io.h"
#include "return.h"
#include "msn-proxy.h"

/* globals */
config_		config;
struct event	evctl;
struct event	ev;
struct event_base *base = (struct event_base *)0;

/* prevents memory leaks when program exits */
void cleanup(void) {

	db_close(&config.mysql);
	str_free(&config.default_ns_host);
	str_free(&config.default_ns_port);
	str_free(&config.defaults.internal_host);
	str_free(&config.file);
	str_free(&config.listen_host);
	str_free(&config.listen_port);
	str_free(&config.defaults.internal_host);
	str_free(&config.mysql.db);
	str_free(&config.mysql.host);
	str_free(&config.mysql.pass);
	str_free(&config.mysql.user);
	if (base) {
			event_base_free(base);
	}
	/* free sql tree - tables */
	free_sqltable_tree(&config.mysql);
	/* free users tree - contacts and users */
	free_users_tree();

}

void die_nomem(void) { perror("malloc"); cleanup(); exit(51); }
void die_err(char *msg) { if (msg) { io_printf(1, "%S\n", msg); } cleanup(); exit(1); }
void die_bug(void) { io_printf(1, "oops bug here ! call my doctor !\n"); exit(2); }
void die_suc(char *msg) { if (msg) { io_printf(1, "%S\n", msg); cleanup(); } exit(1); }

void usage(void) {
    io_printf(1,
	"msn-proxy: take control of messenger in your network\n\n"
	"	-f /path/to/msn-proxy.conf (default to /usr/local/etc)\n"
	"	-b start in background\n"
	"	-d show debug info\n"
	"	-i show status info\n"
	"	-p show protocol\n"
	"	-s show sql\n"
	"	-v verbose (show all above)\n"
	"\n");
    exit(1);
}

void
detach(void) {
    if (setsid() == -1) {
	perror("setsid");
	config.log.debug("debug: setsid() failed probably because you aren't "
		   "running under a process management tool like daemontools\n");
	exit(1);
    }
}

void
daemonize(void) {
 pid_t		pid;

    pid = fork();
    if (pid == -1) {
	perror("fork");
	exit(1);
    }

    if (pid == 0)
	return;

    exit(0);
}

void sigdie(void) {
	/* Exit with SUCCESS */
	die_suc("Exit msn-proxy!\n");
}


int
main(int argc, char **argv) {

 int		bg;
 int		ch;
 int		ctlfd;
 int		listenfd;
 log_		*l = &config.log;


    /* bug bug bug */
//    sig_bugcatch(die_bug);

    /* zero config and set null log */
    if (config_default(&config) == -1)
	die_err("cannot reset config data (?)");

    /* setup log functions */
    start_log(l, LOG_IDENT);

    bg = 0;
    /* check options */
    while ((ch = getopt(argc, argv, "f:dipsvb")) != -1) {
	switch (ch) {
	    case 'b':
		bg = 1;
		break;

	    case 'f':
		/* path to config */
		if (config_set_path(&config.file, optarg) == -1)
		    usage();
		break;

	    case 'd':
		/* debug */
		l->debug    = log;
		break;

	    case 'i':
		/* info */
		l->info     = log;
		break;

	    case 'p':
		/* protocol */
		l->protocol = log;
		break;

	    case 's':
		/* sql */
		l->sql      = log;
		break;

	    case 'v':
		/* verbose (all) */
		l->protocol = log;
		l->debug    = log;
		l->info     = log;
		l->sql      = log;
		break;

	    case '?':
	    default:
		usage();
	}
    }
    argc -= optind;
    argv += optind;

    /* detach */
    if (bg == 0) {
	//detach();
   } else {
	daemonize();
    }

    /* read config */
    if (parse_config(&config) == RFAIL)
	die_err("fail to parse config file");

    /* read mysql configuration */
    if (db_read_conf(&config.mysql, MYSQLCONFDIR) == RFAIL)
        die_err("cannot read database config");

    /* connect to mysql */
    if (db_init(&config.mysql, l->debug, l->sql) == RFAIL)
	die_err("cannot init database");

    /* read config defaults */
    if (sql_read_defaults() == RFAIL)
	die_err("fail to read mysql config (check defaults table)");

    /* set SIGALRM to reload ACL defaults */
    sig_catch(SIGALRM, (void *)sql_read_defaults);

    /* set some signals */
    sig_catch(SIGTERM, sigdie);
    sig_catch(SIGQUIT, sigdie);
    sig_catch(SIGINT, sigdie);

    print_config(&config);

    ctlfd = unix_listen(config.backlog);
    if (ctlfd == -1)
	die_err("cannot create unix socket [" MSNPROXYCTL "]");

    /* setup the server socket */
    if ((listenfd = net_listen("0.0.0.0", /* XXX - config.listen_host.s, */
			       (char *)config.listen_port.s,
			       config.backlog)) == -1) {

	io_printf(1, "listen: %S\n", strerror(errno));
	die_err((char *)0);
    }

    /* remove stale records */
    if (sql_disconnect_all() == RFAIL)
	die_err("fail to reset database");

    /* users rb list init */
    user_rb_init();

    /* setup libevent */
    base = event_init();

    /*
     * setup persistent event on listenfd
     * each received connection start a new connection to default
     * notification server
     */
    event_set(&ev, listenfd, EV_READ | EV_PERSIST, ns_client, &config);
    event_add(&ev, (struct timeval *)0);

    event_set(&evctl, ctlfd, EV_READ | EV_PERSIST, ctl_client, &config);
    event_add(&evctl, (struct timeval *)0);

    event_dispatch();

    (void)close(listenfd);
    exit(0);
}
