/*   ____  __  _ _____ ____     _ _            _   
**  / ___||  \/ |_   _|  _ \___| (_) ___ _ __ | |_ 
**  \___ \| |\/| || | | |_)/ __| | |/ _ \ '_ \| __|
**   ___) | |  | || | |  _| (__| | |  __/ | | | |_ 
**  |____/|_|  |_||_| |_|  \___|_|_|\___|_| |_|\__|
**   
**  SMTPclient -- simple SMTP client
**
**  This program is a minimal SMTP client that takes an email
**  message body and passes it on to a SMTP server (default is the
**  MTA on the local host). Since it is completely self-supporting,
**  it is especially suitable for use in restricted environments.
**
**  ======================================================================
**
**  Copyright (c) 1997 Ralf S. Engelschall, All rights reserved.
**
**  This program is free software; it may be redistributed and/or modified
**  only under the terms of either the Artistic License or the GNU General
**  Public License, which may be found in the SMTP source distribution.
**  Look at the file COPYING. 
**
**  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.
**
**  ======================================================================
**
**  smtpclient_main.c -- program source
**
**  Based on smtp.c as of August 11, 1995 from
**      W.Z. Venema,
**      Eindhoven University of Technology,
**      Department of Mathematics and Computer Science,
**      Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands.
*/

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <syslog.h>
#include <stdio.h>
#include <netdb.h>
#include <string.h>
#include <ctype.h>
#include <pwd.h>

#include "smtpclient_getopt.h"
#include "smtpclient_errno.h"
#include "smtpclient_vers.h"

static char *cc_addr    = 0;
static char *err_addr   = 0;
static char *from_addr  = NULL;
static char *mailhost   = NULL;
static int   mailport   = 25;
static char *reply_addr = 0;
static char *subject    = 0;
static int   mime_style = 0;
static int   verbose    = 0;
static int   usesyslog  = 0;

static FILE *sfp;
static FILE *rfp;

#define dprintf  if (verbose) printf
#define dvprintf if (verbose) vprintf

/* hack for Ultrix */
#ifndef LOG_DAEMON
#define LOG_DAEMON 0
#endif

/*
**  logging support
*/
void log(char *str, ...)
{
    va_list ap;
    char buf[1024];

    va_start(ap, str);
    vsnprintf(buf, 1024, str, ap);
    if (usesyslog)
        syslog(LOG_ERR, "SMTPclient: %s", buf);
    else
        fprintf(stderr, "SMTPclient: %s\n", buf);
    va_end(ap);
    return;
}

/*
**  usage page
*/
void usage(void)
{
    fprintf(stderr, "Usage: smtp [options] recipients ...\n");
    fprintf(stderr, "\n");
    fprintf(stderr, "Message Header Options:\n");
    fprintf(stderr, "  -s, --subject=STR      subject line of message\n");
    fprintf(stderr, "  -f, --from=ADDR        address of the sender\n");
    fprintf(stderr, "  -r, --reply-to=ADDR    address of the sender for replies\n");
    fprintf(stderr, "  -e, --errors-to=ADDR   address to send delivery errors to\n");
    fprintf(stderr, "  -c, --carbon-copy=ADDR address to send copy of message to\n");
    fprintf(stderr, "\n");
    fprintf(stderr, "Processing Options:\n");
    fprintf(stderr, "  -S, --smtp-host=HOST   host where MTA can be contacted via SMTP\n");
    fprintf(stderr, "  -P, --smtp-port=NUM    port where MTA can be contacted via SMTP\n");
    fprintf(stderr, "  -M, --mime-encode      use MIME-style translation to quoted-printable\n");
    fprintf(stderr, "  -L, --use-syslog       log errors to syslog facility instead of stderr\n");
    fprintf(stderr, "\n");
    fprintf(stderr, "Giving Feedback:\n");
    fprintf(stderr, "  -v, --verbose          enable verbose logging messages\n");
    fprintf(stderr, "  -V, --version          display version string\n");
    fprintf(stderr, "  -h, --help             display this page\n");
    fprintf(stderr, "\n");
    return;
}

/*
**  version page
*/
void version(void)
{
    fprintf(stdout, "%s\n", SMTPclient_Hello);
    fprintf(stdout, "\n");
    fprintf(stdout, "Copyright (c) 1997 Ralf S. Engelschall, All rights reserved.\n");
    fprintf(stdout, "\n");
    fprintf(stdout, "This program is distributed in the hope that it will be useful,\n");
    fprintf(stdout, "but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
    fprintf(stdout, "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n");
    fprintf(stdout, "the GNU General Public License for more details.\n");
    fprintf(stdout, "\n");
    return;
}

/*
**  examine message from server 
*/
void get_response(void)
{
    char buf[BUFSIZ];

    while (fgets(buf, sizeof(buf), rfp)) {
        buf[strlen(buf)-1] = 0;
        dprintf("%s --> %s\n", mailhost, buf);
        if (!isdigit(buf[0]) || buf[0] > '3') {
            log("unexpected reply: %s", buf);
            exit(1);
        }
        if (buf[4] != '-')
            break;
    }
    return;
}

/*
**  say something to server and check the response
*/
void chat(char *fmt, ...)
{
    va_list ap;

    va_start(ap, fmt);
    vfprintf(sfp, fmt, ap);
    va_end(ap);
  
    va_start(ap, fmt);
    dprintf("%s <-- ", mailhost);
    dvprintf(fmt, ap);
    va_end(ap);

    fflush(sfp);
    get_response();
}

/*
**  transform to MIME-style quoted printable
**
**  Extracted from the METAMAIL version 2.7 source code (codes.c)
**  and modified to emit \r\n at line boundaries.
*/

static char basis_hex[] = "0123456789ABCDEF";

void toqp(FILE *infile, FILE *outfile)
{
    int c;
    int ct = 0;
    int prevc = 255;

    while ((c = getc(infile)) != EOF) {
        if (   (c < 32 && (c != '\n' && c != '\t'))
            || (c == '=')
            || (c >= 127)
            || (ct == 0 && c == '.')               ) {
        putc('=', outfile);
        putc(basis_hex[c >> 4], outfile);
        putc(basis_hex[c & 0xF], outfile);
        ct += 3;
        prevc = 'A'; /* close enough */
    }
    else if (c == '\n') {
        if (prevc == ' ' || prevc == '\t') {
	    putc('=', outfile);  /* soft & hard lines */
	    putc(c, outfile);
        }
        putc(c, outfile);
        ct = 0;
        prevc = c;
    } 
    else {
        if (c == 'F' && prevc == '\n') {
        /*
         * HORRIBLE but clever hack suggested by MTR for
         * sendmail-avoidance
         */
        c = getc(infile);
        if (c == 'r') {
            c = getc(infile);
            if (c == 'o') {
            c = getc(infile);
            if (c == 'm') {
                c = getc(infile);
                if (c == ' ') {
                /* This is the case we are looking for */
                fputs("=46rom", outfile);
                ct += 6;
                } else {
                fputs("From", outfile);
                ct += 4;
                }
            } else {
                fputs("Fro", outfile);
                ct += 3;
            }
            } 
            else {
            fputs("Fr", outfile);
            ct += 2;
            }
        }
        else {
            putc('F', outfile);
            ++ct;
        }
        ungetc(c, infile);
        prevc = 'x'; /* close enough -- printable */
        } 
        else { 
        putc(c, outfile);
        ++ct;
        prevc = c;
        }
    }
    if (ct > 72) {
        putc('=', outfile);
        putc('\r', outfile); 
        putc('\n', outfile);
        ct = 0;
        prevc = '\n';
    }
    }
    if (ct) {
    putc('=', outfile);
    putc('\r', outfile); 
    putc('\n', outfile);
    }
    return;
}


/*
**  main procedure
*/

struct option options[] = {
    { "subject",      1, NULL, 's' },
    { "from",         1, NULL, 'f' },
    { "replay-to",    1, NULL, 'r' },
    { "errors-to",    1, NULL, 'e' },
    { "carbon-copy",  1, NULL, 'c' },
    { "smtp-host",    1, NULL, 'h' },
    { "smtp-port",    1, NULL, 'p' },
    { "mime-encode",  0, NULL, 'M' },
    { "use-syslog",   0, NULL, 'L' },
    { "verbose",      0, NULL, 'v' },
    { "version",      0, NULL, 'V' },
    { "help",         0, NULL, 'h' }
};

int main(int argc, char **argv)
{
    char buf[BUFSIZ];
    char my_name[BUFSIZ];
    struct sockaddr_in sin;
    struct hostent *hp;
    struct servent *sp;
    int c;
    int s;
    int r;
    int i;
    struct passwd *pwd;
    char *cp;

    /* 
     *  Go away when something gets stuck.
     */
    alarm(60);

    /*
     *  Parse options
     */
    while ((c = getopt_long(argc, argv, ":s:f:r:e:c:S:P:MLvVh", options, NULL)) != EOF) {
        switch (c) {
            case 's':
                subject = optarg;
                break;
            case 'f':
                from_addr = optarg;
                break;
            case 'r':
                reply_addr = optarg;
                break;
            case 'e':
                err_addr = optarg;
                break;
            case 'c':
                cc_addr = optarg;
                break;
            case 'S':
                mailhost = optarg;
                break;
            case 'P':
                mailport = atoi(optarg);
                break;
            case 'M':
                mime_style = 1;
                break;
            case 'L':
                usesyslog = 1;
                break;
            case 'v':
                verbose = 1;
                break;
            case 'V':
                version();
                exit(0);
            case 'h':
                usage();
                exit(0);
            default:
                fprintf(stderr, "SMTP: invalid option `%c'\n", optopt);
                fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]);
                exit(1);
        }
    }
    if (argc == optind) {
        fprintf(stderr, "SMTP: wrong number of arguments\n");
        fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]);
        exit(1);
    }

    /*  
     *  Open Syslog facility
     */
    if (usesyslog)
        openlog(argv[0], LOG_PID, LOG_DAEMON);

    /*
     *  Determine SMTP server
     */
    if (mailhost == NULL) {
        if ((cp = getenv("SMTPSERVER")) != NULL)
            mailhost = cp;
        else
            mailhost = "localhost";
    }

    /*
     *  Find out my own host name for HELO; 
     *  if possible, get the FQDN.
     */
    if (gethostname(my_name, sizeof(my_name) - 1) < 0) {
        log("gethostname: %s", errorstr(errno));
        exit(1);
    }
    if ((hp = gethostbyname(my_name)) == NULL) {
        log("%s: unknown host\n", my_name);
        exit(1);
    }
    strlcpy(my_name, hp->h_name, sizeof(my_name));

    /*
     *  Determine from address.
     */
    if (from_addr == NULL) {
        if ((pwd = getpwuid(getuid())) == 0) {
            snprintf(buf, (sizeof(buf) - 1), "userid-%d@%s", getuid(), my_name);
        } else {
            snprintf(buf, (sizeof(buf) - 1), "%s@%s", pwd->pw_name, my_name);
        }
        from_addr = strdup(buf);
    }

    /*
     *  Connect to smtp daemon on mailhost.
     */
    if ((hp = gethostbyname(mailhost)) == NULL) {
        log("%s: unknown host\n", mailhost);
        exit(1);
    }
    if (hp->h_addrtype != AF_INET) {
        log("unknown address family: %d", hp->h_addrtype);
        exit(1);
    }
    memset((char *)&sin, 0, sizeof(sin));
    memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
    sin.sin_family = hp->h_addrtype;
    sin.sin_port = htons(mailport);
    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        log("socket: %s", errorstr(errno));
        exit(1);
    }
    if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
        log("connect: %s", errorstr(errno));
        exit(1);
    }
    if ((r = dup(s)) < 0) {
        log("dup: %s", errorstr(errno));
        exit(1);
    }
    if ((sfp = fdopen(s, "w")) == 0) {
        log("fdopen: %s", errorstr(errno));
        exit(1);
    }
    if ((rfp = fdopen(r, "r")) == 0) {
        log("fdopen: %s", errorstr(errno));
        exit(1);
    }

    /* 
     *  Give out SMTP headers.
     */
    get_response(); /* banner */
    chat("HELO %s\r\n", my_name);
    chat("MAIL FROM: <%s>\r\n", from_addr);
    for (i = optind; i < argc; i++)
        chat("RCPT TO: <%s>\r\n", argv[i]);
    if (cc_addr) {
    	char *cc_tmp,*p;
	if(!(cc_tmp=malloc(strlen(cc_addr+1)))) {
	    log("memory allocation failed.");
	    exit(1);
	}
	strcpy(cc_tmp,cc_addr);
	p=strtok(cc_tmp,",");
	while(p) {
	    chat("RCPT TO: <%s>\r\n", p);
	    p=strtok(NULL,",");
	}
	free(cc_tmp);
    }
    chat("DATA\r\n");

    /* 
     *  Give out Message header. 
     */
    fprintf(sfp, "From: %s\r\n", from_addr);
    if (subject)
        fprintf(sfp, "Subject: %s\r\n", subject);

    if (reply_addr)
        fprintf(sfp, "Reply-To: %s\r\n", reply_addr);
    if (err_addr)
        fprintf(sfp, "Errors-To: %s\r\n", err_addr);
    if ((pwd = getpwuid(getuid())) == 0) {
        fprintf(sfp, "Sender: userid-%d@%s\r\n", getuid(), my_name);
    } else {
        fprintf(sfp, "Sender: %s@%s\r\n", pwd->pw_name, my_name);
    }

    fprintf(sfp, "To: %s", argv[optind]);
    for (i = optind + 1; i < argc; i++)
        fprintf(sfp, ",%s", argv[i]);
    fprintf(sfp, "\r\n");
    if (cc_addr)
        fprintf(sfp, "Cc: %s\r\n", cc_addr);

    if (mime_style) {
        fprintf(sfp, "MIME-Version: 1.0\r\n");
        fprintf(sfp, "Content-Type: text/plain; charset=ISO-8859-1\r\n");
        fprintf(sfp, "Content-Transfer-Encoding: quoted-printable\r\n");
    }

    fprintf(sfp, "\r\n");

    /* 
     *  Give out Message body.
     */
    if (mime_style) {
        toqp(stdin, sfp);
    } else {
        while (fgets(buf, sizeof(buf), stdin)) {
            buf[strlen(buf)-1] = 0;
            if (strcmp(buf, ".") == 0) { /* quote alone dots */
                fprintf(sfp, "..\r\n");
            } else { /* pass thru mode */
                fprintf(sfp, "%s\r\n", buf);
            }
        }
    }

    /* 
     *  Give out SMTP end.
     */
    chat(".\r\n");
    chat("QUIT\r\n");

    /* 
     *  Die gracefully ...
     */
    exit(0);
}

/*EOF*/


syntax highlighted by Code2HTML, v. 0.9.1