/* fakesmtp - A fake SMTP server used by the nmh test suite * * This code is Copyright (c) 2012, by the authors of nmh. See the * COPYRIGHT file in the root directory of the nmh distribution for * complete copyright information. */ #include #include #include #include #include #include #include #include #define PIDFILE "/tmp/fakesmtp.pid" #define LINESIZE 1024 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[]) { 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]); exit(1); } if (!(f = fopen(argv[1], "w"))) { fprintf(stderr, "Unable to open output file \"%s\": %s\n", argv[1], strerror(errno)); exit(1); } conn = serve(PIDFILE, argv[2]); /* * Pretend to be an SMTP server. */ putcrlf(conn, "220 Not really an ESMTP server"); smtp_state = SMTP_TOP; for (;;) { char line[LINESIZE]; rc = getsmtp(conn, line); if (rc == -1) break; /* EOF */ fputs(line, f); putc('\n', f); switch (smtp_state) { case SMTP_DATA: if (strcmp(line, ".") == 0) { 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; } /* * Most commands we ignore and send the same response to. */ if (strcmp(line, "QUIT") == 0) { fclose(f); f = NULL; putcrlf(conn, "221 Later alligator!"); close(conn); break; } 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!"); } if (f) fclose(f); exit(0); } /* * Read a line (up to the \r\n) */ static int getsmtp(int socket, char *data) { int cc; static unsigned int bytesinbuf = 0; static char buffer[LINESIZE * 2], *p; for (;;) { /* * Find our \r\n */ if (bytesinbuf > 0 && (p = strchr(buffer, '\r')) && *(p + 1) == '\n') { *p = '\0'; strncpy(data, buffer, LINESIZE); data[LINESIZE - 1] = '\0'; cc = strlen(buffer); /* * Shuffle leftover bytes back to the beginning */ bytesinbuf -= cc + 2; /* Don't forget \r\n */ if (bytesinbuf > 0) { memmove(buffer, buffer + cc + 2, bytesinbuf); } return cc; } if (bytesinbuf >= sizeof(buffer)) { fprintf(stderr, "Buffer overflow in getsmtp()!\n"); exit(1); } memset(buffer + bytesinbuf, 0, sizeof(buffer) - bytesinbuf); cc = read(socket, buffer + bytesinbuf, sizeof(buffer) - bytesinbuf); if (cc < 0) { fprintf(stderr, "Read failed: %s\n", strerror(errno)); exit(1); } if (cc == 0) return -1; bytesinbuf += cc; } }