1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2019, 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 https://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 * RFC1734 POP3 Authentication
22 * RFC1939 POP3 protocol
23 * RFC2195 CRAM-MD5 authentication
24 * RFC2384 POP URL Scheme
25 * RFC2449 POP3 Extension Mechanism
26 * RFC2595 Using TLS with IMAP, POP3 and ACAP
27 * RFC2831 DIGEST-MD5 authentication
28 * RFC4422 Simple Authentication and Security Layer (SASL)
29 * RFC4616 PLAIN authentication
30 * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
31 * RFC5034 POP3 SASL Authentication Mechanism
32 * RFC6749 OAuth 2.0 Authorization Framework
33 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
34 *
35 ***************************************************************************/
36
37 #include "curl_setup.h"
38
39 #ifndef CURL_DISABLE_POP3
40
41 #ifdef HAVE_NETINET_IN_H
42 #include <netinet/in.h>
43 #endif
44 #ifdef HAVE_ARPA_INET_H
45 #include <arpa/inet.h>
46 #endif
47 #ifdef HAVE_UTSNAME_H
48 #include <sys/utsname.h>
49 #endif
50 #ifdef HAVE_NETDB_H
51 #include <netdb.h>
52 #endif
53 #ifdef __VMS
54 #include <in.h>
55 #include <inet.h>
56 #endif
57
58 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
59 #undef in_addr_t
60 #define in_addr_t unsigned long
61 #endif
62
63 #include <curl/curl.h>
64 #include "urldata.h"
65 #include "sendf.h"
66 #include "hostip.h"
67 #include "progress.h"
68 #include "transfer.h"
69 #include "escape.h"
70 #include "http.h" /* for HTTP proxy tunnel stuff */
71 #include "socks.h"
72 #include "pop3.h"
73 #include "strtoofft.h"
74 #include "strcase.h"
75 #include "vtls/vtls.h"
76 #include "connect.h"
77 #include "strerror.h"
78 #include "select.h"
79 #include "multiif.h"
80 #include "url.h"
81 #include "curl_sasl.h"
82 #include "curl_md5.h"
83 #include "warnless.h"
84 /* The last 3 #include files should be in this order */
85 #include "curl_printf.h"
86 #include "curl_memory.h"
87 #include "memdebug.h"
88
89 /* Local API functions */
90 static CURLcode pop3_regular_transfer(struct connectdata *conn, bool *done);
91 static CURLcode pop3_do(struct connectdata *conn, bool *done);
92 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
93 bool premature);
94 static CURLcode pop3_connect(struct connectdata *conn, bool *done);
95 static CURLcode pop3_disconnect(struct connectdata *conn, bool dead);
96 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done);
97 static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
98 int numsocks);
99 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done);
100 static CURLcode pop3_setup_connection(struct connectdata *conn);
101 static CURLcode pop3_parse_url_options(struct connectdata *conn);
102 static CURLcode pop3_parse_url_path(struct connectdata *conn);
103 static CURLcode pop3_parse_custom_request(struct connectdata *conn);
104 static CURLcode pop3_perform_auth(struct connectdata *conn, const char *mech,
105 const char *initresp);
106 static CURLcode pop3_continue_auth(struct connectdata *conn, const char *resp);
107 static void pop3_get_message(char *buffer, char **outptr);
108
109 /*
110 * POP3 protocol handler.
111 */
112
113 const struct Curl_handler Curl_handler_pop3 = {
114 "POP3", /* scheme */
115 pop3_setup_connection, /* setup_connection */
116 pop3_do, /* do_it */
117 pop3_done, /* done */
118 ZERO_NULL, /* do_more */
119 pop3_connect, /* connect_it */
120 pop3_multi_statemach, /* connecting */
121 pop3_doing, /* doing */
122 pop3_getsock, /* proto_getsock */
123 pop3_getsock, /* doing_getsock */
124 ZERO_NULL, /* domore_getsock */
125 ZERO_NULL, /* perform_getsock */
126 pop3_disconnect, /* disconnect */
127 ZERO_NULL, /* readwrite */
128 ZERO_NULL, /* connection_check */
129 PORT_POP3, /* defport */
130 CURLPROTO_POP3, /* protocol */
131 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
132 PROTOPT_URLOPTIONS
133 };
134
135 #ifdef USE_SSL
136 /*
137 * POP3S protocol handler.
138 */
139
140 const struct Curl_handler Curl_handler_pop3s = {
141 "POP3S", /* scheme */
142 pop3_setup_connection, /* setup_connection */
143 pop3_do, /* do_it */
144 pop3_done, /* done */
145 ZERO_NULL, /* do_more */
146 pop3_connect, /* connect_it */
147 pop3_multi_statemach, /* connecting */
148 pop3_doing, /* doing */
149 pop3_getsock, /* proto_getsock */
150 pop3_getsock, /* doing_getsock */
151 ZERO_NULL, /* domore_getsock */
152 ZERO_NULL, /* perform_getsock */
153 pop3_disconnect, /* disconnect */
154 ZERO_NULL, /* readwrite */
155 ZERO_NULL, /* connection_check */
156 PORT_POP3S, /* defport */
157 CURLPROTO_POP3S, /* protocol */
158 PROTOPT_CLOSEACTION | PROTOPT_SSL
159 | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */
160 };
161 #endif
162
163 /* SASL parameters for the pop3 protocol */
164 static const struct SASLproto saslpop3 = {
165 "pop", /* The service name */
166 '*', /* Code received when continuation is expected */
167 '+', /* Code to receive upon authentication success */
168 255 - 8, /* Maximum initial response length (no max) */
169 pop3_perform_auth, /* Send authentication command */
170 pop3_continue_auth, /* Send authentication continuation */
171 pop3_get_message /* Get SASL response message */
172 };
173
174 #ifdef USE_SSL
pop3_to_pop3s(struct connectdata * conn)175 static void pop3_to_pop3s(struct connectdata *conn)
176 {
177 /* Change the connection handler */
178 conn->handler = &Curl_handler_pop3s;
179
180 /* Set the connection's upgraded to TLS flag */
181 conn->tls_upgraded = TRUE;
182 }
183 #else
184 #define pop3_to_pop3s(x) Curl_nop_stmt
185 #endif
186
187 /***********************************************************************
188 *
189 * pop3_endofresp()
190 *
191 * Checks for an ending POP3 status code at the start of the given string, but
192 * also detects the APOP timestamp from the server greeting and various
193 * capabilities from the CAPA response including the supported authentication
194 * types and allowed SASL mechanisms.
195 */
pop3_endofresp(struct connectdata * conn,char * line,size_t len,int * resp)196 static bool pop3_endofresp(struct connectdata *conn, char *line, size_t len,
197 int *resp)
198 {
199 struct pop3_conn *pop3c = &conn->proto.pop3c;
200
201 /* Do we have an error response? */
202 if(len >= 4 && !memcmp("-ERR", line, 4)) {
203 *resp = '-';
204
205 return TRUE;
206 }
207
208 /* Are we processing CAPA command responses? */
209 if(pop3c->state == POP3_CAPA) {
210 /* Do we have the terminating line? */
211 if(len >= 1 && line[0] == '.')
212 /* Treat the response as a success */
213 *resp = '+';
214 else
215 /* Treat the response as an untagged continuation */
216 *resp = '*';
217
218 return TRUE;
219 }
220
221 /* Do we have a success response? */
222 if(len >= 3 && !memcmp("+OK", line, 3)) {
223 *resp = '+';
224
225 return TRUE;
226 }
227
228 /* Do we have a continuation response? */
229 if(len >= 1 && line[0] == '+') {
230 *resp = '*';
231
232 return TRUE;
233 }
234
235 return FALSE; /* Nothing for us */
236 }
237
238 /***********************************************************************
239 *
240 * pop3_get_message()
241 *
242 * Gets the authentication message from the response buffer.
243 */
pop3_get_message(char * buffer,char ** outptr)244 static void pop3_get_message(char *buffer, char **outptr)
245 {
246 size_t len = strlen(buffer);
247 char *message = NULL;
248
249 if(len > 2) {
250 /* Find the start of the message */
251 len -= 2;
252 for(message = buffer + 2; *message == ' ' || *message == '\t';
253 message++, len--)
254 ;
255
256 /* Find the end of the message */
257 for(; len--;)
258 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
259 message[len] != '\t')
260 break;
261
262 /* Terminate the message */
263 if(++len) {
264 message[len] = '\0';
265 }
266 }
267 else
268 /* junk input => zero length output */
269 message = &buffer[len];
270
271 *outptr = message;
272 }
273
274 /***********************************************************************
275 *
276 * state()
277 *
278 * This is the ONLY way to change POP3 state!
279 */
state(struct connectdata * conn,pop3state newstate)280 static void state(struct connectdata *conn, pop3state newstate)
281 {
282 struct pop3_conn *pop3c = &conn->proto.pop3c;
283 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
284 /* for debug purposes */
285 static const char * const names[] = {
286 "STOP",
287 "SERVERGREET",
288 "CAPA",
289 "STARTTLS",
290 "UPGRADETLS",
291 "AUTH",
292 "APOP",
293 "USER",
294 "PASS",
295 "COMMAND",
296 "QUIT",
297 /* LAST */
298 };
299
300 if(pop3c->state != newstate)
301 infof(conn->data, "POP3 %p state change from %s to %s\n",
302 (void *)pop3c, names[pop3c->state], names[newstate]);
303 #endif
304
305 pop3c->state = newstate;
306 }
307
308 /***********************************************************************
309 *
310 * pop3_perform_capa()
311 *
312 * Sends the CAPA command in order to obtain a list of server side supported
313 * capabilities.
314 */
pop3_perform_capa(struct connectdata * conn)315 static CURLcode pop3_perform_capa(struct connectdata *conn)
316 {
317 CURLcode result = CURLE_OK;
318 struct pop3_conn *pop3c = &conn->proto.pop3c;
319
320 pop3c->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
321 pop3c->sasl.authused = SASL_AUTH_NONE; /* Clear the auth. mechanism used */
322 pop3c->tls_supported = FALSE; /* Clear the TLS capability */
323
324 /* Send the CAPA command */
325 result = Curl_pp_sendf(&pop3c->pp, "%s", "CAPA");
326
327 if(!result)
328 state(conn, POP3_CAPA);
329
330 return result;
331 }
332
333 /***********************************************************************
334 *
335 * pop3_perform_starttls()
336 *
337 * Sends the STLS command to start the upgrade to TLS.
338 */
pop3_perform_starttls(struct connectdata * conn)339 static CURLcode pop3_perform_starttls(struct connectdata *conn)
340 {
341 CURLcode result = CURLE_OK;
342
343 /* Send the STLS command */
344 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "STLS");
345
346 if(!result)
347 state(conn, POP3_STARTTLS);
348
349 return result;
350 }
351
352 /***********************************************************************
353 *
354 * pop3_perform_upgrade_tls()
355 *
356 * Performs the upgrade to TLS.
357 */
pop3_perform_upgrade_tls(struct connectdata * conn)358 static CURLcode pop3_perform_upgrade_tls(struct connectdata *conn)
359 {
360 CURLcode result = CURLE_OK;
361 struct pop3_conn *pop3c = &conn->proto.pop3c;
362
363 /* Start the SSL connection */
364 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
365
366 if(!result) {
367 if(pop3c->state != POP3_UPGRADETLS)
368 state(conn, POP3_UPGRADETLS);
369
370 if(pop3c->ssldone) {
371 pop3_to_pop3s(conn);
372 result = pop3_perform_capa(conn);
373 }
374 }
375
376 return result;
377 }
378
379 /***********************************************************************
380 *
381 * pop3_perform_user()
382 *
383 * Sends a clear text USER command to authenticate with.
384 */
pop3_perform_user(struct connectdata * conn)385 static CURLcode pop3_perform_user(struct connectdata *conn)
386 {
387 CURLcode result = CURLE_OK;
388
389 /* Check we have a username and password to authenticate with and end the
390 connect phase if we don't */
391 if(!conn->bits.user_passwd) {
392 state(conn, POP3_STOP);
393
394 return result;
395 }
396
397 /* Send the USER command */
398 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s",
399 conn->user ? conn->user : "");
400 if(!result)
401 state(conn, POP3_USER);
402
403 return result;
404 }
405
406 #ifndef CURL_DISABLE_CRYPTO_AUTH
407 /***********************************************************************
408 *
409 * pop3_perform_apop()
410 *
411 * Sends an APOP command to authenticate with.
412 */
pop3_perform_apop(struct connectdata * conn)413 static CURLcode pop3_perform_apop(struct connectdata *conn)
414 {
415 CURLcode result = CURLE_OK;
416 struct pop3_conn *pop3c = &conn->proto.pop3c;
417 size_t i;
418 MD5_context *ctxt;
419 unsigned char digest[MD5_DIGEST_LEN];
420 char secret[2 * MD5_DIGEST_LEN + 1];
421
422 /* Check we have a username and password to authenticate with and end the
423 connect phase if we don't */
424 if(!conn->bits.user_passwd) {
425 state(conn, POP3_STOP);
426
427 return result;
428 }
429
430 /* Create the digest */
431 ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
432 if(!ctxt)
433 return CURLE_OUT_OF_MEMORY;
434
435 Curl_MD5_update(ctxt, (const unsigned char *) pop3c->apoptimestamp,
436 curlx_uztoui(strlen(pop3c->apoptimestamp)));
437
438 Curl_MD5_update(ctxt, (const unsigned char *) conn->passwd,
439 curlx_uztoui(strlen(conn->passwd)));
440
441 /* Finalise the digest */
442 Curl_MD5_final(ctxt, digest);
443
444 /* Convert the calculated 16 octet digest into a 32 byte hex string */
445 for(i = 0; i < MD5_DIGEST_LEN; i++)
446 msnprintf(&secret[2 * i], 3, "%02x", digest[i]);
447
448 result = Curl_pp_sendf(&pop3c->pp, "APOP %s %s", conn->user, secret);
449
450 if(!result)
451 state(conn, POP3_APOP);
452
453 return result;
454 }
455 #endif
456
457 /***********************************************************************
458 *
459 * pop3_perform_auth()
460 *
461 * Sends an AUTH command allowing the client to login with the given SASL
462 * authentication mechanism.
463 */
pop3_perform_auth(struct connectdata * conn,const char * mech,const char * initresp)464 static CURLcode pop3_perform_auth(struct connectdata *conn,
465 const char *mech,
466 const char *initresp)
467 {
468 CURLcode result = CURLE_OK;
469 struct pop3_conn *pop3c = &conn->proto.pop3c;
470
471 if(initresp) { /* AUTH <mech> ...<crlf> */
472 /* Send the AUTH command with the initial response */
473 result = Curl_pp_sendf(&pop3c->pp, "AUTH %s %s", mech, initresp);
474 }
475 else {
476 /* Send the AUTH command */
477 result = Curl_pp_sendf(&pop3c->pp, "AUTH %s", mech);
478 }
479
480 return result;
481 }
482
483 /***********************************************************************
484 *
485 * pop3_continue_auth()
486 *
487 * Sends SASL continuation data or cancellation.
488 */
pop3_continue_auth(struct connectdata * conn,const char * resp)489 static CURLcode pop3_continue_auth(struct connectdata *conn,
490 const char *resp)
491 {
492 struct pop3_conn *pop3c = &conn->proto.pop3c;
493
494 return Curl_pp_sendf(&pop3c->pp, "%s", resp);
495 }
496
497 /***********************************************************************
498 *
499 * pop3_perform_authentication()
500 *
501 * Initiates the authentication sequence, with the appropriate SASL
502 * authentication mechanism, falling back to APOP and clear text should a
503 * common mechanism not be available between the client and server.
504 */
pop3_perform_authentication(struct connectdata * conn)505 static CURLcode pop3_perform_authentication(struct connectdata *conn)
506 {
507 CURLcode result = CURLE_OK;
508 struct pop3_conn *pop3c = &conn->proto.pop3c;
509 saslprogress progress = SASL_IDLE;
510
511 /* Check we have enough data to authenticate with and end the
512 connect phase if we don't */
513 if(!Curl_sasl_can_authenticate(&pop3c->sasl, conn)) {
514 state(conn, POP3_STOP);
515 return result;
516 }
517
518 if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_SASL) {
519 /* Calculate the SASL login details */
520 result = Curl_sasl_start(&pop3c->sasl, conn, FALSE, &progress);
521
522 if(!result)
523 if(progress == SASL_INPROGRESS)
524 state(conn, POP3_AUTH);
525 }
526
527 if(!result && progress == SASL_IDLE) {
528 #ifndef CURL_DISABLE_CRYPTO_AUTH
529 if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP)
530 /* Perform APOP authentication */
531 result = pop3_perform_apop(conn);
532 else
533 #endif
534 if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT)
535 /* Perform clear text authentication */
536 result = pop3_perform_user(conn);
537 else {
538 /* Other mechanisms not supported */
539 infof(conn->data, "No known authentication mechanisms supported!\n");
540 result = CURLE_LOGIN_DENIED;
541 }
542 }
543
544 return result;
545 }
546
547 /***********************************************************************
548 *
549 * pop3_perform_command()
550 *
551 * Sends a POP3 based command.
552 */
pop3_perform_command(struct connectdata * conn)553 static CURLcode pop3_perform_command(struct connectdata *conn)
554 {
555 CURLcode result = CURLE_OK;
556 struct Curl_easy *data = conn->data;
557 struct POP3 *pop3 = data->req.protop;
558 const char *command = NULL;
559
560 /* Calculate the default command */
561 if(pop3->id[0] == '\0' || conn->data->set.ftp_list_only) {
562 command = "LIST";
563
564 if(pop3->id[0] != '\0')
565 /* Message specific LIST so skip the BODY transfer */
566 pop3->transfer = FTPTRANSFER_INFO;
567 }
568 else
569 command = "RETR";
570
571 /* Send the command */
572 if(pop3->id[0] != '\0')
573 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s %s",
574 (pop3->custom && pop3->custom[0] != '\0' ?
575 pop3->custom : command), pop3->id);
576 else
577 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s",
578 (pop3->custom && pop3->custom[0] != '\0' ?
579 pop3->custom : command));
580
581 if(!result)
582 state(conn, POP3_COMMAND);
583
584 return result;
585 }
586
587 /***********************************************************************
588 *
589 * pop3_perform_quit()
590 *
591 * Performs the quit action prior to sclose() be called.
592 */
pop3_perform_quit(struct connectdata * conn)593 static CURLcode pop3_perform_quit(struct connectdata *conn)
594 {
595 CURLcode result = CURLE_OK;
596
597 /* Send the QUIT command */
598 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "QUIT");
599
600 if(!result)
601 state(conn, POP3_QUIT);
602
603 return result;
604 }
605
606 /* For the initial server greeting */
pop3_state_servergreet_resp(struct connectdata * conn,int pop3code,pop3state instate)607 static CURLcode pop3_state_servergreet_resp(struct connectdata *conn,
608 int pop3code,
609 pop3state instate)
610 {
611 CURLcode result = CURLE_OK;
612 struct Curl_easy *data = conn->data;
613 struct pop3_conn *pop3c = &conn->proto.pop3c;
614 const char *line = data->state.buffer;
615 size_t len = strlen(line);
616
617 (void)instate; /* no use for this yet */
618
619 if(pop3code != '+') {
620 failf(data, "Got unexpected pop3-server response");
621 result = CURLE_WEIRD_SERVER_REPLY;
622 }
623 else {
624 /* Does the server support APOP authentication? */
625 if(len >= 4 && line[len - 2] == '>') {
626 /* Look for the APOP timestamp */
627 size_t i;
628 for(i = 3; i < len - 2; ++i) {
629 if(line[i] == '<') {
630 /* Calculate the length of the timestamp */
631 size_t timestamplen = len - 1 - i;
632 char *at;
633 if(!timestamplen)
634 break;
635
636 /* Allocate some memory for the timestamp */
637 pop3c->apoptimestamp = (char *)calloc(1, timestamplen + 1);
638
639 if(!pop3c->apoptimestamp)
640 break;
641
642 /* Copy the timestamp */
643 memcpy(pop3c->apoptimestamp, line + i, timestamplen);
644 pop3c->apoptimestamp[timestamplen] = '\0';
645
646 /* If the timestamp does not contain '@' it is not (as required by
647 RFC-1939) conformant to the RFC-822 message id syntax, and we
648 therefore do not use APOP authentication. */
649 at = strchr(pop3c->apoptimestamp, '@');
650 if(!at)
651 Curl_safefree(pop3c->apoptimestamp);
652 else
653 /* Store the APOP capability */
654 pop3c->authtypes |= POP3_TYPE_APOP;
655 break;
656 }
657 }
658 }
659
660 result = pop3_perform_capa(conn);
661 }
662
663 return result;
664 }
665
666 /* For CAPA responses */
pop3_state_capa_resp(struct connectdata * conn,int pop3code,pop3state instate)667 static CURLcode pop3_state_capa_resp(struct connectdata *conn, int pop3code,
668 pop3state instate)
669 {
670 CURLcode result = CURLE_OK;
671 struct Curl_easy *data = conn->data;
672 struct pop3_conn *pop3c = &conn->proto.pop3c;
673 const char *line = data->state.buffer;
674 size_t len = strlen(line);
675
676 (void)instate; /* no use for this yet */
677
678 /* Do we have a untagged continuation response? */
679 if(pop3code == '*') {
680 /* Does the server support the STLS capability? */
681 if(len >= 4 && !memcmp(line, "STLS", 4))
682 pop3c->tls_supported = TRUE;
683
684 /* Does the server support clear text authentication? */
685 else if(len >= 4 && !memcmp(line, "USER", 4))
686 pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
687
688 /* Does the server support SASL based authentication? */
689 else if(len >= 5 && !memcmp(line, "SASL ", 5)) {
690 pop3c->authtypes |= POP3_TYPE_SASL;
691
692 /* Advance past the SASL keyword */
693 line += 5;
694 len -= 5;
695
696 /* Loop through the data line */
697 for(;;) {
698 size_t llen;
699 size_t wordlen;
700 unsigned int mechbit;
701
702 while(len &&
703 (*line == ' ' || *line == '\t' ||
704 *line == '\r' || *line == '\n')) {
705
706 line++;
707 len--;
708 }
709
710 if(!len)
711 break;
712
713 /* Extract the word */
714 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
715 line[wordlen] != '\t' && line[wordlen] != '\r' &&
716 line[wordlen] != '\n';)
717 wordlen++;
718
719 /* Test the word for a matching authentication mechanism */
720 mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
721 if(mechbit && llen == wordlen)
722 pop3c->sasl.authmechs |= mechbit;
723
724 line += wordlen;
725 len -= wordlen;
726 }
727 }
728 }
729 else if(pop3code == '+') {
730 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
731 /* We don't have a SSL/TLS connection yet, but SSL is requested */
732 if(pop3c->tls_supported)
733 /* Switch to TLS connection now */
734 result = pop3_perform_starttls(conn);
735 else if(data->set.use_ssl == CURLUSESSL_TRY)
736 /* Fallback and carry on with authentication */
737 result = pop3_perform_authentication(conn);
738 else {
739 failf(data, "STLS not supported.");
740 result = CURLE_USE_SSL_FAILED;
741 }
742 }
743 else
744 result = pop3_perform_authentication(conn);
745 }
746 else {
747 /* Clear text is supported when CAPA isn't recognised */
748 pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
749
750 result = pop3_perform_authentication(conn);
751 }
752
753 return result;
754 }
755
756 /* For STARTTLS responses */
pop3_state_starttls_resp(struct connectdata * conn,int pop3code,pop3state instate)757 static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
758 int pop3code,
759 pop3state instate)
760 {
761 CURLcode result = CURLE_OK;
762 struct Curl_easy *data = conn->data;
763
764 (void)instate; /* no use for this yet */
765
766 if(pop3code != '+') {
767 if(data->set.use_ssl != CURLUSESSL_TRY) {
768 failf(data, "STARTTLS denied");
769 result = CURLE_USE_SSL_FAILED;
770 }
771 else
772 result = pop3_perform_authentication(conn);
773 }
774 else
775 result = pop3_perform_upgrade_tls(conn);
776
777 return result;
778 }
779
780 /* For SASL authentication responses */
pop3_state_auth_resp(struct connectdata * conn,int pop3code,pop3state instate)781 static CURLcode pop3_state_auth_resp(struct connectdata *conn,
782 int pop3code,
783 pop3state instate)
784 {
785 CURLcode result = CURLE_OK;
786 struct Curl_easy *data = conn->data;
787 struct pop3_conn *pop3c = &conn->proto.pop3c;
788 saslprogress progress;
789
790 (void)instate; /* no use for this yet */
791
792 result = Curl_sasl_continue(&pop3c->sasl, conn, pop3code, &progress);
793 if(!result)
794 switch(progress) {
795 case SASL_DONE:
796 state(conn, POP3_STOP); /* Authenticated */
797 break;
798 case SASL_IDLE: /* No mechanism left after cancellation */
799 #ifndef CURL_DISABLE_CRYPTO_AUTH
800 if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP)
801 /* Perform APOP authentication */
802 result = pop3_perform_apop(conn);
803 else
804 #endif
805 if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT)
806 /* Perform clear text authentication */
807 result = pop3_perform_user(conn);
808 else {
809 failf(data, "Authentication cancelled");
810 result = CURLE_LOGIN_DENIED;
811 }
812 break;
813 default:
814 break;
815 }
816
817 return result;
818 }
819
820 #ifndef CURL_DISABLE_CRYPTO_AUTH
821 /* For APOP responses */
pop3_state_apop_resp(struct connectdata * conn,int pop3code,pop3state instate)822 static CURLcode pop3_state_apop_resp(struct connectdata *conn, int pop3code,
823 pop3state instate)
824 {
825 CURLcode result = CURLE_OK;
826 struct Curl_easy *data = conn->data;
827
828 (void)instate; /* no use for this yet */
829
830 if(pop3code != '+') {
831 failf(data, "Authentication failed: %d", pop3code);
832 result = CURLE_LOGIN_DENIED;
833 }
834 else
835 /* End of connect phase */
836 state(conn, POP3_STOP);
837
838 return result;
839 }
840 #endif
841
842 /* For USER responses */
pop3_state_user_resp(struct connectdata * conn,int pop3code,pop3state instate)843 static CURLcode pop3_state_user_resp(struct connectdata *conn, int pop3code,
844 pop3state instate)
845 {
846 CURLcode result = CURLE_OK;
847 struct Curl_easy *data = conn->data;
848
849 (void)instate; /* no use for this yet */
850
851 if(pop3code != '+') {
852 failf(data, "Access denied. %c", pop3code);
853 result = CURLE_LOGIN_DENIED;
854 }
855 else
856 /* Send the PASS command */
857 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s",
858 conn->passwd ? conn->passwd : "");
859 if(!result)
860 state(conn, POP3_PASS);
861
862 return result;
863 }
864
865 /* For PASS responses */
pop3_state_pass_resp(struct connectdata * conn,int pop3code,pop3state instate)866 static CURLcode pop3_state_pass_resp(struct connectdata *conn, int pop3code,
867 pop3state instate)
868 {
869 CURLcode result = CURLE_OK;
870 struct Curl_easy *data = conn->data;
871
872 (void)instate; /* no use for this yet */
873
874 if(pop3code != '+') {
875 failf(data, "Access denied. %c", pop3code);
876 result = CURLE_LOGIN_DENIED;
877 }
878 else
879 /* End of connect phase */
880 state(conn, POP3_STOP);
881
882 return result;
883 }
884
885 /* For command responses */
pop3_state_command_resp(struct connectdata * conn,int pop3code,pop3state instate)886 static CURLcode pop3_state_command_resp(struct connectdata *conn,
887 int pop3code,
888 pop3state instate)
889 {
890 CURLcode result = CURLE_OK;
891 struct Curl_easy *data = conn->data;
892 struct POP3 *pop3 = data->req.protop;
893 struct pop3_conn *pop3c = &conn->proto.pop3c;
894 struct pingpong *pp = &pop3c->pp;
895
896 (void)instate; /* no use for this yet */
897
898 if(pop3code != '+') {
899 state(conn, POP3_STOP);
900 return CURLE_RECV_ERROR;
901 }
902
903 /* This 'OK' line ends with a CR LF pair which is the two first bytes of the
904 EOB string so count this is two matching bytes. This is necessary to make
905 the code detect the EOB if the only data than comes now is %2e CR LF like
906 when there is no body to return. */
907 pop3c->eob = 2;
908
909 /* But since this initial CR LF pair is not part of the actual body, we set
910 the strip counter here so that these bytes won't be delivered. */
911 pop3c->strip = 2;
912
913 if(pop3->transfer == FTPTRANSFER_BODY) {
914 /* POP3 download */
915 Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
916
917 if(pp->cache) {
918 /* The header "cache" contains a bunch of data that is actually body
919 content so send it as such. Note that there may even be additional
920 "headers" after the body */
921
922 if(!data->set.opt_no_body) {
923 result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
924 if(result)
925 return result;
926 }
927
928 /* Free the cache */
929 Curl_safefree(pp->cache);
930
931 /* Reset the cache size */
932 pp->cache_size = 0;
933 }
934 }
935
936 /* End of DO phase */
937 state(conn, POP3_STOP);
938
939 return result;
940 }
941
pop3_statemach_act(struct connectdata * conn)942 static CURLcode pop3_statemach_act(struct connectdata *conn)
943 {
944 CURLcode result = CURLE_OK;
945 curl_socket_t sock = conn->sock[FIRSTSOCKET];
946 int pop3code;
947 struct pop3_conn *pop3c = &conn->proto.pop3c;
948 struct pingpong *pp = &pop3c->pp;
949 size_t nread = 0;
950
951 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */
952 if(pop3c->state == POP3_UPGRADETLS)
953 return pop3_perform_upgrade_tls(conn);
954
955 /* Flush any data that needs to be sent */
956 if(pp->sendleft)
957 return Curl_pp_flushsend(pp);
958
959 do {
960 /* Read the response from the server */
961 result = Curl_pp_readresp(sock, pp, &pop3code, &nread);
962 if(result)
963 return result;
964
965 if(!pop3code)
966 break;
967
968 /* We have now received a full POP3 server response */
969 switch(pop3c->state) {
970 case POP3_SERVERGREET:
971 result = pop3_state_servergreet_resp(conn, pop3code, pop3c->state);
972 break;
973
974 case POP3_CAPA:
975 result = pop3_state_capa_resp(conn, pop3code, pop3c->state);
976 break;
977
978 case POP3_STARTTLS:
979 result = pop3_state_starttls_resp(conn, pop3code, pop3c->state);
980 break;
981
982 case POP3_AUTH:
983 result = pop3_state_auth_resp(conn, pop3code, pop3c->state);
984 break;
985
986 #ifndef CURL_DISABLE_CRYPTO_AUTH
987 case POP3_APOP:
988 result = pop3_state_apop_resp(conn, pop3code, pop3c->state);
989 break;
990 #endif
991
992 case POP3_USER:
993 result = pop3_state_user_resp(conn, pop3code, pop3c->state);
994 break;
995
996 case POP3_PASS:
997 result = pop3_state_pass_resp(conn, pop3code, pop3c->state);
998 break;
999
1000 case POP3_COMMAND:
1001 result = pop3_state_command_resp(conn, pop3code, pop3c->state);
1002 break;
1003
1004 case POP3_QUIT:
1005 /* fallthrough, just stop! */
1006 default:
1007 /* internal error */
1008 state(conn, POP3_STOP);
1009 break;
1010 }
1011 } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp));
1012
1013 return result;
1014 }
1015
1016 /* Called repeatedly until done from multi.c */
pop3_multi_statemach(struct connectdata * conn,bool * done)1017 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done)
1018 {
1019 CURLcode result = CURLE_OK;
1020 struct pop3_conn *pop3c = &conn->proto.pop3c;
1021
1022 if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) {
1023 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
1024 if(result || !pop3c->ssldone)
1025 return result;
1026 }
1027
1028 result = Curl_pp_statemach(&pop3c->pp, FALSE, FALSE);
1029 *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE;
1030
1031 return result;
1032 }
1033
pop3_block_statemach(struct connectdata * conn,bool disconnecting)1034 static CURLcode pop3_block_statemach(struct connectdata *conn,
1035 bool disconnecting)
1036 {
1037 CURLcode result = CURLE_OK;
1038 struct pop3_conn *pop3c = &conn->proto.pop3c;
1039
1040 while(pop3c->state != POP3_STOP && !result)
1041 result = Curl_pp_statemach(&pop3c->pp, TRUE, disconnecting);
1042
1043 return result;
1044 }
1045
1046 /* Allocate and initialize the POP3 struct for the current Curl_easy if
1047 required */
pop3_init(struct connectdata * conn)1048 static CURLcode pop3_init(struct connectdata *conn)
1049 {
1050 CURLcode result = CURLE_OK;
1051 struct Curl_easy *data = conn->data;
1052 struct POP3 *pop3;
1053
1054 pop3 = data->req.protop = calloc(sizeof(struct POP3), 1);
1055 if(!pop3)
1056 result = CURLE_OUT_OF_MEMORY;
1057
1058 return result;
1059 }
1060
1061 /* For the POP3 "protocol connect" and "doing" phases only */
pop3_getsock(struct connectdata * conn,curl_socket_t * socks,int numsocks)1062 static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
1063 int numsocks)
1064 {
1065 return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks);
1066 }
1067
1068 /***********************************************************************
1069 *
1070 * pop3_connect()
1071 *
1072 * This function should do everything that is to be considered a part of the
1073 * connection phase.
1074 *
1075 * The variable 'done' points to will be TRUE if the protocol-layer connect
1076 * phase is done when this function returns, or FALSE if not.
1077 */
pop3_connect(struct connectdata * conn,bool * done)1078 static CURLcode pop3_connect(struct connectdata *conn, bool *done)
1079 {
1080 CURLcode result = CURLE_OK;
1081 struct pop3_conn *pop3c = &conn->proto.pop3c;
1082 struct pingpong *pp = &pop3c->pp;
1083
1084 *done = FALSE; /* default to not done yet */
1085
1086 /* We always support persistent connections in POP3 */
1087 connkeep(conn, "POP3 default");
1088
1089 /* Set the default response time-out */
1090 pp->response_time = RESP_TIMEOUT;
1091 pp->statemach_act = pop3_statemach_act;
1092 pp->endofresp = pop3_endofresp;
1093 pp->conn = conn;
1094
1095 /* Set the default preferred authentication type and mechanism */
1096 pop3c->preftype = POP3_TYPE_ANY;
1097 Curl_sasl_init(&pop3c->sasl, &saslpop3);
1098
1099 /* Initialise the pingpong layer */
1100 Curl_pp_init(pp);
1101
1102 /* Parse the URL options */
1103 result = pop3_parse_url_options(conn);
1104 if(result)
1105 return result;
1106
1107 /* Start off waiting for the server greeting response */
1108 state(conn, POP3_SERVERGREET);
1109
1110 result = pop3_multi_statemach(conn, done);
1111
1112 return result;
1113 }
1114
1115 /***********************************************************************
1116 *
1117 * pop3_done()
1118 *
1119 * The DONE function. This does what needs to be done after a single DO has
1120 * performed.
1121 *
1122 * Input argument is already checked for validity.
1123 */
pop3_done(struct connectdata * conn,CURLcode status,bool premature)1124 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
1125 bool premature)
1126 {
1127 CURLcode result = CURLE_OK;
1128 struct Curl_easy *data = conn->data;
1129 struct POP3 *pop3 = data->req.protop;
1130
1131 (void)premature;
1132
1133 if(!pop3)
1134 return CURLE_OK;
1135
1136 if(status) {
1137 connclose(conn, "POP3 done with bad status");
1138 result = status; /* use the already set error code */
1139 }
1140
1141 /* Cleanup our per-request based variables */
1142 Curl_safefree(pop3->id);
1143 Curl_safefree(pop3->custom);
1144
1145 /* Clear the transfer mode for the next request */
1146 pop3->transfer = FTPTRANSFER_BODY;
1147
1148 return result;
1149 }
1150
1151 /***********************************************************************
1152 *
1153 * pop3_perform()
1154 *
1155 * This is the actual DO function for POP3. Get a message/listing according to
1156 * the options previously setup.
1157 */
pop3_perform(struct connectdata * conn,bool * connected,bool * dophase_done)1158 static CURLcode pop3_perform(struct connectdata *conn, bool *connected,
1159 bool *dophase_done)
1160 {
1161 /* This is POP3 and no proxy */
1162 CURLcode result = CURLE_OK;
1163 struct POP3 *pop3 = conn->data->req.protop;
1164
1165 DEBUGF(infof(conn->data, "DO phase starts\n"));
1166
1167 if(conn->data->set.opt_no_body) {
1168 /* Requested no body means no transfer */
1169 pop3->transfer = FTPTRANSFER_INFO;
1170 }
1171
1172 *dophase_done = FALSE; /* not done yet */
1173
1174 /* Start the first command in the DO phase */
1175 result = pop3_perform_command(conn);
1176 if(result)
1177 return result;
1178
1179 /* Run the state-machine */
1180 result = pop3_multi_statemach(conn, dophase_done);
1181
1182 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1183
1184 if(*dophase_done)
1185 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1186
1187 return result;
1188 }
1189
1190 /***********************************************************************
1191 *
1192 * pop3_do()
1193 *
1194 * This function is registered as 'curl_do' function. It decodes the path
1195 * parts etc as a wrapper to the actual DO function (pop3_perform).
1196 *
1197 * The input argument is already checked for validity.
1198 */
pop3_do(struct connectdata * conn,bool * done)1199 static CURLcode pop3_do(struct connectdata *conn, bool *done)
1200 {
1201 CURLcode result = CURLE_OK;
1202
1203 *done = FALSE; /* default to false */
1204
1205 /* Parse the URL path */
1206 result = pop3_parse_url_path(conn);
1207 if(result)
1208 return result;
1209
1210 /* Parse the custom request */
1211 result = pop3_parse_custom_request(conn);
1212 if(result)
1213 return result;
1214
1215 result = pop3_regular_transfer(conn, done);
1216
1217 return result;
1218 }
1219
1220 /***********************************************************************
1221 *
1222 * pop3_disconnect()
1223 *
1224 * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
1225 * resources. BLOCKING.
1226 */
pop3_disconnect(struct connectdata * conn,bool dead_connection)1227 static CURLcode pop3_disconnect(struct connectdata *conn, bool dead_connection)
1228 {
1229 struct pop3_conn *pop3c = &conn->proto.pop3c;
1230
1231 /* We cannot send quit unconditionally. If this connection is stale or
1232 bad in any way, sending quit and waiting around here will make the
1233 disconnect wait in vain and cause more problems than we need to. */
1234
1235 /* The POP3 session may or may not have been allocated/setup at this
1236 point! */
1237 if(!dead_connection && pop3c->pp.conn && pop3c->pp.conn->bits.protoconnstart)
1238 if(!pop3_perform_quit(conn))
1239 (void)pop3_block_statemach(conn, TRUE); /* ignore errors on QUIT */
1240
1241 /* Disconnect from the server */
1242 Curl_pp_disconnect(&pop3c->pp);
1243
1244 /* Cleanup the SASL module */
1245 Curl_sasl_cleanup(conn, pop3c->sasl.authused);
1246
1247 /* Cleanup our connection based variables */
1248 Curl_safefree(pop3c->apoptimestamp);
1249
1250 return CURLE_OK;
1251 }
1252
1253 /* Call this when the DO phase has completed */
pop3_dophase_done(struct connectdata * conn,bool connected)1254 static CURLcode pop3_dophase_done(struct connectdata *conn, bool connected)
1255 {
1256 (void)conn;
1257 (void)connected;
1258
1259 return CURLE_OK;
1260 }
1261
1262 /* Called from multi.c while DOing */
pop3_doing(struct connectdata * conn,bool * dophase_done)1263 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done)
1264 {
1265 CURLcode result = pop3_multi_statemach(conn, dophase_done);
1266
1267 if(result)
1268 DEBUGF(infof(conn->data, "DO phase failed\n"));
1269 else if(*dophase_done) {
1270 result = pop3_dophase_done(conn, FALSE /* not connected */);
1271
1272 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1273 }
1274
1275 return result;
1276 }
1277
1278 /***********************************************************************
1279 *
1280 * pop3_regular_transfer()
1281 *
1282 * The input argument is already checked for validity.
1283 *
1284 * Performs all commands done before a regular transfer between a local and a
1285 * remote host.
1286 */
pop3_regular_transfer(struct connectdata * conn,bool * dophase_done)1287 static CURLcode pop3_regular_transfer(struct connectdata *conn,
1288 bool *dophase_done)
1289 {
1290 CURLcode result = CURLE_OK;
1291 bool connected = FALSE;
1292 struct Curl_easy *data = conn->data;
1293
1294 /* Make sure size is unknown at this point */
1295 data->req.size = -1;
1296
1297 /* Set the progress data */
1298 Curl_pgrsSetUploadCounter(data, 0);
1299 Curl_pgrsSetDownloadCounter(data, 0);
1300 Curl_pgrsSetUploadSize(data, -1);
1301 Curl_pgrsSetDownloadSize(data, -1);
1302
1303 /* Carry out the perform */
1304 result = pop3_perform(conn, &connected, dophase_done);
1305
1306 /* Perform post DO phase operations if necessary */
1307 if(!result && *dophase_done)
1308 result = pop3_dophase_done(conn, connected);
1309
1310 return result;
1311 }
1312
pop3_setup_connection(struct connectdata * conn)1313 static CURLcode pop3_setup_connection(struct connectdata *conn)
1314 {
1315 /* Initialise the POP3 layer */
1316 CURLcode result = pop3_init(conn);
1317 if(result)
1318 return result;
1319
1320 /* Clear the TLS upgraded flag */
1321 conn->tls_upgraded = FALSE;
1322
1323 return CURLE_OK;
1324 }
1325
1326 /***********************************************************************
1327 *
1328 * pop3_parse_url_options()
1329 *
1330 * Parse the URL login options.
1331 */
pop3_parse_url_options(struct connectdata * conn)1332 static CURLcode pop3_parse_url_options(struct connectdata *conn)
1333 {
1334 CURLcode result = CURLE_OK;
1335 struct pop3_conn *pop3c = &conn->proto.pop3c;
1336 const char *ptr = conn->options;
1337
1338 pop3c->sasl.resetprefs = TRUE;
1339
1340 while(!result && ptr && *ptr) {
1341 const char *key = ptr;
1342 const char *value;
1343
1344 while(*ptr && *ptr != '=')
1345 ptr++;
1346
1347 value = ptr + 1;
1348
1349 while(*ptr && *ptr != ';')
1350 ptr++;
1351
1352 if(strncasecompare(key, "AUTH=", 5)) {
1353 result = Curl_sasl_parse_url_auth_option(&pop3c->sasl,
1354 value, ptr - value);
1355
1356 if(result && strncasecompare(value, "+APOP", ptr - value)) {
1357 pop3c->preftype = POP3_TYPE_APOP;
1358 pop3c->sasl.prefmech = SASL_AUTH_NONE;
1359 result = CURLE_OK;
1360 }
1361 }
1362 else
1363 result = CURLE_URL_MALFORMAT;
1364
1365 if(*ptr == ';')
1366 ptr++;
1367 }
1368
1369 if(pop3c->preftype != POP3_TYPE_APOP)
1370 switch(pop3c->sasl.prefmech) {
1371 case SASL_AUTH_NONE:
1372 pop3c->preftype = POP3_TYPE_NONE;
1373 break;
1374 case SASL_AUTH_DEFAULT:
1375 pop3c->preftype = POP3_TYPE_ANY;
1376 break;
1377 default:
1378 pop3c->preftype = POP3_TYPE_SASL;
1379 break;
1380 }
1381
1382 return result;
1383 }
1384
1385 /***********************************************************************
1386 *
1387 * pop3_parse_url_path()
1388 *
1389 * Parse the URL path into separate path components.
1390 */
pop3_parse_url_path(struct connectdata * conn)1391 static CURLcode pop3_parse_url_path(struct connectdata *conn)
1392 {
1393 /* The POP3 struct is already initialised in pop3_connect() */
1394 struct Curl_easy *data = conn->data;
1395 struct POP3 *pop3 = data->req.protop;
1396 const char *path = &data->state.up.path[1]; /* skip leading path */
1397
1398 /* URL decode the path for the message ID */
1399 return Curl_urldecode(data, path, 0, &pop3->id, NULL, TRUE);
1400 }
1401
1402 /***********************************************************************
1403 *
1404 * pop3_parse_custom_request()
1405 *
1406 * Parse the custom request.
1407 */
pop3_parse_custom_request(struct connectdata * conn)1408 static CURLcode pop3_parse_custom_request(struct connectdata *conn)
1409 {
1410 CURLcode result = CURLE_OK;
1411 struct Curl_easy *data = conn->data;
1412 struct POP3 *pop3 = data->req.protop;
1413 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1414
1415 /* URL decode the custom request */
1416 if(custom)
1417 result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, TRUE);
1418
1419 return result;
1420 }
1421
1422 /***********************************************************************
1423 *
1424 * Curl_pop3_write()
1425 *
1426 * This function scans the body after the end-of-body and writes everything
1427 * until the end is found.
1428 */
Curl_pop3_write(struct connectdata * conn,char * str,size_t nread)1429 CURLcode Curl_pop3_write(struct connectdata *conn, char *str, size_t nread)
1430 {
1431 /* This code could be made into a special function in the handler struct */
1432 CURLcode result = CURLE_OK;
1433 struct Curl_easy *data = conn->data;
1434 struct SingleRequest *k = &data->req;
1435
1436 struct pop3_conn *pop3c = &conn->proto.pop3c;
1437 bool strip_dot = FALSE;
1438 size_t last = 0;
1439 size_t i;
1440
1441 /* Search through the buffer looking for the end-of-body marker which is
1442 5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
1443 the eob so the server will have prefixed it with an extra dot which we
1444 need to strip out. Additionally the marker could of course be spread out
1445 over 5 different data chunks. */
1446 for(i = 0; i < nread; i++) {
1447 size_t prev = pop3c->eob;
1448
1449 switch(str[i]) {
1450 case 0x0d:
1451 if(pop3c->eob == 0) {
1452 pop3c->eob++;
1453
1454 if(i) {
1455 /* Write out the body part that didn't match */
1456 result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
1457 i - last);
1458
1459 if(result)
1460 return result;
1461
1462 last = i;
1463 }
1464 }
1465 else if(pop3c->eob == 3)
1466 pop3c->eob++;
1467 else
1468 /* If the character match wasn't at position 0 or 3 then restart the
1469 pattern matching */
1470 pop3c->eob = 1;
1471 break;
1472
1473 case 0x0a:
1474 if(pop3c->eob == 1 || pop3c->eob == 4)
1475 pop3c->eob++;
1476 else
1477 /* If the character match wasn't at position 1 or 4 then start the
1478 search again */
1479 pop3c->eob = 0;
1480 break;
1481
1482 case 0x2e:
1483 if(pop3c->eob == 2)
1484 pop3c->eob++;
1485 else if(pop3c->eob == 3) {
1486 /* We have an extra dot after the CRLF which we need to strip off */
1487 strip_dot = TRUE;
1488 pop3c->eob = 0;
1489 }
1490 else
1491 /* If the character match wasn't at position 2 then start the search
1492 again */
1493 pop3c->eob = 0;
1494 break;
1495
1496 default:
1497 pop3c->eob = 0;
1498 break;
1499 }
1500
1501 /* Did we have a partial match which has subsequently failed? */
1502 if(prev && prev >= pop3c->eob) {
1503 /* Strip can only be non-zero for the very first mismatch after CRLF
1504 and then both prev and strip are equal and nothing will be output
1505 below */
1506 while(prev && pop3c->strip) {
1507 prev--;
1508 pop3c->strip--;
1509 }
1510
1511 if(prev) {
1512 /* If the partial match was the CRLF and dot then only write the CRLF
1513 as the server would have inserted the dot */
1514 result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB,
1515 strip_dot ? prev - 1 : prev);
1516
1517 if(result)
1518 return result;
1519
1520 last = i;
1521 strip_dot = FALSE;
1522 }
1523 }
1524 }
1525
1526 if(pop3c->eob == POP3_EOB_LEN) {
1527 /* We have a full match so the transfer is done, however we must transfer
1528 the CRLF at the start of the EOB as this is considered to be part of the
1529 message as per RFC-1939, sect. 3 */
1530 result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB, 2);
1531
1532 k->keepon &= ~KEEP_RECV;
1533 pop3c->eob = 0;
1534
1535 return result;
1536 }
1537
1538 if(pop3c->eob)
1539 /* While EOB is matching nothing should be output */
1540 return CURLE_OK;
1541
1542 if(nread - last) {
1543 result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
1544 nread - last);
1545 }
1546
1547 return result;
1548 }
1549
1550 #endif /* CURL_DISABLE_POP3 */
1551