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