email: Added support for cancelling DIGEST-MD5 authentication
[platform/upstream/curl.git] / lib / smtp.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at http://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * RFC1870 SMTP Service Extension for Message Size
22  * RFC2195 CRAM-MD5 authentication
23  * RFC2831 DIGEST-MD5 authentication
24  * RFC3207 SMTP over TLS
25  * RFC4422 Simple Authentication and Security Layer (SASL)
26  * RFC4616 PLAIN authentication
27  * RFC4954 SMTP Authentication
28  * RFC5321 SMTP protocol
29  * RFC6749 OAuth 2.0 Authorization Framework
30  * Draft   SMTP URL Interface   <draft-earhart-url-smtp-00.txt>
31  * Draft   LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
32  *
33  ***************************************************************************/
34
35 #include "curl_setup.h"
36
37 #ifndef CURL_DISABLE_SMTP
38
39 #ifdef HAVE_NETINET_IN_H
40 #include <netinet/in.h>
41 #endif
42 #ifdef HAVE_ARPA_INET_H
43 #include <arpa/inet.h>
44 #endif
45 #ifdef HAVE_UTSNAME_H
46 #include <sys/utsname.h>
47 #endif
48 #ifdef HAVE_NETDB_H
49 #include <netdb.h>
50 #endif
51 #ifdef __VMS
52 #include <in.h>
53 #include <inet.h>
54 #endif
55
56 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
57 #undef in_addr_t
58 #define in_addr_t unsigned long
59 #endif
60
61 #include <curl/curl.h>
62 #include "urldata.h"
63 #include "sendf.h"
64 #include "if2ip.h"
65 #include "hostip.h"
66 #include "progress.h"
67 #include "transfer.h"
68 #include "escape.h"
69 #include "http.h" /* for HTTP proxy tunnel stuff */
70 #include "socks.h"
71 #include "smtp.h"
72
73 #include "strtoofft.h"
74 #include "strequal.h"
75 #include "sslgen.h"
76 #include "connect.h"
77 #include "strerror.h"
78 #include "select.h"
79 #include "multiif.h"
80 #include "url.h"
81 #include "rawstr.h"
82 #include "curl_gethostname.h"
83 #include "curl_sasl.h"
84 #include "warnless.h"
85
86 #define _MPRINTF_REPLACE /* use our functions only */
87 #include <curl/mprintf.h>
88
89 #include "curl_memory.h"
90 /* The last #include file should be: */
91 #include "memdebug.h"
92
93 /* Local API functions */
94 static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
95 static CURLcode smtp_do(struct connectdata *conn, bool *done);
96 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
97                           bool premature);
98 static CURLcode smtp_connect(struct connectdata *conn, bool *done);
99 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead);
100 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
101 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
102                         int numsocks);
103 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done);
104 static CURLcode smtp_setup_connection(struct connectdata *conn);
105 static CURLcode smtp_parse_url_options(struct connectdata *conn);
106 static CURLcode smtp_parse_url_path(struct connectdata *conn);
107
108 /*
109  * SMTP protocol handler.
110  */
111
112 const struct Curl_handler Curl_handler_smtp = {
113   "SMTP",                           /* scheme */
114   smtp_setup_connection,            /* setup_connection */
115   smtp_do,                          /* do_it */
116   smtp_done,                        /* done */
117   ZERO_NULL,                        /* do_more */
118   smtp_connect,                     /* connect_it */
119   smtp_multi_statemach,             /* connecting */
120   smtp_doing,                       /* doing */
121   smtp_getsock,                     /* proto_getsock */
122   smtp_getsock,                     /* doing_getsock */
123   ZERO_NULL,                        /* domore_getsock */
124   ZERO_NULL,                        /* perform_getsock */
125   smtp_disconnect,                  /* disconnect */
126   ZERO_NULL,                        /* readwrite */
127   PORT_SMTP,                        /* defport */
128   CURLPROTO_SMTP,                   /* protocol */
129   PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
130 };
131
132 #ifdef USE_SSL
133 /*
134  * SMTPS protocol handler.
135  */
136
137 const struct Curl_handler Curl_handler_smtps = {
138   "SMTPS",                          /* scheme */
139   smtp_setup_connection,            /* setup_connection */
140   smtp_do,                          /* do_it */
141   smtp_done,                        /* done */
142   ZERO_NULL,                        /* do_more */
143   smtp_connect,                     /* connect_it */
144   smtp_multi_statemach,             /* connecting */
145   smtp_doing,                       /* doing */
146   smtp_getsock,                     /* proto_getsock */
147   smtp_getsock,                     /* doing_getsock */
148   ZERO_NULL,                        /* domore_getsock */
149   ZERO_NULL,                        /* perform_getsock */
150   smtp_disconnect,                  /* disconnect */
151   ZERO_NULL,                        /* readwrite */
152   PORT_SMTPS,                       /* defport */
153   CURLPROTO_SMTP | CURLPROTO_SMTPS, /* protocol */
154   PROTOPT_CLOSEACTION | PROTOPT_SSL
155   | PROTOPT_NOURLQUERY              /* flags */
156 };
157 #endif
158
159 #ifndef CURL_DISABLE_HTTP
160 /*
161  * HTTP-proxyed SMTP protocol handler.
162  */
163
164 static const struct Curl_handler Curl_handler_smtp_proxy = {
165   "SMTP",                               /* scheme */
166   Curl_http_setup_conn,                 /* setup_connection */
167   Curl_http,                            /* do_it */
168   Curl_http_done,                       /* done */
169   ZERO_NULL,                            /* do_more */
170   ZERO_NULL,                            /* connect_it */
171   ZERO_NULL,                            /* connecting */
172   ZERO_NULL,                            /* doing */
173   ZERO_NULL,                            /* proto_getsock */
174   ZERO_NULL,                            /* doing_getsock */
175   ZERO_NULL,                            /* domore_getsock */
176   ZERO_NULL,                            /* perform_getsock */
177   ZERO_NULL,                            /* disconnect */
178   ZERO_NULL,                            /* readwrite */
179   PORT_SMTP,                            /* defport */
180   CURLPROTO_HTTP,                       /* protocol */
181   PROTOPT_NONE                          /* flags */
182 };
183
184 #ifdef USE_SSL
185 /*
186  * HTTP-proxyed SMTPS protocol handler.
187  */
188
189 static const struct Curl_handler Curl_handler_smtps_proxy = {
190   "SMTPS",                              /* scheme */
191   Curl_http_setup_conn,                 /* setup_connection */
192   Curl_http,                            /* do_it */
193   Curl_http_done,                       /* done */
194   ZERO_NULL,                            /* do_more */
195   ZERO_NULL,                            /* connect_it */
196   ZERO_NULL,                            /* connecting */
197   ZERO_NULL,                            /* doing */
198   ZERO_NULL,                            /* proto_getsock */
199   ZERO_NULL,                            /* doing_getsock */
200   ZERO_NULL,                            /* domore_getsock */
201   ZERO_NULL,                            /* perform_getsock */
202   ZERO_NULL,                            /* disconnect */
203   ZERO_NULL,                            /* readwrite */
204   PORT_SMTPS,                           /* defport */
205   CURLPROTO_HTTP,                       /* protocol */
206   PROTOPT_NONE                          /* flags */
207 };
208 #endif
209 #endif
210
211 #ifdef USE_SSL
212 static void smtp_to_smtps(struct connectdata *conn)
213 {
214   conn->handler = &Curl_handler_smtps;
215 }
216 #else
217 #define smtp_to_smtps(x) Curl_nop_stmt
218 #endif
219
220 /***********************************************************************
221  *
222  * smtp_endofresp()
223  *
224  * Checks for an ending SMTP status code at the start of the given string, but
225  * also detects various capabilities from the EHLO response including the
226  * supported authentication mechanisms.
227  */
228 static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len,
229                            int *resp)
230 {
231   struct smtp_conn *smtpc = &conn->proto.smtpc;
232   bool result = FALSE;
233   size_t wordlen;
234
235   if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
236     return FALSE;       /* Nothing for us */
237
238   /* Do we have a command response? This should be the response code followed
239      by a space and optionally some text as per RFC-5321 and as outlined in
240      Section 4. Examples of RFC-4954 but some e-mail servers ignore this and
241      only send the response code instead. */
242   result = (line[3] == ' ' || len == 5) ? TRUE : FALSE;
243   if(result)
244     *resp = curlx_sltosi(strtol(line, NULL, 10));
245
246   /* Are we processing EHLO command data? */
247   if(smtpc->state == SMTP_EHLO && (!result || (result && *resp/100 == 2))) {
248     line += 4;
249     len -= 4;
250
251     /* Does the server support the STARTTLS capability? */
252     if(len >= 8 && !memcmp(line, "STARTTLS", 8))
253       smtpc->tls_supported = TRUE;
254
255     /* Does the server support the SIZE capability? */
256     else if(len >= 4 && !memcmp(line, "SIZE", 4))
257       smtpc->size_supported = TRUE;
258
259     /* Do we have the authentication mechanism list? */
260     else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
261       line += 5;
262       len -= 5;
263
264       /* Loop through the data line */
265       for(;;) {
266         while(len &&
267               (*line == ' ' || *line == '\t' ||
268                *line == '\r' || *line == '\n')) {
269
270           line++;
271           len--;
272         }
273
274         if(!len)
275           break;
276
277         /* Extract the word */
278         for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
279               line[wordlen] != '\t' && line[wordlen] != '\r' &&
280               line[wordlen] != '\n';)
281           wordlen++;
282
283         /* Test the word for a matching authentication mechanism */
284         if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN))
285           smtpc->authmechs |= SASL_MECH_LOGIN;
286         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN))
287           smtpc->authmechs |= SASL_MECH_PLAIN;
288         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5))
289           smtpc->authmechs |= SASL_MECH_CRAM_MD5;
290         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5))
291           smtpc->authmechs |= SASL_MECH_DIGEST_MD5;
292         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI))
293           smtpc->authmechs |= SASL_MECH_GSSAPI;
294         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL))
295           smtpc->authmechs |= SASL_MECH_EXTERNAL;
296         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM))
297           smtpc->authmechs |= SASL_MECH_NTLM;
298         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2))
299           smtpc->authmechs |= SASL_MECH_XOAUTH2;
300
301         line += wordlen;
302         len -= wordlen;
303       }
304     }
305   }
306
307   return result;
308 }
309
310 /***********************************************************************
311  *
312  * smtp_get_message()
313  *
314  * Gets the authentication message from the response buffer.
315  */
316 static void smtp_get_message(char *buffer, char** outptr)
317 {
318   size_t len = 0;
319   char* message = NULL;
320
321   /* Find the start of the message */
322   for(message = buffer + 4; *message == ' ' || *message == '\t'; message++)
323     ;
324
325   /* Find the end of the message */
326   for(len = strlen(message); len--;)
327     if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
328         message[len] != '\t')
329       break;
330
331   /* Terminate the challenge */
332   if(++len) {
333     message[len] = '\0';
334   }
335
336   *outptr = message;
337 }
338
339 /***********************************************************************
340  *
341  * state()
342  *
343  * This is the ONLY way to change SMTP state!
344  */
345 static void state(struct connectdata *conn, smtpstate newstate)
346 {
347   struct smtp_conn *smtpc = &conn->proto.smtpc;
348 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
349   /* for debug purposes */
350   static const char * const names[] = {
351     "STOP",
352     "SERVERGREET",
353     "EHLO",
354     "HELO",
355     "STARTTLS",
356     "UPGRADETLS",
357     "AUTH_PLAIN",
358     "AUTH_LOGIN",
359     "AUTH_LOGIN_PASSWD",
360     "AUTH_CRAMMD5",
361     "AUTH_DIGESTMD5",
362     "AUTH_DIGESTMD5_RESP",
363     "AUTH_NTLM",
364     "AUTH_NTLM_TYPE2MSG",
365     "AUTH_XOAUTH2",
366     "AUTH_CANCEL",
367     "AUTH_FINAL",
368     "MAIL",
369     "RCPT",
370     "DATA",
371     "POSTDATA",
372     "QUIT",
373     /* LAST */
374   };
375
376   if(smtpc->state != newstate)
377     infof(conn->data, "SMTP %p state change from %s to %s\n",
378           (void *)smtpc, names[smtpc->state], names[newstate]);
379 #endif
380
381   smtpc->state = newstate;
382 }
383
384 /***********************************************************************
385  *
386  * smtp_perform_ehlo()
387  *
388  * Sends the EHLO command to not only initialise communication with the ESMTP
389  * server but to also obtain a list of server side supported capabilities.
390  */
391 static CURLcode smtp_perform_ehlo(struct connectdata *conn)
392 {
393   CURLcode result = CURLE_OK;
394   struct smtp_conn *smtpc = &conn->proto.smtpc;
395
396   smtpc->authmechs = 0;         /* No known authentication mechanisms yet */
397   smtpc->authused = 0;          /* Clear the authentication mechanism used
398                                    for esmtp connections */
399   smtpc->tls_supported = FALSE; /* Clear the TLS capability */
400
401   /* Send the EHLO command */
402   result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
403
404   if(!result)
405     state(conn, SMTP_EHLO);
406
407   return result;
408 }
409
410 /***********************************************************************
411  *
412  * smtp_perform_helo()
413  *
414  * Sends the HELO command to initialise communication with the SMTP server.
415  */
416 static CURLcode smtp_perform_helo(struct connectdata *conn)
417 {
418   CURLcode result = CURLE_OK;
419   struct smtp_conn *smtpc = &conn->proto.smtpc;
420
421   smtpc->authused = 0;          /* No authentication mechanism used in smtp
422                                    connections */
423
424   /* Send the HELO command */
425   result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
426
427   if(!result)
428     state(conn, SMTP_HELO);
429
430   return result;
431 }
432
433 /***********************************************************************
434  *
435  * smtp_perform_starttls()
436  *
437  * Sends the STLS command to start the upgrade to TLS.
438  */
439 static CURLcode smtp_perform_starttls(struct connectdata *conn)
440 {
441   CURLcode result = CURLE_OK;
442
443   /* Send the STARTTLS command */
444   result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "STARTTLS");
445
446   if(!result)
447     state(conn, SMTP_STARTTLS);
448
449   return result;
450 }
451
452 /***********************************************************************
453  *
454  * smtp_perform_upgrade_tls()
455  *
456  * Performs the upgrade to TLS.
457  */
458 static CURLcode smtp_perform_upgrade_tls(struct connectdata *conn)
459 {
460   CURLcode result = CURLE_OK;
461   struct smtp_conn *smtpc = &conn->proto.smtpc;
462
463   /* Start the SSL connection */
464   result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
465
466   if(!result) {
467     if(smtpc->state != SMTP_UPGRADETLS)
468       state(conn, SMTP_UPGRADETLS);
469
470     if(smtpc->ssldone) {
471       smtp_to_smtps(conn);
472       result = smtp_perform_ehlo(conn);
473     }
474   }
475
476   return result;
477 }
478
479 /***********************************************************************
480  *
481  * smtp_perform_authenticate()
482  *
483  * Sends an AUTH command allowing the client to login with the appropriate
484  * SASL authentication mechanism.
485  */
486 static CURLcode smtp_perform_authenticate(struct connectdata *conn)
487 {
488   CURLcode result = CURLE_OK;
489   struct SessionHandle *data = conn->data;
490   struct smtp_conn *smtpc = &conn->proto.smtpc;
491   const char *mech = NULL;
492   char *initresp = NULL;
493   size_t len = 0;
494   smtpstate state1 = SMTP_STOP;
495   smtpstate state2 = SMTP_STOP;
496
497   /* Check we have a username and password to authenticate with and end the
498      connect phase if we don't */
499   if(!conn->bits.user_passwd) {
500     state(conn, SMTP_STOP);
501
502     return result;
503   }
504
505   /* Calculate the supported authentication mechanism, by decreasing order of
506      security, as well as the initial response where appropriate */
507 #ifndef CURL_DISABLE_CRYPTO_AUTH
508   if((smtpc->authmechs & SASL_MECH_DIGEST_MD5) &&
509      (smtpc->prefmech & SASL_MECH_DIGEST_MD5)) {
510     mech = SASL_MECH_STRING_DIGEST_MD5;
511     state1 = SMTP_AUTH_DIGESTMD5;
512     smtpc->authused = SASL_MECH_DIGEST_MD5;
513   }
514   else if((smtpc->authmechs & SASL_MECH_CRAM_MD5) &&
515           (smtpc->prefmech & SASL_MECH_CRAM_MD5)) {
516     mech = SASL_MECH_STRING_CRAM_MD5;
517     state1 = SMTP_AUTH_CRAMMD5;
518     smtpc->authused = SASL_MECH_CRAM_MD5;
519   }
520   else
521 #endif
522 #ifdef USE_NTLM
523   if((smtpc->authmechs & SASL_MECH_NTLM) &&
524      (smtpc->prefmech & SASL_MECH_NTLM)) {
525     mech = SASL_MECH_STRING_NTLM;
526     state1 = SMTP_AUTH_NTLM;
527     state2 = SMTP_AUTH_NTLM_TYPE2MSG;
528     smtpc->authused = SASL_MECH_NTLM;
529
530     if(data->set.sasl_ir)
531       result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
532                                                    &conn->ntlm,
533                                                    &initresp, &len);
534     }
535   else
536 #endif
537   if(((smtpc->authmechs & SASL_MECH_XOAUTH2) &&
538       (smtpc->prefmech & SASL_MECH_XOAUTH2) &&
539       (smtpc->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) {
540     mech = SASL_MECH_STRING_XOAUTH2;
541     state1 = SMTP_AUTH_XOAUTH2;
542     state2 = SMTP_AUTH_FINAL;
543     smtpc->authused = SASL_MECH_XOAUTH2;
544
545     if(data->set.sasl_ir)
546       result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
547                                                 conn->xoauth2_bearer,
548                                                 &initresp, &len);
549   }
550   else if((smtpc->authmechs & SASL_MECH_LOGIN) &&
551      (smtpc->prefmech & SASL_MECH_LOGIN)) {
552     mech = SASL_MECH_STRING_LOGIN;
553     state1 = SMTP_AUTH_LOGIN;
554     state2 = SMTP_AUTH_LOGIN_PASSWD;
555     smtpc->authused = SASL_MECH_LOGIN;
556
557     if(data->set.sasl_ir)
558       result = Curl_sasl_create_login_message(conn->data, conn->user,
559                                               &initresp, &len);
560   }
561   else if((smtpc->authmechs & SASL_MECH_PLAIN) &&
562           (smtpc->prefmech & SASL_MECH_PLAIN)) {
563     mech = SASL_MECH_STRING_PLAIN;
564     state1 = SMTP_AUTH_PLAIN;
565     state2 = SMTP_AUTH_FINAL;
566     smtpc->authused = SASL_MECH_PLAIN;
567
568     if(data->set.sasl_ir)
569       result = Curl_sasl_create_plain_message(conn->data, conn->user,
570                                               conn->passwd, &initresp, &len);
571   }
572
573   if(!result) {
574     if(mech) {
575       /* Perform SASL based authentication */
576       if(initresp &&
577          8 + strlen(mech) + len <= 512) { /* AUTH <mech> ...<crlf> */
578         result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
579
580         if(!result)
581           state(conn, state2);
582       }
583       else {
584         result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
585
586         if(!result)
587           state(conn, state1);
588       }
589
590       Curl_safefree(initresp);
591     }
592     else {
593       /* Other mechanisms not supported */
594       infof(conn->data, "No known authentication mechanisms supported!\n");
595       result = CURLE_LOGIN_DENIED;
596     }
597   }
598
599   return result;
600 }
601
602 /***********************************************************************
603  *
604  * smtp_perform_mail()
605  *
606  * Sends an MAIL command to initiate the upload of a message.
607  */
608 static CURLcode smtp_perform_mail(struct connectdata *conn)
609 {
610   char *from = NULL;
611   char *auth = NULL;
612   char *size = NULL;
613   CURLcode result = CURLE_OK;
614   struct SessionHandle *data = conn->data;
615
616   /* Calculate the FROM parameter */
617   if(!data->set.str[STRING_MAIL_FROM])
618     /* Null reverse-path, RFC-5321, sect. 3.6.3 */
619     from = strdup("<>");
620   else if(data->set.str[STRING_MAIL_FROM][0] == '<')
621     from = aprintf("%s", data->set.str[STRING_MAIL_FROM]);
622   else
623     from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]);
624
625   if(!from)
626     return CURLE_OUT_OF_MEMORY;
627
628   /* Calculate the optional AUTH parameter */
629   if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.authused) {
630     if(data->set.str[STRING_MAIL_AUTH][0] != '\0')
631       auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]);
632     else
633       /* Empty AUTH, RFC-2554, sect. 5 */
634       auth = strdup("<>");
635
636     if(!auth) {
637       Curl_safefree(from);
638
639       return CURLE_OUT_OF_MEMORY;
640     }
641   }
642
643   /* Calculate the optional SIZE parameter */
644   if(conn->proto.smtpc.size_supported && conn->data->set.infilesize > 0) {
645     size = aprintf("%" FORMAT_OFF_T, data->set.infilesize);
646
647     if(!size) {
648       Curl_safefree(from);
649       Curl_safefree(auth);
650
651       return CURLE_OUT_OF_MEMORY;
652     }
653   }
654
655   /* Send the MAIL command */
656   if(!auth && !size)
657     result = Curl_pp_sendf(&conn->proto.smtpc.pp,
658                            "MAIL FROM:%s", from);
659   else if(auth && !size)
660     result = Curl_pp_sendf(&conn->proto.smtpc.pp,
661                            "MAIL FROM:%s AUTH=%s", from, auth);
662   else if(auth && size)
663     result = Curl_pp_sendf(&conn->proto.smtpc.pp,
664                            "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size);
665   else
666     result = Curl_pp_sendf(&conn->proto.smtpc.pp,
667                            "MAIL FROM:%s SIZE=%s", from, size);
668
669   Curl_safefree(from);
670   Curl_safefree(auth);
671   Curl_safefree(size);
672
673   if(!result)
674     state(conn, SMTP_MAIL);
675
676   return result;
677 }
678
679 /***********************************************************************
680  *
681  * smtp_perform_rcpt_to()
682  *
683  * Sends a RCPT TO command for a given recipient as part of the message upload
684  * process.
685  */
686 static CURLcode smtp_perform_rcpt_to(struct connectdata *conn)
687 {
688   CURLcode result = CURLE_OK;
689   struct SessionHandle *data = conn->data;
690   struct SMTP *smtp = data->req.protop;
691
692   /* Send the RCPT TO command */
693   if(smtp->rcpt) {
694     if(smtp->rcpt->data[0] == '<')
695       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
696                              smtp->rcpt->data);
697     else
698       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
699                              smtp->rcpt->data);
700     if(!result)
701       state(conn, SMTP_RCPT);
702   }
703
704   return result;
705 }
706
707 /***********************************************************************
708  *
709  * smtp_perform_quit()
710  *
711  * Performs the quit action prior to sclose() being called.
712  */
713 static CURLcode smtp_perform_quit(struct connectdata *conn)
714 {
715   CURLcode result = CURLE_OK;
716
717   /* Send the QUIT command */
718   result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "QUIT");
719
720   if(!result)
721     state(conn, SMTP_QUIT);
722
723   return result;
724 }
725
726 /* For the initial server greeting */
727 static CURLcode smtp_state_servergreet_resp(struct connectdata *conn,
728                                             int smtpcode,
729                                             smtpstate instate)
730 {
731   CURLcode result = CURLE_OK;
732   struct SessionHandle *data = conn->data;
733
734   (void)instate; /* no use for this yet */
735
736   if(smtpcode/100 != 2) {
737     failf(data, "Got unexpected smtp-server response: %d", smtpcode);
738     result = CURLE_FTP_WEIRD_SERVER_REPLY;
739   }
740   else
741     result = smtp_perform_ehlo(conn);
742
743   return result;
744 }
745
746 /* For STARTTLS responses */
747 static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
748                                          int smtpcode,
749                                          smtpstate instate)
750 {
751   CURLcode result = CURLE_OK;
752   struct SessionHandle *data = conn->data;
753
754   (void)instate; /* no use for this yet */
755
756   if(smtpcode != 220) {
757     if(data->set.use_ssl != CURLUSESSL_TRY) {
758       failf(data, "STARTTLS denied. %c", smtpcode);
759       result = CURLE_USE_SSL_FAILED;
760     }
761     else
762       result = smtp_perform_authenticate(conn);
763   }
764   else
765     result = smtp_perform_upgrade_tls(conn);
766
767   return result;
768 }
769
770 /* For EHLO responses */
771 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
772                                      smtpstate instate)
773 {
774   CURLcode result = CURLE_OK;
775   struct SessionHandle *data = conn->data;
776   struct smtp_conn *smtpc = &conn->proto.smtpc;
777
778   (void)instate; /* no use for this yet */
779
780   if(smtpcode/100 != 2) {
781     if((data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) &&
782      !conn->bits.user_passwd)
783       result = smtp_perform_helo(conn);
784     else {
785       failf(data, "Remote access denied: %d", smtpcode);
786       result = CURLE_REMOTE_ACCESS_DENIED;
787     }
788   }
789   else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
790     /* We don't have a SSL/TLS connection yet, but SSL is requested */
791     if(smtpc->tls_supported)
792       /* Switch to TLS connection now */
793       result = smtp_perform_starttls(conn);
794     else if(data->set.use_ssl == CURLUSESSL_TRY)
795       /* Fallback and carry on with authentication */
796       result = smtp_perform_authenticate(conn);
797     else {
798       failf(data, "STARTTLS not supported.");
799       result = CURLE_USE_SSL_FAILED;
800     }
801   }
802   else
803     result = smtp_perform_authenticate(conn);
804
805   return result;
806 }
807
808 /* For HELO responses */
809 static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode,
810                                      smtpstate instate)
811 {
812   CURLcode result = CURLE_OK;
813   struct SessionHandle *data = conn->data;
814
815   (void)instate; /* no use for this yet */
816
817   if(smtpcode/100 != 2) {
818     failf(data, "Remote access denied: %d", smtpcode);
819     result = CURLE_REMOTE_ACCESS_DENIED;
820   }
821   else
822     /* End of connect phase */
823     state(conn, SMTP_STOP);
824
825   return result;
826 }
827
828 /* For AUTH PLAIN (without initial response) responses */
829 static CURLcode smtp_state_auth_plain_resp(struct connectdata *conn,
830                                            int smtpcode,
831                                            smtpstate instate)
832 {
833   CURLcode result = CURLE_OK;
834   struct SessionHandle *data = conn->data;
835   size_t len = 0;
836   char *plainauth = NULL;
837
838   (void)instate; /* no use for this yet */
839
840   if(smtpcode != 334) {
841     failf(data, "Access denied: %d", smtpcode);
842     result = CURLE_LOGIN_DENIED;
843   }
844   else {
845     /* Create the authorisation message */
846     result = Curl_sasl_create_plain_message(conn->data, conn->user,
847                                             conn->passwd, &plainauth, &len);
848
849     /* Send the message */
850     if(!result) {
851       if(plainauth) {
852         result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth);
853
854         if(!result)
855           state(conn, SMTP_AUTH_FINAL);
856       }
857
858       Curl_safefree(plainauth);
859     }
860   }
861
862   return result;
863 }
864
865 /* For AUTH LOGIN (without initial response) responses */
866 static CURLcode smtp_state_auth_login_resp(struct connectdata *conn,
867                                            int smtpcode,
868                                            smtpstate instate)
869 {
870   CURLcode result = CURLE_OK;
871   struct SessionHandle *data = conn->data;
872   size_t len = 0;
873   char *authuser = NULL;
874
875   (void)instate; /* no use for this yet */
876
877   if(smtpcode != 334) {
878     failf(data, "Access denied: %d", smtpcode);
879     result = CURLE_LOGIN_DENIED;
880   }
881   else {
882     /* Create the user message */
883     result = Curl_sasl_create_login_message(conn->data, conn->user,
884                                             &authuser, &len);
885
886     /* Send the user */
887     if(!result) {
888       if(authuser) {
889         result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser);
890
891         if(!result)
892           state(conn, SMTP_AUTH_LOGIN_PASSWD);
893       }
894
895       Curl_safefree(authuser);
896     }
897   }
898
899   return result;
900 }
901
902 /* For AUTH LOGIN user entry responses */
903 static CURLcode smtp_state_auth_login_password_resp(struct connectdata *conn,
904                                                     int smtpcode,
905                                                     smtpstate instate)
906 {
907   CURLcode result = CURLE_OK;
908   struct SessionHandle *data = conn->data;
909   size_t len = 0;
910   char *authpasswd = NULL;
911
912   (void)instate; /* no use for this yet */
913
914   if(smtpcode != 334) {
915     failf(data, "Access denied: %d", smtpcode);
916     result = CURLE_LOGIN_DENIED;
917   }
918   else {
919     /* Create the password message */
920     result = Curl_sasl_create_login_message(conn->data, conn->passwd,
921                                             &authpasswd, &len);
922
923     /* Send the password */
924     if(!result) {
925       if(authpasswd) {
926         result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd);
927
928         if(!result)
929           state(conn, SMTP_AUTH_FINAL);
930       }
931
932       Curl_safefree(authpasswd);
933     }
934   }
935
936   return result;
937 }
938
939 #ifndef CURL_DISABLE_CRYPTO_AUTH
940 /* For AUTH CRAM-MD5 responses */
941 static CURLcode smtp_state_auth_cram_resp(struct connectdata *conn,
942                                           int smtpcode,
943                                           smtpstate instate)
944 {
945   CURLcode result = CURLE_OK;
946   struct SessionHandle *data = conn->data;
947   char *chlg = NULL;
948   char *chlg64 = NULL;
949   char *rplyb64 = NULL;
950   size_t len = 0;
951
952   (void)instate; /* no use for this yet */
953
954   if(smtpcode != 334) {
955     failf(data, "Access denied: %d", smtpcode);
956     return CURLE_LOGIN_DENIED;
957   }
958
959   /* Get the challenge message */
960   smtp_get_message(data->state.buffer, &chlg64);
961
962   /* Decode the challenge message */
963   result = Curl_sasl_decode_cram_md5_message(chlg64, &chlg, &len);
964   if(result) {
965     /* Send the cancellation */
966     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*");
967
968     if(!result)
969       state(conn, SMTP_AUTH_CANCEL);
970   }
971   else {
972     /* Create the response message */
973     result = Curl_sasl_create_cram_md5_message(data, chlg, conn->user,
974                                                conn->passwd, &rplyb64, &len);
975     if(!result && rplyb64) {
976       /* Send the response */
977       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
978
979       if(!result)
980         state(conn, SMTP_AUTH_FINAL);
981     }
982   }
983
984   Curl_safefree(chlg);
985   Curl_safefree(rplyb64);
986
987   return result;
988 }
989
990 /* For AUTH DIGEST-MD5 challenge responses */
991 static CURLcode smtp_state_auth_digest_resp(struct connectdata *conn,
992                                             int smtpcode,
993                                             smtpstate instate)
994 {
995   CURLcode result = CURLE_OK;
996   struct SessionHandle *data = conn->data;
997   char *chlg64 = NULL;
998   char *rplyb64 = NULL;
999   size_t len = 0;
1000
1001   char nonce[64];
1002   char realm[128];
1003   char algorithm[64];
1004
1005   (void)instate; /* no use for this yet */
1006
1007   if(smtpcode != 334) {
1008     failf(data, "Access denied: %d", smtpcode);
1009     return CURLE_LOGIN_DENIED;
1010   }
1011
1012   /* Get the challenge message */
1013   smtp_get_message(data->state.buffer, &chlg64);
1014
1015   /* Decode the challange message */
1016   result = Curl_sasl_decode_digest_md5_message(chlg64, nonce, sizeof(nonce),
1017                                                realm, sizeof(realm),
1018                                                algorithm, sizeof(algorithm));
1019   if(result || strcmp(algorithm, "md5-sess") != 0) {
1020     /* Send the cancellation */
1021     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*");
1022
1023     if(!result)
1024       state(conn, SMTP_AUTH_CANCEL);
1025   }
1026   else {
1027     /* Create the response message */
1028     result = Curl_sasl_create_digest_md5_message(data, nonce, realm,
1029                                                  conn->user, conn->passwd,
1030                                                  "smtp", &rplyb64, &len);
1031     if(!result && rplyb64) {
1032       /* Send the response */
1033       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
1034
1035       if(!result)
1036         state(conn, SMTP_AUTH_DIGESTMD5_RESP);
1037     }
1038   }
1039
1040   Curl_safefree(rplyb64);
1041
1042   return result;
1043 }
1044
1045 /* For AUTH DIGEST-MD5 challenge-response responses */
1046 static CURLcode smtp_state_auth_digest_resp_resp(struct connectdata *conn,
1047                                                  int smtpcode,
1048                                                  smtpstate instate)
1049 {
1050   CURLcode result = CURLE_OK;
1051   struct SessionHandle *data = conn->data;
1052
1053   (void)instate; /* no use for this yet */
1054
1055   if(smtpcode != 334) {
1056     failf(data, "Authentication failed: %d", smtpcode);
1057     result = CURLE_LOGIN_DENIED;
1058   }
1059   else {
1060     /* Send an empty response */
1061     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "");
1062
1063     if(!result)
1064       state(conn, SMTP_AUTH_FINAL);
1065   }
1066
1067   return result;
1068 }
1069
1070 #endif
1071
1072 #ifdef USE_NTLM
1073 /* For AUTH NTLM (without initial response) responses */
1074 static CURLcode smtp_state_auth_ntlm_resp(struct connectdata *conn,
1075                                           int smtpcode,
1076                                           smtpstate instate)
1077 {
1078   CURLcode result = CURLE_OK;
1079   struct SessionHandle *data = conn->data;
1080   char *type1msg = NULL;
1081   size_t len = 0;
1082
1083   (void)instate; /* no use for this yet */
1084
1085   if(smtpcode != 334) {
1086     failf(data, "Access denied: %d", smtpcode);
1087     result = CURLE_LOGIN_DENIED;
1088   }
1089   else {
1090     /* Create the type-1 message */
1091     result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1092                                                  &conn->ntlm,
1093                                                  &type1msg, &len);
1094
1095     /* Send the message */
1096     if(!result) {
1097       if(type1msg) {
1098         result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type1msg);
1099
1100         if(!result)
1101           state(conn, SMTP_AUTH_NTLM_TYPE2MSG);
1102       }
1103
1104       Curl_safefree(type1msg);
1105     }
1106   }
1107
1108   return result;
1109 }
1110
1111 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1112 static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1113                                                    int smtpcode,
1114                                                    smtpstate instate)
1115 {
1116   CURLcode result = CURLE_OK;
1117   struct SessionHandle *data = conn->data;
1118   char *type2msg = NULL;
1119   char *type3msg = NULL;
1120   size_t len = 0;
1121
1122   (void)instate; /* no use for this yet */
1123
1124   if(smtpcode != 334) {
1125     failf(data, "Access denied: %d", smtpcode);
1126     result = CURLE_LOGIN_DENIED;
1127   }
1128   else {
1129     /* Get the type-2 message */
1130     smtp_get_message(data->state.buffer, &type2msg);
1131
1132     /* Create the type-3 message */
1133     result = Curl_sasl_create_ntlm_type3_message(data, type2msg, conn->user,
1134                                                  conn->passwd, &conn->ntlm,
1135                                                  &type3msg, &len);
1136
1137     /* Send the message */
1138     if(!result) {
1139       if(type3msg) {
1140         result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3msg);
1141
1142         if(!result)
1143           state(conn, SMTP_AUTH_FINAL);
1144       }
1145
1146       Curl_safefree(type3msg);
1147     }
1148   }
1149
1150   return result;
1151 }
1152 #endif
1153
1154 /* For AUTH XOAUTH2 (without initial response) responses */
1155 static CURLcode smtp_state_auth_xoauth2_resp(struct connectdata *conn,
1156                                              int smtpcode, smtpstate instate)
1157 {
1158   CURLcode result = CURLE_OK;
1159   struct SessionHandle *data = conn->data;
1160   size_t len = 0;
1161   char *xoauth = NULL;
1162
1163   (void)instate; /* no use for this yet */
1164
1165   if(smtpcode != 334) {
1166     failf(data, "Access denied: %d", smtpcode);
1167     result = CURLE_LOGIN_DENIED;
1168   }
1169   else {
1170     /* Create the authorisation message */
1171     result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
1172                                               conn->xoauth2_bearer,
1173                                               &xoauth, &len);
1174
1175     /* Send the message */
1176     if(!result) {
1177       if(xoauth) {
1178         result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", xoauth);
1179
1180         if(!result)
1181           state(conn, SMTP_AUTH_FINAL);
1182       }
1183
1184       Curl_safefree(xoauth);
1185     }
1186   }
1187
1188   return result;
1189 }
1190
1191 /* For AUTH cancellation responses */
1192 static CURLcode smtp_state_auth_cancel_resp(struct connectdata *conn,
1193                                             int smtpcode,
1194                                             smtpstate instate)
1195 {
1196   struct SessionHandle *data = conn->data;
1197
1198   (void)smtpcode;
1199   (void)instate; /* no use for this yet */
1200
1201   failf(data, "Authentication cancelled");
1202
1203   return CURLE_LOGIN_DENIED;
1204 }
1205
1206 /* For final responses in the AUTH sequence */
1207 static CURLcode smtp_state_auth_final_resp(struct connectdata *conn,
1208                                            int smtpcode,
1209                                            smtpstate instate)
1210 {
1211   CURLcode result = CURLE_OK;
1212   struct SessionHandle *data = conn->data;
1213
1214   (void)instate; /* no use for this yet */
1215
1216   if(smtpcode != 235) {
1217     failf(data, "Authentication failed: %d", smtpcode);
1218     result = CURLE_LOGIN_DENIED;
1219   }
1220   else
1221     /* End of connect phase */
1222     state(conn, SMTP_STOP);
1223
1224   return result;
1225 }
1226
1227 /* For MAIL responses */
1228 static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode,
1229                                      smtpstate instate)
1230 {
1231   CURLcode result = CURLE_OK;
1232   struct SessionHandle *data = conn->data;
1233   struct SMTP *smtp = data->req.protop;
1234
1235   (void)instate; /* no use for this yet */
1236
1237   if(smtpcode/100 != 2) {
1238     failf(data, "MAIL failed: %d", smtpcode);
1239     result = CURLE_SEND_ERROR;
1240     state(conn, SMTP_STOP);
1241   }
1242   else {
1243     smtp->rcpt = data->set.mail_rcpt;
1244
1245     result = smtp_perform_rcpt_to(conn);
1246   }
1247
1248   return result;
1249 }
1250
1251 /* For RCPT responses */
1252 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode,
1253                                      smtpstate instate)
1254 {
1255   CURLcode result = CURLE_OK;
1256   struct SessionHandle *data = conn->data;
1257   struct SMTP *smtp = data->req.protop;
1258
1259   (void)instate; /* no use for this yet */
1260
1261   if(smtpcode/100 != 2) {
1262     failf(data, "RCPT failed: %d", smtpcode);
1263     result = CURLE_SEND_ERROR;
1264     state(conn, SMTP_STOP);
1265   }
1266   else {
1267     if(smtp->rcpt) {
1268       smtp->rcpt = smtp->rcpt->next;
1269       result = smtp_perform_rcpt_to(conn);
1270
1271       /* If we failed or still are sending RCPT data then return */
1272       if(result || smtp->rcpt)
1273         return result;
1274     }
1275
1276     /* Send the DATA command */
1277     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "DATA");
1278
1279     if(!result)
1280       state(conn, SMTP_DATA);
1281   }
1282
1283   return result;
1284 }
1285
1286 /* For DATA response */
1287 static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode,
1288                                      smtpstate instate)
1289 {
1290   struct SessionHandle *data = conn->data;
1291
1292   (void)instate; /* no use for this yet */
1293
1294   if(smtpcode != 354) {
1295     state(conn, SMTP_STOP);
1296     return CURLE_SEND_ERROR;
1297   }
1298
1299   /* Set the progress upload size */
1300   Curl_pgrsSetUploadSize(data, data->set.infilesize);
1301
1302   /* SMTP upload */
1303   Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1304
1305   /* End of DO phase */
1306   state(conn, SMTP_STOP);
1307
1308   return CURLE_OK;
1309 }
1310
1311 /* For POSTDATA responses, which are received after the entire DATA
1312    part has been sent to the server */
1313 static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
1314                                          int smtpcode,
1315                                          smtpstate instate)
1316 {
1317   CURLcode result = CURLE_OK;
1318
1319   (void)instate; /* no use for this yet */
1320
1321   if(smtpcode != 250)
1322     result = CURLE_RECV_ERROR;
1323
1324   /* End of DONE phase */
1325   state(conn, SMTP_STOP);
1326
1327   return result;
1328 }
1329
1330 static CURLcode smtp_statemach_act(struct connectdata *conn)
1331 {
1332   CURLcode result = CURLE_OK;
1333   curl_socket_t sock = conn->sock[FIRSTSOCKET];
1334   struct SessionHandle *data = conn->data;
1335   int smtpcode;
1336   struct smtp_conn *smtpc = &conn->proto.smtpc;
1337   struct pingpong *pp = &smtpc->pp;
1338   size_t nread = 0;
1339
1340   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
1341   if(smtpc->state == SMTP_UPGRADETLS)
1342     return smtp_perform_upgrade_tls(conn);
1343
1344   /* Flush any data that needs to be sent */
1345   if(pp->sendleft)
1346     return Curl_pp_flushsend(pp);
1347
1348   /* Read the response from the server */
1349   result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
1350   if(result)
1351     return result;
1352
1353   /* Store the latest response for later retrieval */
1354   if(smtpc->state != SMTP_QUIT)
1355     data->info.httpcode = smtpcode;
1356
1357   if(smtpcode) {
1358     /* We have now received a full SMTP server response */
1359     switch(smtpc->state) {
1360     case SMTP_SERVERGREET:
1361       result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state);
1362       break;
1363
1364     case SMTP_EHLO:
1365       result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
1366       break;
1367
1368     case SMTP_HELO:
1369       result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
1370       break;
1371
1372     case SMTP_STARTTLS:
1373       result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
1374       break;
1375
1376     case SMTP_AUTH_PLAIN:
1377       result = smtp_state_auth_plain_resp(conn, smtpcode, smtpc->state);
1378       break;
1379
1380     case SMTP_AUTH_LOGIN:
1381       result = smtp_state_auth_login_resp(conn, smtpcode, smtpc->state);
1382       break;
1383
1384     case SMTP_AUTH_LOGIN_PASSWD:
1385       result = smtp_state_auth_login_password_resp(conn, smtpcode,
1386                                                    smtpc->state);
1387       break;
1388
1389 #ifndef CURL_DISABLE_CRYPTO_AUTH
1390     case SMTP_AUTH_CRAMMD5:
1391       result = smtp_state_auth_cram_resp(conn, smtpcode, smtpc->state);
1392       break;
1393
1394     case SMTP_AUTH_DIGESTMD5:
1395       result = smtp_state_auth_digest_resp(conn, smtpcode, smtpc->state);
1396       break;
1397
1398     case SMTP_AUTH_DIGESTMD5_RESP:
1399       result = smtp_state_auth_digest_resp_resp(conn, smtpcode, smtpc->state);
1400       break;
1401 #endif
1402
1403 #ifdef USE_NTLM
1404     case SMTP_AUTH_NTLM:
1405       result = smtp_state_auth_ntlm_resp(conn, smtpcode, smtpc->state);
1406       break;
1407
1408     case SMTP_AUTH_NTLM_TYPE2MSG:
1409       result = smtp_state_auth_ntlm_type2msg_resp(conn, smtpcode,
1410                                                   smtpc->state);
1411       break;
1412 #endif
1413
1414     case SMTP_AUTH_XOAUTH2:
1415       result = smtp_state_auth_xoauth2_resp(conn, smtpcode, smtpc->state);
1416       break;
1417
1418     case SMTP_AUTH_CANCEL:
1419       result = smtp_state_auth_cancel_resp(conn, smtpcode, smtpc->state);
1420       break;
1421
1422     case SMTP_AUTH_FINAL:
1423       result = smtp_state_auth_final_resp(conn, smtpcode, smtpc->state);
1424       break;
1425
1426     case SMTP_MAIL:
1427       result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
1428       break;
1429
1430     case SMTP_RCPT:
1431       result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
1432       break;
1433
1434     case SMTP_DATA:
1435       result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
1436       break;
1437
1438     case SMTP_POSTDATA:
1439       result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
1440       break;
1441
1442     case SMTP_QUIT:
1443       /* fallthrough, just stop! */
1444     default:
1445       /* internal error */
1446       state(conn, SMTP_STOP);
1447       break;
1448     }
1449   }
1450
1451   return result;
1452 }
1453
1454 /* Called repeatedly until done from multi.c */
1455 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
1456 {
1457   CURLcode result = CURLE_OK;
1458   struct smtp_conn *smtpc = &conn->proto.smtpc;
1459
1460   if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
1461     result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
1462     if(result || !smtpc->ssldone)
1463       return result;
1464   }
1465
1466   result = Curl_pp_statemach(&smtpc->pp, FALSE);
1467   *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1468
1469   return result;
1470 }
1471
1472 static CURLcode smtp_block_statemach(struct connectdata *conn)
1473 {
1474   CURLcode result = CURLE_OK;
1475   struct smtp_conn *smtpc = &conn->proto.smtpc;
1476
1477   while(smtpc->state != SMTP_STOP && !result)
1478     result = Curl_pp_statemach(&smtpc->pp, TRUE);
1479
1480   return result;
1481 }
1482
1483 /* Allocate and initialize the SMTP struct for the current SessionHandle if
1484    required */
1485 static CURLcode smtp_init(struct connectdata *conn)
1486 {
1487   CURLcode result = CURLE_OK;
1488   struct SessionHandle *data = conn->data;
1489   struct SMTP *smtp;
1490
1491   smtp = data->req.protop = calloc(sizeof(struct SMTP), 1);
1492   if(!smtp)
1493     result = CURLE_OUT_OF_MEMORY;
1494
1495   return result;
1496 }
1497
1498 /* For the SMTP "protocol connect" and "doing" phases only */
1499 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
1500                         int numsocks)
1501 {
1502   return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
1503 }
1504
1505 /***********************************************************************
1506  *
1507  * smtp_connect()
1508  *
1509  * This function should do everything that is to be considered a part of
1510  * the connection phase.
1511  *
1512  * The variable pointed to by 'done' will be TRUE if the protocol-layer
1513  * connect phase is done when this function returns, or FALSE if not.
1514  */
1515 static CURLcode smtp_connect(struct connectdata *conn, bool *done)
1516 {
1517   CURLcode result = CURLE_OK;
1518   struct smtp_conn *smtpc = &conn->proto.smtpc;
1519   struct pingpong *pp = &smtpc->pp;
1520
1521   *done = FALSE; /* default to not done yet */
1522
1523   /* We always support persistent connections in SMTP */
1524   conn->bits.close = FALSE;
1525
1526   /* Set the default response time-out */
1527   pp->response_time = RESP_TIMEOUT;
1528   pp->statemach_act = smtp_statemach_act;
1529   pp->endofresp = smtp_endofresp;
1530   pp->conn = conn;
1531
1532   /* Set the default preferred authentication mechanism */
1533   smtpc->prefmech = SASL_AUTH_ANY;
1534
1535   /* Initialise the pingpong layer */
1536   Curl_pp_init(pp);
1537
1538   /* Parse the URL options */
1539   result = smtp_parse_url_options(conn);
1540   if(result)
1541     return result;
1542
1543   /* Parse the URL path */
1544   result = smtp_parse_url_path(conn);
1545   if(result)
1546     return result;
1547
1548   /* Start off waiting for the server greeting response */
1549   state(conn, SMTP_SERVERGREET);
1550
1551   result = smtp_multi_statemach(conn, done);
1552
1553   return result;
1554 }
1555
1556 /***********************************************************************
1557  *
1558  * smtp_done()
1559  *
1560  * The DONE function. This does what needs to be done after a single DO has
1561  * performed.
1562  *
1563  * Input argument is already checked for validity.
1564  */
1565 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1566                           bool premature)
1567 {
1568   CURLcode result = CURLE_OK;
1569   struct SessionHandle *data = conn->data;
1570   struct SMTP *smtp = data->req.protop;
1571   struct pingpong *pp = &conn->proto.smtpc.pp;
1572   const char *eob;
1573   ssize_t len;
1574   ssize_t bytes_written;
1575
1576   (void)premature;
1577
1578   if(!smtp)
1579     /* When the easy handle is removed from the multi interface while libcurl
1580        is still trying to resolve the host name, the SMTP struct is not yet
1581        initialized. However, the removal action calls Curl_done() which in
1582        turn calls this function, so we simply return success. */
1583     return CURLE_OK;
1584
1585   if(status) {
1586     conn->bits.close = TRUE; /* marked for closure */
1587     result = status;         /* use the already set error code */
1588   }
1589   else if(!data->set.connect_only) {
1590     /* Calculate the EOB taking into account any terminating CRLF from the
1591        previous line of the email or the CRLF of the DATA command when there
1592        is "no mail data". RFC-5321, sect. 4.1.1.4. */
1593     eob = SMTP_EOB;
1594     len = SMTP_EOB_LEN;
1595     if(smtp->trailing_crlf || !conn->data->set.infilesize) {
1596       eob += 2;
1597       len -= 2;
1598     }
1599
1600     /* Send the end of block data */
1601     result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written);
1602     if(result)
1603       return result;
1604
1605     if(bytes_written != len) {
1606       /* The whole chunk was not sent so keep it around and adjust the
1607          pingpong structure accordingly */
1608       pp->sendthis = strdup(eob);
1609       pp->sendsize = len;
1610       pp->sendleft = len - bytes_written;
1611     }
1612     else
1613       /* Successfully sent so adjust the response timeout relative to now */
1614       pp->response = Curl_tvnow();
1615
1616     state(conn, SMTP_POSTDATA);
1617
1618     /* Run the state-machine
1619
1620        TODO: when the multi interface is used, this _really_ should be using
1621        the smtp_multi_statemach function but we have no general support for
1622        non-blocking DONE operations, not in the multi state machine and with
1623        Curl_done() invokes on several places in the code!
1624     */
1625     result = smtp_block_statemach(conn);
1626   }
1627
1628   /* Clear the transfer mode for the next request */
1629   smtp->transfer = FTPTRANSFER_BODY;
1630
1631   return result;
1632 }
1633
1634 /***********************************************************************
1635  *
1636  * smtp_perform()
1637  *
1638  * This is the actual DO function for SMTP. Send a mail according to the
1639  * options previously setup.
1640  */
1641 static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
1642                              bool *dophase_done)
1643 {
1644   /* This is SMTP and no proxy */
1645   CURLcode result = CURLE_OK;
1646
1647   DEBUGF(infof(conn->data, "DO phase starts\n"));
1648
1649   if(conn->data->set.opt_no_body) {
1650     /* Requested no body means no transfer */
1651     struct SMTP *smtp = conn->data->req.protop;
1652     smtp->transfer = FTPTRANSFER_INFO;
1653   }
1654
1655   *dophase_done = FALSE; /* not done yet */
1656
1657   /* Start the first command in the DO phase */
1658   result = smtp_perform_mail(conn);
1659   if(result)
1660     return result;
1661
1662   /* run the state-machine */
1663   result = smtp_multi_statemach(conn, dophase_done);
1664
1665   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1666
1667   if(*dophase_done)
1668     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1669
1670   return result;
1671 }
1672
1673 /***********************************************************************
1674  *
1675  * smtp_do()
1676  *
1677  * This function is registered as 'curl_do' function. It decodes the path
1678  * parts etc as a wrapper to the actual DO function (smtp_perform).
1679  *
1680  * The input argument is already checked for validity.
1681  */
1682 static CURLcode smtp_do(struct connectdata *conn, bool *done)
1683 {
1684   CURLcode result = CURLE_OK;
1685
1686   *done = FALSE; /* default to false */
1687
1688   result = smtp_regular_transfer(conn, done);
1689
1690   return result;
1691 }
1692
1693 /***********************************************************************
1694  *
1695  * smtp_disconnect()
1696  *
1697  * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1698  * resources. BLOCKING.
1699  */
1700 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection)
1701 {
1702   struct smtp_conn *smtpc = &conn->proto.smtpc;
1703
1704   /* We cannot send quit unconditionally. If this connection is stale or
1705      bad in any way, sending quit and waiting around here will make the
1706      disconnect wait in vain and cause more problems than we need to. */
1707
1708   /* The SMTP session may or may not have been allocated/setup at this
1709      point! */
1710   if(!dead_connection && smtpc->pp.conn && smtpc->pp.conn->bits.protoconnstart)
1711     if(!smtp_perform_quit(conn))
1712       (void)smtp_block_statemach(conn); /* ignore errors on QUIT */
1713
1714   /* Disconnect from the server */
1715   Curl_pp_disconnect(&smtpc->pp);
1716
1717   /* Cleanup the SASL module */
1718   Curl_sasl_cleanup(conn, smtpc->authused);
1719
1720   /* Cleanup our connection based variables */
1721   Curl_safefree(smtpc->domain);
1722
1723   return CURLE_OK;
1724 }
1725
1726 /* Call this when the DO phase has completed */
1727 static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
1728 {
1729   struct SMTP *smtp = conn->data->req.protop;
1730
1731   (void)connected;
1732
1733   if(smtp->transfer != FTPTRANSFER_BODY)
1734     /* no data to transfer */
1735     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1736
1737   return CURLE_OK;
1738 }
1739
1740 /* Called from multi.c while DOing */
1741 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
1742 {
1743   CURLcode result = smtp_multi_statemach(conn, dophase_done);
1744
1745   if(result)
1746     DEBUGF(infof(conn->data, "DO phase failed\n"));
1747   else if(*dophase_done) {
1748     result = smtp_dophase_done(conn, FALSE /* not connected */);
1749
1750     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1751   }
1752
1753   return result;
1754 }
1755
1756 /***********************************************************************
1757  *
1758  * smtp_regular_transfer()
1759  *
1760  * The input argument is already checked for validity.
1761  *
1762  * Performs all commands done before a regular transfer between a local and a
1763  * remote host.
1764  */
1765 static CURLcode smtp_regular_transfer(struct connectdata *conn,
1766                                       bool *dophase_done)
1767 {
1768   CURLcode result = CURLE_OK;
1769   bool connected = FALSE;
1770   struct SessionHandle *data = conn->data;
1771
1772   /* Make sure size is unknown at this point */
1773   data->req.size = -1;
1774
1775   /* Set the progress data */
1776   Curl_pgrsSetUploadCounter(data, 0);
1777   Curl_pgrsSetDownloadCounter(data, 0);
1778   Curl_pgrsSetUploadSize(data, 0);
1779   Curl_pgrsSetDownloadSize(data, 0);
1780
1781   /* Carry out the perform */
1782   result = smtp_perform(conn, &connected, dophase_done);
1783
1784   /* Perform post DO phase operations if necessary */
1785   if(!result && *dophase_done)
1786     result = smtp_dophase_done(conn, connected);
1787
1788   return result;
1789 }
1790
1791 static CURLcode smtp_setup_connection(struct connectdata *conn)
1792 {
1793   struct SessionHandle *data = conn->data;
1794   CURLcode result;
1795
1796   if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1797     /* Unless we have asked to tunnel SMTP operations through the proxy, we
1798        switch and use HTTP operations only */
1799 #ifndef CURL_DISABLE_HTTP
1800     if(conn->handler == &Curl_handler_smtp)
1801       conn->handler = &Curl_handler_smtp_proxy;
1802     else {
1803 #ifdef USE_SSL
1804       conn->handler = &Curl_handler_smtps_proxy;
1805 #else
1806       failf(data, "SMTPS not supported!");
1807       return CURLE_UNSUPPORTED_PROTOCOL;
1808 #endif
1809     }
1810     /* set it up as a HTTP connection instead */
1811     return conn->handler->setup_connection(conn);
1812
1813 #else
1814     failf(data, "SMTP over http proxy requires HTTP support built-in!");
1815     return CURLE_UNSUPPORTED_PROTOCOL;
1816 #endif
1817   }
1818
1819   /* Initialise the SMTP layer */
1820   result = smtp_init(conn);
1821   if(result)
1822     return result;
1823
1824   data->state.path++;   /* don't include the initial slash */
1825
1826   return CURLE_OK;
1827 }
1828
1829 /***********************************************************************
1830  *
1831  * smtp_parse_url_options()
1832  *
1833  * Parse the URL login options.
1834  */
1835 static CURLcode smtp_parse_url_options(struct connectdata *conn)
1836 {
1837   CURLcode result = CURLE_OK;
1838   struct smtp_conn *smtpc = &conn->proto.smtpc;
1839   const char *options = conn->options;
1840   const char *ptr = options;
1841
1842   if(options) {
1843     const char *key = ptr;
1844
1845     while(*ptr && *ptr != '=')
1846         ptr++;
1847
1848     if(strnequal(key, "AUTH", 4)) {
1849       const char *value = ptr + 1;
1850
1851       if(strequal(value, "*"))
1852         smtpc->prefmech = SASL_AUTH_ANY;
1853       else if(strequal(value, SASL_MECH_STRING_LOGIN))
1854         smtpc->prefmech = SASL_MECH_LOGIN;
1855       else if(strequal(value, SASL_MECH_STRING_PLAIN))
1856         smtpc->prefmech = SASL_MECH_PLAIN;
1857       else if(strequal(value, SASL_MECH_STRING_CRAM_MD5))
1858         smtpc->prefmech = SASL_MECH_CRAM_MD5;
1859       else if(strequal(value, SASL_MECH_STRING_DIGEST_MD5))
1860         smtpc->prefmech = SASL_MECH_DIGEST_MD5;
1861       else if(strequal(value, SASL_MECH_STRING_GSSAPI))
1862         smtpc->prefmech = SASL_MECH_GSSAPI;
1863       else if(strequal(value, SASL_MECH_STRING_NTLM))
1864         smtpc->prefmech = SASL_MECH_NTLM;
1865       else if(strequal(value, SASL_MECH_STRING_XOAUTH2))
1866         smtpc->prefmech = SASL_MECH_XOAUTH2;
1867       else
1868         smtpc->prefmech = SASL_AUTH_NONE;
1869     }
1870     else
1871       result = CURLE_URL_MALFORMAT;
1872   }
1873
1874   return result;
1875 }
1876
1877 /***********************************************************************
1878  *
1879  * smtp_parse_url_path()
1880  *
1881  * Parse the URL path into separate path components.
1882  */
1883 static CURLcode smtp_parse_url_path(struct connectdata *conn)
1884 {
1885   /* The SMTP struct is already initialised in smtp_connect() */
1886   struct SessionHandle *data = conn->data;
1887   struct smtp_conn *smtpc = &conn->proto.smtpc;
1888   const char *path = data->state.path;
1889   char localhost[HOSTNAME_MAX + 1];
1890
1891   /* Calculate the path if necessary */
1892   if(!*path) {
1893     if(!Curl_gethostname(localhost, sizeof(localhost)))
1894       path = localhost;
1895     else
1896       path = "localhost";
1897   }
1898
1899   /* URL decode the path and use it as the domain in our EHLO */
1900   return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
1901 }
1902
1903 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread)
1904 {
1905   /* When sending a SMTP payload we must detect CRLF. sequences making sure
1906      they are sent as CRLF.. instead, as a . on the beginning of a line will
1907      be deleted by the server when not part of an EOB terminator and a
1908      genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
1909      data by the server
1910   */
1911   ssize_t i;
1912   ssize_t si;
1913   struct SessionHandle *data = conn->data;
1914   struct SMTP *smtp = data->req.protop;
1915
1916   /* Do we need to allocate the scatch buffer? */
1917   if(!data->state.scratch) {
1918     data->state.scratch = malloc(2 * BUFSIZE);
1919
1920     if(!data->state.scratch) {
1921       failf (data, "Failed to alloc scratch buffer!");
1922       return CURLE_OUT_OF_MEMORY;
1923     }
1924   }
1925
1926   /* This loop can be improved by some kind of Boyer-Moore style of
1927      approach but that is saved for later... */
1928   for(i = 0, si = 0; i < nread; i++) {
1929     if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) {
1930       smtp->eob++;
1931
1932       /* Is the EOB potentially the terminating CRLF? */
1933       if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob)
1934         smtp->trailing_crlf = TRUE;
1935       else
1936         smtp->trailing_crlf = FALSE;
1937     }
1938     else if(smtp->eob) {
1939       /* A previous substring matched so output that first */
1940       memcpy(&data->state.scratch[si], SMTP_EOB, smtp->eob);
1941       si += smtp->eob;
1942
1943       /* Then compare the first byte */
1944       if(SMTP_EOB[0] == data->req.upload_fromhere[i])
1945         smtp->eob = 1;
1946       else
1947         smtp->eob = 0;
1948
1949       /* Reset the trailing CRLF flag as there was more data */
1950       smtp->trailing_crlf = FALSE;
1951     }
1952
1953     /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */
1954     if(SMTP_EOB_FIND_LEN == smtp->eob) {
1955       /* Copy the replacement data to the target buffer */
1956       memcpy(&data->state.scratch[si], SMTP_EOB_REPL, SMTP_EOB_REPL_LEN);
1957       si += SMTP_EOB_REPL_LEN;
1958       smtp->eob = 0;
1959     }
1960     else if(!smtp->eob)
1961       data->state.scratch[si++] = data->req.upload_fromhere[i];
1962   }
1963
1964   if(smtp->eob) {
1965     /* A substring matched before processing ended so output that now */
1966     memcpy(&data->state.scratch[si], SMTP_EOB, smtp->eob);
1967     si += smtp->eob;
1968     smtp->eob = 0;
1969   }
1970
1971   if(si != nread) {
1972     /* Only use the new buffer if we replaced something */
1973     nread = si;
1974
1975     /* Upload from the new (replaced) buffer instead */
1976     data->req.upload_fromhere = data->state.scratch;
1977
1978     /* Set the new amount too */
1979     data->req.upload_present = nread;
1980   }
1981
1982   return CURLE_OK;
1983 }
1984
1985 #endif /* CURL_DISABLE_SMTP */