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