diff options
-rw-r--r-- | README | 6 | ||||
-rw-r--r-- | README.mutt | 7 | ||||
-rw-r--r-- | TODO | 1 | ||||
-rw-r--r-- | lexer.l | 19 | ||||
-rw-r--r-- | main.c | 782 | ||||
-rw-r--r-- | parser.y | 10 |
6 files changed, 405 insertions, 420 deletions
@@ -1,6 +1,6 @@ -ESMTP is a user configurable relay-only Mail Transfer Agent (MTA) with a -sendmail-compatible syntax. It's based on libESMTP supporting the AUTH -(including the CRAM-MD5 and NTLM SASL mechanisms) and the StartTLS SMTP +ESMTP is a user configurable relay-only Mail Transfer Agent (MTA) with a +sendmail-compatible syntax. It's based on libESMTP supporting the AUTH +(including the CRAM-MD5 and NTLM SASL mechanisms) and the StartTLS SMTP extensions. See ESMTP home page http://esmtp.sourceforge.net/ for more information. diff --git a/README.mutt b/README.mutt index bb01f64..c80cde2 100644 --- a/README.mutt +++ b/README.mutt @@ -2,8 +2,8 @@ To use ESMTP with mutt add the following line set sendmail="/path/to/esmtp" -to your ~/.muttrc and create a ~/.esmtprc (see the included sample.esmtprc -for an example.) +to your ~/.muttrc and create a ~/.esmtprc (see the included sample.esmtprc for +an example.) ESMTP supports SENDMAIL envelope sender '-f' flag, so don't forget to add @@ -11,7 +11,8 @@ ESMTP supports SENDMAIL envelope sender '-f' flag, so don't forget to add as well. -For debugging purposes you may prefer using the follwing shell script which logs ESMTP to +For debugging purposes you may prefer using the follwing shell script which +logs ESMTP to #!/bin/sh LOGFILE=/tmp/esmtp.log @@ -1,3 +1,2 @@ - handle the '-t' option - implement the '-X' option to log the traffic to a file -- in general better handling of most sendmail options @@ -82,10 +82,11 @@ required { return REQUIRED; } %% -void escapes(cp, tp) /* process standard C-style escape sequences in a string */ -const char *cp; /* source string with escapes */ -char *tp; /* target buffer for digested string */ +void escapes( + const char *cp, /* source string with escapes */ + char *tp /* target buffer for digested string */ +) { while (*cp) { @@ -111,12 +112,12 @@ char *tp; /* target buffer for digested string */ { switch (*++cp) { - case '\\': cval = '\\'; break; - case 'n': cval = '\n'; break; - case 't': cval = '\t'; break; - case 'b': cval = '\b'; break; - case 'r': cval = '\r'; break; - default: cval = *cp; + case '\\': cval = '\\'; break; + case 'n': cval = '\n'; break; + case 't': cval = '\t'; break; + case 'b': cval = '\b'; break; + case 'r': cval = '\r'; break; + default: cval = *cp; } cp++; } @@ -18,7 +18,7 @@ */ /* This program attemps to mimic the sendmail program behavior using libESMTP. - * + * * Adapted from the libESMTP's mail-file example by José Fonseca. */ #define _XOPEN_SOURCE 500 @@ -45,484 +45,466 @@ char *certificate_passphrase = NULL; extern void parse_rcfile(void); -/* Callback function to read the message from a file. Since libESMTP - does not provide callbacks which translate line endings, one must - be provided by the application. - - The message is read a line at a time and the newlines converted - to \r\n. Unfortunately, RFC 822 states that bare \n and \r are - acceptable in messages and that individually they do not constitute a - line termination. This requirement cannot be reconciled with storing - messages with Unix line terminations. RFC 2822 rescues this situation - slightly by prohibiting lone \r and \n in messages. - - The following code cannot therefore work correctly in all situations. - Furthermore it is very inefficient since it must search for the \n. +/* Callback function to read the message from a file. Since libESMTP does not + * provide callbacks which translate line endings, one must be provided by the + * application. + * + * The message is read a line at a time and the newlines converted to \r\n. + * Unfortunately, RFC 822 states that bare \n and \r are acceptable in messages + * and that individually they do not constitute a line termination. This + * requirement cannot be reconciled with storing messages with Unix line + * terminations. RFC 2822 rescues this situation slightly by prohibiting lone + * \r and \n in messages. + * + * The following code cannot therefore work correctly in all situations. + * Furthermore it is very inefficient since it must search for the \n. */ #define BUFLEN 8192 -const char * -readlinefp_cb (void **buf, int *len, void *arg) +const char * readlinefp_cb (void **buf, int *len, void *arg) { - int octets; + int octets; - if (*buf == NULL) - *buf = malloc (BUFLEN); + if (*buf == NULL) + *buf = malloc (BUFLEN); - if (len == NULL) + if (len == NULL) { - rewind ((FILE *) arg); - return NULL; + rewind ((FILE *) arg); + return NULL; } - if (fgets (*buf, BUFLEN - 2, (FILE *) arg) == NULL) - octets = 0; - else + if (fgets (*buf, BUFLEN - 2, (FILE *) arg) == NULL) + octets = 0; + else { - char *p = strchr (*buf, '\0'); - - if (p[-1] == '\n' && p[-2] != '\r') - { - strcpy (p - 1, "\r\n"); - p++; - } - octets = p - (char *) *buf; + char *p = strchr (*buf, '\0'); + + if (p[-1] == '\n' && p[-2] != '\r') + { + strcpy (p - 1, "\r\n"); + p++; + } + octets = p - (char *) *buf; } - *len = octets; - return *buf; + *len = octets; + return *buf; } -void -monitor_cb (const char *buf, int buflen, int writing, void *arg) +void monitor_cb (const char *buf, int buflen, int writing, void *arg) { - FILE *fp = arg; + FILE *fp = arg; - if (writing == SMTP_CB_HEADERS) + if (writing == SMTP_CB_HEADERS) { - fputs ("H: ", fp); - fwrite (buf, 1, buflen, fp); - return; + fputs ("H: ", fp); + fwrite (buf, 1, buflen, fp); + return; } - fputs (writing ? "C: " : "S: ", fp); - fwrite (buf, 1, buflen, fp); - if (buf[buflen - 1] != '\n') - putc ('\n', fp); + fputs (writing ? "C: " : "S: ", fp); + fwrite (buf, 1, buflen, fp); + if (buf[buflen - 1] != '\n') + putc ('\n', fp); } -void -usage (void) +void usage (void) { - fputs ("usage: esmtp [options] mailbox [mailbox ...]\n", - stderr); + fputs ("usage: esmtp [options] mailbox [mailbox ...]\n", + stderr); } -void -version (void) +void version (void) { - char buf[32]; + char buf[32]; - smtp_version (buf, sizeof buf, 0); - printf ("libESMTP version %s\n", buf); + smtp_version (buf, sizeof buf, 0); + printf ("libESMTP version %s\n", buf); } /* Callback to request user/password info. Not thread safe. */ -int -authinteract (auth_client_request_t request, char **result, int fields, - void *arg) +int authinteract (auth_client_request_t request, char **result, int fields, + void *arg) { - int i; + int i; - for (i = 0; i < fields; i++) + for (i = 0; i < fields; i++) { - if (request[i].flags & AUTH_USER && user) - result[i] = user; - else if (request[i].flags & AUTH_PASS && pass) - result[i] = pass; - else - return 0; + if (request[i].flags & AUTH_USER && user) + result[i] = user; + else if (request[i].flags & AUTH_PASS && pass) + result[i] = pass; + else + return 0; } - return 1; + return 1; } -int -tlsinteract (char *buf, int buflen, int rwflag, void *arg) +int tlsinteract (char *buf, int buflen, int rwflag, void *arg) { - char *pw; - int len; + char *pw; + int len; - if (certificate_passphrase) + if (certificate_passphrase) { - pw = certificate_passphrase; - len = strlen (pw); - if (len + 1 > buflen) - return 0; - strcpy (buf, pw); - return len; + pw = certificate_passphrase; + len = strlen (pw); + if (len + 1 > buflen) + return 0; + strcpy (buf, pw); + return len; } - else - return 0; + else + return 0; } -/* Callback to print the recipient status */ -void -print_recipient_status (smtp_recipient_t recipient, +/* Callback to print the recipient status. */ +void print_recipient_status (smtp_recipient_t recipient, const char *mailbox, void *arg) { - const smtp_status_t *status; + const smtp_status_t *status; - status = smtp_recipient_status (recipient); - fprintf (stderr, "%s: %d %s\n", mailbox, status->code, status->text); + status = smtp_recipient_status (recipient); + fprintf (stderr, "%s: %d %s\n", mailbox, status->code, status->text); } -int -main (int argc, char **argv) +int main (int argc, char **argv) { - smtp_session_t session; - smtp_message_t message; - smtp_recipient_t recipient; - auth_context_t authctx; - const smtp_status_t *status; - struct sigaction sa; - int c; - int ret; - enum notify_flags notify = Notify_NOTSET; - - /* Parse the rc file. */ - parse_rcfile(); - - /* This program sends only one message at a time. Create an SMTP - session. */ - auth_client_init (); - session = smtp_create_session (); - - while ((c = getopt (argc, argv, - "A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:")) != EOF) - switch (c) - { - case 'A': - /* Use alternate sendmail/submit.cf */ - break; - - case 'B': - /* Body type */ - break; - - case 'C': - /* Select configuration file */ - break; - - case 'F': - /* Set full name */ - break; - - case 'G': - /* Relay (gateway) submission */ - break; - - case 'I': - /* Initialize alias database */ - break; - - case 'L': - /* Program label */ - break; - - case 'M': - /* Define macro */ - break; - - case 'N': - /* Delivery status notifications */ - if (strcmp (optarg, "never") == 0) - notify = Notify_NEVER; - else - { - if (strstr (optarg, "failure")) - notify |= Notify_FAILURE; - if (strstr (optarg, "delay")) - notify |= Notify_DELAY; - if (strstr (optarg, "success")) - notify |= Notify_SUCCESS; - } - break; - - case 'R': - /* What to return */ - break; - - case 'T': - /* Set timeout interval */ - break; - - case 'X': - /* Traffic log file */ - break; - - case 'V': - /* Set original envelope id */ - break; - - case 'b': - /* Operations mode */ - c = (optarg == NULL) ? ' ' : *optarg; + smtp_session_t session; + smtp_message_t message; + smtp_recipient_t recipient; + auth_context_t authctx; + const smtp_status_t *status; + struct sigaction sa; + int c; + int ret; + enum notify_flags notify = Notify_NOTSET; + + /* Parse the rc file. */ + parse_rcfile(); + + /* This program sends only one message at a time. Create an SMTP session. + */ + auth_client_init (); session = smtp_create_session (); + + while ((c = getopt (argc, argv, + "A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:")) != EOF) switch (c) - { - case 'm': - /* Deliver mail in the usual way */ - break; + { + case 'A': + /* Use alternate sendmail/submit.cf */ + break; - case 'i': - /* Initialize the alias database */ - break; + case 'B': + /* Body type */ + break; - case 'a': - /* Go into ARPANET mode */ - case 'd': - /* Run as a daemon */ - case 'D': - /* Run as a daemon in foreground */ - case 'h': - /* Print the persistent host status database */ - case 'H': - /* Purge expired entries from the persistent host status database */ - case 'p': - /* Print a listing of the queue(s) */ - case 'P': - /* Print number of entries in the queue(s) */ - case 's': - /* Use the SMTP protocol as described in RFC821 - * on standard input and output */ - case 't': - /* Run in address test mode */ - case 'v': - /* Verify names only */ - fprintf (stderr, "Unsupported operation mode %c\n", c); - exit (64); - break; + case 'C': + /* Select configuration file */ + break; + + case 'F': + /* Set full name */ + break; - default: - fprintf (stderr, "Invalid operation mode %c\n", c); - exit (64); - break; - } - break; - - case 'c': - /* Connect to non-local mailers */ - break; - - case 'd': - /* Debugging */ - break; - - case 'e': - /* Error message disposition */ - break; - - case 'f': - /* From address */ - case 'r': - /* Obsolete -f flag */ - from = optarg; - break; - - case 'h': - /* Hop count */ - break; - - case 'i': - /* Don't let dot stop me */ - break; - - case 'm': - /* Send to me too */ - break; - - case 'n': - /* don't alias */ - break; - - case 'o': - /* Set option */ - break; - - case 'p': - /* Set protocol */ - break; - - case 'q': - /* Run queue files at intervals */ - if (optarg[0] == '!') - { - /* Negate the meaning of pattern match */ - optarg++; - } - - switch (optarg[0]) - { case 'G': - /* Limit by queue group name */ - break; + /* Relay (gateway) submission */ + break; case 'I': - /* Limit by ID */ - break; + /* Initialize alias database */ + break; + + case 'L': + /* Program label */ + break; + + case 'M': + /* Define macro */ + break; + + case 'N': + /* Delivery status notifications */ + if (strcmp (optarg, "never") == 0) + notify = Notify_NEVER; + else + { + if (strstr (optarg, "failure")) + notify |= Notify_FAILURE; + if (strstr (optarg, "delay")) + notify |= Notify_DELAY; + if (strstr (optarg, "success")) + notify |= Notify_SUCCESS; + } + break; case 'R': - /* Limit by recipient */ - break; + /* What to return */ + break; + + case 'T': + /* Set timeout interval */ + break; + + case 'X': + /* Traffic log file */ + break; + + case 'V': + /* Set original envelope id */ + break; + + case 'b': + /* Operations mode */ + c = (optarg == NULL) ? ' ' : *optarg; + switch (c) + { + case 'm': + /* Deliver mail in the usual way */ + break; + + case 'i': + /* Initialize the alias database */ + break; + + case 'a': + /* Go into ARPANET mode */ + case 'd': + /* Run as a daemon */ + case 'D': + /* Run as a daemon in foreground */ + case 'h': + /* Print the persistent host status database */ + case 'H': + /* Purge expired entries from the persistent host + * status database */ + case 'p': + /* Print a listing of the queue(s) */ + case 'P': + /* Print number of entries in the queue(s) */ + case 's': + /* Use the SMTP protocol as described in RFC821 + * on standard input and output */ + case 't': + /* Run in address test mode */ + case 'v': + /* Verify names only */ + fprintf (stderr, "Unsupported operation mode %c\n", c); + exit (64); + break; + + default: + fprintf (stderr, "Invalid operation mode %c\n", c); + exit (64); + break; + } + break; + + case 'c': + /* Connect to non-local mailers */ + break; + + case 'd': + /* Debugging */ + break; - case 'S': - /* Limit by sender */ - break; + case 'e': + /* Error message disposition */ + break; case 'f': - /* Forground queue run */ - break; + /* From address */ + case 'r': + /* Obsolete -f flag */ + from = optarg; + break; + + case 'h': + /* Hop count */ + break; + + case 'i': + /* Don't let dot stop me */ + break; + + case 'm': + /* Send to me too */ + break; + + case 'n': + /* don't alias */ + break; + + case 'o': + /* Set option */ + break; case 'p': - /* Persistent queue */ - if (optarg[1] == '\0') + /* Set protocol */ + break; + + case 'q': + /* Run queue files at intervals */ + if (optarg[0] == '!') + { + /* Negate the meaning of pattern match */ + optarg++; + } + + switch (optarg[0]) + { + case 'G': + /* Limit by queue group name */ + break; + + case 'I': + /* Limit by ID */ + break; + + case 'R': + /* Limit by recipient */ + break; + + case 'S': + /* Limit by sender */ + break; + + case 'f': + /* Foreground queue run */ + break; + + case 'p': + /* Persistent queue */ + if (optarg[1] == '\0') + break; + ++optarg; + + default: + fprintf (stderr, "Invalid -q value\n"); + exit (64); + break; + } + break; + + case 's': + /* Save From lines in headers */ + break; + + case 't': + /* Read recipients from message */ + fprintf (stderr, "Unsupported option 't'\n"); + exit (64); + break; + + case 'v': + /* Verbose */ + smtp_set_monitorcb (session, monitor_cb, stdout, 1); break; - ++optarg; default: - fprintf (stderr, "Invalid -q value\n"); - exit (64); - break; - } - break; - - case 's': - /* Save From lines in headers */ - break; - - case 't': - /* Read recipients from message */ - fprintf (stderr, "Unsupported option 't'\n"); - exit (64); - break; - - case 'v': - /* Verbose */ - smtp_set_monitorcb (session, monitor_cb, stdout, 1); - break; - - default: - usage (); - exit (64); - } - - /* At least one more argument is needed. - */ - if (optind > argc - 1) + usage (); + exit (64); + } + + /* At least one more argument is needed. */ + if (optind > argc - 1) { - usage (); - exit (64); + usage (); + exit (64); } - /* NB. libESMTP sets timeouts as it progresses through the protocol. - In addition the remote server might close its socket on a timeout. - Consequently libESMTP may sometimes try to write to a socket with - no reader. Ignore SIGPIPE, then the program doesn't get killed - if/when this happens. */ - sa.sa_handler = SIG_IGN; - sigemptyset (&sa.sa_mask); - sa.sa_flags = 0; - sigaction (SIGPIPE, &sa, NULL); - - /* Set the host running the SMTP server. LibESMTP has a default port - number of 587, however this is not widely deployed so the port - is specified as 25 along with the default MTA host. */ - smtp_set_server (session, host ? host : "localhost:25"); - - /* Set the SMTP Starttls extension. */ - smtp_starttls_enable (session, starttls); - - /* Do what's needed at application level to use authentication. - */ - authctx = auth_create_context (); - auth_set_mechanism_flags (authctx, AUTH_PLUGIN_PLAIN, 0); - auth_set_interact_cb (authctx, authinteract, NULL); - - /* Use our callback for X.509 certificate passwords. If STARTTLS is - not in use or disabled in configure, the following is harmless. */ - smtp_starttls_set_password_cb (tlsinteract, NULL); - - /* Now tell libESMTP it can use the SMTP AUTH extension. - */ - smtp_auth_set_context (session, authctx); - - /* At present it can't handle one recipient only out of many - failing. Make libESMTP require all specified recipients to - succeed before transferring a message. */ - smtp_option_require_all_recipients (session, 1); - - /* Add a message to the SMTP session. - */ - message = smtp_add_message (session); - - /* Set the reverse path for the mail envelope. (NULL is ok) - */ - smtp_set_reverse_path (message, from); - - /* Open the message file and set the callback to read it. - */ + /* NB. libESMTP sets timeouts as it progresses through the protocol. In + * addition the remote server might close its socket on a timeout. + * Consequently libESMTP may sometimes try to write to a socket with no + * reader. Ignore SIGPIPE, then the program doesn't get killed if/when + * this happens. + */ + sa.sa_handler = SIG_IGN; sigemptyset (&sa.sa_mask); sa.sa_flags = 0; + sigaction (SIGPIPE, &sa, NULL); + + /* Set the host running the SMTP server. LibESMTP has a default port + * number of 587, however this is not widely deployed so the port is + * specified as 25 along with the default MTA host. + */ + smtp_set_server (session, host ? host : "localhost:25"); + + /* Set the SMTP Starttls extension. */ + smtp_starttls_enable (session, starttls); + + /* Do what's needed at application level to use authentication. */ + authctx = auth_create_context (); + auth_set_mechanism_flags (authctx, AUTH_PLUGIN_PLAIN, 0); + auth_set_interact_cb (authctx, authinteract, NULL); + + /* Use our callback for X.509 certificate passwords. If STARTTLS is not in + * use or disabled in configure, the following is harmless. + */ + smtp_starttls_set_password_cb (tlsinteract, NULL); + + /* Now tell libESMTP it can use the SMTP AUTH extension. */ + smtp_auth_set_context (session, authctx); + + /* At present it can't handle one recipient only out of many failing. Make + * libESMTP require all specified recipients to succeed before transferring + * a message. + */ + smtp_option_require_all_recipients (session, 1); + + /* Add a message to the SMTP session. */ + message = smtp_add_message (session); + + /* Set the reverse path for the mail envelope. (NULL is ok) */ + smtp_set_reverse_path (message, from); + + /* Open the message file and set the callback to read it. */ #if 0 - smtp_set_message_fp (message, stdin); + smtp_set_message_fp (message, stdin); #else - smtp_set_messagecb (message, readlinefp_cb, stdin); + smtp_set_messagecb (message, readlinefp_cb, stdin); #endif - /* Add remaining program arguments as message recipients. - */ - while (optind < argc) + /* Add remaining program arguments as message recipients. */ + while (optind < argc) { - recipient = smtp_add_recipient (message, argv[optind++]); + recipient = smtp_add_recipient (message, argv[optind++]); - /* Recipient options set here */ - if (notify != Notify_NOTSET) - smtp_dsn_set_notify (recipient, notify); + /* Recipient options set here */ + if (notify != Notify_NOTSET) + smtp_dsn_set_notify (recipient, notify); } - /* Initiate a connection to the SMTP server and transfer the - message. */ - if (!smtp_start_session (session)) + /* Initiate a connection to the SMTP server and transfer the message. */ + if (!smtp_start_session (session)) { - char buf[128]; + char buf[128]; + + fprintf (stderr, "SMTP server problem %s\n", + smtp_strerror (smtp_errno (), buf, sizeof buf)); - fprintf (stderr, "SMTP server problem %s\n", - smtp_strerror (smtp_errno (), buf, sizeof buf)); - - ret = 69; + ret = 69; } - else + else { - /* Report on the success or otherwise of the mail transfer. - */ - status = smtp_message_transfer_status (message); - if (status->code / 100 == 2) - { - ret = 0; + /* Report on the success or otherwise of the mail transfer. */ + status = smtp_message_transfer_status (message); + if (status->code / 100 == 2) + { + ret = 0; } - else - { - /* Report on the failure of the mail transfer. - */ - status = smtp_message_transfer_status (message); - fprintf (stderr, "%d %s\n", status->code, status->text); - smtp_enumerate_recipients (message, print_recipient_status, NULL); - - ret = 70; + else + { + /* Report on the failure of the mail transfer. */ + status = smtp_message_transfer_status (message); + fprintf (stderr, "%d %s\n", status->code, status->text); + smtp_enumerate_recipients (message, print_recipient_status, NULL); + + ret = 70; } } - - /* Free resources consumed by the program. - */ - smtp_destroy_session (session); - auth_destroy_context (authctx); - auth_client_exit (); - exit (ret); -} + /* Free resources consumed by the program. */ + smtp_destroy_session (session); + auth_destroy_context (authctx); + auth_client_exit (); + exit (ret); +} @@ -34,8 +34,8 @@ void yyerror (const char *s); %} %union { - int number; - char *sval; + int number; + char *sval; } %token HOSTNAME USERNAME PASSWORD STARTTLS CERTIFICATE_PASSPHRASE @@ -99,8 +99,10 @@ void parse_rcfile (void) strcat(rcfile, RCFILE); /* Open the configuration file and feed it to the lexer. */ - if (!(yyin = fopen(rcfile, "r"))) { - if (errno != ENOENT) { + if (!(yyin = fopen(rcfile, "r"))) + { + if (errno != ENOENT) + { fprintf(stderr, "open: %s: %s\n", rcfile, strerror(errno)); } } |