/* MemoServ forwarding module.
 *
 * IRC Services is copyright (c) 1996-2002 Andrew Church.
 *     E-mail: <achurch@achurch.org>
 * Parts written by Andrew Kempe and others.
 * This program is free but copyrighted software; see the file COPYING for
 * details.
 */

#include "services.h"
#include "modules.h"
#include "conffile.h"
#include "commands.h"
#include "language.h"
#include "encrypt.h"
#include "modules/mail/mail.h"
#include "modules/nickserv/nickserv.h"

#include "memoserv.h"

/*************************************************************************/
/*************************** Local variables *****************************/
/*************************************************************************/

static Module *module;
static Module *module_memoserv;
static Module *module_nickserv_mail_auth;
static Module *module_mail;

static int    MSAllowForward = 0;
static time_t MSForwardDelay = 0;

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

static void do_forward(User *u);

/* NOTE: {init,exit}_module() assume that FORWARD is the last item in the list,
 *       so don't rearrange things here! */
static Command commands[] = {
    { "SET FORWARD", NULL,     NULL,           MEMO_HELP_SET_FORWARD, -1,-1 },
    { "FORWARD",  do_forward,  NULL,           MEMO_HELP_FORWARD,     -1,-1 },
    { NULL }
};

/*************************************************************************/
/*************************** Command handlers ****************************/
/*************************************************************************/

/* Handle the FORWARD command. */

static int fwd_memo(MemoInfo *mi, int num, const User *u);
static int fwd_memo_callback(User *u, int num, va_list args);

static void do_forward(User *u)
{
    MemoInfo *mi;
    char *numstr = strtok_remaining();
    int last, i;
    int fwdcount, count;
    time_t now = time(NULL);

    if (!user_identified(u)) {
	notice_lang(s_MemoServ, u, NICK_IDENTIFY_REQUIRED, s_NickServ);
	return;
    }
    mi = &u->ngi->memos;
    if (!numstr || (!isdigit(*numstr) && stricmp(numstr, "ALL") != 0)) {
	syntax_error(s_MemoServ, u, "FORWARD", MEMO_FORWARD_SYNTAX);
    } else if (mi->memos_count == 0) {
	notice_lang(s_MemoServ, u, MEMO_HAVE_NO_MEMOS);
    } else if (MSForwardDelay > 0 && u->lastmemofwd+MSForwardDelay > now) {
	u->lastmemofwd = now;
	notice_lang(s_MemoServ, u, MEMO_FORWARD_PLEASE_WAIT,
		    maketime(u->ngi,MSForwardDelay,MT_SECONDS));
    } else {
	int tempfail = 0, permfail = 0;

	if (isdigit(*numstr)) {
	    /* Forward a specific memo or memos. */

	    fwdcount = process_numlist(numstr, &count, fwd_memo_callback,
				       u, mi, &last, &tempfail, &permfail);
	    if (fwdcount) {
		/* Some memos got forwarded. */
		if (fwdcount > 1)
		    notice_lang(s_MemoServ, u, MEMO_FORWARDED_SEVERAL,
				fwdcount);
		else
		    notice_lang(s_MemoServ, u, MEMO_FORWARDED_ONE, last);
		if (tempfail)
		    notice_lang(s_MemoServ, u, MEMO_FORWARDED_ALSO_TEMPFAIL);
		if (permfail)
		    notice_lang(s_MemoServ, u, MEMO_FORWARDED_ALSO_PERMFAIL);
	    } else {
		/* No memos were forwarded. */
		if (count == 1) {
		    if (tempfail)
			notice_lang(s_MemoServ, u, SENDMAIL_NO_RESOURCES);
		    else if (permfail)
			notice_lang(s_MemoServ, u, MEMO_FORWARD_FAILED);
		    else
			notice_lang(s_MemoServ, u, MEMO_DOES_NOT_EXIST,
				    atoi(numstr));
		} else {
		    notice_lang(s_MemoServ, u, MEMO_FORWARDED_NONE);
		    if (tempfail)
			notice_lang(s_MemoServ, u,
				    MEMO_FORWARDED_ALSO_TEMPFAIL);
		    if (permfail)
			notice_lang(s_MemoServ, u,
				    MEMO_FORWARDED_ALSO_PERMFAIL);
		}
	    }
	} else {
	    /* Forward all memos. */
	    ARRAY_FOREACH (i, mi->memos) {
		int res = fwd_memo(mi, mi->memos[i].number, u);
		if (res == -1) {
		    tempfail++;
		} else if (res <= 0) {
		    if (res == 0)
			module_log("do_forward(): BUG: memo %d not found for"
				   " ALL (index %d, nick %s, nickgroup %u)",
				   mi->memos[i].number, i, u->nick,
				   u->ngi->id);
		}
	    }
	    notice_lang(s_MemoServ, u, MEMO_FORWARDED_ALL);
	    if (tempfail)
		notice_lang(s_MemoServ, u, MEMO_FORWARDED_ALSO_TEMPFAIL);
	    if (permfail)
		notice_lang(s_MemoServ, u, MEMO_FORWARDED_ALSO_PERMFAIL);
	}
	u->lastmemofwd = now;
    }
}


/* Forward a memo by number.  Return:
 *     1 if the memo was found and delivered
 *     0 if the memo was not found
 *    -1 if the memo was found but could not be delivered (temporary failure)
 *    -2 if the memo was found but could not be delivered (permanent failure)
 */
static int fwd_memo(MemoInfo *mi, int num, const User *u)
{
    int i;

    ARRAY_SEARCH_SCALAR(mi->memos, number, num, i);
    if (i < mi->memos_count) {
	char subject[BUFSIZE], body[BUFSIZE*2], timebuf[BUFSIZE];

	strftime_lang(timebuf, sizeof(timebuf), u->ngi,
		      STRFTIME_DATE_TIME_FORMAT, mi->memos[i].time);
	timebuf[sizeof(timebuf)-1] = 0;
	snprintf(subject, sizeof(subject),
		 getstring(u->ngi,MEMO_FORWARD_MAIL_SUBJECT), u->ni->nick);
	snprintf(body, sizeof(body), getstring(u->ngi,MEMO_FORWARD_MAIL_BODY),
		 mi->memos[i].sender, timebuf, mi->memos[i].text);
	i = sendmail(u->ngi->email, subject, body);
	return i==1 ? -1 : i==-1 ? -2 : 1;
    } else {
	return 0;
    }
}

/* Forward a single memo from a MemoInfo. */
static int fwd_memo_callback(User *u, int num, va_list args)
{
    MemoInfo *mi = va_arg(args, MemoInfo *);
    int *last = va_arg(args, int *);
    int *tempfail = va_arg(args, int *);
    int *permfail = va_arg(args, int *);
    int i;

    i = fwd_memo(mi, num, u);
    if (i > 0) {
	*last = num;
	return 1;
    } else {
	if (i == -1)
	    (*tempfail)++;
	else if (i == -2)
	    (*permfail)++;
	return 0;
    }
}

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

/* Handle the SET FORWARD command. */

static int do_set_forward(User *u, MemoInfo *mi, const char *option,
			  const char *param)
{
    if (stricmp(option, "FORWARD") != 0)
	return 0;
    if (!u->ngi->email) {
	notice_lang(s_MemoServ, u, MEMO_FORWARD_NEED_EMAIL);
    } else if (stricmp(param, "ON") == 0) {
	u->ngi->flags |= NF_MEMO_FWD;
	u->ngi->flags &= ~NF_MEMO_FWDCOPY;
	notice_lang(s_MemoServ, u, MEMO_SET_FORWARD_ON, u->ngi->email);
    } else if (stricmp(param, "COPY") == 0) {
	u->ngi->flags |= NF_MEMO_FWD | NF_MEMO_FWDCOPY;
	notice_lang(s_MemoServ, u, MEMO_SET_FORWARD_COPY, u->ngi->email);
    } else if (stricmp(param, "OFF") == 0) {
	u->ngi->flags &= ~(NF_MEMO_FWD | NF_MEMO_FWDCOPY);
	notice_lang(s_MemoServ, u, MEMO_SET_FORWARD_OFF);
    } else {
	syntax_error(s_MemoServ, u, "SET FORWARD", MEMO_SET_FORWARD_SYNTAX);
    }
    return 1;
}

/*************************************************************************/
/****************************** Callbacks ********************************/
/*************************************************************************/

static int do_receive_memo(int ischan, void *who, const char *whoname,
			   const User *sender, const char *text)
{
    NickGroupInfo *ngi;
    char subject[BUFSIZE], body[BUFSIZE*2], timebuf[BUFSIZE];
    time_t now = time(NULL);
    int res;

    if (ischan)
	return 0;
    ngi = who;
    if (!(ngi->flags & NF_MEMO_FWD))
	return 0;

    strftime_lang(timebuf, sizeof(timebuf), ngi,
		  STRFTIME_DATE_TIME_FORMAT, now);
    timebuf[sizeof(timebuf)-1] = 0;
    snprintf(subject, sizeof(subject),
	     getstring(ngi,MEMO_FORWARD_MAIL_SUBJECT),
	     ngi->nicks[ngi->mainnick]);
    snprintf(body, sizeof(body), getstring(ngi,MEMO_FORWARD_MAIL_BODY),
	     sender->nick, timebuf, text);
    if ((res = sendmail(ngi->email, subject, body)) != 0) {
	module_log("sendmail() for memo to %s failed%s, delivering normally",
		   whoname, res==1 ? " temporarily" : "");
	return 0;
    }
    if (!(ngi->flags & NF_MEMO_FWDCOPY)) {
	notice_lang(s_MemoServ, sender, MEMO_SENT, whoname);
	return 1;
    }
    return 0;
}

/*************************************************************************/
/***************************** Module stuff ******************************/
/*************************************************************************/

const int32 module_version = MODULE_VERSION_CODE;

ConfigDirective module_config[] = {
    { "MSAllowForward",   { { CD_SET, 0, &MSAllowForward } } },
    { "MSForwardDelay",   { { CD_TIME, 0, &MSForwardDelay } } },
    { NULL }
};

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

int init_module(Module *module_)
{
    module = module_;

    module_memoserv = find_module("memoserv/main");
    if (!module_memoserv) {
	module_log("Main MemoServ module not loaded");
	exit_module(0);
	return 0;
    }
    use_module(module_memoserv);

    module_nickserv_mail_auth = find_module("nickserv/mail-auth");
    if (!module_nickserv_mail_auth) {
	module_log("NickServ AUTH module (mail-auth) required for FORWARD");
	exit_module(0);
	return 0;
    }
    use_module(module_nickserv_mail_auth);

    module_mail = find_module("mail/main");
    if (!module_mail) {
	module_log("Mail module not loaded");
	exit_module(0);
	return 0;
    }
    use_module(module_mail);

    if (!MSAllowForward)
	commands[lenof(commands)-2].name = NULL;
    if (!register_commands(module_memoserv, commands)) {
	module_log("Unable to register commands");
	exit_module(0);
	return 0;
    }

    if (!add_callback_pri(module_memoserv, "receive memo", do_receive_memo,
			  MS_RECEIVE_PRI_DELIVER)
     || !add_callback(module_memoserv, "SET", do_set_forward)
    ) {
	module_log("Unable to add callbacks");
	exit_module(0);
	return 0;
    }

    return 1;
}

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

int exit_module(int shutdown_unused)
{
#ifdef CLEAN_COMPILE
    shutdown_unused = shutdown_unused;
#endif

    if (module_mail) {
	unuse_module(module_mail);
	module_mail = NULL;
    }
    if (module_nickserv_mail_auth) {
	unuse_module(module_nickserv_mail_auth);
	module_nickserv_mail_auth = NULL;
    }
    if (module_memoserv) {
	remove_callback(module_memoserv, "SET", do_set_forward);
	remove_callback(module_memoserv, "receive memo", do_receive_memo);
	unregister_commands(module_memoserv, commands);
	unuse_module(module_memoserv);
	module_memoserv = NULL;
    }

    if (!MSAllowForward)
	commands[lenof(commands)-2].name = "FORWARD";

    return 1;
}

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