/* MemoServ forwarding module. * * IRC Services is copyright (c) 1996-2007 Andrew Church. * E-mail: * 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; } /*************************************************************************/