1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at http://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * RFC2821 SMTP protocol
22  * RFC3207 SMTP over TLS
23  * RFC4954 SMTP Authentication
24  * RFC2195 CRAM-MD5 authentication
25  * RFC4616 PLAIN authentication
26  *
27  ***************************************************************************/
28 
29 #include "setup.h"
30 
31 #ifndef CURL_DISABLE_SMTP
32 #include <stdio.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <stdarg.h>
36 #include <ctype.h>
37 
38 #ifdef HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41 
42 #ifdef HAVE_SYS_SOCKET_H
43 #include <sys/socket.h>
44 #endif
45 #ifdef HAVE_NETINET_IN_H
46 #include <netinet/in.h>
47 #endif
48 #ifdef HAVE_ARPA_INET_H
49 #include <arpa/inet.h>
50 #endif
51 #ifdef HAVE_UTSNAME_H
52 #include <sys/utsname.h>
53 #endif
54 #ifdef HAVE_NETDB_H
55 #include <netdb.h>
56 #endif
57 #ifdef __VMS
58 #include <in.h>
59 #include <inet.h>
60 #endif
61 
62 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
63 #undef in_addr_t
64 #define in_addr_t unsigned long
65 #endif
66 
67 #include <curl/curl.h>
68 #include "urldata.h"
69 #include "sendf.h"
70 #include "if2ip.h"
71 #include "hostip.h"
72 #include "progress.h"
73 #include "transfer.h"
74 #include "escape.h"
75 #include "http.h" /* for HTTP proxy tunnel stuff */
76 #include "socks.h"
77 #include "smtp.h"
78 
79 #include "strtoofft.h"
80 #include "strequal.h"
81 #include "sslgen.h"
82 #include "connect.h"
83 #include "strerror.h"
84 #include "select.h"
85 #include "multiif.h"
86 #include "url.h"
87 #include "rawstr.h"
88 #include "strtoofft.h"
89 #include "curl_base64.h"
90 #include "curl_md5.h"
91 #include "curl_hmac.h"
92 #include "curl_gethostname.h"
93 #include "warnless.h"
94 #include "http_proxy.h"
95 
96 #define _MPRINTF_REPLACE /* use our functions only */
97 #include <curl/mprintf.h>
98 
99 #include "curl_memory.h"
100 /* The last #include file should be: */
101 #include "memdebug.h"
102 
103 /* Local API functions */
104 static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
105 static CURLcode smtp_do(struct connectdata *conn, bool *done);
106 static CURLcode smtp_done(struct connectdata *conn,
107                           CURLcode, bool premature);
108 static CURLcode smtp_connect(struct connectdata *conn, bool *done);
109 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection);
110 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
111 static int smtp_getsock(struct connectdata *conn,
112                         curl_socket_t *socks,
113                         int numsocks);
114 static CURLcode smtp_doing(struct connectdata *conn,
115                            bool *dophase_done);
116 static CURLcode smtp_setup_connection(struct connectdata * conn);
117 static CURLcode smtp_state_upgrade_tls(struct connectdata *conn);
118 
119 
120 /*
121  * SMTP protocol handler.
122  */
123 
124 const struct Curl_handler Curl_handler_smtp = {
125   "SMTP",                           /* scheme */
126   smtp_setup_connection,            /* setup_connection */
127   smtp_do,                          /* do_it */
128   smtp_done,                        /* done */
129   ZERO_NULL,                        /* do_more */
130   smtp_connect,                     /* connect_it */
131   smtp_multi_statemach,             /* connecting */
132   smtp_doing,                       /* doing */
133   smtp_getsock,                     /* proto_getsock */
134   smtp_getsock,                     /* doing_getsock */
135   ZERO_NULL,                        /* perform_getsock */
136   smtp_disconnect,                  /* disconnect */
137   PORT_SMTP,                        /* defport */
138   CURLPROTO_SMTP,                   /* protocol */
139   PROTOPT_CLOSEACTION               /* flags */
140 };
141 
142 
143 #ifdef USE_SSL
144 /*
145  * SMTPS protocol handler.
146  */
147 
148 const struct Curl_handler Curl_handler_smtps = {
149   "SMTPS",                          /* scheme */
150   smtp_setup_connection,            /* setup_connection */
151   smtp_do,                          /* do_it */
152   smtp_done,                        /* done */
153   ZERO_NULL,                        /* do_more */
154   smtp_connect,                     /* connect_it */
155   smtp_multi_statemach,             /* connecting */
156   smtp_doing,                       /* doing */
157   smtp_getsock,                     /* proto_getsock */
158   smtp_getsock,                     /* doing_getsock */
159   ZERO_NULL,                        /* perform_getsock */
160   smtp_disconnect,                  /* disconnect */
161   PORT_SMTPS,                       /* defport */
162   CURLPROTO_SMTP | CURLPROTO_SMTPS, /* protocol */
163   PROTOPT_CLOSEACTION | PROTOPT_SSL /* flags */
164 };
165 #endif
166 
167 #ifndef CURL_DISABLE_HTTP
168 /*
169  * HTTP-proxyed SMTP protocol handler.
170  */
171 
172 static const struct Curl_handler Curl_handler_smtp_proxy = {
173   "SMTP",                               /* scheme */
174   ZERO_NULL,                            /* setup_connection */
175   Curl_http,                            /* do_it */
176   Curl_http_done,                       /* done */
177   ZERO_NULL,                            /* do_more */
178   ZERO_NULL,                            /* connect_it */
179   ZERO_NULL,                            /* connecting */
180   ZERO_NULL,                            /* doing */
181   ZERO_NULL,                            /* proto_getsock */
182   ZERO_NULL,                            /* doing_getsock */
183   ZERO_NULL,                            /* perform_getsock */
184   ZERO_NULL,                            /* disconnect */
185   PORT_SMTP,                            /* defport */
186   CURLPROTO_HTTP,                       /* protocol */
187   PROTOPT_NONE                          /* flags */
188 };
189 
190 
191 #ifdef USE_SSL
192 /*
193  * HTTP-proxyed SMTPS protocol handler.
194  */
195 
196 static const struct Curl_handler Curl_handler_smtps_proxy = {
197   "SMTPS",                              /* scheme */
198   ZERO_NULL,                            /* setup_connection */
199   Curl_http,                            /* do_it */
200   Curl_http_done,                       /* done */
201   ZERO_NULL,                            /* do_more */
202   ZERO_NULL,                            /* connect_it */
203   ZERO_NULL,                            /* connecting */
204   ZERO_NULL,                            /* doing */
205   ZERO_NULL,                            /* proto_getsock */
206   ZERO_NULL,                            /* doing_getsock */
207   ZERO_NULL,                            /* perform_getsock */
208   ZERO_NULL,                            /* disconnect */
209   PORT_SMTPS,                           /* defport */
210   CURLPROTO_HTTP,                       /* protocol */
211   PROTOPT_NONE                          /* flags */
212 };
213 #endif
214 #endif
215 
216 
217 /* Function that checks for an ending smtp status code at the start of the
218    given string.
219    As a side effect, it also flags allowed authentication mechanisms according
220    to EHLO AUTH response. */
smtp_endofresp(struct pingpong * pp,int * resp)221 static int smtp_endofresp(struct pingpong *pp, int *resp)
222 {
223   char *line = pp->linestart_resp;
224   size_t len = pp->nread_resp;
225   struct connectdata *conn = pp->conn;
226   struct smtp_conn *smtpc = &conn->proto.smtpc;
227   int result;
228   size_t wordlen;
229 
230   if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
231     return FALSE;       /* Nothing for us. */
232 
233   if((result = (line[3] == ' ')) != 0)
234     *resp = curlx_sltosi(strtol(line, NULL, 10));
235 
236   line += 4;
237   len -= 4;
238 
239   if(smtpc->state == SMTP_EHLO && len >= 5 && !memcmp(line, "AUTH ", 5)) {
240     line += 5;
241     len -= 5;
242 
243     for (;;) {
244       while (len &&
245              (*line == ' ' || *line == '\t' ||
246               *line == '\r' || *line == '\n')) {
247         line++;
248         len--;
249       }
250 
251       if(!len)
252         break;
253 
254       for (wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
255              line[wordlen] != '\t' && line[wordlen] != '\r' &&
256              line[wordlen] != '\n';)
257         wordlen++;
258 
259       if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
260         smtpc->authmechs |= SMTP_AUTH_LOGIN;
261       else if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
262         smtpc->authmechs |= SMTP_AUTH_PLAIN;
263       else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
264         smtpc->authmechs |= SMTP_AUTH_CRAM_MD5;
265       else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
266         smtpc->authmechs |= SMTP_AUTH_DIGEST_MD5;
267       else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
268         smtpc->authmechs |= SMTP_AUTH_GSSAPI;
269       else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
270         smtpc->authmechs |= SMTP_AUTH_EXTERNAL;
271 
272       line += wordlen;
273       len -= wordlen;
274     }
275   }
276 
277   return result;
278 }
279 
280 /* This is the ONLY way to change SMTP state! */
state(struct connectdata * conn,smtpstate newstate)281 static void state(struct connectdata *conn,
282                   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     "AUTHPLAIN",
295     "AUTHLOGIN",
296     "AUTHPASSWD",
297     "AUTHCRAM",
298     "AUTH",
299     "MAIL",
300     "RCPT",
301     "DATA",
302     "POSTDATA",
303     "QUIT",
304     /* LAST */
305   };
306   if(smtpc->state != newstate)
307     infof(conn->data, "SMTP %p state change from %s to %s\n",
308           smtpc, names[smtpc->state], names[newstate]);
309 #endif
310   smtpc->state = newstate;
311 }
312 
smtp_state_ehlo(struct connectdata * conn)313 static CURLcode smtp_state_ehlo(struct connectdata *conn)
314 {
315   CURLcode result;
316   struct smtp_conn *smtpc = &conn->proto.smtpc;
317 
318   smtpc->authmechs = 0;         /* No known authentication mechanisms yet. */
319 
320   /* send EHLO */
321   result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
322 
323   if(result)
324     return result;
325 
326   state(conn, SMTP_EHLO);
327   return CURLE_OK;
328 }
329 
smtp_state_helo(struct connectdata * conn)330 static CURLcode smtp_state_helo(struct connectdata *conn)
331 {
332   CURLcode result;
333   struct smtp_conn *smtpc = &conn->proto.smtpc;
334 
335   /* send HELO */
336   result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
337 
338   if(result)
339     return result;
340 
341   state(conn, SMTP_HELO);
342   return CURLE_OK;
343 }
344 
smtp_auth_plain_data(struct connectdata * conn,char ** outptr)345 static size_t smtp_auth_plain_data(struct connectdata * conn, char * * outptr)
346 {
347   char plainauth[2 * MAX_CURL_USER_LENGTH + MAX_CURL_PASSWORD_LENGTH];
348   size_t ulen;
349   size_t plen;
350 
351   ulen = strlen(conn->user);
352   plen = strlen(conn->passwd);
353 
354   if(2 * ulen + plen + 2 > sizeof plainauth)
355     return 0;
356 
357   memcpy(plainauth, conn->user, ulen);
358   plainauth[ulen] = '\0';
359   memcpy(plainauth + ulen + 1, conn->user, ulen);
360   plainauth[2 * ulen + 1] = '\0';
361   memcpy(plainauth + 2 * ulen + 2, conn->passwd, plen);
362   return Curl_base64_encode(conn->data, plainauth, 2 * ulen + plen + 2, outptr);
363 }
364 
smtp_auth_login_user(struct connectdata * conn,char ** outptr)365 static size_t smtp_auth_login_user(struct connectdata * conn, char * * outptr)
366 {
367   size_t ulen;
368 
369   ulen = strlen(conn->user);
370 
371   if(!ulen) {
372     *outptr = strdup("=");
373     return *outptr? 1: 0;
374   }
375 
376   return Curl_base64_encode(conn->data, conn->user, ulen, outptr);
377 }
378 
smtp_authenticate(struct connectdata * conn)379 static CURLcode smtp_authenticate(struct connectdata *conn)
380 {
381   CURLcode result = CURLE_OK;
382   struct smtp_conn *smtpc = &conn->proto.smtpc;
383   char * initresp;
384   const char * mech;
385   size_t l;
386   smtpstate state1;
387   smtpstate state2;
388 
389   if(!conn->bits.user_passwd)
390     state(conn, SMTP_STOP);             /* End of connect phase. */
391   else {
392     initresp = (char *) NULL;
393     l = 1;
394 
395     /* Check supported authentication mechanisms by decreasing order of
396        preference. */
397     mech = (const char *) NULL;         /* Avoid compiler warnings. */
398     state1 = SMTP_STOP;
399     state2 = SMTP_STOP;
400 
401 #ifndef CURL_DISABLE_CRYPTO_AUTH
402     if(smtpc->authmechs & SMTP_AUTH_CRAM_MD5) {
403       mech = "CRAM-MD5";
404       state1 = SMTP_AUTHCRAM;
405     }
406     else
407 #endif
408     if(smtpc->authmechs & SMTP_AUTH_PLAIN) {
409       mech = "PLAIN";
410       state1 = SMTP_AUTHPLAIN;
411       state2 = SMTP_AUTH;
412       l = smtp_auth_plain_data(conn, &initresp);
413     }
414     else if(smtpc->authmechs & SMTP_AUTH_LOGIN) {
415       mech = "LOGIN";
416       state1 = SMTP_AUTHLOGIN;
417       state2 = SMTP_AUTHPASSWD;
418       l = smtp_auth_login_user(conn, &initresp);
419     }
420     else {
421       infof(conn->data, "No known auth mechanisms supported!\n");
422       result = CURLE_LOGIN_DENIED;      /* Other mechanisms not supported. */
423     }
424 
425     if(!result) {
426       if(!l)
427         result = CURLE_OUT_OF_MEMORY;
428       else if(initresp &&
429               l + strlen(mech) <= 512 - 8) {   /* AUTH <mech> ...<crlf> */
430         result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
431         free(initresp);
432 
433         if(!result)
434           state(conn, state2);
435       }
436       else {
437         Curl_safefree(initresp);
438 
439         result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
440 
441         if(!result)
442           state(conn, state1);
443       }
444     }
445   }
446 
447   return result;
448 }
449 
450 /* For the SMTP "protocol connect" and "doing" phases only */
smtp_getsock(struct connectdata * conn,curl_socket_t * socks,int numsocks)451 static int smtp_getsock(struct connectdata *conn,
452                         curl_socket_t *socks,
453                         int numsocks)
454 {
455   return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
456 }
457 
458 #ifdef USE_SSL
smtp_to_smtps(struct connectdata * conn)459 static void smtp_to_smtps(struct connectdata *conn)
460 {
461   conn->handler = &Curl_handler_smtps;
462 }
463 #else
464 #define smtp_to_smtps(x)
465 #endif
466 
467 /* for STARTTLS responses */
smtp_state_starttls_resp(struct connectdata * conn,int smtpcode,smtpstate instate)468 static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
469                                          int smtpcode,
470                                          smtpstate instate)
471 {
472   CURLcode result = CURLE_OK;
473   struct SessionHandle *data = conn->data;
474   (void)instate; /* no use for this yet */
475 
476   if(smtpcode != 220) {
477     if(data->set.ftp_ssl != CURLUSESSL_TRY) {
478       failf(data, "STARTTLS denied. %c", smtpcode);
479       result = CURLE_LOGIN_DENIED;
480     }
481     else
482       result = smtp_authenticate(conn);
483   }
484   else {
485     if(data->state.used_interface == Curl_if_multi) {
486       state(conn, SMTP_UPGRADETLS);
487       return smtp_state_upgrade_tls(conn);
488     }
489     else {
490       result = Curl_ssl_connect(conn, FIRSTSOCKET);
491       if(CURLE_OK == result) {
492         smtp_to_smtps(conn);
493         result = smtp_state_ehlo(conn);
494       }
495     }
496   }
497   return result;
498 }
499 
smtp_state_upgrade_tls(struct connectdata * conn)500 static CURLcode smtp_state_upgrade_tls(struct connectdata *conn)
501 {
502   struct smtp_conn *smtpc = &conn->proto.smtpc;
503   CURLcode result;
504 
505   result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
506 
507   if(smtpc->ssldone) {
508     smtp_to_smtps(conn);
509     result = smtp_state_ehlo(conn);
510   }
511 
512   return result;
513 }
514 
515 /* for EHLO responses */
smtp_state_ehlo_resp(struct connectdata * conn,int smtpcode,smtpstate instate)516 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn,
517                                      int smtpcode,
518                                      smtpstate instate)
519 {
520   CURLcode result = CURLE_OK;
521   struct SessionHandle *data = conn->data;
522 
523   (void)instate; /* no use for this yet */
524 
525   if(smtpcode/100 != 2) {
526     if((data->set.ftp_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) &&
527      !conn->bits.user_passwd)
528       result = smtp_state_helo(conn);
529     else {
530       failf(data, "Access denied: %d", smtpcode);
531       result = CURLE_LOGIN_DENIED;
532     }
533   }
534   else if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
535     /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
536        to TLS connection now */
537     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "STARTTLS");
538     state(conn, SMTP_STARTTLS);
539   }
540   else
541     result = smtp_authenticate(conn);
542 
543   return result;
544 }
545 
546 /* for HELO responses */
smtp_state_helo_resp(struct connectdata * conn,int smtpcode,smtpstate instate)547 static CURLcode smtp_state_helo_resp(struct connectdata *conn,
548                                      int smtpcode,
549                                      smtpstate instate)
550 {
551   CURLcode result = CURLE_OK;
552   struct SessionHandle *data = conn->data;
553 
554   (void)instate; /* no use for this yet */
555 
556   if(smtpcode/100 != 2) {
557     failf(data, "Access denied: %d", smtpcode);
558     result = CURLE_LOGIN_DENIED;
559   }
560   else {
561     /* end the connect phase */
562     state(conn, SMTP_STOP);
563   }
564   return result;
565 }
566 
567 /* for AUTH PLAIN (without initial response) responses */
smtp_state_authplain_resp(struct connectdata * conn,int smtpcode,smtpstate instate)568 static CURLcode smtp_state_authplain_resp(struct connectdata *conn,
569                                           int smtpcode,
570                                           smtpstate instate)
571 {
572   CURLcode result = CURLE_OK;
573   struct SessionHandle *data = conn->data;
574   size_t l;
575   char * plainauth;
576 
577   (void)instate; /* no use for this yet */
578 
579   if(smtpcode != 334) {
580     failf(data, "Access denied: %d", smtpcode);
581     result = CURLE_LOGIN_DENIED;
582   }
583   else {
584     l = smtp_auth_plain_data(conn, &plainauth);
585 
586     if(!l)
587       result = CURLE_OUT_OF_MEMORY;
588     else {
589       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth);
590       free(plainauth);
591 
592       if(!result)
593         state(conn, SMTP_AUTH);
594     }
595   }
596 
597   return result;
598 }
599 
600 /* for AUTH LOGIN (without initial response) responses */
smtp_state_authlogin_resp(struct connectdata * conn,int smtpcode,smtpstate instate)601 static CURLcode smtp_state_authlogin_resp(struct connectdata *conn,
602                                           int smtpcode,
603                                           smtpstate instate)
604 {
605   CURLcode result = CURLE_OK;
606   struct SessionHandle *data = conn->data;
607   size_t l;
608   char * authuser;
609 
610   (void)instate; /* no use for this yet */
611 
612   if(smtpcode != 334) {
613     failf(data, "Access denied: %d", smtpcode);
614     result = CURLE_LOGIN_DENIED;
615   }
616   else {
617     l = smtp_auth_login_user(conn, &authuser);
618 
619     if(!l)
620       result = CURLE_OUT_OF_MEMORY;
621     else {
622       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser);
623       free(authuser);
624 
625       if(!result)
626         state(conn, SMTP_AUTHPASSWD);
627     }
628   }
629 
630   return result;
631 }
632 
633 /* for responses to user entry of AUTH LOGIN. */
smtp_state_authpasswd_resp(struct connectdata * conn,int smtpcode,smtpstate instate)634 static CURLcode smtp_state_authpasswd_resp(struct connectdata *conn,
635                                            int smtpcode,
636                                            smtpstate instate)
637 {
638   CURLcode result = CURLE_OK;
639   struct SessionHandle *data = conn->data;
640   size_t plen;
641   size_t l;
642   char *authpasswd;
643 
644   (void)instate; /* no use for this yet */
645 
646   if(smtpcode != 334) {
647     failf(data, "Access denied: %d", smtpcode);
648     result = CURLE_LOGIN_DENIED;
649   }
650   else {
651     plen = strlen(conn->passwd);
652 
653     if(!plen)
654       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "=");
655     else {
656       l = Curl_base64_encode(data, conn->passwd, plen, &authpasswd);
657 
658       if(!l)
659         result = CURLE_OUT_OF_MEMORY;
660       else {
661         result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd);
662         free(authpasswd);
663 
664         if(!result)
665           state(conn, SMTP_AUTH);
666       }
667     }
668   }
669 
670   return result;
671 }
672 
673 #ifndef CURL_DISABLE_CRYPTO_AUTH
674 
675 /* for AUTH CRAM-MD5 responses. */
smtp_state_authcram_resp(struct connectdata * conn,int smtpcode,smtpstate instate)676 static CURLcode smtp_state_authcram_resp(struct connectdata *conn,
677                                          int smtpcode,
678                                          smtpstate instate)
679 {
680   CURLcode result = CURLE_OK;
681   struct SessionHandle *data = conn->data;
682   char * chlg64 = data->state.buffer;
683   unsigned char * chlg;
684   size_t chlglen;
685   size_t l;
686   char * rplyb64;
687   HMAC_context * ctxt;
688   unsigned char digest[16];
689   char reply[MAX_CURL_USER_LENGTH + 32 /* 2 * size of MD5 digest */ + 1];
690 
691   (void)instate; /* no use for this yet */
692 
693   if(smtpcode != 334) {
694     failf(data, "Access denied: %d", smtpcode);
695     return CURLE_LOGIN_DENIED;
696   }
697 
698   /* Get the challenge. */
699   for (chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
700     ;
701 
702   chlg = (unsigned char *) NULL;
703   chlglen = 0;
704 
705   if(*chlg64 != '=') {
706     for (l = strlen(chlg64); l--;)
707       if(chlg64[l] != '\r' && chlg64[l] != '\n' && chlg64[l] != ' ' &&
708        chlg64[l] != '\t')
709         break;
710 
711     if(++l) {
712       chlg64[l] = '\0';
713 
714       chlglen = Curl_base64_decode(chlg64, &chlg);
715       if(!chlglen)
716         return CURLE_OUT_OF_MEMORY;
717     }
718   }
719 
720   /* Compute digest. */
721   ctxt = Curl_HMAC_init(Curl_HMAC_MD5,
722                         (const unsigned char *) conn->passwd,
723                         (unsigned int)(strlen(conn->passwd)));
724 
725   if(!ctxt) {
726     if(chlg)
727       free(chlg);
728 
729     return CURLE_OUT_OF_MEMORY;
730   }
731 
732   if(chlglen > 0)
733     Curl_HMAC_update(ctxt, chlg, (unsigned int)(chlglen));
734 
735   if(chlg)
736     free(chlg);
737 
738   Curl_HMAC_final(ctxt, digest);
739 
740   /* Prepare the reply. */
741   snprintf(reply, sizeof reply,
742    "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
743    conn->user, digest[0], digest[1], digest[2], digest[3], digest[4], digest[5],
744    digest[6], digest[7], digest[8], digest[9], digest[10], digest[11],
745    digest[12], digest[13], digest[14], digest[15]);
746 
747   /* Encode it to base64 and send it. */
748   l = Curl_base64_encode(data, reply, 0, &rplyb64);
749 
750   if(!l)
751     result = CURLE_OUT_OF_MEMORY;
752   else {
753     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
754     free(rplyb64);
755 
756     if(!result)
757       state(conn, SMTP_AUTH);
758   }
759 
760   return result;
761 }
762 
763 #endif
764 
765 /* for final responses to AUTH sequences. */
smtp_state_auth_resp(struct connectdata * conn,int smtpcode,smtpstate instate)766 static CURLcode smtp_state_auth_resp(struct connectdata *conn,
767                                      int smtpcode,
768                                      smtpstate instate)
769 {
770   CURLcode result = CURLE_OK;
771   struct SessionHandle *data = conn->data;
772 
773   (void)instate; /* no use for this yet */
774 
775   if(smtpcode != 235) {
776     failf(data, "Authentication failed: %d", smtpcode);
777     result = CURLE_LOGIN_DENIED;
778   }
779   else
780     state(conn, SMTP_STOP);             /* End of connect phase. */
781 
782   return result;
783 }
784 
785 /* start the DO phase */
smtp_mail(struct connectdata * conn)786 static CURLcode smtp_mail(struct connectdata *conn)
787 {
788   CURLcode result = CURLE_OK;
789   struct SessionHandle *data = conn->data;
790 
791   /* send MAIL FROM */
792   if (data->set.str[STRING_MAIL_FROM][0] == '<')
793     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "MAIL FROM:%s",
794                            data->set.str[STRING_MAIL_FROM]);
795   else
796     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "MAIL FROM:<%s>",
797                            data->set.str[STRING_MAIL_FROM]);
798   if(result)
799     return result;
800 
801   state(conn, SMTP_MAIL);
802   return result;
803 }
804 
smtp_rcpt_to(struct connectdata * conn)805 static CURLcode smtp_rcpt_to(struct connectdata *conn)
806 {
807   CURLcode result = CURLE_OK;
808   struct smtp_conn *smtpc = &conn->proto.smtpc;
809 
810   /* send RCPT TO */
811   if(smtpc->rcpt) {
812     if(smtpc->rcpt->data[0] == '<')
813       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
814                              smtpc->rcpt->data);
815     else
816       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
817                              smtpc->rcpt->data);
818     if(!result)
819       state(conn, SMTP_RCPT);
820   }
821   return result;
822 }
823 
824 /* for MAIL responses */
smtp_state_mail_resp(struct connectdata * conn,int smtpcode,smtpstate instate)825 static CURLcode smtp_state_mail_resp(struct connectdata *conn,
826                                      int smtpcode,
827                                      smtpstate instate)
828 {
829   CURLcode result = CURLE_OK;
830   struct SessionHandle *data = conn->data;
831   (void)instate; /* no use for this yet */
832 
833   if(smtpcode/100 != 2) {
834     failf(data, "Access denied: %d", smtpcode);
835     result = CURLE_LOGIN_DENIED;
836     state(conn, SMTP_STOP);
837   }
838   else {
839     struct smtp_conn *smtpc = &conn->proto.smtpc;
840     smtpc->rcpt = data->set.mail_rcpt;
841 
842     result = smtp_rcpt_to(conn);
843   }
844 
845   return result;
846 }
847 
848 /* for RCPT responses */
smtp_state_rcpt_resp(struct connectdata * conn,int smtpcode,smtpstate instate)849 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn,
850                                      int smtpcode,
851                                      smtpstate instate)
852 {
853   CURLcode result = CURLE_OK;
854   struct SessionHandle *data = conn->data;
855   (void)instate; /* no use for this yet */
856 
857   if(smtpcode/100 != 2) {
858     failf(data, "Access denied: %d", smtpcode);
859     result = CURLE_LOGIN_DENIED;
860     state(conn, SMTP_STOP);
861   }
862   else {
863     struct smtp_conn *smtpc = &conn->proto.smtpc;
864 
865     if(smtpc->rcpt) {
866       smtpc->rcpt = smtpc->rcpt->next;
867       result = smtp_rcpt_to(conn);
868 
869       /* if we failed or still is in RCPT sending, return */
870       if(result || smtpc->rcpt)
871         return result;
872     }
873 
874     /* send DATA */
875     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "DATA");
876     if(result)
877       return result;
878 
879     state(conn, SMTP_DATA);
880   }
881   return result;
882 }
883 
884 /* for the DATA response */
smtp_state_data_resp(struct connectdata * conn,int smtpcode,smtpstate instate)885 static CURLcode smtp_state_data_resp(struct connectdata *conn,
886                                      int smtpcode,
887                                      smtpstate instate)
888 {
889   struct SessionHandle *data = conn->data;
890   struct FTP *smtp = data->state.proto.smtp;
891 
892   (void)instate; /* no use for this yet */
893 
894   if(smtpcode != 354) {
895     state(conn, SMTP_STOP);
896     return CURLE_RECV_ERROR;
897   }
898 
899   /* SMTP upload */
900   Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
901                       FIRSTSOCKET, smtp->bytecountp);
902 
903   state(conn, SMTP_STOP);
904   return CURLE_OK;
905 }
906 
907 /* for the POSTDATA response, which is received after the entire DATA
908    part has been sent off to the server */
smtp_state_postdata_resp(struct connectdata * conn,int smtpcode,smtpstate instate)909 static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
910                                      int smtpcode,
911                                      smtpstate instate)
912 {
913   CURLcode result = CURLE_OK;
914 
915   (void)instate; /* no use for this yet */
916 
917   if(smtpcode != 250)
918     result = CURLE_RECV_ERROR;
919 
920   state(conn, SMTP_STOP);
921   return result;
922 }
923 
smtp_statemach_act(struct connectdata * conn)924 static CURLcode smtp_statemach_act(struct connectdata *conn)
925 {
926   CURLcode result;
927   curl_socket_t sock = conn->sock[FIRSTSOCKET];
928   struct SessionHandle *data=conn->data;
929   int smtpcode;
930   struct smtp_conn *smtpc = &conn->proto.smtpc;
931   struct pingpong *pp = &smtpc->pp;
932   size_t nread = 0;
933 
934   if(smtpc->state == SMTP_UPGRADETLS)
935     return smtp_state_upgrade_tls(conn);
936 
937   if(pp->sendleft)
938     /* we have a piece of a command still left to send */
939     return Curl_pp_flushsend(pp);
940 
941   /* we read a piece of response */
942   result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
943   if(result)
944     return result;
945 
946   if(smtpcode) {
947     /* we have now received a full SMTP server response */
948     switch(smtpc->state) {
949     case SMTP_SERVERGREET:
950       if(smtpcode/100 != 2) {
951         failf(data, "Got unexpected smtp-server response: %d", smtpcode);
952         return CURLE_FTP_WEIRD_SERVER_REPLY;
953       }
954 
955       result = smtp_state_ehlo(conn);
956       if(result)
957         return result;
958       break;
959 
960     case SMTP_EHLO:
961       result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
962       break;
963 
964     case SMTP_HELO:
965       result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
966       break;
967 
968     case SMTP_STARTTLS:
969       result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
970       break;
971 
972     case SMTP_AUTHPLAIN:
973       result = smtp_state_authplain_resp(conn, smtpcode, smtpc->state);
974       break;
975 
976     case SMTP_AUTHLOGIN:
977       result = smtp_state_authlogin_resp(conn, smtpcode, smtpc->state);
978       break;
979 
980     case SMTP_AUTHPASSWD:
981       result = smtp_state_authpasswd_resp(conn, smtpcode, smtpc->state);
982       break;
983 
984 #ifndef CURL_DISABLE_CRYPTO_AUTH
985     case SMTP_AUTHCRAM:
986       result = smtp_state_authcram_resp(conn, smtpcode, smtpc->state);
987       break;
988 #endif
989 
990     case SMTP_AUTH:
991       result = smtp_state_auth_resp(conn, smtpcode, smtpc->state);
992       break;
993 
994     case SMTP_MAIL:
995       result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
996       break;
997 
998     case SMTP_RCPT:
999       result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
1000       break;
1001 
1002     case SMTP_DATA:
1003       result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
1004       break;
1005 
1006     case SMTP_POSTDATA:
1007       result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
1008       break;
1009 
1010     case SMTP_QUIT:
1011       /* fallthrough, just stop! */
1012     default:
1013       /* internal error */
1014       state(conn, SMTP_STOP);
1015       break;
1016     }
1017   }
1018   return result;
1019 }
1020 
1021 /* called repeatedly until done from multi.c */
smtp_multi_statemach(struct connectdata * conn,bool * done)1022 static CURLcode smtp_multi_statemach(struct connectdata *conn,
1023                                      bool *done)
1024 {
1025   struct smtp_conn *smtpc = &conn->proto.smtpc;
1026   CURLcode result;
1027 
1028   if((conn->handler->protocol & CURLPROTO_SMTPS) && !smtpc->ssldone) {
1029     result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
1030   }
1031   else {
1032     result = Curl_pp_multi_statemach(&smtpc->pp);
1033   }
1034 
1035   *done = (bool)(smtpc->state == SMTP_STOP);
1036 
1037   return result;
1038 }
1039 
smtp_easy_statemach(struct connectdata * conn)1040 static CURLcode smtp_easy_statemach(struct connectdata *conn)
1041 {
1042   struct smtp_conn *smtpc = &conn->proto.smtpc;
1043   struct pingpong *pp = &smtpc->pp;
1044   CURLcode result = CURLE_OK;
1045 
1046   while(smtpc->state != SMTP_STOP) {
1047     result = Curl_pp_easy_statemach(pp);
1048     if(result)
1049       break;
1050   }
1051 
1052   return result;
1053 }
1054 
1055 /*
1056  * Allocate and initialize the struct SMTP for the current SessionHandle.  If
1057  * need be.
1058  */
smtp_init(struct connectdata * conn)1059 static CURLcode smtp_init(struct connectdata *conn)
1060 {
1061   struct SessionHandle *data = conn->data;
1062   struct FTP *smtp = data->state.proto.smtp;
1063   if(!smtp) {
1064     smtp = data->state.proto.smtp = calloc(sizeof(struct FTP), 1);
1065     if(!smtp)
1066       return CURLE_OUT_OF_MEMORY;
1067   }
1068 
1069   /* get some initial data into the smtp struct */
1070   smtp->bytecountp = &data->req.bytecount;
1071 
1072   /* No need to duplicate user+password, the connectdata struct won't change
1073      during a session, but we re-init them here since on subsequent inits
1074      since the conn struct may have changed or been replaced.
1075   */
1076   smtp->user = conn->user;
1077   smtp->passwd = conn->passwd;
1078 
1079   return CURLE_OK;
1080 }
1081 
1082 /*
1083  * smtp_connect() should do everything that is to be considered a part of
1084  * the connection phase.
1085  *
1086  * The variable 'done' points to will be TRUE if the protocol-layer connect
1087  * phase is done when this function returns, or FALSE is not. When called as
1088  * a part of the easy interface, it will always be TRUE.
1089  */
smtp_connect(struct connectdata * conn,bool * done)1090 static CURLcode smtp_connect(struct connectdata *conn,
1091                              bool *done) /* see description above */
1092 {
1093   CURLcode result;
1094   struct smtp_conn *smtpc = &conn->proto.smtpc;
1095   struct SessionHandle *data=conn->data;
1096   struct pingpong *pp=&smtpc->pp;
1097   const char *path = conn->data->state.path;
1098   int len;
1099   char localhost[1024 + 1];
1100 
1101   *done = FALSE; /* default to not done yet */
1102 
1103   /* If there already is a protocol-specific struct allocated for this
1104      sessionhandle, deal with it */
1105   Curl_reset_reqproto(conn);
1106 
1107   result = smtp_init(conn);
1108   if(CURLE_OK != result)
1109     return result;
1110 
1111   /* We always support persistent connections on smtp */
1112   conn->bits.close = FALSE;
1113 
1114   pp->response_time = RESP_TIMEOUT; /* set default response time-out */
1115   pp->statemach_act = smtp_statemach_act;
1116   pp->endofresp = smtp_endofresp;
1117   pp->conn = conn;
1118 
1119   if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
1120     /* for SMTP over HTTP proxy */
1121     struct HTTP http_proxy;
1122     struct FTP *smtp_save;
1123 
1124     /* BLOCKING */
1125     /* We want "seamless" SMTP operations through HTTP proxy tunnel */
1126 
1127     /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
1128      * conn->proto.http; we want SMTP through HTTP and we have to change the
1129      * member temporarily for connecting to the HTTP proxy. After
1130      * Curl_proxyCONNECT we have to set back the member to the original struct
1131      * SMTP pointer
1132      */
1133     smtp_save = data->state.proto.smtp;
1134     memset(&http_proxy, 0, sizeof(http_proxy));
1135     data->state.proto.http = &http_proxy;
1136 
1137     result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
1138                                conn->host.name, conn->remote_port);
1139 
1140     data->state.proto.smtp = smtp_save;
1141 
1142     if(CURLE_OK != result)
1143       return result;
1144   }
1145 
1146   if((conn->handler->protocol & CURLPROTO_SMTPS) &&
1147       data->state.used_interface != Curl_if_multi) {
1148     /* SMTPS is simply smtp with SSL for the control channel */
1149     /* now, perform the SSL initialization for this socket */
1150     result = Curl_ssl_connect(conn, FIRSTSOCKET);
1151     if(result)
1152       return result;
1153   }
1154 
1155   Curl_pp_init(pp); /* init the response reader stuff */
1156 
1157   pp->response_time = RESP_TIMEOUT; /* set default response time-out */
1158   pp->statemach_act = smtp_statemach_act;
1159   pp->endofresp = smtp_endofresp;
1160   pp->conn = conn;
1161 
1162   if(!*path) {
1163     if(!Curl_gethostname(localhost, sizeof localhost))
1164       path = localhost;
1165     else
1166       path = "localhost";
1167   }
1168 
1169   /* url decode the path and use it as domain with EHLO */
1170   smtpc->domain = curl_easy_unescape(conn->data, path, 0, &len);
1171   if(!smtpc->domain)
1172     return CURLE_OUT_OF_MEMORY;
1173 
1174   /* When we connect, we start in the state where we await the server greeting
1175    */
1176   state(conn, SMTP_SERVERGREET);
1177 
1178   if(data->state.used_interface == Curl_if_multi)
1179     result = smtp_multi_statemach(conn, done);
1180   else {
1181     result = smtp_easy_statemach(conn);
1182     if(!result)
1183       *done = TRUE;
1184   }
1185 
1186   return result;
1187 }
1188 
1189 /***********************************************************************
1190  *
1191  * smtp_done()
1192  *
1193  * The DONE function. This does what needs to be done after a single DO has
1194  * performed.
1195  *
1196  * Input argument is already checked for validity.
1197  */
smtp_done(struct connectdata * conn,CURLcode status,bool premature)1198 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1199                           bool premature)
1200 {
1201   struct SessionHandle *data = conn->data;
1202   struct FTP *smtp = data->state.proto.smtp;
1203   CURLcode result=CURLE_OK;
1204   ssize_t bytes_written;
1205   (void)premature;
1206 
1207   if(!smtp)
1208     /* When the easy handle is removed from the multi while libcurl is still
1209      * trying to resolve the host name, it seems that the smtp struct is not
1210      * yet initialized, but the removal action calls Curl_done() which calls
1211      * this function. So we simply return success if no smtp pointer is set.
1212      */
1213     return CURLE_OK;
1214 
1215   if(status) {
1216     conn->bits.close = TRUE; /* marked for closure */
1217     result = status;      /* use the already set error code */
1218   }
1219   else
1220     /* TODO: make this work even when the socket is EWOULDBLOCK in this call! */
1221 
1222     /* write to socket (send away data) */
1223     result = Curl_write(conn,
1224                         conn->writesockfd,  /* socket to send to */
1225                         SMTP_EOB,           /* buffer pointer */
1226                         SMTP_EOB_LEN,       /* buffer size */
1227                         &bytes_written);    /* actually sent away */
1228 
1229 
1230   if(status == CURLE_OK) {
1231     struct smtp_conn *smtpc = &conn->proto.smtpc;
1232     struct pingpong *pp= &smtpc->pp;
1233     pp->response = Curl_tvnow(); /* timeout relative now */
1234 
1235     state(conn, SMTP_POSTDATA);
1236     /* run the state-machine
1237 
1238        TODO: when the multi interface is used, this _really_ should be using
1239        the smtp_multi_statemach function but we have no general support for
1240        non-blocking DONE operations, not in the multi state machine and with
1241        Curl_done() invokes on several places in the code!
1242     */
1243     result = smtp_easy_statemach(conn);
1244   }
1245 
1246   /* clear these for next connection */
1247   smtp->transfer = FTPTRANSFER_BODY;
1248 
1249   return result;
1250 }
1251 
1252 /***********************************************************************
1253  *
1254  * smtp_perform()
1255  *
1256  * This is the actual DO function for SMTP. Get a file/directory according to
1257  * the options previously setup.
1258  */
1259 
1260 static
smtp_perform(struct connectdata * conn,bool * connected,bool * dophase_done)1261 CURLcode smtp_perform(struct connectdata *conn,
1262                      bool *connected,  /* connect status after PASV / PORT */
1263                      bool *dophase_done)
1264 {
1265   /* this is SMTP and no proxy */
1266   CURLcode result=CURLE_OK;
1267 
1268   DEBUGF(infof(conn->data, "DO phase starts\n"));
1269 
1270   if(conn->data->set.opt_no_body) {
1271     /* requested no body means no transfer... */
1272     struct FTP *smtp = conn->data->state.proto.smtp;
1273     smtp->transfer = FTPTRANSFER_INFO;
1274   }
1275 
1276   *dophase_done = FALSE; /* not done yet */
1277 
1278   /* start the first command in the DO phase */
1279   result = smtp_mail(conn);
1280   if(result)
1281     return result;
1282 
1283   /* run the state-machine */
1284   if(conn->data->state.used_interface == Curl_if_multi)
1285     result = smtp_multi_statemach(conn, dophase_done);
1286   else {
1287     result = smtp_easy_statemach(conn);
1288     *dophase_done = TRUE; /* with the easy interface we are done here */
1289   }
1290   *connected = conn->bits.tcpconnect;
1291 
1292   if(*dophase_done)
1293     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1294 
1295   return result;
1296 }
1297 
1298 /***********************************************************************
1299  *
1300  * smtp_do()
1301  *
1302  * This function is registered as 'curl_do' function. It decodes the path
1303  * parts etc as a wrapper to the actual DO function (smtp_perform).
1304  *
1305  * The input argument is already checked for validity.
1306  */
smtp_do(struct connectdata * conn,bool * done)1307 static CURLcode smtp_do(struct connectdata *conn, bool *done)
1308 {
1309   CURLcode retcode = CURLE_OK;
1310 
1311   *done = FALSE; /* default to false */
1312 
1313   /*
1314     Since connections can be re-used between SessionHandles, this might be a
1315     connection already existing but on a fresh SessionHandle struct so we must
1316     make sure we have a good 'struct SMTP' to play with. For new connections,
1317     the struct SMTP is allocated and setup in the smtp_connect() function.
1318   */
1319   Curl_reset_reqproto(conn);
1320   retcode = smtp_init(conn);
1321   if(retcode)
1322     return retcode;
1323 
1324   retcode = smtp_regular_transfer(conn, done);
1325 
1326   return retcode;
1327 }
1328 
1329 /***********************************************************************
1330  *
1331  * smtp_quit()
1332  *
1333  * This should be called before calling sclose().  We should then wait for the
1334  * response from the server before returning. The calling code should then try
1335  * to close the connection.
1336  *
1337  */
smtp_quit(struct connectdata * conn)1338 static CURLcode smtp_quit(struct connectdata *conn)
1339 {
1340   CURLcode result = CURLE_OK;
1341 
1342   result = Curl_pp_sendf(&conn->proto.smtpc.pp, "QUIT");
1343   if(result)
1344     return result;
1345   state(conn, SMTP_QUIT);
1346 
1347   result = smtp_easy_statemach(conn);
1348 
1349   return result;
1350 }
1351 
1352 /***********************************************************************
1353  *
1354  * smtp_disconnect()
1355  *
1356  * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1357  * resources. BLOCKING.
1358  */
smtp_disconnect(struct connectdata * conn,bool dead_connection)1359 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection)
1360 {
1361   struct smtp_conn *smtpc= &conn->proto.smtpc;
1362 
1363   /* We cannot send quit unconditionally. If this connection is stale or
1364      bad in any way, sending quit and waiting around here will make the
1365      disconnect wait in vain and cause more problems than we need to.
1366   */
1367 
1368   /* The SMTP session may or may not have been allocated/setup at this
1369      point! */
1370   if(!dead_connection && smtpc->pp.conn)
1371     (void)smtp_quit(conn); /* ignore errors on the LOGOUT */
1372 
1373   Curl_pp_disconnect(&smtpc->pp);
1374 
1375   /* This won't already be freed in some error cases */
1376   Curl_safefree(smtpc->domain);
1377   smtpc->domain = NULL;
1378 
1379   return CURLE_OK;
1380 }
1381 
1382 /* call this when the DO phase has completed */
smtp_dophase_done(struct connectdata * conn,bool connected)1383 static CURLcode smtp_dophase_done(struct connectdata *conn,
1384                                   bool connected)
1385 {
1386   struct FTP *smtp = conn->data->state.proto.smtp;
1387   struct smtp_conn *smtpc= &conn->proto.smtpc;
1388   (void)connected;
1389 
1390   if(smtp->transfer != FTPTRANSFER_BODY)
1391     /* no data to transfer */
1392     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1393 
1394   free(smtpc->domain);
1395   smtpc->domain = NULL;
1396 
1397   return CURLE_OK;
1398 }
1399 
1400 /* called from multi.c while DOing */
smtp_doing(struct connectdata * conn,bool * dophase_done)1401 static CURLcode smtp_doing(struct connectdata *conn,
1402                                bool *dophase_done)
1403 {
1404   CURLcode result;
1405   result = smtp_multi_statemach(conn, dophase_done);
1406 
1407   if(*dophase_done) {
1408     result = smtp_dophase_done(conn, FALSE /* not connected */);
1409 
1410     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1411   }
1412   return result;
1413 }
1414 
1415 /***********************************************************************
1416  *
1417  * smtp_regular_transfer()
1418  *
1419  * The input argument is already checked for validity.
1420  *
1421  * Performs all commands done before a regular transfer between a local and a
1422  * remote host.
1423  */
1424 static
smtp_regular_transfer(struct connectdata * conn,bool * dophase_done)1425 CURLcode smtp_regular_transfer(struct connectdata *conn,
1426                               bool *dophase_done)
1427 {
1428   CURLcode result=CURLE_OK;
1429   bool connected=FALSE;
1430   struct SessionHandle *data = conn->data;
1431   data->req.size = -1; /* make sure this is unknown at this point */
1432 
1433   Curl_pgrsSetUploadCounter(data, 0);
1434   Curl_pgrsSetDownloadCounter(data, 0);
1435   Curl_pgrsSetUploadSize(data, 0);
1436   Curl_pgrsSetDownloadSize(data, 0);
1437 
1438   result = smtp_perform(conn,
1439                         &connected, /* have we connected after PASV/PORT */
1440                         dophase_done); /* all commands in the DO-phase done? */
1441 
1442   if(CURLE_OK == result) {
1443 
1444     if(!*dophase_done)
1445       /* the DO phase has not completed yet */
1446       return CURLE_OK;
1447 
1448     result = smtp_dophase_done(conn, connected);
1449     if(result)
1450       return result;
1451   }
1452 
1453   return result;
1454 }
1455 
smtp_setup_connection(struct connectdata * conn)1456 static CURLcode smtp_setup_connection(struct connectdata * conn)
1457 {
1458   struct SessionHandle *data = conn->data;
1459 
1460   if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1461     /* Unless we have asked to tunnel smtp operations through the proxy, we
1462        switch and use HTTP operations only */
1463 #ifndef CURL_DISABLE_HTTP
1464     if(conn->handler == &Curl_handler_smtp)
1465       conn->handler = &Curl_handler_smtp_proxy;
1466     else {
1467 #ifdef USE_SSL
1468       conn->handler = &Curl_handler_smtps_proxy;
1469 #else
1470       failf(data, "SMTPS not supported!");
1471       return CURLE_UNSUPPORTED_PROTOCOL;
1472 #endif
1473     }
1474     /*
1475      * We explicitly mark this connection as persistent here as we're doing
1476      * SMTP over HTTP and thus we accidentally avoid setting this value
1477      * otherwise.
1478      */
1479     conn->bits.close = FALSE;
1480 #else
1481     failf(data, "SMTP over http proxy requires HTTP support built-in!");
1482     return CURLE_UNSUPPORTED_PROTOCOL;
1483 #endif
1484   }
1485 
1486   data->state.path++;   /* don't include the initial slash */
1487 
1488   return CURLE_OK;
1489 }
1490 
Curl_smtp_escape_eob(struct connectdata * conn,ssize_t nread)1491 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread)
1492 {
1493   /* When sending SMTP payload, we must detect CRLF.CRLF sequences in
1494    * the data and make sure it is sent as CRLF..CRLF instead, as
1495    * otherwise it will wrongly be detected as end of data by the server.
1496    */
1497   ssize_t i;
1498   ssize_t si;
1499   struct smtp_conn *smtpc = &conn->proto.smtpc;
1500   struct SessionHandle *data = conn->data;
1501 
1502   if(data->state.scratch == NULL)
1503     data->state.scratch = malloc(2*BUFSIZE);
1504   if(data->state.scratch == NULL) {
1505     failf (data, "Failed to alloc scratch buffer!");
1506     return CURLE_OUT_OF_MEMORY;
1507   }
1508   /* This loop can be improved by some kind of Boyer-Moore style of
1509      approach but that is saved for later... */
1510   for(i = 0, si = 0; i < nread; i++, si++) {
1511     ssize_t left = nread - i;
1512 
1513     if(left>= (ssize_t)(SMTP_EOB_LEN-smtpc->eob)) {
1514       if(!memcmp(SMTP_EOB+smtpc->eob, &data->req.upload_fromhere[i],
1515                  SMTP_EOB_LEN-smtpc->eob)) {
1516         /* It matched, copy the replacement data to the target buffer
1517            instead. Note that the replacement does not contain the
1518            trailing CRLF but we instead continue to match on that one
1519            to deal with repeated sequences. Like CRLF.CRLF.CRLF etc
1520         */
1521         memcpy(&data->state.scratch[si], SMTP_EOB_REPL,
1522                SMTP_EOB_REPL_LEN);
1523         si+=SMTP_EOB_REPL_LEN-1; /* minus one since the for() increments
1524                                           it */
1525         i+=SMTP_EOB_LEN-smtpc->eob-1-2;
1526         smtpc->eob = 0; /* start over */
1527         continue;
1528       }
1529     }
1530     else if(!memcmp(SMTP_EOB+smtpc->eob, &data->req.upload_fromhere[i],
1531                     left)) {
1532       /* the last piece of the data matches the EOB so we can't send that
1533          until we know the rest of it */
1534       smtpc->eob += left;
1535       break;
1536     }
1537 
1538     data->state.scratch[si] = data->req.upload_fromhere[i];
1539   } /* for() */
1540 
1541   if(si != nread) {
1542     /* only use the new buffer if we replaced something */
1543     nread = si;
1544 
1545     /* upload from the new (replaced) buffer instead */
1546     data->req.upload_fromhere = data->state.scratch;
1547 
1548     /* set the new amount too */
1549     data->req.upload_present = nread;
1550   }
1551   return CURLE_OK;
1552 }
1553 
1554 #endif /* CURL_DISABLE_SMTP */
1555