]> granicus.if.org Git - neomutt/blob - smtp.c
Fix mutt_write_mime_body() application/pgp-encrypted handling
[neomutt] / smtp.c
1 /**
2  * @file
3  * Send email to an SMTP server
4  *
5  * @authors
6  * Copyright (C) 2002 Michael R. Elkins <me@mutt.org>
7  * Copyright (C) 2005-2009 Brendan Cully <brendan@kublai.com>
8  * Copyright (C) 2019 Pietro Cerutti <gahr@gahr.ch>
9  *
10  * @copyright
11  * This program is free software: you can redistribute it and/or modify it under
12  * the terms of the GNU General Public License as published by the Free Software
13  * Foundation, either version 2 of the License, or (at your option) any later
14  * version.
15  *
16  * This program is distributed in the hope that it will be useful, but WITHOUT
17  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
19  * details.
20  *
21  * You should have received a copy of the GNU General Public License along with
22  * this program.  If not, see <http://www.gnu.org/licenses/>.
23  */
24
25 /**
26  * @page smtp Send email to an SMTP server
27  *
28  * Send email to an SMTP server
29  */
30
31 /* This file contains code for direct SMTP delivery of email messages. */
32
33 #include "config.h"
34 #include <netdb.h>
35 #include <netinet/in.h>
36 #include <stdbool.h>
37 #include <stdint.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
42 #include "mutt/mutt.h"
43 #include "address/lib.h"
44 #include "config/lib.h"
45 #include "email/lib.h"
46 #include "conn/conn.h"
47 #include "mutt.h"
48 #include "globals.h"
49 #include "mutt_account.h"
50 #include "mutt_socket.h"
51 #include "options.h"
52 #include "progress.h"
53 #include "sendlib.h"
54 #ifdef USE_SASL
55 #include <sasl/sasl.h>
56 #include <sasl/saslutil.h>
57 #endif
58
59 /* These Config Variables are only used in smtp.c */
60 struct Slist *C_SmtpAuthenticators; ///< Config: (smtp) List of allowed authentication methods
61
62 #define smtp_success(x) ((x) / 100 == 2)
63 #define SMTP_READY 334
64 #define SMTP_CONTINUE 354
65
66 #define SMTP_ERR_READ -2
67 #define SMTP_ERR_WRITE -3
68 #define SMTP_ERR_CODE -4
69
70 #define SMTP_PORT 25
71 #define SMTPS_PORT 465
72
73 #define SMTP_AUTH_SUCCESS 0
74 #define SMTP_AUTH_UNAVAIL 1
75 #define SMTP_AUTH_FAIL -1
76
77 // clang-format off
78 /**
79  * typedef SmtpCapFlags - SMTP server capabilities
80  */
81 typedef uint8_t SmtpCapFlags;           ///< Flags, e.g. #SMTP_CAP_STARTTLS
82 #define SMTP_CAP_NO_FLAGS            0  ///< No flags are set
83 #define SMTP_CAP_STARTTLS     (1 << 0) ///< Server supports STARTTLS command
84 #define SMTP_CAP_AUTH         (1 << 1) ///< Server supports AUTH command
85 #define SMTP_CAP_DSN          (1 << 2) ///< Server supports Delivery Status Notification
86 #define SMTP_CAP_EIGHTBITMIME (1 << 3) ///< Server supports 8-bit MIME content
87 #define SMTP_CAP_SMTPUTF8     (1 << 4) ///< Server accepts UTF-8 strings
88
89 #define SMTP_CAP_ALL         ((1 << 5) - 1)
90 // clang-format on
91
92 static char *AuthMechs = NULL;
93 static SmtpCapFlags Capabilities;
94
95 /**
96  * valid_smtp_code - Is the is a valid SMTP return code?
97  * @param[in]  buf String to check
98  * @param[in]  buflen Length of string
99  * @param[out] n   Numeric value of code
100  * @retval true Valid number
101  */
102 static bool valid_smtp_code(char *buf, size_t buflen, int *n)
103 {
104   char code[4];
105
106   if (buflen < 4)
107     return false;
108   code[0] = buf[0];
109   code[1] = buf[1];
110   code[2] = buf[2];
111   code[3] = '\0';
112   if (mutt_str_atoi(code, n) < 0)
113     return false;
114   return true;
115 }
116
117 /**
118  * smtp_get_resp - Read a command response from the SMTP server
119  * @param conn SMTP connection
120  * @retval  0 Success (2xx code) or continue (354 code)
121  * @retval -1 Write error, or any other response code
122  */
123 static int smtp_get_resp(struct Connection *conn)
124 {
125   int n;
126   char buf[1024];
127
128   do
129   {
130     n = mutt_socket_readln(buf, sizeof(buf), conn);
131     if (n < 4)
132     {
133       /* read error, or no response code */
134       return SMTP_ERR_READ;
135     }
136     const char *s = buf + 4; /* Skip the response code and the space/dash */
137     size_t plen;
138
139     if (mutt_str_startswith(s, "8BITMIME", CASE_IGNORE))
140       Capabilities |= SMTP_CAP_EIGHTBITMIME;
141     else if ((plen = mutt_str_startswith(s, "AUTH ", CASE_IGNORE)))
142     {
143       Capabilities |= SMTP_CAP_AUTH;
144       FREE(&AuthMechs);
145       AuthMechs = mutt_str_strdup(s + plen);
146     }
147     else if (mutt_str_startswith(s, "DSN", CASE_IGNORE))
148       Capabilities |= SMTP_CAP_DSN;
149     else if (mutt_str_startswith(s, "STARTTLS", CASE_IGNORE))
150       Capabilities |= SMTP_CAP_STARTTLS;
151     else if (mutt_str_startswith(s, "SMTPUTF8", CASE_IGNORE))
152       Capabilities |= SMTP_CAP_SMTPUTF8;
153
154     if (!valid_smtp_code(buf, n, &n))
155       return SMTP_ERR_CODE;
156
157   } while (buf[3] == '-');
158
159   if (smtp_success(n) || (n == SMTP_CONTINUE))
160     return 0;
161
162   mutt_error(_("SMTP session failed: %s"), buf);
163   return -1;
164 }
165
166 /**
167  * smtp_rcpt_to - Set the recipient to an Address
168  * @param conn Server Connection
169  * @param al   AddressList to use
170  * @retval  0 Success
171  * @retval <0 Error, e.g. #SMTP_ERR_WRITE
172  */
173 static int smtp_rcpt_to(struct Connection *conn, const struct AddressList *al)
174 {
175   if (!al)
176     return 0;
177
178   struct Address *a = NULL;
179   TAILQ_FOREACH(a, al, entries)
180   {
181     /* weed out group mailboxes, since those are for display only */
182     if (!a->mailbox || a->group)
183     {
184       continue;
185     }
186     char buf[1024];
187     if ((Capabilities & SMTP_CAP_DSN) && C_DsnNotify)
188       snprintf(buf, sizeof(buf), "RCPT TO:<%s> NOTIFY=%s\r\n", a->mailbox, C_DsnNotify);
189     else
190       snprintf(buf, sizeof(buf), "RCPT TO:<%s>\r\n", a->mailbox);
191     if (mutt_socket_send(conn, buf) == -1)
192       return SMTP_ERR_WRITE;
193     int rc = smtp_get_resp(conn);
194     if (rc != 0)
195       return rc;
196   }
197
198   return 0;
199 }
200
201 /**
202  * smtp_data - Send data to an SMTP server
203  * @param conn    SMTP Connection
204  * @param msgfile Filename containing data
205  * @retval  0 Success
206  * @retval <0 Error, e.g. #SMTP_ERR_WRITE
207  */
208 static int smtp_data(struct Connection *conn, const char *msgfile)
209 {
210   char buf[1024];
211   struct Progress progress;
212   struct stat st;
213   int rc, term = 0;
214   size_t buflen = 0;
215
216   FILE *fp = fopen(msgfile, "r");
217   if (!fp)
218   {
219     mutt_error(_("SMTP session failed: unable to open %s"), msgfile);
220     return -1;
221   }
222   stat(msgfile, &st);
223   unlink(msgfile);
224   mutt_progress_init(&progress, _("Sending message..."), MUTT_PROGRESS_NET, st.st_size);
225
226   snprintf(buf, sizeof(buf), "DATA\r\n");
227   if (mutt_socket_send(conn, buf) == -1)
228   {
229     mutt_file_fclose(&fp);
230     return SMTP_ERR_WRITE;
231   }
232   rc = smtp_get_resp(conn);
233   if (rc != 0)
234   {
235     mutt_file_fclose(&fp);
236     return rc;
237   }
238
239   while (fgets(buf, sizeof(buf) - 1, fp))
240   {
241     buflen = mutt_str_strlen(buf);
242     term = buflen && buf[buflen - 1] == '\n';
243     if (term && ((buflen == 1) || (buf[buflen - 2] != '\r')))
244       snprintf(buf + buflen - 1, sizeof(buf) - buflen + 1, "\r\n");
245     if (buf[0] == '.')
246     {
247       if (mutt_socket_send_d(conn, ".", MUTT_SOCK_LOG_FULL) == -1)
248       {
249         mutt_file_fclose(&fp);
250         return SMTP_ERR_WRITE;
251       }
252     }
253     if (mutt_socket_send_d(conn, buf, MUTT_SOCK_LOG_FULL) == -1)
254     {
255       mutt_file_fclose(&fp);
256       return SMTP_ERR_WRITE;
257     }
258     mutt_progress_update(&progress, ftell(fp), -1);
259   }
260   if (!term && buflen && (mutt_socket_send_d(conn, "\r\n", MUTT_SOCK_LOG_FULL) == -1))
261   {
262     mutt_file_fclose(&fp);
263     return SMTP_ERR_WRITE;
264   }
265   mutt_file_fclose(&fp);
266
267   /* terminate the message body */
268   if (mutt_socket_send(conn, ".\r\n") == -1)
269     return SMTP_ERR_WRITE;
270
271   rc = smtp_get_resp(conn);
272   if (rc != 0)
273     return rc;
274
275   return 0;
276 }
277
278 /**
279  * address_uses_unicode - Do any addresses use Unicode
280  * @param a Address list to check
281  * @retval true if any of the string of addresses use 8-bit characters
282  */
283 static bool address_uses_unicode(const char *a)
284 {
285   if (!a)
286     return false;
287
288   while (*a)
289   {
290     if ((unsigned char) *a & (1 << 7))
291       return true;
292     a++;
293   }
294
295   return false;
296 }
297
298 /**
299  * addresses_use_unicode - Do any of a list of addresses use Unicode
300  * @param al Address list to check
301  * @retval true if any use 8-bit characters
302  */
303 static bool addresses_use_unicode(const struct AddressList *al)
304 {
305   if (!al)
306   {
307     return false;
308   }
309
310   struct Address *a = NULL;
311   TAILQ_FOREACH(a, al, entries)
312   {
313     if (a->mailbox && !a->group && address_uses_unicode(a->mailbox))
314       return true;
315   }
316   return false;
317 }
318
319 /**
320  * smtp_fill_account - Create ConnAccount object from SMTP Url
321  * @param account ConnAccount to populate
322  * @retval  0 Success
323  * @retval -1 Error
324  */
325 static int smtp_fill_account(struct ConnAccount *account)
326 {
327   account->flags = 0;
328   account->port = 0;
329   account->type = MUTT_ACCT_TYPE_SMTP;
330
331   struct Url *url = url_parse(C_SmtpUrl);
332   if (!url || ((url->scheme != U_SMTP) && (url->scheme != U_SMTPS)) ||
333       !url->host || (mutt_account_fromurl(account, url) < 0))
334   {
335     url_free(&url);
336     mutt_error(_("Invalid SMTP URL: %s"), C_SmtpUrl);
337     return -1;
338   }
339
340   if (url->scheme == U_SMTPS)
341     account->flags |= MUTT_ACCT_SSL;
342
343   if (account->port == 0)
344   {
345     if (account->flags & MUTT_ACCT_SSL)
346       account->port = SMTPS_PORT;
347     else
348     {
349       static unsigned short SmtpPort = 0;
350       if (SmtpPort == 0)
351       {
352         struct servent *service = getservbyname("smtp", "tcp");
353         if (service)
354           SmtpPort = ntohs(service->s_port);
355         else
356           SmtpPort = SMTP_PORT;
357         mutt_debug(LL_DEBUG3, "Using default SMTP port %d\n", SmtpPort);
358       }
359       account->port = SmtpPort;
360     }
361   }
362
363   url_free(&url);
364   return 0;
365 }
366
367 /**
368  * smtp_helo - Say hello to an SMTP Server
369  * @param conn  SMTP Connection
370  * @param esmtp If true, use ESMTP
371  * @retval  0 Success
372  * @retval <0 Error, e.g. #SMTP_ERR_WRITE
373  */
374 static int smtp_helo(struct Connection *conn, bool esmtp)
375 {
376   Capabilities = 0;
377
378   if (!esmtp)
379   {
380     /* if TLS or AUTH are requested, use EHLO */
381     if (conn->account.flags & MUTT_ACCT_USER)
382       esmtp = true;
383 #ifdef USE_SSL
384     if (C_SslForceTls || (C_SslStarttls != MUTT_NO))
385       esmtp = true;
386 #endif
387   }
388
389   const char *fqdn = mutt_fqdn(false);
390   if (!fqdn)
391     fqdn = NONULL(ShortHostname);
392
393   char buf[1024];
394   snprintf(buf, sizeof(buf), "%s %s\r\n", esmtp ? "EHLO" : "HELO", fqdn);
395   /* XXX there should probably be a wrapper in mutt_socket.c that
396    * repeatedly calls conn->write until all data is sent.  This
397    * currently doesn't check for a short write.  */
398   if (mutt_socket_send(conn, buf) == -1)
399     return SMTP_ERR_WRITE;
400   return smtp_get_resp(conn);
401 }
402
403 #ifdef USE_SASL
404 /**
405  * smtp_auth_sasl - Authenticate using SASL
406  * @param conn     SMTP Connection
407  * @param mechlist List of mechanisms to use
408  * @retval  0 Success
409  * @retval <0 Error, e.g. #SMTP_AUTH_FAIL
410  */
411 static int smtp_auth_sasl(struct Connection *conn, const char *mechlist)
412 {
413   sasl_conn_t *saslconn = NULL;
414   sasl_interact_t *interaction = NULL;
415   const char *mech = NULL;
416   const char *data = NULL;
417   unsigned int len;
418   char *buf = NULL;
419   size_t bufsize = 0;
420   int rc, saslrc;
421
422   if (mutt_sasl_client_new(conn, &saslconn) < 0)
423     return SMTP_AUTH_FAIL;
424
425   do
426   {
427     rc = sasl_client_start(saslconn, mechlist, &interaction, &data, &len, &mech);
428     if (rc == SASL_INTERACT)
429       mutt_sasl_interact(interaction);
430   } while (rc == SASL_INTERACT);
431
432   if ((rc != SASL_OK) && (rc != SASL_CONTINUE))
433   {
434     mutt_debug(LL_DEBUG2, "%s unavailable\n", mech);
435     sasl_dispose(&saslconn);
436     return SMTP_AUTH_UNAVAIL;
437   }
438
439   if (!OptNoCurses)
440     mutt_message(_("Authenticating (%s)..."), mech);
441
442   bufsize = MAX((len * 2), 1024);
443   buf = mutt_mem_malloc(bufsize);
444
445   snprintf(buf, bufsize, "AUTH %s", mech);
446   if (len)
447   {
448     mutt_str_strcat(buf, bufsize, " ");
449     if (sasl_encode64(data, len, buf + mutt_str_strlen(buf),
450                       bufsize - mutt_str_strlen(buf), &len) != SASL_OK)
451     {
452       mutt_debug(LL_DEBUG1, "#1 error base64-encoding client response\n");
453       goto fail;
454     }
455   }
456   mutt_str_strcat(buf, bufsize, "\r\n");
457
458   do
459   {
460     if (mutt_socket_send(conn, buf) < 0)
461       goto fail;
462     rc = mutt_socket_readln_d(buf, bufsize, conn, MUTT_SOCK_LOG_FULL);
463     if (rc < 0)
464       goto fail;
465     if (!valid_smtp_code(buf, rc, &rc))
466       goto fail;
467
468     if (rc != SMTP_READY)
469       break;
470
471     if (sasl_decode64(buf + 4, strlen(buf + 4), buf, bufsize - 1, &len) != SASL_OK)
472     {
473       mutt_debug(LL_DEBUG1, "error base64-decoding server response\n");
474       goto fail;
475     }
476
477     do
478     {
479       saslrc = sasl_client_step(saslconn, buf, len, &interaction, &data, &len);
480       if (saslrc == SASL_INTERACT)
481         mutt_sasl_interact(interaction);
482     } while (saslrc == SASL_INTERACT);
483
484     if (len)
485     {
486       if ((len * 2) > bufsize)
487       {
488         bufsize = len * 2;
489         mutt_mem_realloc(&buf, bufsize);
490       }
491       if (sasl_encode64(data, len, buf, bufsize, &len) != SASL_OK)
492       {
493         mutt_debug(LL_DEBUG1, "#2 error base64-encoding client response\n");
494         goto fail;
495       }
496     }
497     mutt_str_strfcpy(buf + len, "\r\n", bufsize - len);
498   } while (rc == SMTP_READY && saslrc != SASL_FAIL);
499
500   if (smtp_success(rc))
501   {
502     mutt_sasl_setup_conn(conn, saslconn);
503     FREE(&buf);
504     return SMTP_AUTH_SUCCESS;
505   }
506
507 fail:
508   sasl_dispose(&saslconn);
509   FREE(&buf);
510   return SMTP_AUTH_FAIL;
511 }
512 #endif
513
514 /**
515  * smtp_auth_oauth - Authenticate an SMTP connection using OAUTHBEARER
516  * @param conn Connection info
517  * @retval num Result, e.g. #SMTP_AUTH_SUCCESS
518  */
519 static int smtp_auth_oauth(struct Connection *conn)
520 {
521   mutt_message(_("Authenticating (OAUTHBEARER)..."));
522
523   /* We get the access token from the smtp_oauth_refresh_command */
524   char *oauthbearer = mutt_account_getoauthbearer(&conn->account);
525   if (!oauthbearer)
526     return SMTP_AUTH_FAIL;
527
528   size_t ilen = strlen(oauthbearer) + 30;
529   char *ibuf = mutt_mem_malloc(ilen);
530   snprintf(ibuf, ilen, "AUTH OAUTHBEARER %s\r\n", oauthbearer);
531
532   int rc = mutt_socket_send(conn, ibuf);
533   FREE(&oauthbearer);
534   FREE(&ibuf);
535
536   if (rc == -1)
537     return SMTP_AUTH_FAIL;
538   if (smtp_get_resp(conn) != 0)
539     return SMTP_AUTH_FAIL;
540
541   return SMTP_AUTH_SUCCESS;
542 }
543
544 /**
545  * smtp_auth_plain - Authenticate using plain text
546  * @param conn SMTP Connection
547  * @retval  0 Success
548  * @retval <0 Error, e.g. #SMTP_AUTH_FAIL
549  */
550 static int smtp_auth_plain(struct Connection *conn)
551 {
552   char buf[1024];
553
554   /* Get username and password. Bail out of any can't be retrieved. */
555   if ((mutt_account_getuser(&conn->account) < 0) ||
556       (mutt_account_getpass(&conn->account) < 0))
557   {
558     goto error;
559   }
560
561   /* Build the initial client response. */
562   size_t len = mutt_sasl_plain_msg(buf, sizeof(buf), "AUTH PLAIN", conn->account.user,
563                                    conn->account.user, conn->account.pass);
564
565   /* Terminate as per SMTP protocol. Bail out if there's no room left. */
566   if (snprintf(buf + len, sizeof(buf) - len, "\r\n") != 2)
567   {
568     goto error;
569   }
570
571   /* Send request, receive response (with a check for OK code). */
572   if ((mutt_socket_send(conn, buf) < 0) || smtp_get_resp(conn))
573   {
574     goto error;
575   }
576
577   /* If we got here, auth was successful. */
578   return 0;
579
580 error:
581   mutt_error(_("SASL authentication failed"));
582   return -1;
583 }
584
585 /**
586  * smtp_auth - Authenticate to an SMTP server
587  * @param conn SMTP Connection
588  * @retval  0 Success
589  * @retval <0 Error, e.g. #SMTP_AUTH_FAIL
590  */
591 static int smtp_auth(struct Connection *conn)
592 {
593   int r = SMTP_AUTH_UNAVAIL;
594
595   if (C_SmtpAuthenticators)
596   {
597     struct ListNode *np = NULL;
598     STAILQ_FOREACH(np, &C_SmtpAuthenticators->head, entries)
599     {
600       mutt_debug(LL_DEBUG2, "Trying method %s\n", np->data);
601
602       if (strcmp(np->data, "oauthbearer") == 0)
603       {
604         r = smtp_auth_oauth(conn);
605       }
606       else if (strcmp(np->data, "plain") == 0)
607       {
608         r = smtp_auth_plain(conn);
609       }
610       else
611       {
612 #ifdef USE_SASL
613         r = smtp_auth_sasl(conn, np->data);
614 #else
615         mutt_error(_("SMTP authentication method %s requires SASL"), np->data);
616         continue;
617 #endif
618       }
619
620       if ((r == SMTP_AUTH_FAIL) && (C_SmtpAuthenticators->count > 1))
621       {
622         mutt_error(_("%s authentication failed, trying next method"), np->data);
623       }
624       else if (r != SMTP_AUTH_UNAVAIL)
625         break;
626     }
627   }
628   else
629   {
630 #ifdef USE_SASL
631     r = smtp_auth_sasl(conn, AuthMechs);
632 #else
633     mutt_error(_("SMTP authentication requires SASL"));
634     r = SMTP_AUTH_UNAVAIL;
635 #endif
636   }
637
638   if (r != SMTP_AUTH_SUCCESS)
639     mutt_account_unsetpass(&conn->account);
640
641   if (r == SMTP_AUTH_FAIL)
642   {
643     mutt_error(_("SASL authentication failed"));
644   }
645   else if (r == SMTP_AUTH_UNAVAIL)
646   {
647     mutt_error(_("No authenticators available"));
648   }
649
650   return (r == SMTP_AUTH_SUCCESS) ? 0 : -1;
651 }
652
653 /**
654  * smtp_open - Open an SMTP Connection
655  * @param conn  SMTP Connection
656  * @param esmtp If true, use ESMTP
657  * @retval  0 Success
658  * @retval -1 Error
659  */
660 static int smtp_open(struct Connection *conn, bool esmtp)
661 {
662   int rc;
663
664   if (mutt_socket_open(conn))
665     return -1;
666
667   /* get greeting string */
668   rc = smtp_get_resp(conn);
669   if (rc != 0)
670     return rc;
671
672   rc = smtp_helo(conn, esmtp);
673   if (rc != 0)
674     return rc;
675
676 #ifdef USE_SSL
677   enum QuadOption ans = MUTT_NO;
678   if (conn->ssf)
679     ans = MUTT_NO;
680   else if (C_SslForceTls)
681     ans = MUTT_YES;
682   else if ((Capabilities & SMTP_CAP_STARTTLS) &&
683            ((ans = query_quadoption(C_SslStarttls,
684                                     _("Secure connection with TLS?"))) == MUTT_ABORT))
685   {
686     return -1;
687   }
688
689   if (ans == MUTT_YES)
690   {
691     if (mutt_socket_send(conn, "STARTTLS\r\n") < 0)
692       return SMTP_ERR_WRITE;
693     rc = smtp_get_resp(conn);
694     if (rc != 0)
695       return rc;
696
697     if (mutt_ssl_starttls(conn))
698     {
699       mutt_error(_("Could not negotiate TLS connection"));
700       return -1;
701     }
702
703     /* re-EHLO to get authentication mechanisms */
704     rc = smtp_helo(conn, esmtp);
705     if (rc != 0)
706       return rc;
707   }
708 #endif
709
710   if (conn->account.flags & MUTT_ACCT_USER)
711   {
712     if (!(Capabilities & SMTP_CAP_AUTH))
713     {
714       mutt_error(_("SMTP server does not support authentication"));
715       return -1;
716     }
717
718     return smtp_auth(conn);
719   }
720
721   return 0;
722 }
723
724 /**
725  * mutt_smtp_send - Send a message using SMTP
726  * @param from     From Address
727  * @param to       To Address
728  * @param cc       Cc Address
729  * @param bcc      Bcc Address
730  * @param msgfile  Message to send to the server
731  * @param eightbit If true, try for an 8-bit friendly connection
732  * @retval  0 Success
733  * @retval -1 Error
734  */
735 int mutt_smtp_send(const struct AddressList *from, const struct AddressList *to,
736                    const struct AddressList *cc, const struct AddressList *bcc,
737                    const char *msgfile, bool eightbit)
738 {
739   struct Connection *conn = NULL;
740   struct ConnAccount account;
741   const char *envfrom = NULL;
742   char buf[1024];
743   int rc = -1;
744
745   /* it might be better to synthesize an envelope from from user and host
746    * but this condition is most likely arrived at accidentally */
747   if (C_EnvelopeFromAddress)
748     envfrom = C_EnvelopeFromAddress->mailbox;
749   else if (from && !TAILQ_EMPTY(from))
750     envfrom = TAILQ_FIRST(from)->mailbox;
751   else
752   {
753     mutt_error(_("No from address given"));
754     return -1;
755   }
756
757   if (smtp_fill_account(&account) < 0)
758     return rc;
759
760   conn = mutt_conn_find(NULL, &account);
761   if (!conn)
762     return -1;
763
764   do
765   {
766     /* send our greeting */
767     rc = smtp_open(conn, eightbit);
768     if (rc != 0)
769       break;
770     FREE(&AuthMechs);
771
772     /* send the sender's address */
773     int len = snprintf(buf, sizeof(buf), "MAIL FROM:<%s>", envfrom);
774     if (eightbit && (Capabilities & SMTP_CAP_EIGHTBITMIME))
775     {
776       mutt_str_strncat(buf, sizeof(buf), " BODY=8BITMIME", 15);
777       len += 14;
778     }
779     if (C_DsnReturn && (Capabilities & SMTP_CAP_DSN))
780       len += snprintf(buf + len, sizeof(buf) - len, " RET=%s", C_DsnReturn);
781     if ((Capabilities & SMTP_CAP_SMTPUTF8) &&
782         (address_uses_unicode(envfrom) || addresses_use_unicode(to) ||
783          addresses_use_unicode(cc) || addresses_use_unicode(bcc)))
784     {
785       snprintf(buf + len, sizeof(buf) - len, " SMTPUTF8");
786     }
787     mutt_str_strncat(buf, sizeof(buf), "\r\n", 3);
788     if (mutt_socket_send(conn, buf) == -1)
789     {
790       rc = SMTP_ERR_WRITE;
791       break;
792     }
793     rc = smtp_get_resp(conn);
794     if (rc != 0)
795       break;
796
797     /* send the recipient list */
798     if ((rc = smtp_rcpt_to(conn, to)) || (rc = smtp_rcpt_to(conn, cc)) ||
799         (rc = smtp_rcpt_to(conn, bcc)))
800     {
801       break;
802     }
803
804     /* send the message data */
805     rc = smtp_data(conn, msgfile);
806     if (rc != 0)
807       break;
808
809     mutt_socket_send(conn, "QUIT\r\n");
810
811     rc = 0;
812   } while (false);
813
814   mutt_socket_close(conn);
815   FREE(&conn);
816
817   if (rc == SMTP_ERR_READ)
818     mutt_error(_("SMTP session failed: read error"));
819   else if (rc == SMTP_ERR_WRITE)
820     mutt_error(_("SMTP session failed: write error"));
821   else if (rc == SMTP_ERR_CODE)
822     mutt_error(_("Invalid server response"));
823
824   return rc;
825 }