/* MemoServ forwarding module.
*
* IRC Services is copyright (c) 1996-2007 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_mainnick(ngi));
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 }
};
/*************************************************************************/
static int do_reconfigure(int after_configure)
{
if (after_configure) {
if (MSAllowForward)
commands[lenof(commands)-2].name = "FORWARD";
else
commands[lenof(commands)-2].name = NULL;
}
return 0;
}
/*************************************************************************/
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(NULL, "reconfigure", do_reconfigure)
|| !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;
}
remove_callback(NULL, "reconfigure", do_reconfigure);
commands[lenof(commands)-2].name = "FORWARD";
return 1;
}
/*************************************************************************/
syntax highlighted by Code2HTML, v. 0.9.1