From: Michael Poole Date: Thu, 8 Mar 2007 00:52:57 +0000 (-0500) Subject: Split sendmail into common and sendmail-specific portions. X-Git-Tag: v1.4.0-rc1~63 X-Git-Url: http://git.pk910.de/?p=srvx.git;a=commitdiff_plain;h=0337f0404b04012117633b4795508b06ab91ca87 Split sendmail into common and sendmail-specific portions. This is in preparation for an SMTP alternative to the current code. --- diff --git a/configure.in b/configure.in index a821044..57deaae 100644 --- a/configure.in +++ b/configure.in @@ -239,7 +239,6 @@ if test "x$withval" = "xp10" ; then AC_MSG_RESULT(P10) AC_DEFINE(WITH_PROTOCOL_P10, 1, [Define if using the P10 dialect of IRC]) MODULE_OBJS="$MODULE_OBJS proto-p10.\$(OBJEXT)" - PROTO_FILES=proto-p10.c elif test "x$withval" = "xbahamut" ; then AC_MSG_RESULT(Bahamut) AC_DEFINE(WITH_PROTOCOL_BAHAMUT, 1, [Define if using the Bahamut dialect of IRC]) @@ -248,6 +247,19 @@ else AC_MSG_ERROR([Unknown IRC dialect $withval]) fi +AC_MSG_CHECKING(how to send mail) +AC_ARG_WITH(mail, +[ --with-mail=name How to send mail; one of: + sendmail (the default)], +[], +[withval="sendmail"]) +if test "x$withval" = "xsendmail" ; then + AC_MSG_RESULT(sendmail) + MODULE_OBJS="$MODULE_OBJS mail-sendmail.\$(OBJEXT)" +else + AC_MSG_ERROR([Unknown mail method $withval]) +fi + AC_MSG_CHECKING([I/O multiplexing backends]) IOMUXES="" diff --git a/src/Makefile.am b/src/Makefile.am index 4ce9b7f..34456de 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,7 +10,7 @@ noinst_DATA = \ nickserv.help \ opserv.help \ saxdb.help \ - sendmail.help \ + mail.help \ mod-helpserv.help \ mod-memoserv.help \ mod-sockcheck.help @@ -48,6 +48,8 @@ EXTRA_srvx_SOURCES = \ ioset-epoll.c \ ioset-select.c \ ioset-win32.c \ + mail-common.c \ + mail-sendmail.c \ main-common.c \ main-win32.c \ proto-bahamut.c \ @@ -75,6 +77,7 @@ srvx_SOURCES = \ helpfile.c helpfile.h \ ioset.c ioset.h ioset-impl.h \ log.c log.h \ + mail.h \ main.c common.h \ md5.c md5.h \ modcmd.c modcmd.h \ @@ -85,7 +88,6 @@ srvx_SOURCES = \ proto.h \ recdb.c recdb.h \ saxdb.c saxdb.h \ - sendmail.c sendmail.h \ timeq.c timeq.h \ tools.c diff --git a/src/mail-common.c b/src/mail-common.c new file mode 100644 index 0000000..84a9cf4 --- /dev/null +++ b/src/mail-common.c @@ -0,0 +1,163 @@ +/* mail-common.c - mail sending utilities + * Copyright 2002-2004, 2007 srvx Development Team + * + * This file is part of srvx. + * + * srvx is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with srvx; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "conf.h" +#include "modcmd.h" +#include "nickserv.h" +#include "saxdb.h" + +#ifdef HAVE_SYS_WAIT_H +#include +#endif + +#define KEY_PROHIBITED "prohibited" + +static const struct message_entry msgtab[] = { + { "MAILMSG_EMAIL_ALREADY_BANNED", "%s is already banned (%s)." }, + { "MAILMSG_EMAIL_BANNED", "Email to %s has been forbidden." }, + { "MAILMSG_EMAIL_NOT_BANNED", "Email to %s was not forbidden." }, + { "MAILMSG_EMAIL_UNBANNED", "Email to %s is now allowed." }, + { "MAILMSG_PROHIBITED_EMAIL", "%s: %s" }, + { "MAILMSG_NO_PROHIBITED_EMAIL", "All email addresses are accepted." }, + { NULL, NULL } +}; + +static dict_t prohibited_addrs, prohibited_masks; +struct module *mail_module; + +const char * +mail_prohibited_address(const char *addr) +{ + dict_iterator_t it; + const char *data; + + if (prohibited_addrs && (data = dict_find(prohibited_addrs, addr, NULL))) + return data; + if (prohibited_masks) + for (it = dict_first(prohibited_masks); it; it = iter_next(it)) + if (match_ircglob(addr, iter_key(it))) + return iter_data(it); + return NULL; +} + +static int +mail_ban_address(struct userNode *user, struct userNode *bot, const char *addr, const char *reason) { + dict_t target; + const char *str; + + target = strpbrk(addr, "*?") ? prohibited_masks : prohibited_addrs; + if ((str = dict_find(target, addr, NULL))) { + if (user) + send_message(user, bot, "MAILMSG_EMAIL_ALREADY_BANNED", addr, str); + return 0; + } + dict_insert(target, strdup(addr), strdup(reason)); + if (user) send_message(user, bot, "MAILMSG_EMAIL_BANNED", addr); + return 1; +} + +static MODCMD_FUNC(cmd_banemail) { + char *reason = unsplit_string(argv+2, argc-2, NULL); + return mail_ban_address(user, cmd->parent->bot, argv[1], reason); +} + +static MODCMD_FUNC(cmd_unbanemail) { + dict_t target; + const char *addr; + + addr = argv[1]; + target = strpbrk(addr, "*?") ? prohibited_masks : prohibited_addrs; + if (dict_remove(target, addr)) + reply("MAILMSG_EMAIL_UNBANNED", addr); + else + reply("MAILMSG_EMAIL_NOT_BANNED", addr); + return 1; +} + +static MODCMD_FUNC(cmd_stats_email) { + dict_iterator_t it; + int found = 0; + + for (it=dict_first(prohibited_addrs); it; it=iter_next(it)) { + reply("MAILMSG_PROHIBITED_EMAIL", iter_key(it), (const char*)iter_data(it)); + found = 1; + } + for (it=dict_first(prohibited_masks); it; it=iter_next(it)) { + reply("MAILMSG_PROHIBITED_EMAIL", iter_key(it), (const char*)iter_data(it)); + found = 1; + } + if (!found) + reply("MAILMSG_NO_PROHIBITED_EMAIL"); + return 0; +} + +static int +mail_saxdb_read(struct dict *db) { + struct dict *subdb; + struct record_data *rd; + dict_iterator_t it; + + if ((subdb = database_get_data(db, KEY_PROHIBITED, RECDB_OBJECT))) { + for (it = dict_first(subdb); it; it = iter_next(it)) { + rd = iter_data(it); + if (rd->type == RECDB_QSTRING) + mail_ban_address(NULL, NULL, iter_key(it), rd->d.qstring); + } + } + return 0; +} + +static int +mail_saxdb_write(struct saxdb_context *ctx) { + dict_iterator_t it; + + saxdb_start_record(ctx, KEY_PROHIBITED, 0); + for (it = dict_first(prohibited_masks); it; it = iter_next(it)) + saxdb_write_string(ctx, iter_key(it), iter_data(it)); + for (it = dict_first(prohibited_addrs); it; it = iter_next(it)) + saxdb_write_string(ctx, iter_key(it), iter_data(it)); + saxdb_end_record(ctx); + return 0; +} + +static void +mail_common_cleanup(void) +{ + dict_delete(prohibited_addrs); + dict_delete(prohibited_masks); +} + +static void +mail_common_init(void) +{ + prohibited_addrs = dict_new(); + dict_set_free_keys(prohibited_addrs, free); + dict_set_free_data(prohibited_addrs, free); + prohibited_masks = dict_new(); + dict_set_free_keys(prohibited_masks, free); + dict_set_free_data(prohibited_masks, free); + reg_exit_func(mail_common_cleanup); + saxdb_register("sendmail", mail_saxdb_read, mail_saxdb_write); + mail_module = module_register("sendmail", MAIN_LOG, "mail.help", NULL); + modcmd_register(mail_module, "banemail", cmd_banemail, 3, 0, "level", "601", NULL); + modcmd_register(mail_module, "stats email", cmd_stats_email, 0, 0, "flags", "+oper", NULL); + modcmd_register(mail_module, "unbanemail", cmd_unbanemail, 2, 0, "level", "601", NULL); + message_register_table(msgtab); +} diff --git a/src/mail-sendmail.c b/src/mail-sendmail.c new file mode 100644 index 0000000..d6530b4 --- /dev/null +++ b/src/mail-sendmail.c @@ -0,0 +1,208 @@ +/* mail-common.c - mail sending utilities + * Copyright 2002-2004, 2007 srvx Development Team + * + * This file is part of srvx. + * + * srvx is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with srvx; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifdef HAVE_SYS_WAIT_H +#include +#endif + +#include "mail-common.c" + +/* This function sends the given "paragraph" as flowed text, as + * defined in RFC 2646. It lets us only worry about line wrapping + * here, and not in the code that generates mail. + */ +static void +send_flowed_text(FILE *where, const char *para) +{ + const char *eol = strchr(para, '\n'); + unsigned int shift; + + while (*para) { + /* Do we need to space-stuff the line? */ + if ((*para == ' ') || (*para == '>') || !strncmp(para, "From ", 5)) { + fputc(' ', where); + shift = 1; + } else { + shift = 0; + } + /* How much can we put on this line? */ + if (!eol && (strlen(para) < (80 - shift))) { + /* End of paragraph; can put on one line. */ + fputs(para, where); + fputs("\n", where); + break; + } else if (eol && (eol < para + (80 - shift))) { + /* Newline inside paragraph, no need to wrap. */ + fprintf(where, "%.*s\n", (int)(eol - para), para); + para = eol + 1; + } else { + int pos; + /* Need to wrap. Where's the last space in the line? */ + for (pos=72-shift; pos && (para[pos] != ' '); pos--) ; + /* If we didn't find a space, look ahead instead. */ + if (pos == 0) pos = strcspn(para, " \n"); + fprintf(where, "%.*s\n", pos+1, para); + para += pos + 1; + } + if (eol && (eol < para)) eol = strchr(para, '\n'); + } +} + +void +mail_send(struct userNode *from, struct handle_info *to, const char *subject, const char *body, int first_time) +{ + pid_t child; + int infds[2], outfds[2]; + const char *fromaddr, *str; + + /* Grab some config items first. */ + str = conf_get_data("mail/enable", RECDB_QSTRING); + if (!str || !enabled_string(str)) + return; + fromaddr = conf_get_data("mail/from_address", RECDB_QSTRING); + + /* How this works: We fork, and the child tries to send the mail. + * It does this by setting up a pipe pair, and forking again (the + * grandchild exec()'s the mailer program). The mid-level child + * sends the text to the grandchild's stdin, and then logs the + * success or failure. + */ + + child = fork(); + if (child < 0) { + log_module(MAIN_LOG, LOG_ERROR, "sendmail() to %s couldn't fork(): %s (%d)", to->email_addr, strerror(errno), errno); + return; + } else if (child > 0) { + return; + } + /* We're in a child now; must _exit() to die properly. */ + if (pipe(infds) < 0) { + log_module(MAIN_LOG, LOG_ERROR, "sendmail() child to %s couldn't pipe(infds): %s (%d)", to->email_addr, strerror(errno), errno); + _exit(1); + } + if (pipe(outfds) < 0) { + log_module(MAIN_LOG, LOG_ERROR, "sendmail() child to %s couldn't pipe(outfds): %s (%d)", to->email_addr, strerror(errno), errno); + _exit(1); + } + child = fork(); + if (child < 0) { + log_module(MAIN_LOG, LOG_ERROR, "sendmail() child to %s couldn't fork(): %s (%d)", to->email_addr, strerror(errno), errno); + _exit(1); + } else if (child > 0) { + /* Mid-level child; get ready to send the mail. */ + FILE *out = fdopen(infds[1], "w"); + struct string_list *extras; + unsigned int nn; + int res, rv; + + /* Close the end of pipes we do not use. */ + close(infds[0]); + close(outfds[1]); + + /* Do we have any "extra" headers to send? */ + extras = conf_get_data("mail/extra_headers", RECDB_STRING_LIST); + if (extras) { + for (nn=0; nnused; nn++) { + fputs(extras->list[nn], out); + fputs("\n", out); + } + } + + /* Content type? (format=flowed is a standard for plain text + * that lets the receiver reconstruct paragraphs, defined in + * RFC 2646. See comment above send_flowed_text() for more.) + */ + if (!(str = conf_get_data("mail/charset", RECDB_QSTRING))) str = "us-ascii"; + fprintf(out, "Content-Type: text/plain; charset=%s; format=flowed\n", str); + + /* Send From, To and Subject headers */ + if (!fromaddr) fromaddr = "admin@poorly.configured.network"; + fprintf(out, "From: %s <%s>\n", from->nick, fromaddr); + fprintf(out, "To: \"%s\" <%s>\n", to->handle, to->email_addr); + fprintf(out, "Subject: %s\n", subject); + + /* Send mail body */ + fputs("\n", out); /* terminate headers */ + extras = conf_get_data((first_time?"mail/body_prefix_first":"mail/body_prefix"), RECDB_STRING_LIST); + if (extras) { + for (nn=0; nnused; nn++) { + send_flowed_text(out, extras->list[nn]); + } + fputs("\n", out); + } + send_flowed_text(out, body); + extras = conf_get_data((first_time?"mail/body_suffix_first":"mail/body_suffix"), RECDB_STRING_LIST); + if (extras) { + fputs("\n", out); + for (nn=0; nnused; nn++) + send_flowed_text(out, extras->list[nn]); + } + + /* Close file (sending mail) and check for return code */ + fflush(out); + fclose(out); + do { + rv = wait4(child, &res, 0, NULL); + } while ((rv == -1) && (errno == EINTR)); + if (rv == child) { + /* accept the wait() result */ + } else { + log_module(MAIN_LOG, LOG_ERROR, "sendmail() child to %s: Bad wait() return code %d: %s (%d)", to->email_addr, rv, strerror(errno), errno); + _exit(1); + } + if (res) { + log_module(MAIN_LOG, LOG_ERROR, "sendmail() grandchild to %s: Exited with code %d", to->email_addr, res); + _exit(1); + } else { + log_module(MAIN_LOG, LOG_INFO, "sendmail() sent email to %s <%s>: %s", to->handle, to->email_addr, subject); + } + _exit(0); + } else { + /* Grandchild; dup2 the fds and exec the mailer. */ + const char *argv[10], *mpath; + unsigned int argc = 0; + + /* Close the end of pipes we do not use. */ + close(infds[1]); + close(outfds[0]); + + dup2(infds[0], STDIN_FILENO); + dup2(outfds[1], STDOUT_FILENO); + mpath = conf_get_data("mail/mailer", RECDB_QSTRING); + if (!mpath) mpath = "/usr/sbin/sendmail"; + argv[argc++] = mpath; + if (fromaddr) { + argv[argc++] = "-f"; + argv[argc++] = fromaddr; + } + argv[argc++] = to->email_addr; + argv[argc++] = NULL; + if (execv(mpath, (char**)argv) < 0) { + log_module(MAIN_LOG, LOG_ERROR, "sendmail() grandchild to %s couldn't execv(): %s (%d)", to->email_addr, strerror(errno), errno); + } + _exit(1); + } +} + +void +mail_init(void) +{ + mail_common_init(); +} diff --git a/src/mail.h b/src/mail.h new file mode 100644 index 0000000..208020c --- /dev/null +++ b/src/mail.h @@ -0,0 +1,28 @@ +/* mail.h - mail sending utilities + * Copyright 2002 srvx Development Team + * + * This file is part of srvx. + * + * srvx is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with srvx; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#if !defined(MAIL_H) +#define MAIL_H + +void mail_init(void); +void mail_send(struct userNode *from, struct handle_info *to, const char *subject, const char *body, int first_time); +const char *mail_prohibited_address(const char *addr); + +#endif diff --git a/src/mail.help b/src/mail.help new file mode 100644 index 0000000..6951b5a --- /dev/null +++ b/src/mail.help @@ -0,0 +1,7 @@ +"BANEMAIL" ("/msg $S BANEMAIL
", + "Keeps srvx from sending mail to the address. The address may be a real address, or a glob that uses * and ? wildcards.", + "This also prevents anyone from using matching addresses as their account email address.", + "$uSee Also:$u unbanemail, stats email"); +"UNBANEMAIL" ("/msg $S UNBANEMAIL
", + "Removes an email address (or glob) from the banned email address list.", + "$uSee Also:$u banemail, stats email"); diff --git a/src/main-win32.c b/src/main-win32.c index ddf8380..03b0036 100644 --- a/src/main-win32.c +++ b/src/main-win32.c @@ -4,7 +4,7 @@ #include "ioset.h" #include "modcmd.h" #include "saxdb.h" -#include "sendmail.h" +#include "mail.h" #include "timeq.h" #include "chanserv.h" @@ -41,7 +41,7 @@ int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int modcmd_init(); saxdb_init(); gline_init(); - sendmail_init(); + mail_init(); helpfile_init(); conf_globals(); conf_rlimits(); diff --git a/src/main.c b/src/main.c index 577f25b..bffe2be 100644 --- a/src/main.c +++ b/src/main.c @@ -25,7 +25,7 @@ #include "ioset.h" #include "modcmd.h" #include "saxdb.h" -#include "sendmail.h" +#include "mail.h" #include "timeq.h" #include "chanserv.h" @@ -283,7 +283,7 @@ int main(int argc, char *argv[]) modcmd_init(); saxdb_init(); gline_init(); - sendmail_init(); + mail_init(); helpfile_init(); conf_globals(); /* initializes the core services */ conf_rlimits(); diff --git a/src/nickserv.c b/src/nickserv.c index 39428a0..bf22dd6 100644 --- a/src/nickserv.c +++ b/src/nickserv.c @@ -24,7 +24,7 @@ #include "modcmd.h" #include "opserv.h" /* for gag_create(), opserv_bad_channel() */ #include "saxdb.h" -#include "sendmail.h" +#include "mail.h" #include "timeq.h" #ifdef HAVE_REGEX_H @@ -1044,7 +1044,7 @@ nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_ snprintf(subject, sizeof(subject), fmt, netname); fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW"); snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2); - sendmail(nickserv, hi, subject, body, 1); + mail_send(nickserv, hi, subject, body, 1); fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD"); snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr); } else { @@ -1053,7 +1053,7 @@ nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_ snprintf(subject, sizeof(subject), fmt, netname); fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY"); snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle); - sendmail(nickserv, hi, subject, body, 1); + mail_send(nickserv, hi, subject, body, 1); subject[0] = 0; } hi->email_addr = misc; @@ -1070,7 +1070,7 @@ nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_ break; } if (subject[0]) - sendmail(nickserv, hi, subject, body, first_time); + mail_send(nickserv, hi, subject, body, first_time); nickserv_bake_cookie(cookie); } @@ -1164,7 +1164,7 @@ static NICKSERV_FUNC(cmd_register) } /* .. and that we are allowed to send to it. */ - if ((str = sendmail_prohibited_address(email_addr))) { + if ((str = mail_prohibited_address(email_addr))) { reply("NSMSG_EMAIL_PROHIBITED", email_addr, str); return 0; } @@ -2328,7 +2328,7 @@ static OPTION_FUNC(opt_email) send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR"); return 0; } - if ((str = sendmail_prohibited_address(argv[1]))) { + if ((str = mail_prohibited_address(argv[1]))) { send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str); return 0; } diff --git a/src/sendmail.c b/src/sendmail.c deleted file mode 100644 index 63bb548..0000000 --- a/src/sendmail.c +++ /dev/null @@ -1,340 +0,0 @@ -/* sendmail.c - mail sending utilities - * Copyright 2002-2004 srvx Development Team - * - * This file is part of srvx. - * - * srvx is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with srvx; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - */ - -#include "conf.h" -#include "modcmd.h" -#include "nickserv.h" -#include "saxdb.h" - -#ifdef HAVE_SYS_WAIT_H -#include -#endif - -#define KEY_PROHIBITED "prohibited" - -static const struct message_entry msgtab[] = { - { "MAILMSG_EMAIL_ALREADY_BANNED", "%s is already banned (%s)." }, - { "MAILMSG_EMAIL_BANNED", "Email to %s has been forbidden." }, - { "MAILMSG_EMAIL_NOT_BANNED", "Email to %s was not forbidden." }, - { "MAILMSG_EMAIL_UNBANNED", "Email to %s is now allowed." }, - { "MAILMSG_PROHIBITED_EMAIL", "%s: %s" }, - { "MAILMSG_NO_PROHIBITED_EMAIL", "All email addresses are accepted." }, - { NULL, NULL } -}; - -static dict_t prohibited_addrs, prohibited_masks; -struct module *sendmail_module; - -const char * -sendmail_prohibited_address(const char *addr) -{ - dict_iterator_t it; - const char *data; - - if (prohibited_addrs && (data = dict_find(prohibited_addrs, addr, NULL))) - return data; - if (prohibited_masks) - for (it = dict_first(prohibited_masks); it; it = iter_next(it)) - if (match_ircglob(addr, iter_key(it))) - return iter_data(it); - return NULL; -} - -/* This function sends the given "paragraph" as flowed text, as - * defined in RFC 2646. It lets us only worry about line wrapping - * here, and not in the code that generates mail. - */ -static void -send_flowed_text(FILE *where, const char *para) -{ - const char *eol = strchr(para, '\n'); - unsigned int shift; - - while (*para) { - /* Do we need to space-stuff the line? */ - if ((*para == ' ') || (*para == '>') || !strncmp(para, "From ", 5)) { - fputc(' ', where); - shift = 1; - } else { - shift = 0; - } - /* How much can we put on this line? */ - if (!eol && (strlen(para) < (80 - shift))) { - /* End of paragraph; can put on one line. */ - fputs(para, where); - fputs("\n", where); - break; - } else if (eol && (eol < para + (80 - shift))) { - /* Newline inside paragraph, no need to wrap. */ - fprintf(where, "%.*s\n", (int)(eol - para), para); - para = eol + 1; - } else { - int pos; - /* Need to wrap. Where's the last space in the line? */ - for (pos=72-shift; pos && (para[pos] != ' '); pos--) ; - /* If we didn't find a space, look ahead instead. */ - if (pos == 0) pos = strcspn(para, " \n"); - fprintf(where, "%.*s\n", pos+1, para); - para += pos + 1; - } - if (eol && (eol < para)) eol = strchr(para, '\n'); - } -} - -void -sendmail(struct userNode *from, struct handle_info *to, const char *subject, const char *body, int first_time) -{ - pid_t child; - int infds[2], outfds[2]; - const char *fromaddr, *str; - - /* Grab some config items first. */ - str = conf_get_data("mail/enable", RECDB_QSTRING); - if (!str || !enabled_string(str)) - return; - fromaddr = conf_get_data("mail/from_address", RECDB_QSTRING); - - /* How this works: We fork, and the child tries to send the mail. - * It does this by setting up a pipe pair, and forking again (the - * grandchild exec()'s the mailer program). The mid-level child - * sends the text to the grandchild's stdin, and then logs the - * success or failure. - */ - - child = fork(); - if (child < 0) { - log_module(MAIN_LOG, LOG_ERROR, "sendmail() to %s couldn't fork(): %s (%d)", to->email_addr, strerror(errno), errno); - return; - } else if (child > 0) { - return; - } - /* We're in a child now; must _exit() to die properly. */ - if (pipe(infds) < 0) { - log_module(MAIN_LOG, LOG_ERROR, "sendmail() child to %s couldn't pipe(infds): %s (%d)", to->email_addr, strerror(errno), errno); - _exit(1); - } - if (pipe(outfds) < 0) { - log_module(MAIN_LOG, LOG_ERROR, "sendmail() child to %s couldn't pipe(outfds): %s (%d)", to->email_addr, strerror(errno), errno); - _exit(1); - } - child = fork(); - if (child < 0) { - log_module(MAIN_LOG, LOG_ERROR, "sendmail() child to %s couldn't fork(): %s (%d)", to->email_addr, strerror(errno), errno); - _exit(1); - } else if (child > 0) { - /* Mid-level child; get ready to send the mail. */ - FILE *out = fdopen(infds[1], "w"); - struct string_list *extras; - unsigned int nn; - int res, rv; - - /* Close the end of pipes we do not use. */ - close(infds[0]); - close(outfds[1]); - - /* Do we have any "extra" headers to send? */ - extras = conf_get_data("mail/extra_headers", RECDB_STRING_LIST); - if (extras) { - for (nn=0; nnused; nn++) { - fputs(extras->list[nn], out); - fputs("\n", out); - } - } - - /* Content type? (format=flowed is a standard for plain text - * that lets the receiver reconstruct paragraphs, defined in - * RFC 2646. See comment above send_flowed_text() for more.) - */ - if (!(str = conf_get_data("mail/charset", RECDB_QSTRING))) str = "us-ascii"; - fprintf(out, "Content-Type: text/plain; charset=%s; format=flowed\n", str); - - /* Send From, To and Subject headers */ - if (!fromaddr) fromaddr = "admin@poorly.configured.network"; - fprintf(out, "From: %s <%s>\n", from->nick, fromaddr); - fprintf(out, "To: \"%s\" <%s>\n", to->handle, to->email_addr); - fprintf(out, "Subject: %s\n", subject); - - /* Send mail body */ - fputs("\n", out); /* terminate headers */ - extras = conf_get_data((first_time?"mail/body_prefix_first":"mail/body_prefix"), RECDB_STRING_LIST); - if (extras) { - for (nn=0; nnused; nn++) { - send_flowed_text(out, extras->list[nn]); - } - fputs("\n", out); - } - send_flowed_text(out, body); - extras = conf_get_data((first_time?"mail/body_suffix_first":"mail/body_suffix"), RECDB_STRING_LIST); - if (extras) { - fputs("\n", out); - for (nn=0; nnused; nn++) - send_flowed_text(out, extras->list[nn]); - } - - /* Close file (sending mail) and check for return code */ - fflush(out); - fclose(out); - do { - rv = wait4(child, &res, 0, NULL); - } while ((rv == -1) && (errno == EINTR)); - if (rv == child) { - /* accept the wait() result */ - } else { - log_module(MAIN_LOG, LOG_ERROR, "sendmail() child to %s: Bad wait() return code %d: %s (%d)", to->email_addr, rv, strerror(errno), errno); - _exit(1); - } - if (res) { - log_module(MAIN_LOG, LOG_ERROR, "sendmail() grandchild to %s: Exited with code %d", to->email_addr, res); - _exit(1); - } else { - log_module(MAIN_LOG, LOG_INFO, "sendmail() sent email to %s <%s>: %s", to->handle, to->email_addr, subject); - } - _exit(0); - } else { - /* Grandchild; dup2 the fds and exec the mailer. */ - const char *argv[10], *mpath; - unsigned int argc = 0; - - /* Close the end of pipes we do not use. */ - close(infds[1]); - close(outfds[0]); - - dup2(infds[0], STDIN_FILENO); - dup2(outfds[1], STDOUT_FILENO); - mpath = conf_get_data("mail/mailer", RECDB_QSTRING); - if (!mpath) mpath = "/usr/sbin/sendmail"; - argv[argc++] = mpath; - if (fromaddr) { - argv[argc++] = "-f"; - argv[argc++] = fromaddr; - } - argv[argc++] = to->email_addr; - argv[argc++] = NULL; - if (execv(mpath, (char**)argv) < 0) { - log_module(MAIN_LOG, LOG_ERROR, "sendmail() grandchild to %s couldn't execv(): %s (%d)", to->email_addr, strerror(errno), errno); - } - _exit(1); - } -} - -static int -sendmail_ban_address(struct userNode *user, struct userNode *bot, const char *addr, const char *reason) { - dict_t target; - const char *str; - - target = strpbrk(addr, "*?") ? prohibited_masks : prohibited_addrs; - if ((str = dict_find(target, addr, NULL))) { - if (user) - send_message(user, bot, "MAILMSG_EMAIL_ALREADY_BANNED", addr, str); - return 0; - } - dict_insert(target, strdup(addr), strdup(reason)); - if (user) send_message(user, bot, "MAILMSG_EMAIL_BANNED", addr); - return 1; -} - -static MODCMD_FUNC(cmd_banemail) { - char *reason = unsplit_string(argv+2, argc-2, NULL); - return sendmail_ban_address(user, cmd->parent->bot, argv[1], reason); -} - -static MODCMD_FUNC(cmd_unbanemail) { - dict_t target; - const char *addr; - - addr = argv[1]; - target = strpbrk(addr, "*?") ? prohibited_masks : prohibited_addrs; - if (dict_remove(target, addr)) - reply("MAILMSG_EMAIL_UNBANNED", addr); - else - reply("MAILMSG_EMAIL_NOT_BANNED", addr); - return 1; -} - -static MODCMD_FUNC(cmd_stats_email) { - dict_iterator_t it; - int found = 0; - - for (it=dict_first(prohibited_addrs); it; it=iter_next(it)) { - reply("MAILMSG_PROHIBITED_EMAIL", iter_key(it), (const char*)iter_data(it)); - found = 1; - } - for (it=dict_first(prohibited_masks); it; it=iter_next(it)) { - reply("MAILMSG_PROHIBITED_EMAIL", iter_key(it), (const char*)iter_data(it)); - found = 1; - } - if (!found) - reply("MAILMSG_NO_PROHIBITED_EMAIL"); - return 0; -} - -static int -sendmail_saxdb_read(struct dict *db) { - struct dict *subdb; - struct record_data *rd; - dict_iterator_t it; - - if ((subdb = database_get_data(db, KEY_PROHIBITED, RECDB_OBJECT))) { - for (it = dict_first(subdb); it; it = iter_next(it)) { - rd = iter_data(it); - if (rd->type == RECDB_QSTRING) - sendmail_ban_address(NULL, NULL, iter_key(it), rd->d.qstring); - } - } - return 0; -} - -static int -sendmail_saxdb_write(struct saxdb_context *ctx) { - dict_iterator_t it; - - saxdb_start_record(ctx, KEY_PROHIBITED, 0); - for (it = dict_first(prohibited_masks); it; it = iter_next(it)) - saxdb_write_string(ctx, iter_key(it), iter_data(it)); - for (it = dict_first(prohibited_addrs); it; it = iter_next(it)) - saxdb_write_string(ctx, iter_key(it), iter_data(it)); - saxdb_end_record(ctx); - return 0; -} - -static void -sendmail_cleanup(void) -{ - dict_delete(prohibited_addrs); - dict_delete(prohibited_masks); -} - -void -sendmail_init(void) -{ - prohibited_addrs = dict_new(); - dict_set_free_keys(prohibited_addrs, free); - dict_set_free_data(prohibited_addrs, free); - prohibited_masks = dict_new(); - dict_set_free_keys(prohibited_masks, free); - dict_set_free_data(prohibited_masks, free); - reg_exit_func(sendmail_cleanup); - saxdb_register("sendmail", sendmail_saxdb_read, sendmail_saxdb_write); - sendmail_module = module_register("sendmail", MAIN_LOG, "sendmail.help", NULL); - modcmd_register(sendmail_module, "banemail", cmd_banemail, 3, 0, "level", "601", NULL); - modcmd_register(sendmail_module, "stats email", cmd_stats_email, 0, 0, "flags", "+oper", NULL); - modcmd_register(sendmail_module, "unbanemail", cmd_unbanemail, 2, 0, "level", "601", NULL); - message_register_table(msgtab); -} diff --git a/src/sendmail.h b/src/sendmail.h deleted file mode 100644 index 2a11fc1..0000000 --- a/src/sendmail.h +++ /dev/null @@ -1,28 +0,0 @@ -/* sendmail.h - mail sending utilities - * Copyright 2002 srvx Development Team - * - * This file is part of srvx. - * - * srvx is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with srvx; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - */ - -#if !defined(SENDMAIL_H) -#define SENDMAIL_H - -void sendmail_init(void); -void sendmail(struct userNode *from, struct handle_info *to, const char *subject, const char *body, int first_time); -const char *sendmail_prohibited_address(const char *addr); - -#endif diff --git a/src/sendmail.help b/src/sendmail.help deleted file mode 100644 index 6951b5a..0000000 --- a/src/sendmail.help +++ /dev/null @@ -1,7 +0,0 @@ -"BANEMAIL" ("/msg $S BANEMAIL
", - "Keeps srvx from sending mail to the address. The address may be a real address, or a glob that uses * and ? wildcards.", - "This also prevents anyone from using matching addresses as their account email address.", - "$uSee Also:$u unbanemail, stats email"); -"UNBANEMAIL" ("/msg $S UNBANEMAIL
", - "Removes an email address (or glob) from the banned email address list.", - "$uSee Also:$u banemail, stats email");