From 60b9cfd5abf4e2c68821b8435cc73ec3a7d1e058 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Fonseca?= Date: Sat, 5 Jul 2003 10:49:22 +0000 Subject: Better documentation. Support for more sendmail options. Error verification for libESMTP calls. --- Makefile.am | 2 +- NEWS | 50 ++++++++---- README | 241 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- TODO | 14 +++- esmtp.1 | 8 +- local.c | 17 ++-- main.c | 117 +++++++++++++++------------- message.c | 41 +++++++--- message.h | 12 ++- rfc822.c | 8 +- sample.esmtprc | 30 ++----- smtp.c | 226 +++++++++++++++++++++++++++++++---------------------- smtp.h | 16 +++- 13 files changed, 554 insertions(+), 228 deletions(-) diff --git a/Makefile.am b/Makefile.am index ea96204..2e1d798 100644 --- a/Makefile.am +++ b/Makefile.am @@ -19,7 +19,7 @@ esmtp_SOURCES = \ smtp.h \ xmalloc.h -EXTRA_DIST = README.mutt sample.esmtprc +EXTRA_DIST = sample.esmtprc AM_YFLAGS = -d diff --git a/NEWS b/NEWS index 4036167..44dfae8 100644 --- a/NEWS +++ b/NEWS @@ -1,26 +1,42 @@ -Version 0.4 (under development) +News +~~~~ - * Debian packaging. - * Fixes to compile on FreeBSD (Tim Hemel). - * Local delivery via a MDA. - * Extraction of the recipients from the headers ('-t' option). + * Version 0.4 (under development): + * Debian packaging. -Version 0.3 + * Fixes to compile on FreeBSD (Tim Hemel). - * Capability to generate log files. - * Better verbose output. - * Minor bug fixes and documentation enhancements. - * Multiple identities (Jerome). + * Local delivery via a MDA. + * Extraction of the recipients from the headers ('-t' option). -Version 0.2: + * 8bit-MIME extension ('-B' option). - * Packaging corrections. - * Minor bug fixes and documentation enhancements. - * Use sendmail exit codes. - + * Further Delivery Status Notification ('-R' and '-V' options). -Version 0.1: - * Initial release + * Version 0.3: + + * Capability to generate log files. + + * Better verbose output. + + * Minor bug fixes and documentation enhancements. + + * Multiple identities (Jerome). + + + * Version 0.2: + + * Packaging corrections. + + * Minor bug fixes and documentation enhancements. + + * Use sendmail exit codes. + + + * Version 0.1: + + * Initial release. + diff --git a/README b/README index 9caa605..1a19027 100644 --- a/README +++ b/README @@ -1,6 +1,237 @@ -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. + ----- + ESMTP + ----- + José Fonseca + --- + July 2003 + + +Introduction +~~~~~~~~~~~~ + + <> is a user configurable relay-only Mail Transfer Agent (MTA) with a + <> compatible syntax. It's based on <> supporting the + AUTH (including the CRAM-MD5 and NTLM SASL mechanisms) and the StartTLS SMTP + extensions. + + See the {{{http://esmtp.sourceforge.net/}ESMTP home page}} for updated + information. + + +Rationale +~~~~~~~~~ + + Since the moment I switched to Linux I had some problems to have email + properly configurated to my personal needs. I use a POP3/SMTP mail account + for my personal mail, and I usually connect to the internet with my laptop + via several LANs (home, work, ...) with several different firewall + configurations. The standard MTAs didn't provided the flexibility I needed -- + they either failed to deliver the mail directly or failed to authenticate + with the relay, or were configured system-wide and conflicted with the local + MTA. + + <> always worked fine to get email, but I needed a <> + alike program to send email from <>. Basically I wanted a program + which could be for SMTP what <> was for POP3, i.e., an user + configurable MTA with authorization support. + + Some small modifications to the <<>> example from <> did + the trick. After receiving positive feedback from the <> author, + Brian Stafford, I decided to make the program more confortable for others to + use -- adding a command-line option parser extracted from sendmail, a + configuration file parser from <>, a man page based on <> + and <> man pages and using autotools to wrap it all up. The result + of this cut and paste effort is <>. Well, the first version, that is. + Since then a few more bells and whistles were added, with the help of some + contributors. + + + +Configuration +~~~~~~~~~~~~~ + + +* Sample configuration file +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + This is a simple configuration file for a quick start: + +-------------------------------------- +hostname = mail.myisp.com:25 +username = "myusername" +password = "mysecret" +starttls = enabled + +identity = myself@somewhere.com + hostname = smtp.somewhere.com:25 + username = "myself" + password = "secret" + starttls = enabled + +mda "/usr/bin/procmail -d %T" +-------------------------------------- + + +* Configuration options +~~~~~~~~~~~~~~~~~~~~~~~ + + Options are speficied by giving a in the configuration + file. The equal sign is optional and can be replaced by whitespace. The + value may be enclose in simple or double quotes, in which case special + characters can be escaped as in normal strings. + + [hostname] - set SMTP host and service (port). + + This is specified in the format <<>> with no + whitespace surrounding the colon if service is specified. service may be a + name from <<>> or a decimal port number. If not specified + the port defaults to 587. + + Note (from <> documentation): the default port number is set to + 587 since this is the port that should be used for mail submission, see RFC + 2476. By choosing this default now, the API does not change behaviour + unexpectedly in the future as use of the new standard becomes commonplace. + The hostport notation simplifies things for the application, the user can + type <<>> or <<>> where the application + expects a host name. + + [username] - set the user name. + + [password] - set the password. + + [starttls] - determine the usage of the StartTLS extension. + + It can be one of <"enabled">, <"disabled"> or <"required">. It defaults to + disabled. + + [certificate_passphrase] - set the certificate passphrase. + + [identity] - define an identity. + + An identities is a set of options associated with a given address. For + example: + +-------------------------------------- +identity = myself@somewhere.com + hostname = smtp.somewhere.com:25 + username = "myself" + password = "secret" +-------------------------------------- + + Identities are be selected by the address specified in the <-f> flag. You + can have as many you like. + + The options up to the first option constitute the default + identity. + + + Note: the default identity settings are not shared by the other identities. + Everything (username, password, etc.) must be specified for every identity + even if they don't differ from the default identity. + + [mda] - set the Mail Delivery Agent (MDA). + + Local delivery addresses will be inserted into the MDA command wherever you + place a <%T>. The mail message's From address will be inserted where you + place an <%F>. + + Some common MDAs are <"/usr/bin/procmail -d %T">, + <"/usr/bin/deliver"> and <"/usr/lib/mail.local %T">. + + See below for more information about using <> with an MDA. + + +Invocation +~~~~~~~~~~ + + <> is command line compatible with <>. + + See the <> man page for information on how to invoke it. + + +Interfacing to Mail User Agents +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Most Mail User Agents (MUAs) will work without need to configuration provided + that you install a symbolic from <<>> to the esmtp + executable. This should already be taken care of by the target of + the makefile when building from source. + + If by any reason it is not possible to have (such as no administrator + priviledges or the use of another MTA for local delivery) then you will have + to reconfigure your MUA to use the esmtp executable instead. + + +* Mutt +~~~~~~ + + If not using a symbolic link to the esmtp executable you can make <> use + <> by adding the following line to your <<<~/.muttrc>>>: + +----------------------------- +set sendmail="/path/to/esmtp" +----------------------------- + + <> supports <> envelope sender <-f> flag, and you are + advised to always enable it by adding the following line to <> + configuration file: + +--------------------- +set envelope_from=yes +--------------------- + + For debugging purposes you may prefer to put in your <<<~/.muttrc>>>: + +-------------------------------------------------- +set sendmail="/path/to/esmtp -v -X /tmp/esmtp.log" +-------------------------------------------------- + + This will enable verbose output and logging of the traffic with the SMTP + server. + + +Interfacing to Mail Delivery Agents +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + <> relies upon a Mail Delivery Agent (MDA) for local mail delivery, so + you need one if you want to avoid having another MTA for local delivery. + + Notice that at the moment <> does not honor mail aliases or + <<<.forward>>> files. + + To deliver to other users beside yourself, the MDA must be installed with + flag -- which is done by default in most Linux distributions. + + +* Procmail +~~~~~~~~~~ + + To use <> with <> set the <<>> configuration value to: + +----------------------------- +mda="/usr/bin/procmail -d %T" +----------------------------- + + +Interfacing with other mail applications +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +* Fetchmail +~~~~~~~~~~~ + + By default <> delivers messages via SMTP to port 25 on the machine + it is running. Because <> has no SMTP server if you are not using another + MTA for local delivery then you will need to configure <> to use + <> executable. This is acomplished by adding the following lines to the top + of your <<<~/.fetchmailrc>>>: + +------------------------------------- +defaults + mda "/path/to/esmtp -f %F %T" +------------------------------------- + + Since <> simply forwards the mail to another MDA you can avoid this + redundant step by simply replacing the value inside the quotes above by + whichever value you use on your <<<~/.esmtprc>>>. -See ESMTP home page http://esmtp.sourceforge.net/ for more information. diff --git a/TODO b/TODO index 094a84d..130ec16 100644 --- a/TODO +++ b/TODO @@ -1 +1,13 @@ -- add a man page for the configuration file +To do +~~~~~ + + <> already fullfils all my needs, and I don't plan to spend more time + on it besides bug-fixing and some minor feature enhancements. + + Here is a list of what can still be done: + + * Add a man page for the configuration file. + + * Alias expansion. + + diff --git a/esmtp.1 b/esmtp.1 index f8f6a1c..6842853 100644 --- a/esmtp.1 +++ b/esmtp.1 @@ -49,7 +49,7 @@ Use sendmail.cf even if the operation mode indicates an initial mail submission. .TP -\fB\-B\fR \fItype\fR (ignored) +\fB\-B\fR \fItype\fR Set the body type to \fItype\fR. Current legal values are 7BIT or 8BITMIME. .TP @@ -143,7 +143,7 @@ This should be set if you are reading data from a file. Set the identifier used in syslog messages to the supplied \fItag\fR. .TP -\f\-N\fR \fIdsn\fR +\fB\-N\fR \fIdsn\fR Set delivery status notification conditions to \fIdsn\fR, which can be `never' for no notifications or a comma separated list of the values `failure' to be notified if delivery failed, `delay' to be notified if delivery is delayed, and @@ -202,7 +202,7 @@ Limit processed jobs to those containing \fIsubstr\fR as a substring of the sender or not when \fI!\fR is specified. .TP -\fB\-R\fR \fIreturn\fR (ignored) +\fB\-R\fR \fIreturn\fR Set the amount of the message to be returned if the message bounces. The \fIreturn\fR parameter can be `full' to return the entire message or `hdrs' to return only the headers. In the latter case also local bounces return only the @@ -218,7 +218,7 @@ Read message for recipients. To:, Cc:, and Bcc: lines will be scanned for recipient addresses. The Bcc: line will be deleted before transmission. .TP -\fB\-V\fR \fIenvid\fR (ignored) +\fB\-V\fR \fIenvid\fR Set the original envelope id. .TP diff --git a/local.c b/local.c index a2545bf..29601d2 100644 --- a/local.c +++ b/local.c @@ -4,11 +4,11 @@ */ +#include #include #include #include #include -#include #include "local.h" #include "main.h" @@ -45,7 +45,6 @@ void local_init(message_t *message) struct idlist *idp; int length = 0, fromlen = 0, nameslen = 0; char *names = NULL, *before, *after, *from = NULL; - char *user = NULL; if (!mda) { @@ -70,8 +69,9 @@ void local_init(message_t *message) { recipient_t *recipient = list_entry(ptr, recipient_t, list); - if(recipient->address) - nameslen += (strlen(recipient->address) + 1); /* string + ' ' */ + assert(recipient->address); + + nameslen += (strlen(recipient->address) + 1); /* string + ' ' */ } names = (char *)xmalloc(nameslen + 1); /* account for '\0' */ @@ -80,13 +80,8 @@ void local_init(message_t *message) { recipient_t *recipient = list_entry(ptr, recipient_t, list); - if(recipient->address) - { - if(!user) - user = recipient->address; - strcat(names, recipient->address); - strcat(names, " "); - } + strcat(names, recipient->address); + strcat(names, " "); } names[--nameslen] = '\0'; /* chop trailing space */ diff --git a/main.c b/main.c index 8945533..54085f2 100644 --- a/main.c +++ b/main.c @@ -30,29 +30,50 @@ int verbose = 0; FILE *log_fp = NULL; +static void message_send(message_t *message) +{ + int local, remote; + + local = !list_empty(&message->local_recipients); + remote = !list_empty(&message->remote_recipients); + + if(remote && !local) + smtp_send(message); + else if(!remote && local) + { + local_init(message); + local_flush(message); + local_cleanup(); + } + else + { + local_init(message); + smtp_send(message); + local_flush(message); + local_cleanup(); + } +} + int main (int argc, char **argv) { int c; - enum notify_flags notify = Notify_NOTSET; - char *from = NULL; message_t *message; - int parse_headers = 0, local, remote; + int parse_headers = 0; opmode_t mode; char *rcfile = NULL; - identities_init(); + message = message_new(); /* Set the default mode of operation. */ - if (strcmp(argv[0], "mailq") == 0) { + if (!strcmp(argv[0], "mailq")) { mode = MAILQ; - } else if (strcmp(argv[0], "newaliases") == 0) { + } else if (!strcmp(argv[0], "newaliases")) { mode = NEWALIAS; } else { mode = ENQUEUE; } - 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) + 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': @@ -61,6 +82,15 @@ int main (int argc, char **argv) case 'B': /* Body type */ + if (!strcmp (optarg, "7BIT")) + message->body = E8bitmime_7BIT; + else if (!strcmp (optarg, "8BITMIME")) + message->body = E8bitmime_8BITMIME; + else + { + fprintf (stderr, "Unsupported body type %s\n", optarg); + exit (EX_USAGE); + } break; case 'C': @@ -91,21 +121,25 @@ int main (int argc, char **argv) case 'N': /* Delivery status notifications */ - if (strcmp (optarg, "never") == 0) - notify = Notify_NEVER; + if (!strcmp (optarg, "never")) + message->notify = Notify_NEVER; else { if (strstr (optarg, "failure")) - notify |= Notify_FAILURE; + message->notify |= Notify_FAILURE; if (strstr (optarg, "delay")) - notify |= Notify_DELAY; + message->notify |= Notify_DELAY; if (strstr (optarg, "success")) - notify |= Notify_SUCCESS; + message->notify |= Notify_SUCCESS; } break; case 'R': /* What to return */ + if (!strcmp (optarg, "full")) + message->ret |= Ret_FULL; + if (!strcmp (optarg, "hdrs")) + message->ret |= Ret_HDRS; break; case 'T': @@ -121,7 +155,7 @@ int main (int argc, char **argv) case 'V': /* Set original envelope id */ - break; + message_set_envid(message, optarg); case 'b': /* Operations mode */ @@ -190,7 +224,7 @@ int main (int argc, char **argv) /* From address */ case 'r': /* Obsolete -f flag */ - from = optarg; + message_set_reverse_path(message, optarg); break; case 'h': @@ -265,6 +299,11 @@ int main (int argc, char **argv) case 't': /* Read recipients from message */ + if(!message_parse_headers(message)) + { + fprintf(stderr, "No recipients found\n"); + exit(EX_DATAERR); + } parse_headers = 1; break; @@ -287,7 +326,7 @@ int main (int argc, char **argv) printf ("Mail queue is empty\n"); case NEWALIAS: case FLUSHQ: - exit (0); + goto done; } /* At least one more argument is needed. */ @@ -297,49 +336,21 @@ int main (int argc, char **argv) exit (EX_USAGE); } - /* Parse the rc file. */ - rcfile_parse(rcfile); - - message = message_new(); - - /** Parse the envelope headers */ - if(parse_headers) - if(!message_parse_headers(message)) - { - fprintf(stderr, "No recipients found\n"); - exit(EX_DATAERR); - } - - /* Set the reverse path for the mail envelope. */ - if(from) - message_set_reverse_path (message, from); - /* Add remaining program arguments as message recipients. */ while (optind < argc) message_add_recipient(message, argv[optind++]); - local = !list_empty(&message->local_recipients); - remote = !list_empty(&message->remote_recipients); - - if(remote && !local) - smtp_send(message); - else if(!remote && local) - { - local_init(message); - local_flush(message); - local_cleanup(); - } - else - { - local_init(message); - smtp_send(message); - local_flush(message); - local_cleanup(); - } - - message_free(message); + identities_init(); + + /* Parse the rc file. */ + rcfile_parse(rcfile); + message_send(message); + identities_cleanup(); +done: + message_free(message); + exit(EX_OK); } diff --git a/message.c b/message.c index 8adb463..5f487d6 100644 --- a/message.c +++ b/message.c @@ -28,7 +28,9 @@ message_t *message_new(void) INIT_LIST_HEAD(&message->remote_recipients); INIT_LIST_HEAD(&message->local_recipients); + message->ret = Ret_NOTSET; message->notify = Notify_NOTSET; + message->body = E8bitmime_NOTSET; return message; } @@ -47,8 +49,9 @@ void message_free(message_t *message) recipient = list_entry(ptr, recipient_t, list); list_del(ptr); - if(recipient->address) - free(recipient->address); + assert(recipient->address); + + free(recipient->address); free(ptr); } @@ -59,12 +62,16 @@ void message_free(message_t *message) recipient = list_entry(ptr, recipient_t, list); list_del(ptr); - if(recipient->address) - free(recipient->address); + assert(recipient->address); + + free(recipient->address); free(ptr); } + if(message->envid) + free(message->envid); + if(message->fp) fclose(message->fp); @@ -79,18 +86,29 @@ void message_set_reverse_path(message_t *message, const char *address) message->reverse_path = xstrdup(address); } +void message_set_envid(message_t *message, const char *address) +{ + if(message->envid) + free(message->envid); + + message->envid = xstrdup(address); +} + void message_add_recipient(message_t *message, const char *address) { recipient_t *recipient; - recipient = (recipient_t *)xmalloc(sizeof(recipient_t)); + if(address) + { + recipient = (recipient_t *)xmalloc(sizeof(recipient_t)); - recipient->address = xstrdup(address); + recipient->address = xstrdup(address); - if(local_address(address)) - list_add(&recipient->list, &message->local_recipients); - else - list_add(&recipient->list, &message->remote_recipients); + if(local_address(address)) + list_add(&recipient->list, &message->local_recipients); + else + list_add(&recipient->list, &message->remote_recipients); + } } static void message_buffer_alloc(message_t *message) @@ -305,8 +323,7 @@ static unsigned message_parse_header(message_t *message, size_t start, size_t st unsigned message_parse_headers(message_t *message) { - FILE *fp = message->fp ? message->fp : stdin; - char *line, *header; + char *line; size_t start, stop; unsigned count = 0; diff --git a/message.h b/message.h index 52aba01..a4c4b15 100644 --- a/message.h +++ b/message.h @@ -30,8 +30,16 @@ typedef struct { struct list_head remote_recipients; /**< remote recipients */ struct list_head local_recipients; /**< local recipients */ - enum notify_flags notify; /**< libESMTP notificiation flags */ + /** \name Delivery Status Notification (DSN) flags */ + /*@{*/ + enum ret_flags ret; /**< reporting options */ + char *envid; /**< envelope identifier */ + enum notify_flags notify; /**< notification options */ + /*@}*/ + /** 8bit-MIME transport */ + enum e8bitmime_body body; + /** \name buffering */ /*@{*/ char *buffer; @@ -53,6 +61,8 @@ void message_set_reverse_path(message_t *message, const char *address); void message_add_recipient(message_t *message, const char *address); +void message_set_envid(message_t *message, const char *address); + unsigned message_parse_headers(message_t *message); size_t message_read(message_t *message, char *ptr, size_t size); diff --git a/rfc822.c b/rfc822.c index 0035ad2..546f481 100644 --- a/rfc822.c +++ b/rfc822.c @@ -37,9 +37,9 @@ */ char *next_address(const char *hdr) { - static unsigned char address[BUFSIZ]; + static char address[BUFSIZ]; static int tp; - static const unsigned char *hp; + static const char *hp; static int state, oldstate; int parendepth = 0; @@ -68,14 +68,14 @@ char *next_address(const char *hdr) tp = 0; return (address); } - return((unsigned char *)NULL); + return(NULL); } else if (*hp == '\\') /* handle RFC822 escaping */ { if (state != INSIDE_PARENS) { address[NEXTTP()] = *hp++; /* take the escape */ - address[NEXTTP()] = *hp; /* take following unsigned char */ + address[NEXTTP()] = *hp; /* take following char */ } } else switch (state) diff --git a/sample.esmtprc b/sample.esmtprc index 8ff89f0..f44db65 100644 --- a/sample.esmtprc +++ b/sample.esmtprc @@ -5,25 +5,10 @@ # Set SMTP host and service (port) # hostname = localhost:25 -# -# This is specified in the format host.example.org[:service] with no whitespace -# surrounding the colon if service is specified. service may be a name from -# /etc/services or a decimal port number. If not specified the port defaults to -# 587. -# -# NOTE (from libESMTP documentation): The default port number is set to 587 -# since this is the port that should be used for mail submission, see RFC 2476. -# By choosing this default now, the API does not change behaviour unexpectedly -# in the future as use of the new standard becomes commonplace. The hostport -# nototion simplifies things for the application, the user can type -# localhost:smtp or localhost:25 where the application expects a host name. # Set the user name # username = "USERNAME" -# -# Note that the equal and quotes are optional but can be helpful in certain -# circumstances. # Set the password password = "PASSWORD" @@ -43,10 +28,10 @@ password = "PASSWORD" # Same as above but for a different identity which can be selected with the # '-f' flag. You can have as many you like. # -identity myself@somewhere.com - hostname smtp.somewhere.com:25 - username "myself" - password "secret" +identity = myself@somewhere.com + hostname = smtp.somewhere.com:25 + username = "myself" + password = "secret" #starttls = disabled # # NOTE: the default indentity settings aren't shared by the other identities. @@ -58,8 +43,5 @@ identity myself@somewhere.com # mda = "/usr/bin/procmail -d %T" # -# Some possible MDAs are -# - "/usr/bin/procmail -d %T" -# - "/usr/bin/deliver" -# - "/usr/sbin/sendmail -i -f %F %T" -# - "/usr/lib/mail.local %s" +# Some possible MDAs are "/usr/bin/procmail -d %T", "/usr/bin/deliver" or +# "/usr/lib/mail.local %T". diff --git a/smtp.c b/smtp.c index 3c3e4fa..033edaf 100644 --- a/smtp.c +++ b/smtp.c @@ -155,87 +155,101 @@ static const char * message_cb (void **buf, int *len, void *arg) static void event_cb (smtp_session_t session, int event_no, void *arg, ...) { - FILE *fp = arg; va_list ap; const char *mailbox; smtp_message_t message; smtp_recipient_t recipient; const smtp_status_t *status; - static int sizeticking = 0, sizeticker; - - if (event_no != SMTP_EV_MESSAGEDATA && sizeticking) - { - fputs("\n", fp); - sizeticking = 0; - } va_start (ap, arg); + switch (event_no) { - case SMTP_EV_CONNECT: - fputs("Connected to MTA\n", fp); - break; - - case SMTP_EV_MAILSTATUS: - mailbox = va_arg (ap, const char *); - message = va_arg (ap, smtp_message_t); - status = smtp_reverse_path_status (message); - fprintf (fp, "From %s: %d %s", mailbox, status->code, status->text); + case SMTP_EV_EXTNA_DSN: + fprintf(stderr, "Delivery Status Notification extension not supported by MTA\n"); break; - - case SMTP_EV_RCPTSTATUS: - mailbox = va_arg (ap, const char *); - recipient = va_arg (ap, smtp_recipient_t); - status = smtp_recipient_status (recipient); - fprintf (fp, "To %s: %d %s", mailbox, status->code, status->text); + case SMTP_EV_EXTNA_8BITMIME: + fprintf(stderr, "8bit-MIME extension not supported by MTA\n"); break; - - case SMTP_EV_MESSAGEDATA: - message = va_arg (ap, smtp_message_t); - if (!sizeticking) - { - fputs("Message data: ", fp); - sizeticking = 1; - sizeticker = SIZETICKER - 1; - } - sizeticker += va_arg (ap, int); - while (sizeticker >= SIZETICKER) - { - fputc('.', fp); - sizeticker -= SIZETICKER; - } + case SMTP_EV_EXTNA_STARTTLS: + fprintf(stderr, "StartTLS extension not supported by MTA\n"); break; + } + + if (verbose) + { + static int sizeticking = 0, sizeticker; - case SMTP_EV_MESSAGESENT: - message = va_arg (ap, smtp_message_t); - status = smtp_message_transfer_status (message); - fprintf (fp, "Message sent: %d %s", status->code, status->text); - break; - - case SMTP_EV_DISCONNECT: - fputs("Disconnected to MTA\n", fp); - break; - - default: - break; + if (event_no != SMTP_EV_MESSAGEDATA && sizeticking) + { + fputs("\n", stdout); + sizeticking = 0; + } + + switch (event_no) { + case SMTP_EV_CONNECT: + fputs("Connected to MTA\n", stdout); + break; + + case SMTP_EV_MAILSTATUS: + mailbox = va_arg (ap, const char *); + message = va_arg (ap, smtp_message_t); + status = smtp_reverse_path_status (message); + fprintf (stdout, "From %s: %d %s", mailbox, status->code, status->text); + break; + + case SMTP_EV_RCPTSTATUS: + mailbox = va_arg (ap, const char *); + recipient = va_arg (ap, smtp_recipient_t); + status = smtp_recipient_status (recipient); + fprintf (stdout, "To %s: %d %s", mailbox, status->code, status->text); + break; + + case SMTP_EV_MESSAGEDATA: + message = va_arg (ap, smtp_message_t); + if (!sizeticking) + { + fputs("Message data: ", stdout); + sizeticking = 1; + sizeticker = SIZETICKER - 1; + } + sizeticker += va_arg (ap, int); + while (sizeticker >= SIZETICKER) + { + fputc('.', stdout); + sizeticker -= SIZETICKER; + } + break; + + case SMTP_EV_MESSAGESENT: + message = va_arg (ap, smtp_message_t); + status = smtp_message_transfer_status (message); + fprintf (stdout, "Message sent: %d %s", status->code, status->text); + break; + + case SMTP_EV_DISCONNECT: + fputs("Disconnected to MTA\n", stdout); + break; + } } + va_end (ap); } static void monitor_cb (const char *buf, int buflen, int writing, void *arg) { - FILE *fp = arg; + assert(log_fp); if (writing == SMTP_CB_HEADERS) { - fputs ("H: ", fp); - fwrite (buf, 1, buflen, fp); + fputs ("H: ", log_fp); + fwrite (buf, 1, buflen, log_fp); return; } - fputs (writing ? "C: " : "S: ", fp); - fwrite (buf, 1, buflen, fp); + fputs (writing ? "C: " : "S: ", log_fp); + fwrite (buf, 1, buflen, log_fp); if (buf[buflen - 1] != '\n') - putc ('\n', fp); + putc ('\n', log_fp); } /** @@ -306,7 +320,6 @@ void smtp_send(message_t *msg) auth_context_t authctx; const smtp_status_t *status; struct sigaction sa; - int ret; identity_t *identity; struct list_head *ptr; @@ -314,19 +327,21 @@ void smtp_send(message_t *msg) * session. */ auth_client_init (); - session = smtp_create_session (); + if(!(session = smtp_create_session ())) + goto failure; /* Add a protocol monitor. */ if(log_fp) - smtp_set_monitorcb (session, monitor_cb, log_fp, 1); + if(!smtp_set_monitorcb (session, monitor_cb, NULL, 1)) + goto failure; /* Lookup the identity */ identity = identity_lookup(msg->reverse_path); assert(identity); /* Set the event callback. */ - if(verbose) - smtp_set_eventcb (session, event_cb, stdout); + if(!smtp_set_eventcb (session, event_cb, NULL)) + goto failure; /* NB. libESMTP sets timeouts as it progresses through the protocol. In * addition the remote server might close its socket on a timeout. @@ -341,10 +356,12 @@ void smtp_send(message_t *msg) * 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, identity->host ? identity->host : "localhost:25"); + if(!smtp_set_server (session, identity->host ? identity->host : "localhost:25")) + goto failure; /* Set the SMTP Starttls extension. */ - smtp_starttls_enable (session, identity->starttls); + if(!smtp_starttls_enable (session, identity->starttls)) + goto failure; /* Do what's needed at application level to use authentication. */ authctx = auth_create_context (); @@ -354,39 +371,57 @@ void smtp_send(message_t *msg) /* 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, identity); + if(!smtp_starttls_set_password_cb (tlsinteract, identity)) + goto failure; /* Now tell libESMTP it can use the SMTP AUTH extension. */ - smtp_auth_set_context (session, authctx); + if(!smtp_auth_set_context (session, authctx)) + goto failure; /* 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); + if(!smtp_option_require_all_recipients (session, 1)) + goto failure; /* Add a message to the SMTP session. */ - message = smtp_add_message (session); + if(!(message = smtp_add_message (session))) + goto failure; /* Set the reverse path for the mail envelope. (NULL is ok) */ - smtp_set_reverse_path (message, msg->reverse_path); + if(!smtp_set_reverse_path (message, msg->reverse_path)) + goto failure; /* Open the message file and set the callback to read it. */ - smtp_set_messagecb (message, message_cb, msg); + if(!smtp_set_messagecb (message, message_cb, msg)) + goto failure; + + /* DSN options */ + if(!smtp_dsn_set_ret(message, msg->ret)) + goto failure; + if(msg->envid) + if(!smtp_dsn_set_envid(message, msg->envid)) + goto failure; + + /* 8bit-MIME */ + if(!smtp_8bitmime_set_body(message, msg->body)) + goto failure; /* Add remaining program arguments as message recipients. */ list_for_each(ptr, &msg->remote_recipients) { recipient_t *entry = list_entry(ptr, recipient_t, list); - if(entry->address) - { - recipient = smtp_add_recipient (message, entry->address); - - /* Recipient options set here */ - if (msg->notify != Notify_NOTSET) - smtp_dsn_set_notify (recipient, msg->notify); - } + assert(entry->address); + + if(!(recipient = smtp_add_recipient (message, entry->address))) + goto failure; + + /* Recipient options set here */ + if (msg->notify != Notify_NOTSET) + if(!smtp_dsn_set_notify (recipient, msg->notify)) + goto failure; } /* Initiate a connection to the SMTP server and transfer the message. */ @@ -397,23 +432,21 @@ void smtp_send(message_t *msg) fprintf (stderr, "SMTP server problem %s\n", smtp_strerror (smtp_errno (), buf, sizeof(buf))); - ret = EX_UNAVAILABLE; + exit(EX_UNAVAILABLE); } - else + + + /* Report on the success or otherwise of the mail transfer. */ + if(!(status = smtp_message_transfer_status (message))) + goto failure; + if (status->code / 100 != 2) { - /* Report on the success or otherwise of the mail transfer. */ + /* Report on the failure of the mail transfer. */ status = smtp_message_transfer_status (message); - if (status->code / 100 == 2) - ret = EX_OK; - 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); + fprintf (stderr, "%d %s\n", status->code, status->text); + smtp_enumerate_recipients (message, print_recipient_status, NULL); - ret = EX_SOFTWARE; - } + exit(EX_SOFTWARE); } if (log_fp) @@ -423,8 +456,17 @@ void smtp_send(message_t *msg) auth_destroy_context (authctx); auth_client_exit (); - if (ret != EX_OK) - exit(ret); + return; + +failure: + { + char buf[128]; + + fprintf (stderr, "%s\n", + smtp_strerror (smtp_errno (), buf, sizeof(buf))); + + exit(EX_SOFTWARE); + } } /*@}*/ diff --git a/smtp.h b/smtp.h index 233c078..cc3fac8 100644 --- a/smtp.h +++ b/smtp.h @@ -22,12 +22,22 @@ */ typedef struct { struct list_head list; - char *address; - char *host; + + char *address; /**< reverse path address */ + + char *host; /**< hostname and service (port) */ + + /** \name Auth Extension */ + /*@{*/ char *user; char *pass; - enum starttls_option starttls; /**< it should default to Starttls_DISABLED */ + /*@}*/ + + /** \name StartTLS Extension */ + /*@{*/ + enum starttls_option starttls; char *certificate_passphrase; + /*@}*/ } identity_t; /** -- cgit v1.2.3