]> diplodocus.org Git - nmh/blobdiff - test/fakesmtp.c
Alter HasSuffixC()'s char * to be const.
[nmh] / test / fakesmtp.c
index 700bc45d5ac0946e54733f97ced40db715ba95d5..35230957416a966ae73c369355272f9bcfb75d69 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#include <netdb.h>
 #include <errno.h>
 #include <sys/socket.h>
 #include <sys/types.h>
-#include <sys/select.h>
 #include <sys/stat.h>
-#include <sys/uio.h>
-#include <signal.h>
 
 #define PIDFILE "/tmp/fakesmtp.pid"
 
 #define LINESIZE 1024
 
-static void killpidfile(void);
-static void handleterm(int);
-static void putsmtp(int, char *);
+enum {
+       /* Processing top-level SMTP commands (e.g. EHLO, DATA). */
+       SMTP_TOP,
+
+       /* Processing payload of a DATA command. */
+       SMTP_DATA,
+
+       /* Looking for the blank line required by XOAUTH2 after 334 response. */
+       SMTP_XOAUTH_ERR
+};
+
+void putcrlf(int, char *);
+int serve(const char *, const char *);
+
 static int getsmtp(int, char *);
 
 int
 main(int argc, char *argv[])
 {
-       struct addrinfo hints, *res;
-       int rc, l, conn, on, datamode;
-       FILE *f, *pid;
-       fd_set readfd;
-       struct stat st;
-       struct timeval tv;
+       int rc, conn, smtp_state;
+       FILE *f;
+       const char *xoauth = getenv("XOAUTH");
+       const char *smtputf8 = getenv("SMTPUTF8");
 
        if (argc != 3) {
                fprintf(stderr, "Usage: %s output-filename port\n", argv[0]);
@@ -49,127 +54,14 @@ main(int argc, char *argv[])
                exit(1);
        }
 
-       memset(&hints, 0, sizeof(hints));
-
-       hints.ai_family = PF_INET;
-       hints.ai_socktype = SOCK_STREAM;
-       hints.ai_protocol = IPPROTO_TCP;
-       hints.ai_flags = AI_PASSIVE;
-
-       rc = getaddrinfo("127.0.0.1", argv[2], &hints, &res);
-
-       if (rc) {
-               fprintf(stderr, "Unable to resolve localhost/%s: %s\n",
-                       argv[2], gai_strerror(rc));
-               exit(1);
-       }
-
-       l = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
-
-       if (l == -1) {
-               fprintf(stderr, "Unable to create listening socket: %s\n",
-                       strerror(errno));
-               exit(1);
-       }
-
-       on = 1;
-
-       if (setsockopt(l, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
-               fprintf(stderr, "Unable to set SO_REUSEADDR: %s\n",
-                       strerror(errno));
-               exit(1);
-       }
-
-       if (bind(l, res->ai_addr, res->ai_addrlen) == -1) {
-               fprintf(stderr, "Unable to bind socket: %s\n", strerror(errno));
-               exit(1);
-       }
-
-       if (listen(l, 1) == -1) {
-               fprintf(stderr, "Unable to listen on socket: %s\n",
-                       strerror(errno));
-               exit(1);
-       }
-
-       /*
-        * Now that our socket & files are set up, do the following things:
-        *
-        * - Check for a PID file.  If there is one, kill the old version.
-        * - Wait 30 seconds for a connection.  If there isn't one, then
-        *   exit.
-        */
-
-       if (stat(PIDFILE, &st) == 0) {
-               long oldpid;
-
-               if (!(pid = fopen(PIDFILE, "r"))) {
-                       fprintf(stderr, "Cannot open " PIDFILE
-                               " (%s), continuing ...\n", strerror(errno));
-               } else {
-                       rc = fscanf(pid, "%ld", &oldpid);
-                       fclose(pid);
-
-                       if (rc != 1) {
-                               fprintf(stderr, "Unable to parse pid in "
-                                       PIDFILE ", continuing ...\n");
-                       } else {
-                               kill((pid_t) oldpid, SIGTERM);
-                       }
-               }
-
-               unlink(PIDFILE);
-       }
-
-       if (!(pid = fopen(PIDFILE, "w"))) {
-               fprintf(stderr, "Cannot open " PIDFILE ": %s\n",
-                       strerror(errno));
-               exit(1);
-       }
-
-       fprintf(pid, "%ld\n", (long) getpid());
-       fclose(pid);
-
-       signal(SIGTERM, handleterm);
-       atexit(killpidfile);
-
-       FD_ZERO(&readfd);
-       FD_SET(l, &readfd);
-       tv.tv_sec = 30;
-       tv.tv_usec = 0;
-
-       rc = select(l + 1, &readfd, NULL, NULL, &tv);
-
-       if (rc < 0) {
-               fprintf(stderr, "select() failed: %s\n", strerror(errno));
-               exit(1);
-       }
-
-       /*
-        * I think if we get a timeout, we should just exit quietly.
-        */
-
-       if (rc == 0) {
-               exit(1);
-       }
-
-       /*
-        * Alright, got a connection!  Accept it.
-        */
+       conn = serve(PIDFILE, argv[2]);
 
-       if ((conn = accept(l, NULL, NULL)) == -1) {
-               fprintf(stderr, "Unable to accept connection: %s\n",
-                       strerror(errno));
-               exit(1);
-       }
-
-       close(l);
-       
        /*
         * Pretend to be an SMTP server.
         */
 
-       putsmtp(conn, "220 Not really an ESMTP server");
-       datamode = 0;
+       putcrlf(conn, "220 Not really an ESMTP server");
+       smtp_state = SMTP_TOP;
 
        for (;;) {
                char line[LINESIZE];
@@ -181,17 +73,17 @@ main(int argc, char *argv[])
 
                fprintf(f, "%s\n", line);
 
-               /*
-                * If we're in DATA mode, then check to see if we've got
-                * a "."; otherwise, continue
-                */
-
-               if (datamode) {
+               switch (smtp_state) {
+               case SMTP_DATA:
                        if (strcmp(line, ".") == 0) {
-                               datamode = 0;
-                               putsmtp(conn, "250 Thanks for the info!");
+                               smtp_state = SMTP_TOP;
+                               putcrlf(conn, "250 Thanks for the info!");
                        }
                        continue;
+               case SMTP_XOAUTH_ERR:
+                       smtp_state = SMTP_TOP;
+                       putcrlf(conn, "535 Not no way, not no how!");
+                       continue;
                }
 
                /*
@@ -199,39 +91,51 @@ main(int argc, char *argv[])
                 */
 
                if (strcmp(line, "QUIT") == 0) {
-                       putsmtp(conn, "221 Later alligator!");
+                       fclose(f);
+                       f = NULL;
+                       putcrlf(conn, "221 Later alligator!");
                        close(conn);
                        break;
-               } else if (strcmp(line, "DATA") == 0) {
-                       putsmtp(conn, "354 Go ahead");
-                       datamode = 1;
-               } else {
-                       putsmtp(conn, "250 I'll buy that for a dollar!");
                }
+               if (strcmp(line, "DATA") == 0) {
+                       putcrlf(conn, "354 Go ahead");
+                       smtp_state = SMTP_DATA;
+                       continue;
+               }
+               if (strncmp(line, "EHLO", 4) == 0) {
+                       putcrlf(conn, "250-ready");
+                       if (smtputf8 != NULL) {
+                               putcrlf(conn, "250-8BITMIME");
+                               putcrlf(conn, "250-SMTPUTF8");
+                       }
+                       if (xoauth != NULL) {
+                               putcrlf(conn, "250-AUTH XOAUTH2");
+                       }
+                       putcrlf(conn, "250 I'll buy that for a dollar!");
+                       continue;
+               }
+               if (xoauth != NULL) {
+                       /* XOAUTH2 support enabled; handle AUTH (and EHLO above). */
+                       if (strncmp(line, "AUTH", 4) == 0) {
+                               if (strncmp(line, "AUTH XOAUTH2", 12) == 0
+                                   && strstr(line, xoauth) != NULL) {
+                                       putcrlf(conn, "235 OK come in");
+                                       continue;
+                               }
+                               putcrlf(conn, "334 base64-json-err");
+                               smtp_state = SMTP_XOAUTH_ERR;
+                               continue;
+                       }
+               }
+               putcrlf(conn, "250 I'll buy that for a dollar!");
        }
 
-       fclose(f);
+       if (f)
+               fclose(f);
 
        exit(0);
 }
 
-/*
- * Write a line to the SMTP client on the other end
- */
-
-static void
-putsmtp(int socket, char *data)
-{
-       struct iovec iov[2];
-
-       iov[0].iov_base = data;
-       iov[0].iov_len = strlen(data);
-       iov[1].iov_base = "\r\n";
-       iov[1].iov_len = 2;
-
-       writev(socket, iov, 2);
-}
-
 /*
  * Read a line (up to the \r\n)
  */
@@ -286,27 +190,3 @@ getsmtp(int socket, char *data)
                bytesinbuf += cc;
        }
 }
-
-/*
- * Handle a SIGTERM
- */
-
-static void
-handleterm(int signal)
-{
-       (void) signal;
-
-       killpidfile();
-       fflush(NULL);
-       _exit(1);
-}
-
-/*
- * Get rid of our pid file
- */
-
-static void
-killpidfile(void)
-{
-       unlink(PIDFILE);
-}