/* 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);
}