/* email.c -- quicky module implementing Firan-like +email function for * TinyMUSH 3.1 * * Rachel Blackman <sparks@noderunner.net> * * NOTE: For compatibility with Firan-code, make a +email alias which maps * to @email. * * Config options: * email_server SMTP server to use (mail.bar.com) * email_sender_address return address (foo@bar.com) * email_sender_name RFC822 'name' (FooMUSH) * email_default_subject Default subject link (FooMUSH Mail) */ #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <sys/time.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <errno.h> #include "../../api.h" #include "../../patchlevel.h" /* -------------------------------------------------------------------------- * Conf table. */ struct mod_email_confstorage { char *server; char *helo_id; char *sender_address; char *sender_name; char *default_subject; } mod_email_config; CONF mod_email_conftable[] = { {(char *)"email_server", cf_string, CA_GOD, CA_WIZARD, (int *)&mod_email_config.server, MBUF_SIZE}, {(char *)"email_helo_id", cf_string, CA_GOD, CA_WIZARD, (int *)&mod_email_config.helo_id, MBUF_SIZE}, {(char *)"email_sender_address", cf_string, CA_GOD, CA_WIZARD, (int *)&mod_email_config.sender_address, MBUF_SIZE}, {(char *)"email_sender_name", cf_string, CA_GOD, CA_WIZARD, (int *)&mod_email_config.sender_name, MBUF_SIZE}, {(char *)"email_default_subject", cf_string, CA_GOD, CA_WIZARD, (int *)&mod_email_config.default_subject, MBUF_SIZE}, { NULL, NULL, 0, 0, NULL, 0}}; /* Some basic Socket I/O crap I stole from another project of mine */ /* Write a formatted string to a socket */ int mod_email_sock_printf(int sock, char *format, ...) { va_list vargs; int result; char mybuf[LBUF_SIZE]; if (sock == -1) return 0; va_start(vargs, format); vsnprintf(mybuf, LBUF_SIZE, format, vargs); va_end(vargs); result = write(sock, &mybuf[0], strlen(mybuf)); return(result); } /* Read a line of input from the socket */ int mod_email_sock_readline(int sock, char *buffer, int maxlen) { fd_set read_fds; int done, pos; int duration; struct timeval tv; int possible_close = 0; if (sock == -1) return 0; memset(buffer, 0, maxlen); FD_ZERO(&read_fds); FD_SET(sock, &read_fds); /* wait up to 1 seconds */ tv.tv_sec = 1; tv.tv_usec = 0; done = 0; pos = 0; /* Check for data before giving up. */ if(select(sock+1, &read_fds, NULL, NULL, &tv) <= 0) { return 0; } done = 0; if(!FD_ISSET(sock, &read_fds)) { return 0; } while(!done && (pos < maxlen)) { char getme[2]; int numread; numread = read(sock, &getme[0], 1); if (numread != 0) { possible_close = 0; if (getme[0] != '\n') { *(buffer + pos) = getme[0]; pos++; } else { done = 1; } } else { if(possible_close) { done = 1; } else { FD_ZERO(&read_fds); FD_SET(sock, &read_fds); /* wait up to 5 seconds */ tv.tv_sec = 1; tv.tv_usec = 0; /* Check for data before giving up. */ if(select(sock+1, &read_fds, NULL, NULL, &tv) <= 0) { done = 1; } if(FD_ISSET(sock, &read_fds)) { possible_close = 1; } } } } *(buffer + pos + 1) = 0; return strlen(buffer); } /* Open a socket to a specific host/port */ int mod_email_sock_open(const char *conhostname, int port, int *sock) { struct hostent *conhost; struct sockaddr_in name; int addr_len; int mysock; conhost = gethostbyname(conhostname); if (conhost == 0) return -1; name.sin_port = htons(port); name.sin_family = AF_INET; bcopy((char *)conhost->h_addr, (char *)&name.sin_addr, conhost->h_length); mysock = socket(AF_INET, SOCK_STREAM, 0); addr_len = sizeof(name); if (connect(mysock, (struct sockaddr *)&name, addr_len) == -1) { return -2; } *sock = mysock; return 0; } int mod_email_sock_close(int sock) { return close(sock); } DO_CMD_TWO_ARG(mod_email_do_email) { char *addy; char *subject; char *body, *bodyptr, *bodysrc; int mailsock; int result; char inputline[LBUF_SIZE]; if (!arg1 || !*arg1) { notify(player, "@email: I don't know who you want to e-mail!"); return; } if (!arg2 || !*arg2) { notify(player, "@email: Not sending an empty e-mail!"); return; } addy = alloc_lbuf("mod_email_do_email.headers"); StringCopy(addy, arg1); subject = strchr(addy,'/'); if (subject) { *subject = 0; subject++; } mailsock = -1; result = mod_email_sock_open(mod_email_config.server,25,&mailsock); if (result == -1) { notify(player, tprintf("@email: Unable to resolve hostname %s!", mod_email_config.server)); free_lbuf(addy); return; } else if (result == -2) { /* Periodically, we get a failed connect, for reasons which elude me. * In almost every case, an immediate retry works. Therefore, we give it * one more shot, before we give up. */ result = mod_email_sock_open(mod_email_config.server,25,&mailsock); if (result != 0) { notify(player, "@email: Unable to connect to mailserver, aborting!"); free_lbuf(addy); return; } } bodyptr = body = alloc_lbuf("mod_email_do_email.body"); bodysrc = arg2; exec(body, &bodyptr, player, player, player, EV_STRIP | EV_FCHECK | EV_EVAL, &bodysrc, (char **)NULL, 0); *bodyptr = 0; mod_email_sock_printf(mailsock, "EHLO %s\n", mod_email_config.helo_id); mod_email_sock_printf(mailsock, "MAIL FROM:<%s>\n", mod_email_config.sender_address); mod_email_sock_printf(mailsock, "RCPT TO:<%s>\n", addy); mod_email_sock_printf(mailsock, "DATA\n"); mod_email_sock_printf(mailsock, "From: %s <%s>\n", mod_email_config.sender_name, mod_email_config.sender_address); mod_email_sock_printf(mailsock, "To: %s\n", addy); mod_email_sock_printf(mailsock, "X-Mailer: TinyMUSH %s\n", MUSH_VERSION); mod_email_sock_printf(mailsock, "Subject: %s\n\n", subject ? subject : mod_email_config.default_subject); mod_email_sock_printf(mailsock, "%s\n", body); mod_email_sock_printf(mailsock, "\n.\n"); mod_email_sock_printf(mailsock, "QUIT\n"); mod_email_sock_close(mailsock); notify(player, tprintf("@email: Mail sent to %s", addy)); free_lbuf(addy); free_lbuf(body); } CMDENT mod_email_cmdtable[] = { {(char *)"@email", NULL, CA_WIZARD, 0, CS_TWO_ARG, NULL, NULL, NULL, {mod_email_do_email}}, {(char *)NULL, NULL, 0, 0, 0, NULL, NULL, NULL, {NULL}}}; /* -------------------------------------------------------------------------- * Initialization. */ void mod_email_init() { /* Give our configuration some default values. */ mod_email_config.server = XSTRDUP("localhost", "mod_email_init"); mod_email_config.helo_id = XSTRDUP("localhost", "mod_email_init"); mod_email_config.sender_address = XSTRDUP("mush@localhost", "mod_email_init"); mod_email_config.sender_name = XSTRDUP("TinyMUSH Mailer", "mod_email_init"); mod_email_config.default_subject = XSTRDUP("MUSH Mail", "mod_email_init"); /* Register everything we have to register. */ register_commands(mod_email_cmdtable); }