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