#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdarg.h>
#include <string.h>

#define SMTP_PORT 25

static char smtp_in_buf[1024];
static char smtp_out_buf[8192];

static int ismtp_open(void)
{
        struct in_addr addr;
        int type = SOCK_STREAM;
        struct sockaddr_in sock_out;
        int res;

        /* create a socket to write to */
        res = socket(PF_INET, type, 0);
        if (res == -1) {
                return -1;
        }

        addr.s_addr = inet_addr("127.0.0.1");
 
        sock_out.sin_addr = addr;
        sock_out.sin_port = htons(SMTP_PORT);
        sock_out.sin_family = PF_INET;

        if (connect(res,(struct sockaddr *)&sock_out,sizeof(sock_out))) {
                close(res);
                return -1;
        }

        return res;
}

 int ismtp_write_data(int fd, char *p)
{
        int eom = 0;
        if (strcmp(p,"\n.\n") == 0) {
                eom = 1;
        }

        while (*p) {
                if (p[0] == '\r') {
                        p++; continue;
                }
                if (p[0] == '\n') {
                        if (write(fd,"\r",1) != 1) return 0;
                }
                if (write(fd,p,1) != 1) return 0;
                if (!eom && p[0] == '\n' && p[1] == '.') {
                        if (write(fd,".",1) != 1) return 0;
                }
                p++;
        }
        return 1;
}

int ismtp_write(int fd, char *fmt, ...)
{
        va_list ap;

        va_start(ap, fmt);
        vsnprintf(smtp_out_buf, sizeof(smtp_out_buf), fmt, ap);
        va_end(ap);

        return ismtp_write_data(fd, smtp_out_buf);
}

/* wait for a SMTP success code. If we receive a type 4 or 5 error
   code then return a failure */
 int ismtp_waitfor(int fd, char code)
{
        char c;
        int offset=0;
        char *line=smtp_in_buf;

        memset(smtp_in_buf, 0, sizeof(smtp_in_buf));

        while (read(fd,&c,1) == 1) {
                if (offset < sizeof(smtp_in_buf))
                        smtp_in_buf[offset++] = c;
                if (c != '\n') continue;

                /* we have a complete line */
                if (isdigit(line[0]) && isdigit(line[1]) && isdigit(line[2]) &&
                    line[3] == ' ') {
                        /* we have a SMTP return code */
                        if (line[0] == code) {
                                memset(smtp_in_buf, 0, sizeof(smtp_in_buf));
                                memset(smtp_out_buf, 0, sizeof(smtp_out_buf));
                                return 1;
                        }

                        /* we got an unexpected SMTP error code */
                        return 0;
                }

                line = &smtp_in_buf[offset];
        }

        return 0;
}

 int ismtp_start_mail(char *from, char *to, char *cc, char *bcc, char *subject, int len)
{
        char *p, *tok;
        int fd = ismtp_open();
        char hostname[100];

        if (fd == -1) {
                return -1;
        }

        if (!ismtp_waitfor(fd, '2')) {
                close(fd);
                return -1;
        }

        if (gethostname(hostname,sizeof(hostname)-1)) {
                strcpy(hostname,"localhost");
        }

        ismtp_write(fd,"EHLO %s\n", hostname);
        if (!ismtp_waitfor(fd, '2')) {
                /* try again without esmtp */
                ismtp_write(fd,"HELO %s\n", hostname);

                if (!ismtp_waitfor(fd, '2')) {
                        close(fd);
                        return -1;
                }
        }

        if (strchr(from,'\n') || strchr(from,'\r')) {
                close(fd);
                return -1;
        }

        ismtp_write(fd,"MAIL FROM: <%s> SIZE=%d\n",
                   from,len+5000);

        if (!ismtp_waitfor(fd, '2')) {
                /* try again without the SIZE= option */
                ismtp_write(fd,"MAIL FROM: <%s>\n",
                           from);
                if (!ismtp_waitfor(fd, '2')) {
                        close(fd);
                        return -1;
                }
        }

        p = strdup(to);
        for (tok=strtok(p," ,\t"); tok; tok=strtok(NULL," ,\t")) {
                ismtp_write(fd,"RCPT TO: <%s>\n", tok);
                if (!ismtp_waitfor(fd, '2')) {
                        close(fd);
                        return -1;
                }
        }
        free(p);
        if (cc && *cc) {
                p = strdup(cc);
                for (tok=strtok(p," ,\t"); tok; tok=strtok(NULL," ,\t")) {
                        ismtp_write(fd,"RCPT TO: <%s>\n", tok);
                        if (!ismtp_waitfor(fd, '2')) {
                                close(fd);
                                return -1;
                        }
                }
                free(p);
        }
        if (bcc && *bcc) {
                p = strdup(bcc);
                for (tok=strtok(p," ,\t"); tok; tok=strtok(NULL," ,\t")) {
                        ismtp_write(fd,"RCPT TO: <%s>\n", tok);
                        if (!ismtp_waitfor(fd, '2')) {
                                close(fd);
                                return -1;
                        }
                }
                free(p);
        }
        ismtp_write(fd,"DATA\n");
        if (!ismtp_waitfor(fd, '3')) {
                close(fd);
                return -1;
        }
        if (subject) {
                ismtp_write(fd,"From: %s\n", from);
                ismtp_write(fd,"To: %s\n", to);
                ismtp_write(fd,"Subject: %s\n", subject);
                if (cc && *cc)
                        ismtp_write(fd,"CC: %s\n", cc);
        }

        return fd;
}

 int ismtp_end_mail(int fd)
{
        ismtp_write(fd,"\n.\n");
        if (!ismtp_waitfor(fd, '2')) {
                close(fd);
                return -1;
        }
        ismtp_write(fd,"QUIT\n");
        if (!ismtp_waitfor(fd, '2')) {
                close(fd);
                return -1;
        }
        close(fd);
        return 0;
}
