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