/*
Subject: Re: send mail from chrooted Apache
 From: Wietse Venema (wietseporcupine.org)
 Date: Tue Jun 06 2000 - 07:43:31 CDT
Next message: Wietse Venema: "Re: -"
Previous message: Brad Knowles: "Re: performance issues"
In reply to: ISM Kolemanov, Ivan: "send mail from chrooted Apache"
 ISM Kolemanov, Ivan:
 > hi all,
 >
 > I've just prepared Apache Web Server in chroot env.
 > but now I have a problem, I can't send mails from the web server
 > I mean that on the web server I have some formulars,
 > which my clients are using to send me mail.
 >
 > I guess that there is any solution for that, but up to now I didn't find it.
 > If anybody knows it please respond.
 >
 > 10x in advance
 > Ivan Kolemanov

This is a quick-and-dirty stand-alone SMTP client from long ago.
 Works OK for lines up to BUFSIZ characters, nothing to worry about
 security-wise. You could probably do the same in PERL, but I wrote
 this for a web server that allowed no programming languages inside
 the jail.

        Wietse
 */
/*++
 /* NAME
 /* smtp 1
 /* SUMMARY
 /* simple smtp client
 /* SYNOPSIS
 /* smtp [options] recipient(s)...
 /* DESCRIPTION
 /* \fIsmtp\fP is a minimal smtp client that takes an email
 /* message body and passes it on to an smtp daemon (default
 /* the smtp daemon on the local host). Since it is
 /* completely self-supporting, the smtp client is especially
 /* suitable for use in restricted environments.
 /*
 /* Options:
 /* .TP
 /* -c carbon-copy
 /* Specifies one Cc: address to send one copy of the message to.
 /* .TP
 /* -e errors-to
 /* Specifies the Errors-To: address. This is where delivery
 /* problems should be reported.
 /* .TP
 /* -f from
 /* Sets the From: address. Default is "daemon", which is
 /* probably wrong.
 /* .TP
 /* -m mailhost
 /* Specifies where the mail should be posted. By default, the
 /* mail is posted to the smtp daemon on \fIlocalhost\fR.
 /* .TP
 /* -M
 /* Use MIME-style translation to quoted-printable (base 16).
 /* .TP
 /* -r reply-to
 /* Sets the Reply-To: address.
 /* .TP
 /* -s subject
 /* Specifies the message subject.
 /* .TP
 /* -v
 /* Turn on verbose logging to stdout.
 /* DIAGNOSTICS
 /* Non-zero exit status in case of problems. Errors are reported
 /* to the syslogd, with facility daemon.
 /* AUTHOR(S)
 /* 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
 /* CREATION DATE
 /* Wed Dec 1 14:51:13 MET 1993
 /* LAST UPDATE
 /* Fri Aug 11 12:29:23 MET DST 1995
 /*--*/

/* System libraries */

#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>

#ifdef __STDC__
 #include <stdarg.h>
 #define VARARGS(func,arg,type) func(type arg, ...)
 #define VASTART(ap,name,type) { va_start(ap, name)
 #define VAEND(ap) va_end(ap); }
 #else
 #include <varargs.h>
 #define VARARGS(func,arg,type) func(va_alist) \
                                 va_dcl
 #define VASTART(ap,name,type) { type name; \
                                 va_start(ap); \
                                 name = va_arg(ap, type)
 #define VAEND(ap) va_end(ap); }
 #endif /* __STDC__ */

extern int optind;
 extern char *optarg;

/* Local stuff */

static char *cc_addr = 0;
 static char *err_addr = 0;
 static char *from_addr = "daemon";
 static char *mailhost = "localhost";
 static char *reply_addr = 0;
 static char *subject = 0;
 static int mime_style = 0;
 static int verbose = 0;

static FILE *sfp;
 static FILE *rfp;

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

void toqp();

/* usage - explain and bail out */

void usage()
 {
     syslog(LOG_ERR,
            "usage: smtp [-c cc] [-e errors-to] [-f from] [-m mailhost] [-M] [-r reply-to] [-s subject] [-v] recipents..\n");
     exit(1);
 }

/* get_response - examine message from server */

void get_response()
 {
     char buf[BUFSIZ];

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

/* chat - say something to server and check the response */

void VARARGS(chat, fmt, char *)
 {
     va_list ap;

    /* Format the message. */

    VASTART(ap, fmt, char *);
     vfprintf(sfp, fmt, ap);
     VAEND(ap);

    VASTART(ap, fmt, char *);
     dvprintf(fmt, ap);
     VAEND(ap);

    /* Send message to server and parse its response. */

    fflush(sfp);
     get_response();
 }

main(argc, argv)
 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;

    openlog(argv[0], LOG_PID, LOG_DAEMON);

    /* Go away when something gets stuck. */

    alarm(60);

    /* Parse JCL. */

    while ((c = getopt(argc, argv, "c:e:f:m:Mr:s:v")) != EOF) {
         switch (c) {
         case 'c': /* carbon copy */
             cc_addr = optarg;
             break;
         case 'e': /* errors-to */
             err_addr = optarg;
             break;
         case 'f': /* originator */
             from_addr = optarg;
             break;
         case 'm': /* mailhost */
             mailhost = optarg;
             break;
         case 'M': /* MIME quoted printable */
             mime_style = 1;
             break;
         case 'r': /* reply-to */
             reply_addr = optarg;
             break;
         case 's': /* subject */
             subject = optarg;
             break;
         case 'v': /* log protocol */
             verbose = 1;
             break;
         default:
             usage();
             /* NOTREACHED */
         }
     }
     if (argc == optind)
         usage();

    /* Find out my own host name for HELO; if possible, get the FQDN. */

    if (gethostname(my_name, sizeof(my_name) - 1) < 0) {
         syslog(LOG_ERR, "gethostname: %m");
         exit(1);
     }
     if ((hp = gethostbyname(my_name)) == 0) {
         syslog(LOG_ERR, "%s: unknown host\n", my_name);
         exit(1);
     }
     strncpy(my_name, hp->h_name, sizeof(my_name) - 1);

    /* Connect to smtp daemon on mailhost. */

    if ((hp = gethostbyname(mailhost)) == 0) {
         syslog(LOG_ERR, "%s: unknown host\n", mailhost);
         exit(1);
     }
     if (hp->h_addrtype != AF_INET) {
         syslog(LOG_ERR, "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;
     if ((sp = getservbyname("smtp", "tcp")) != 0) {
         sin.sin_port = sp->s_port;
     } else {
         syslog(LOG_ERR, "smtp/tcp: unknown service");
         exit(1);
     }
     if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
         syslog(LOG_ERR, "socket: %m");
         exit(1);
     }
     if (connect(s, (struct sockaddr *) & sin, sizeof(sin)) < 0) {
         syslog(LOG_ERR, "connect: %m");
         exit(1);
     }
     if ((r = dup(s)) < 0) {
         syslog(LOG_ERR, "dup: %m");
         exit(1);
     }
     if ((sfp = fdopen(s, "w")) == 0) {
         syslog(LOG_ERR, "fdopen: %m");
         exit(1);
     }
     if ((rfp = fdopen(r, "r")) == 0) {
         syslog(LOG_ERR, "fdopen: %m");
         exit(1);
     }
     /* Speak SMTP. */

    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)
         chat("RCPT TO: <%s>\r\n", cc_addr);

    chat("DATA\r\n");

    /* Do message header. */

    fprintf(sfp, "From: %s\r\n", from_addr);

    if (subject)
         fprintf(sfp, "Subject: %s\r\n", subject);

    if (err_addr)
         fprintf(sfp, "Errors-To: %s\r\n", err_addr);

    if (reply_addr)
         fprintf(sfp, "Reply-To: %s\r\n", reply_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");

    /* Do message body. */

    if (mime_style) { /* MIME quoted-printable */
         toqp(stdin, sfp);
     } else { /* traditional... */
         while (fgets(buf, sizeof(buf), stdin)) {
             buf[strlen(buf) - 1] = 0;
             if (strcmp(buf, ".") == 0) { /* quote lone dots */
                 fprintf(sfp, "..\r\n");
             } else { /* pass thru mode */
                 fprintf(sfp, "%s\r\n", buf);
             }
         }
     }
     chat(".\r\n");
     chat("QUIT\r\n");
     exit(0);
 }

 /*
   * Following code was lifted from the metamail version 2.7 source code
   * (codes.c) and modified to emit \r\n at line boundaries.
   */

static char basis_hex[] = "0123456789ABCDEF";

/* toqp - transform to MIME-style quoted printable */

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

    while ((c = getc(infile)) != EOF) {
         if ((c < 32 && (c != '\n' && c != '\t'))
             || (c == '=')
             || (c >= 127)

        /*
          * Following line is to avoid single periods alone on lines, which
          * messes up some dumb smtp implementations, sigh...
          */
             || (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 { /* END horrible hack */
                 putc(c, outfile);
                 ++ct;
                 prevc = c;
             }
         }
         if (ct > 72) {
             putc('=', outfile);
             putc('\r', outfile); /* XXX */
             putc('\n', outfile);
             ct = 0;
             prevc = '\n';
         }
     }
     if (ct) {
         putc('=', outfile);
         putc('\r', outfile); /* XXX */
         putc('\n', outfile);
     }
 }
