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