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, /* write_resp_hd */
130 ZERO_NULL, /* connection_check */
131 ZERO_NULL, /* attach connection */
132 PORT_POP3, /* defport */
133 CURLPROTO_POP3, /* protocol */
134 CURLPROTO_POP3, /* family */
135 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
136 PROTOPT_URLOPTIONS
137 };
138
139 #ifdef USE_SSL
140 /*
141 * POP3S protocol handler.
142 */
143
144 const struct Curl_handler Curl_handler_pop3s = {
145 "pop3s", /* scheme */
146 pop3_setup_connection, /* setup_connection */
147 pop3_do, /* do_it */
148 pop3_done, /* done */
149 ZERO_NULL, /* do_more */
150 pop3_connect, /* connect_it */
151 pop3_multi_statemach, /* connecting */
152 pop3_doing, /* doing */
153 pop3_getsock, /* proto_getsock */
154 pop3_getsock, /* doing_getsock */
155 ZERO_NULL, /* domore_getsock */
156 ZERO_NULL, /* perform_getsock */
157 pop3_disconnect, /* disconnect */
158 ZERO_NULL, /* write_resp */
159 ZERO_NULL, /* write_resp_hd */
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 = Curl_dyn_ptr(&data->conn->proto.pop3c.pp.recvbuf);
258 size_t len = data->conn->proto.pop3c.pp.nfinal;
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 * pop3_state()
286 *
287 * This is the ONLY way to change POP3 state!
288 */
pop3_state(struct Curl_easy * data,pop3state newstate)289 static void pop3_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 pop3_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 pop3_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 pop3_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 pop3_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 pop3_state(data, POP3_USER);
421
422 return result;
423 }
424
425 #ifndef CURL_DISABLE_DIGEST_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 pop3_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 pop3_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 pop3_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 pop3_state(data, POP3_AUTH);
566 }
567
568 if(!result && progress == SASL_IDLE) {
569 #ifndef CURL_DISABLE_DIGEST_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 pop3_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 pop3_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 = Curl_dyn_ptr(&data->conn->proto.pop3c.pp.recvbuf);
655 size_t len = data->conn->proto.pop3c.pp.nfinal;
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 if(len > 3) {
664 /* Does the server support APOP authentication? */
665 char *lt;
666 char *gt = NULL;
667
668 /* Look for the APOP timestamp */
669 lt = memchr(line, '<', len);
670 if(lt)
671 /* search the remainder for '>' */
672 gt = memchr(lt, '>', len - (lt - line));
673 if(gt) {
674 /* the length of the timestamp, including the brackets */
675 size_t timestamplen = gt - lt + 1;
676 char *at = memchr(lt, '@', timestamplen);
677 /* If the timestamp does not contain '@' it is not (as required by
678 RFC-1939) conformant to the RFC-822 message id syntax, and we
679 therefore do not use APOP authentication. */
680 if(at) {
681 /* dupe the timestamp */
682 pop3c->apoptimestamp = Curl_memdup0(lt, timestamplen);
683 if(!pop3c->apoptimestamp)
684 return CURLE_OUT_OF_MEMORY;
685 /* Store the APOP capability */
686 pop3c->authtypes |= POP3_TYPE_APOP;
687 }
688 }
689
690 if(!result)
691 result = pop3_perform_capa(data, conn);
692 }
693
694 return result;
695 }
696
697 /* For CAPA responses */
pop3_state_capa_resp(struct Curl_easy * data,int pop3code,pop3state instate)698 static CURLcode pop3_state_capa_resp(struct Curl_easy *data, int pop3code,
699 pop3state instate)
700 {
701 CURLcode result = CURLE_OK;
702 struct connectdata *conn = data->conn;
703 struct pop3_conn *pop3c = &conn->proto.pop3c;
704 const char *line = Curl_dyn_ptr(&data->conn->proto.pop3c.pp.recvbuf);
705 size_t len = data->conn->proto.pop3c.pp.nfinal;
706
707 (void)instate; /* no use for this yet */
708
709 /* Do we have a untagged continuation response? */
710 if(pop3code == '*') {
711 /* Does the server support the STLS capability? */
712 if(len >= 4 && !memcmp(line, "STLS", 4))
713 pop3c->tls_supported = TRUE;
714
715 /* Does the server support clear text authentication? */
716 else if(len >= 4 && !memcmp(line, "USER", 4))
717 pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
718
719 /* Does the server support SASL based authentication? */
720 else if(len >= 5 && !memcmp(line, "SASL ", 5)) {
721 pop3c->authtypes |= POP3_TYPE_SASL;
722
723 /* Advance past the SASL keyword */
724 line += 5;
725 len -= 5;
726
727 /* Loop through the data line */
728 for(;;) {
729 size_t llen;
730 size_t wordlen;
731 unsigned short mechbit;
732
733 while(len &&
734 (*line == ' ' || *line == '\t' ||
735 *line == '\r' || *line == '\n')) {
736
737 line++;
738 len--;
739 }
740
741 if(!len)
742 break;
743
744 /* Extract the word */
745 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
746 line[wordlen] != '\t' && line[wordlen] != '\r' &&
747 line[wordlen] != '\n';)
748 wordlen++;
749
750 /* Test the word for a matching authentication mechanism */
751 mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
752 if(mechbit && llen == wordlen)
753 pop3c->sasl.authmechs |= mechbit;
754
755 line += wordlen;
756 len -= wordlen;
757 }
758 }
759 }
760 else {
761 /* Clear text is supported when CAPA isn't recognised */
762 if(pop3code != '+')
763 pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
764
765 if(!data->set.use_ssl || Curl_conn_is_ssl(conn, FIRSTSOCKET))
766 result = pop3_perform_authentication(data, conn);
767 else if(pop3code == '+' && pop3c->tls_supported)
768 /* Switch to TLS connection now */
769 result = pop3_perform_starttls(data, conn);
770 else if(data->set.use_ssl <= CURLUSESSL_TRY)
771 /* Fallback and carry on with authentication */
772 result = pop3_perform_authentication(data, conn);
773 else {
774 failf(data, "STLS not supported.");
775 result = CURLE_USE_SSL_FAILED;
776 }
777 }
778
779 return result;
780 }
781
782 /* For STARTTLS responses */
pop3_state_starttls_resp(struct Curl_easy * data,struct connectdata * conn,int pop3code,pop3state instate)783 static CURLcode pop3_state_starttls_resp(struct Curl_easy *data,
784 struct connectdata *conn,
785 int pop3code,
786 pop3state instate)
787 {
788 CURLcode result = CURLE_OK;
789 (void)instate; /* no use for this yet */
790
791 /* Pipelining in response is forbidden. */
792 if(data->conn->proto.pop3c.pp.overflow)
793 return CURLE_WEIRD_SERVER_REPLY;
794
795 if(pop3code != '+') {
796 if(data->set.use_ssl != CURLUSESSL_TRY) {
797 failf(data, "STARTTLS denied");
798 result = CURLE_USE_SSL_FAILED;
799 }
800 else
801 result = pop3_perform_authentication(data, conn);
802 }
803 else
804 result = pop3_perform_upgrade_tls(data, conn);
805
806 return result;
807 }
808
809 /* For SASL authentication responses */
pop3_state_auth_resp(struct Curl_easy * data,int pop3code,pop3state instate)810 static CURLcode pop3_state_auth_resp(struct Curl_easy *data,
811 int pop3code,
812 pop3state instate)
813 {
814 CURLcode result = CURLE_OK;
815 struct connectdata *conn = data->conn;
816 struct pop3_conn *pop3c = &conn->proto.pop3c;
817 saslprogress progress;
818
819 (void)instate; /* no use for this yet */
820
821 result = Curl_sasl_continue(&pop3c->sasl, data, pop3code, &progress);
822 if(!result)
823 switch(progress) {
824 case SASL_DONE:
825 pop3_state(data, POP3_STOP); /* Authenticated */
826 break;
827 case SASL_IDLE: /* No mechanism left after cancellation */
828 #ifndef CURL_DISABLE_DIGEST_AUTH
829 if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP)
830 /* Perform APOP authentication */
831 result = pop3_perform_apop(data, conn);
832 else
833 #endif
834 if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT)
835 /* Perform clear text authentication */
836 result = pop3_perform_user(data, conn);
837 else {
838 failf(data, "Authentication cancelled");
839 result = CURLE_LOGIN_DENIED;
840 }
841 break;
842 default:
843 break;
844 }
845
846 return result;
847 }
848
849 #ifndef CURL_DISABLE_DIGEST_AUTH
850 /* For APOP responses */
pop3_state_apop_resp(struct Curl_easy * data,int pop3code,pop3state instate)851 static CURLcode pop3_state_apop_resp(struct Curl_easy *data, int pop3code,
852 pop3state instate)
853 {
854 CURLcode result = CURLE_OK;
855 (void)instate; /* no use for this yet */
856
857 if(pop3code != '+') {
858 failf(data, "Authentication failed: %d", pop3code);
859 result = CURLE_LOGIN_DENIED;
860 }
861 else
862 /* End of connect phase */
863 pop3_state(data, POP3_STOP);
864
865 return result;
866 }
867 #endif
868
869 /* For USER responses */
pop3_state_user_resp(struct Curl_easy * data,int pop3code,pop3state instate)870 static CURLcode pop3_state_user_resp(struct Curl_easy *data, int pop3code,
871 pop3state instate)
872 {
873 CURLcode result = CURLE_OK;
874 struct connectdata *conn = data->conn;
875 (void)instate; /* no use for this yet */
876
877 if(pop3code != '+') {
878 failf(data, "Access denied. %c", pop3code);
879 result = CURLE_LOGIN_DENIED;
880 }
881 else
882 /* Send the PASS command */
883 result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "PASS %s",
884 conn->passwd ? conn->passwd : "");
885 if(!result)
886 pop3_state(data, POP3_PASS);
887
888 return result;
889 }
890
891 /* For PASS responses */
pop3_state_pass_resp(struct Curl_easy * data,int pop3code,pop3state instate)892 static CURLcode pop3_state_pass_resp(struct Curl_easy *data, int pop3code,
893 pop3state instate)
894 {
895 CURLcode result = CURLE_OK;
896 (void)instate; /* no use for this yet */
897
898 if(pop3code != '+') {
899 failf(data, "Access denied. %c", pop3code);
900 result = CURLE_LOGIN_DENIED;
901 }
902 else
903 /* End of connect phase */
904 pop3_state(data, POP3_STOP);
905
906 return result;
907 }
908
909 /* For command responses */
pop3_state_command_resp(struct Curl_easy * data,int pop3code,pop3state instate)910 static CURLcode pop3_state_command_resp(struct Curl_easy *data,
911 int pop3code,
912 pop3state instate)
913 {
914 CURLcode result = CURLE_OK;
915 struct connectdata *conn = data->conn;
916 struct POP3 *pop3 = data->req.p.pop3;
917 struct pop3_conn *pop3c = &conn->proto.pop3c;
918 struct pingpong *pp = &pop3c->pp;
919
920 (void)instate; /* no use for this yet */
921
922 if(pop3code != '+') {
923 pop3_state(data, POP3_STOP);
924 return CURLE_WEIRD_SERVER_REPLY;
925 }
926
927 /* This 'OK' line ends with a CR LF pair which is the two first bytes of the
928 EOB string so count this is two matching bytes. This is necessary to make
929 the code detect the EOB if the only data than comes now is %2e CR LF like
930 when there is no body to return. */
931 pop3c->eob = 2;
932
933 /* But since this initial CR LF pair is not part of the actual body, we set
934 the strip counter here so that these bytes won't be delivered. */
935 pop3c->strip = 2;
936
937 if(pop3->transfer == PPTRANSFER_BODY) {
938 /* POP3 download */
939 Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1);
940
941 if(pp->overflow) {
942 /* The recv buffer contains data that is actually body content so send
943 it as such. Note that there may even be additional "headers" after
944 the body */
945
946 /* keep only the overflow */
947 Curl_dyn_tail(&pp->recvbuf, pp->overflow);
948 pp->nfinal = 0; /* done */
949
950 if(!data->req.no_body) {
951 result = Curl_pop3_write(data, Curl_dyn_ptr(&pp->recvbuf),
952 Curl_dyn_len(&pp->recvbuf));
953 if(result)
954 return result;
955 }
956
957 /* reset the buffer */
958 Curl_dyn_reset(&pp->recvbuf);
959 pp->overflow = 0;
960 }
961 }
962 else
963 pp->overflow = 0;
964
965 /* End of DO phase */
966 pop3_state(data, POP3_STOP);
967
968 return result;
969 }
970
pop3_statemachine(struct Curl_easy * data,struct connectdata * conn)971 static CURLcode pop3_statemachine(struct Curl_easy *data,
972 struct connectdata *conn)
973 {
974 CURLcode result = CURLE_OK;
975 int pop3code;
976 struct pop3_conn *pop3c = &conn->proto.pop3c;
977 struct pingpong *pp = &pop3c->pp;
978 size_t nread = 0;
979 (void)data;
980
981 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */
982 if(pop3c->state == POP3_UPGRADETLS)
983 return pop3_perform_upgrade_tls(data, conn);
984
985 /* Flush any data that needs to be sent */
986 if(pp->sendleft)
987 return Curl_pp_flushsend(data, pp);
988
989 do {
990 /* Read the response from the server */
991 result = Curl_pp_readresp(data, FIRSTSOCKET, pp, &pop3code, &nread);
992 if(result)
993 return result;
994
995 if(!pop3code)
996 break;
997
998 /* We have now received a full POP3 server response */
999 switch(pop3c->state) {
1000 case POP3_SERVERGREET:
1001 result = pop3_state_servergreet_resp(data, pop3code, pop3c->state);
1002 break;
1003
1004 case POP3_CAPA:
1005 result = pop3_state_capa_resp(data, pop3code, pop3c->state);
1006 break;
1007
1008 case POP3_STARTTLS:
1009 result = pop3_state_starttls_resp(data, conn, pop3code, pop3c->state);
1010 break;
1011
1012 case POP3_AUTH:
1013 result = pop3_state_auth_resp(data, pop3code, pop3c->state);
1014 break;
1015
1016 #ifndef CURL_DISABLE_DIGEST_AUTH
1017 case POP3_APOP:
1018 result = pop3_state_apop_resp(data, pop3code, pop3c->state);
1019 break;
1020 #endif
1021
1022 case POP3_USER:
1023 result = pop3_state_user_resp(data, pop3code, pop3c->state);
1024 break;
1025
1026 case POP3_PASS:
1027 result = pop3_state_pass_resp(data, pop3code, pop3c->state);
1028 break;
1029
1030 case POP3_COMMAND:
1031 result = pop3_state_command_resp(data, pop3code, pop3c->state);
1032 break;
1033
1034 case POP3_QUIT:
1035 pop3_state(data, POP3_STOP);
1036 break;
1037
1038 default:
1039 /* internal error */
1040 pop3_state(data, POP3_STOP);
1041 break;
1042 }
1043 } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp));
1044
1045 return result;
1046 }
1047
1048 /* Called repeatedly until done from multi.c */
pop3_multi_statemach(struct Curl_easy * data,bool * done)1049 static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done)
1050 {
1051 CURLcode result = CURLE_OK;
1052 struct connectdata *conn = data->conn;
1053 struct pop3_conn *pop3c = &conn->proto.pop3c;
1054
1055 if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) {
1056 bool ssldone = FALSE;
1057 result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
1058 pop3c->ssldone = ssldone;
1059 if(result || !pop3c->ssldone)
1060 return result;
1061 }
1062
1063 result = Curl_pp_statemach(data, &pop3c->pp, FALSE, FALSE);
1064 *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE;
1065
1066 return result;
1067 }
1068
pop3_block_statemach(struct Curl_easy * data,struct connectdata * conn,bool disconnecting)1069 static CURLcode pop3_block_statemach(struct Curl_easy *data,
1070 struct connectdata *conn,
1071 bool disconnecting)
1072 {
1073 CURLcode result = CURLE_OK;
1074 struct pop3_conn *pop3c = &conn->proto.pop3c;
1075
1076 while(pop3c->state != POP3_STOP && !result)
1077 result = Curl_pp_statemach(data, &pop3c->pp, TRUE, disconnecting);
1078
1079 return result;
1080 }
1081
1082 /* Allocate and initialize the POP3 struct for the current Curl_easy if
1083 required */
pop3_init(struct Curl_easy * data)1084 static CURLcode pop3_init(struct Curl_easy *data)
1085 {
1086 CURLcode result = CURLE_OK;
1087 struct POP3 *pop3;
1088
1089 pop3 = data->req.p.pop3 = calloc(1, sizeof(struct POP3));
1090 if(!pop3)
1091 result = CURLE_OUT_OF_MEMORY;
1092
1093 return result;
1094 }
1095
1096 /* For the POP3 "protocol connect" and "doing" phases only */
pop3_getsock(struct Curl_easy * data,struct connectdata * conn,curl_socket_t * socks)1097 static int pop3_getsock(struct Curl_easy *data,
1098 struct connectdata *conn, curl_socket_t *socks)
1099 {
1100 return Curl_pp_getsock(data, &conn->proto.pop3c.pp, socks);
1101 }
1102
1103 /***********************************************************************
1104 *
1105 * pop3_connect()
1106 *
1107 * This function should do everything that is to be considered a part of the
1108 * connection phase.
1109 *
1110 * The variable 'done' points to will be TRUE if the protocol-layer connect
1111 * phase is done when this function returns, or FALSE if not.
1112 */
pop3_connect(struct Curl_easy * data,bool * done)1113 static CURLcode pop3_connect(struct Curl_easy *data, bool *done)
1114 {
1115 CURLcode result = CURLE_OK;
1116 struct connectdata *conn = data->conn;
1117 struct pop3_conn *pop3c = &conn->proto.pop3c;
1118 struct pingpong *pp = &pop3c->pp;
1119
1120 *done = FALSE; /* default to not done yet */
1121
1122 /* We always support persistent connections in POP3 */
1123 connkeep(conn, "POP3 default");
1124
1125 PINGPONG_SETUP(pp, pop3_statemachine, pop3_endofresp);
1126
1127 /* Set the default preferred authentication type and mechanism */
1128 pop3c->preftype = POP3_TYPE_ANY;
1129 Curl_sasl_init(&pop3c->sasl, data, &saslpop3);
1130
1131 /* Initialise the pingpong layer */
1132 Curl_pp_init(pp);
1133
1134 /* Parse the URL options */
1135 result = pop3_parse_url_options(conn);
1136 if(result)
1137 return result;
1138
1139 /* Start off waiting for the server greeting response */
1140 pop3_state(data, POP3_SERVERGREET);
1141
1142 result = pop3_multi_statemach(data, done);
1143
1144 return result;
1145 }
1146
1147 /***********************************************************************
1148 *
1149 * pop3_done()
1150 *
1151 * The DONE function. This does what needs to be done after a single DO has
1152 * performed.
1153 *
1154 * Input argument is already checked for validity.
1155 */
pop3_done(struct Curl_easy * data,CURLcode status,bool premature)1156 static CURLcode pop3_done(struct Curl_easy *data, CURLcode status,
1157 bool premature)
1158 {
1159 CURLcode result = CURLE_OK;
1160 struct POP3 *pop3 = data->req.p.pop3;
1161
1162 (void)premature;
1163
1164 if(!pop3)
1165 return CURLE_OK;
1166
1167 if(status) {
1168 connclose(data->conn, "POP3 done with bad status");
1169 result = status; /* use the already set error code */
1170 }
1171
1172 /* Cleanup our per-request based variables */
1173 Curl_safefree(pop3->id);
1174 Curl_safefree(pop3->custom);
1175
1176 /* Clear the transfer mode for the next request */
1177 pop3->transfer = PPTRANSFER_BODY;
1178
1179 return result;
1180 }
1181
1182 /***********************************************************************
1183 *
1184 * pop3_perform()
1185 *
1186 * This is the actual DO function for POP3. Get a message/listing according to
1187 * the options previously setup.
1188 */
pop3_perform(struct Curl_easy * data,bool * connected,bool * dophase_done)1189 static CURLcode pop3_perform(struct Curl_easy *data, bool *connected,
1190 bool *dophase_done)
1191 {
1192 /* This is POP3 and no proxy */
1193 CURLcode result = CURLE_OK;
1194 struct POP3 *pop3 = data->req.p.pop3;
1195
1196 DEBUGF(infof(data, "DO phase starts"));
1197
1198 if(data->req.no_body) {
1199 /* Requested no body means no transfer */
1200 pop3->transfer = PPTRANSFER_INFO;
1201 }
1202
1203 *dophase_done = FALSE; /* not done yet */
1204
1205 /* Start the first command in the DO phase */
1206 result = pop3_perform_command(data);
1207 if(result)
1208 return result;
1209
1210 /* Run the state-machine */
1211 result = pop3_multi_statemach(data, dophase_done);
1212 *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
1213
1214 if(*dophase_done)
1215 DEBUGF(infof(data, "DO phase is complete"));
1216
1217 return result;
1218 }
1219
1220 /***********************************************************************
1221 *
1222 * pop3_do()
1223 *
1224 * This function is registered as 'curl_do' function. It decodes the path
1225 * parts etc as a wrapper to the actual DO function (pop3_perform).
1226 *
1227 * The input argument is already checked for validity.
1228 */
pop3_do(struct Curl_easy * data,bool * done)1229 static CURLcode pop3_do(struct Curl_easy *data, bool *done)
1230 {
1231 CURLcode result = CURLE_OK;
1232 *done = FALSE; /* default to false */
1233
1234 /* Parse the URL path */
1235 result = pop3_parse_url_path(data);
1236 if(result)
1237 return result;
1238
1239 /* Parse the custom request */
1240 result = pop3_parse_custom_request(data);
1241 if(result)
1242 return result;
1243
1244 result = pop3_regular_transfer(data, done);
1245
1246 return result;
1247 }
1248
1249 /***********************************************************************
1250 *
1251 * pop3_disconnect()
1252 *
1253 * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
1254 * resources. BLOCKING.
1255 */
pop3_disconnect(struct Curl_easy * data,struct connectdata * conn,bool dead_connection)1256 static CURLcode pop3_disconnect(struct Curl_easy *data,
1257 struct connectdata *conn, bool dead_connection)
1258 {
1259 struct pop3_conn *pop3c = &conn->proto.pop3c;
1260 (void)data;
1261
1262 /* We cannot send quit unconditionally. If this connection is stale or
1263 bad in any way, sending quit and waiting around here will make the
1264 disconnect wait in vain and cause more problems than we need to. */
1265
1266 if(!dead_connection && conn->bits.protoconnstart) {
1267 if(!pop3_perform_quit(data, conn))
1268 (void)pop3_block_statemach(data, conn, TRUE); /* ignore errors on QUIT */
1269 }
1270
1271 /* Disconnect from the server */
1272 Curl_pp_disconnect(&pop3c->pp);
1273
1274 /* Cleanup the SASL module */
1275 Curl_sasl_cleanup(conn, pop3c->sasl.authused);
1276
1277 /* Cleanup our connection based variables */
1278 Curl_safefree(pop3c->apoptimestamp);
1279
1280 return CURLE_OK;
1281 }
1282
1283 /* Call this when the DO phase has completed */
pop3_dophase_done(struct Curl_easy * data,bool connected)1284 static CURLcode pop3_dophase_done(struct Curl_easy *data, bool connected)
1285 {
1286 (void)data;
1287 (void)connected;
1288
1289 return CURLE_OK;
1290 }
1291
1292 /* Called from multi.c while DOing */
pop3_doing(struct Curl_easy * data,bool * dophase_done)1293 static CURLcode pop3_doing(struct Curl_easy *data, bool *dophase_done)
1294 {
1295 CURLcode result = pop3_multi_statemach(data, dophase_done);
1296
1297 if(result)
1298 DEBUGF(infof(data, "DO phase failed"));
1299 else if(*dophase_done) {
1300 result = pop3_dophase_done(data, FALSE /* not connected */);
1301
1302 DEBUGF(infof(data, "DO phase is complete"));
1303 }
1304
1305 return result;
1306 }
1307
1308 /***********************************************************************
1309 *
1310 * pop3_regular_transfer()
1311 *
1312 * The input argument is already checked for validity.
1313 *
1314 * Performs all commands done before a regular transfer between a local and a
1315 * remote host.
1316 */
pop3_regular_transfer(struct Curl_easy * data,bool * dophase_done)1317 static CURLcode pop3_regular_transfer(struct Curl_easy *data,
1318 bool *dophase_done)
1319 {
1320 CURLcode result = CURLE_OK;
1321 bool connected = FALSE;
1322
1323 /* Make sure size is unknown at this point */
1324 data->req.size = -1;
1325
1326 /* Set the progress data */
1327 Curl_pgrsSetUploadCounter(data, 0);
1328 Curl_pgrsSetDownloadCounter(data, 0);
1329 Curl_pgrsSetUploadSize(data, -1);
1330 Curl_pgrsSetDownloadSize(data, -1);
1331
1332 /* Carry out the perform */
1333 result = pop3_perform(data, &connected, dophase_done);
1334
1335 /* Perform post DO phase operations if necessary */
1336 if(!result && *dophase_done)
1337 result = pop3_dophase_done(data, connected);
1338
1339 return result;
1340 }
1341
pop3_setup_connection(struct Curl_easy * data,struct connectdata * conn)1342 static CURLcode pop3_setup_connection(struct Curl_easy *data,
1343 struct connectdata *conn)
1344 {
1345 /* Initialise the POP3 layer */
1346 CURLcode result = pop3_init(data);
1347 if(result)
1348 return result;
1349
1350 /* Clear the TLS upgraded flag */
1351 conn->bits.tls_upgraded = FALSE;
1352
1353 return CURLE_OK;
1354 }
1355
1356 /***********************************************************************
1357 *
1358 * pop3_parse_url_options()
1359 *
1360 * Parse the URL login options.
1361 */
pop3_parse_url_options(struct connectdata * conn)1362 static CURLcode pop3_parse_url_options(struct connectdata *conn)
1363 {
1364 CURLcode result = CURLE_OK;
1365 struct pop3_conn *pop3c = &conn->proto.pop3c;
1366 const char *ptr = conn->options;
1367
1368 while(!result && ptr && *ptr) {
1369 const char *key = ptr;
1370 const char *value;
1371
1372 while(*ptr && *ptr != '=')
1373 ptr++;
1374
1375 value = ptr + 1;
1376
1377 while(*ptr && *ptr != ';')
1378 ptr++;
1379
1380 if(strncasecompare(key, "AUTH=", 5)) {
1381 result = Curl_sasl_parse_url_auth_option(&pop3c->sasl,
1382 value, ptr - value);
1383
1384 if(result && strncasecompare(value, "+APOP", ptr - value)) {
1385 pop3c->preftype = POP3_TYPE_APOP;
1386 pop3c->sasl.prefmech = SASL_AUTH_NONE;
1387 result = CURLE_OK;
1388 }
1389 }
1390 else
1391 result = CURLE_URL_MALFORMAT;
1392
1393 if(*ptr == ';')
1394 ptr++;
1395 }
1396
1397 if(pop3c->preftype != POP3_TYPE_APOP)
1398 switch(pop3c->sasl.prefmech) {
1399 case SASL_AUTH_NONE:
1400 pop3c->preftype = POP3_TYPE_NONE;
1401 break;
1402 case SASL_AUTH_DEFAULT:
1403 pop3c->preftype = POP3_TYPE_ANY;
1404 break;
1405 default:
1406 pop3c->preftype = POP3_TYPE_SASL;
1407 break;
1408 }
1409
1410 return result;
1411 }
1412
1413 /***********************************************************************
1414 *
1415 * pop3_parse_url_path()
1416 *
1417 * Parse the URL path into separate path components.
1418 */
pop3_parse_url_path(struct Curl_easy * data)1419 static CURLcode pop3_parse_url_path(struct Curl_easy *data)
1420 {
1421 /* The POP3 struct is already initialised in pop3_connect() */
1422 struct POP3 *pop3 = data->req.p.pop3;
1423 const char *path = &data->state.up.path[1]; /* skip leading path */
1424
1425 /* URL decode the path for the message ID */
1426 return Curl_urldecode(path, 0, &pop3->id, NULL, REJECT_CTRL);
1427 }
1428
1429 /***********************************************************************
1430 *
1431 * pop3_parse_custom_request()
1432 *
1433 * Parse the custom request.
1434 */
pop3_parse_custom_request(struct Curl_easy * data)1435 static CURLcode pop3_parse_custom_request(struct Curl_easy *data)
1436 {
1437 CURLcode result = CURLE_OK;
1438 struct POP3 *pop3 = data->req.p.pop3;
1439 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1440
1441 /* URL decode the custom request */
1442 if(custom)
1443 result = Curl_urldecode(custom, 0, &pop3->custom, NULL, REJECT_CTRL);
1444
1445 return result;
1446 }
1447
1448 /***********************************************************************
1449 *
1450 * Curl_pop3_write()
1451 *
1452 * This function scans the body after the end-of-body and writes everything
1453 * until the end is found.
1454 */
Curl_pop3_write(struct Curl_easy * data,const char * str,size_t nread)1455 CURLcode Curl_pop3_write(struct Curl_easy *data, const char *str, size_t nread)
1456 {
1457 /* This code could be made into a special function in the handler struct */
1458 CURLcode result = CURLE_OK;
1459 struct SingleRequest *k = &data->req;
1460 struct connectdata *conn = data->conn;
1461 struct pop3_conn *pop3c = &conn->proto.pop3c;
1462 bool strip_dot = FALSE;
1463 size_t last = 0;
1464 size_t i;
1465
1466 /* Search through the buffer looking for the end-of-body marker which is
1467 5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
1468 the eob so the server will have prefixed it with an extra dot which we
1469 need to strip out. Additionally the marker could of course be spread out
1470 over 5 different data chunks. */
1471 for(i = 0; i < nread; i++) {
1472 size_t prev = pop3c->eob;
1473
1474 switch(str[i]) {
1475 case 0x0d:
1476 if(pop3c->eob == 0) {
1477 pop3c->eob++;
1478
1479 if(i) {
1480 /* Write out the body part that didn't match */
1481 result = Curl_client_write(data, CLIENTWRITE_BODY, &str[last],
1482 i - last);
1483
1484 if(result)
1485 return result;
1486
1487 last = i;
1488 }
1489 }
1490 else if(pop3c->eob == 3)
1491 pop3c->eob++;
1492 else
1493 /* If the character match wasn't at position 0 or 3 then restart the
1494 pattern matching */
1495 pop3c->eob = 1;
1496 break;
1497
1498 case 0x0a:
1499 if(pop3c->eob == 1 || pop3c->eob == 4)
1500 pop3c->eob++;
1501 else
1502 /* If the character match wasn't at position 1 or 4 then start the
1503 search again */
1504 pop3c->eob = 0;
1505 break;
1506
1507 case 0x2e:
1508 if(pop3c->eob == 2)
1509 pop3c->eob++;
1510 else if(pop3c->eob == 3) {
1511 /* We have an extra dot after the CRLF which we need to strip off */
1512 strip_dot = TRUE;
1513 pop3c->eob = 0;
1514 }
1515 else
1516 /* If the character match wasn't at position 2 then start the search
1517 again */
1518 pop3c->eob = 0;
1519 break;
1520
1521 default:
1522 pop3c->eob = 0;
1523 break;
1524 }
1525
1526 /* Did we have a partial match which has subsequently failed? */
1527 if(prev && prev >= pop3c->eob) {
1528 /* Strip can only be non-zero for the very first mismatch after CRLF
1529 and then both prev and strip are equal and nothing will be output
1530 below */
1531 while(prev && pop3c->strip) {
1532 prev--;
1533 pop3c->strip--;
1534 }
1535
1536 if(prev) {
1537 /* If the partial match was the CRLF and dot then only write the CRLF
1538 as the server would have inserted the dot */
1539 if(strip_dot && prev - 1 > 0) {
1540 result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)POP3_EOB,
1541 prev - 1);
1542 }
1543 else if(!strip_dot) {
1544 result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)POP3_EOB,
1545 prev);
1546 }
1547 else {
1548 result = CURLE_OK;
1549 }
1550
1551 if(result)
1552 return result;
1553
1554 last = i;
1555 strip_dot = FALSE;
1556 }
1557 }
1558 }
1559
1560 if(pop3c->eob == POP3_EOB_LEN) {
1561 /* We have a full match so the transfer is done, however we must transfer
1562 the CRLF at the start of the EOB as this is considered to be part of the
1563 message as per RFC-1939, sect. 3 */
1564 result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)POP3_EOB, 2);
1565
1566 k->keepon &= ~KEEP_RECV;
1567 pop3c->eob = 0;
1568
1569 return result;
1570 }
1571
1572 if(pop3c->eob)
1573 /* While EOB is matching nothing should be output */
1574 return CURLE_OK;
1575
1576 if(nread - last) {
1577 result = Curl_client_write(data, CLIENTWRITE_BODY, &str[last],
1578 nread - last);
1579 }
1580
1581 return result;
1582 }
1583
1584 #endif /* CURL_DISABLE_POP3 */
1585