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