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