X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=blobdiff_plain;f=test%2Fsmtp-dummy.c;h=71992edd1d968d5d0d14907ef505f0a0427f3051;hp=e58d0ad68d7b606650b4e04c19a1c64bc6d57286;hb=6a9626a2fdddf6115bcf97982fd10053bf48e942;hpb=44ea57a0d10ddab514abea319c4d25ec4e36b51e diff --git a/test/smtp-dummy.c b/test/smtp-dummy.c index e58d0ad6..71992edd 100644 --- a/test/smtp-dummy.c +++ b/test/smtp-dummy.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Authors: Carl Worth */ @@ -37,7 +37,9 @@ #include #include #include -#include +#include +#include +#include #include #include @@ -47,160 +49,226 @@ static void receive_data_to_file (FILE *peer, FILE *output) { - char *line = NULL; - size_t line_size; - ssize_t line_len; - - while ((line_len = getline (&line, &line_size, peer)) != -1) { - if (STRNCMP_LITERAL (line, ".\r\n") == 0) - break; - if (line_len < 2) - continue; - if (line[line_len-1] == '\n' && line[line_len-2] == '\r') { - line[line_len-2] = '\n'; - line[line_len-1] = '\0'; - } - fprintf (output, "%s", - line[0] == '.' ? line + 1 : line); + char *line = NULL; + size_t line_size; + ssize_t line_len; + + while ((line_len = getline (&line, &line_size, peer)) != -1) { + if (STRNCMP_LITERAL (line, ".\r\n") == 0) + break; + if (line_len < 2) + continue; + if (line[line_len - 1] == '\n' && line[line_len - 2] == '\r') { + line[line_len - 2] = '\n'; + line[line_len - 1] = '\0'; } + fprintf (output, "%s", + line[0] == '.' ? line + 1 : line); + } - free (line); + free (line); } static int process_command (FILE *peer, FILE *output, const char *command) { - if (STRNCMP_LITERAL (command, "EHLO ") == 0) { - fprintf (peer, "502\r\n"); - fflush (peer); - } else if (STRNCMP_LITERAL (command, "HELO ") == 0) { - fprintf (peer, "250 localhost\r\n"); - fflush (peer); - } else if (STRNCMP_LITERAL (command, "MAIL FROM:") == 0 || - STRNCMP_LITERAL (command, "RCPT TO:") == 0) { - fprintf (peer, "250 OK\r\n"); - fflush (peer); - } else if (STRNCMP_LITERAL (command, "DATA") == 0) { - fprintf (peer, "354 End data with .\r\n"); - fflush (peer); - receive_data_to_file (peer, output); - fprintf (peer, "250 OK\r\n"); - fflush (peer); - } else if (STRNCMP_LITERAL (command, "QUIT") == 0) { - fprintf (peer, "221 BYE\r\n"); - fflush (peer); - return 1; - } else { - fprintf (stderr, "Unknown command: %s\n", command); - } - return 0; + if (STRNCMP_LITERAL (command, "EHLO ") == 0) { + fprintf (peer, "502 not implemented\r\n"); + fflush (peer); + } else if (STRNCMP_LITERAL (command, "HELO ") == 0) { + fprintf (peer, "250 localhost\r\n"); + fflush (peer); + } else if (STRNCMP_LITERAL (command, "MAIL FROM:") == 0 || + STRNCMP_LITERAL (command, "RCPT TO:") == 0) { + fprintf (peer, "250 OK\r\n"); + fflush (peer); + } else if (STRNCMP_LITERAL (command, "DATA") == 0) { + fprintf (peer, "354 End data with .\r\n"); + fflush (peer); + receive_data_to_file (peer, output); + fprintf (peer, "250 OK\r\n"); + fflush (peer); + } else if (STRNCMP_LITERAL (command, "QUIT") == 0) { + fprintf (peer, "221 BYE\r\n"); + fflush (peer); + return 1; + } else { + fprintf (stderr, "Unknown command: %s\n", command); + } + return 0; } static void do_smtp_to_file (FILE *peer, FILE *output) { - char buf[4096]; - ssize_t bytes; - char greeting[] = "220 localhost smtp-dummy\r\n"; - char *line = NULL; - size_t line_size; - ssize_t line_len; - - fprintf (peer, "220 localhost smtp-dummy\r\n"); - fflush (peer); + char *line = NULL; + size_t line_size; + ssize_t line_len; - while ((line_len = getline (&line, &line_size, peer)) != -1) { - if (process_command (peer, output, line)) - break; - } + fprintf (peer, "220 localhost smtp-dummy\r\n"); + fflush (peer); + + while ((line_len = getline (&line, &line_size, peer)) != -1) { + if (process_command (peer, output, line)) + break; + } - free (line); + free (line); } int main (int argc, char *argv[]) { - char *output_filename; - FILE *peer_file, *output; - int sock, peer, err; - struct sockaddr_in addr, peer_addr; - struct hostent *hostinfo; - socklen_t peer_addr_len; - int reuse; - - if (argc != 2) { - fprintf (stderr, "Usage: %s \n", argv[0]); - exit (1); - } + const char *progname; + char *output_filename; + FILE *peer_file = NULL, *output = NULL; + int sock = -1, peer, err; + struct sockaddr_in addr, peer_addr; + struct hostent *hostinfo; + socklen_t peer_addr_len; + int reuse; + int background; + int ret = 0; - output_filename = argv[1]; - output = fopen (output_filename, "w"); - if (output == NULL) { - fprintf (stderr, "Failed to open %s for writing: %s\n", - output_filename, strerror (errno)); - exit (1); - } + progname = argv[0]; - sock = socket (AF_INET, SOCK_STREAM, 0); - if (sock == -1) { - fprintf (stderr, "Error: socket() failed: %s\n", - strerror (errno)); - exit (1); + background = 0; + for (; argc >= 2; argc--, argv++) { + if (argv[1][0] != '-') + break; + if (strcmp (argv[1], "--") == 0) { + argc--; + argv++; + break; } - - reuse = 1; - err = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof (reuse)); - if (err) { - fprintf (stderr, "Error: setsockopt() failed: %s\n", - strerror (errno)); - exit (1); + if (strcmp (argv[1], "--background") == 0) { + background = 1; + continue; } + fprintf (stderr, "%s: unregognized option '%s'\n", + progname, argv[1]); + return 1; + } - hostinfo = gethostbyname ("localhost"); - if (hostinfo == NULL) { - fprintf (stderr, "Unknown host: localhost\n"); - exit (1); - } + if (argc != 2) { + fprintf (stderr, + "Usage: %s [--background] \n", progname); + return 1; + } - addr.sin_family = AF_INET; - addr.sin_port = htons (25025); - addr.sin_addr = *(struct in_addr *) hostinfo->h_addr; - err = bind (sock, (struct sockaddr *) &addr, sizeof(addr)); - if (err) { - fprintf (stderr, "Error: bind() failed: %s\n", - strerror (errno)); - close (sock); - exit (1); - } + output_filename = argv[1]; + output = fopen (output_filename, "w"); + if (output == NULL) { + fprintf (stderr, "Failed to open %s for writing: %s\n", + output_filename, strerror (errno)); + ret = 1; + goto DONE; + } - err = listen (sock, 1); - if (err) { - fprintf (stderr, "Error: listen() failed: %s\n", - strerror (errno)); - close (sock); - exit (1); - } + sock = socket (AF_INET, SOCK_STREAM, 0); + if (sock == -1) { + fprintf (stderr, "Error: socket() failed: %s\n", + strerror (errno)); + ret = 1; + goto DONE; + } - peer_addr_len = sizeof (peer_addr); - peer = accept (sock, (struct sockaddr *) &peer_addr, &peer_addr_len); - if (peer == -1) { - fprintf (stderr, "Error: accept() failed: %s\n", - strerror (errno)); - exit (1); - } + reuse = 1; + err = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof (reuse)); + if (err) { + fprintf (stderr, "Error: setsockopt() failed: %s\n", + strerror (errno)); + ret = 1; + goto DONE; + } + + hostinfo = gethostbyname ("localhost"); + if (hostinfo == NULL) { + fprintf (stderr, "Unknown host: localhost\n"); + ret = 1; + goto DONE; + } + + memset (&addr, 0, sizeof (addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons (25025); + addr.sin_addr = *(struct in_addr *) hostinfo->h_addr; + err = bind (sock, (struct sockaddr *) &addr, sizeof (addr)); + if (err) { + fprintf (stderr, "Error: bind() failed: %s\n", + strerror (errno)); + close (sock); + ret = 1; + goto DONE; + } - peer_file = fdopen (peer, "w+"); - if (peer_file == NULL) { - fprintf (stderr, "Error: fdopen() failed: %s\n", - strerror (errno)); - return; + err = listen (sock, 1); + if (err) { + fprintf (stderr, "Error: listen() failed: %s\n", + strerror (errno)); + close (sock); + ret = 1; + goto DONE; + } + + if (background) { + int pid = fork (); + if (pid > 0) { + printf ("smtp_dummy_pid='%d'\n", pid); + fflush (stdout); + close (sock); + ret = 0; + goto DONE; + } + if (pid < 0) { + fprintf (stderr, "Error: fork() failed: %s\n", + strerror (errno)); + close (sock); + ret = 1; + goto DONE; } + /* Reached if pid == 0 (the child process). */ + /* Close stdout so that the one interested in pid value will + * also get EOF. */ + close (STDOUT_FILENO); + /* dup2() will re-reserve fd of stdout (1) (opportunistically), + * in case fd of stderr (2) is open. If that was not open we + * don't care fd of stdout (1) either. */ + dup2 (STDERR_FILENO, STDOUT_FILENO); + + /* This process is now out of reach of shell's job control. + * To resolve the rare but possible condition where this + * "daemon" is started but never connected this process will + * (only) have 30 seconds to exist. */ + alarm (30); + } + + peer_addr_len = sizeof (peer_addr); + peer = accept (sock, (struct sockaddr *) &peer_addr, &peer_addr_len); + if (peer == -1) { + fprintf (stderr, "Error: accept() failed: %s\n", + strerror (errno)); + ret = 1; + goto DONE; + } + + peer_file = fdopen (peer, "w+"); + if (peer_file == NULL) { + fprintf (stderr, "Error: fdopen() failed: %s\n", + strerror (errno)); + ret = 1; + goto DONE; + } - do_smtp_to_file (peer_file, output); + do_smtp_to_file (peer_file, output); + DONE: + if (output) fclose (output); + if (peer_file) fclose (peer_file); + if (sock >= 0) close (sock); - return 0; + return ret; }