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