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