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