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