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