blob: 4894f3c44c8476ede19db28e0a059318d9274a9a [file] [log] [blame]
Kristian Monsen5ab50182010-05-14 18:53:44 +01001/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2010, 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 http://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 * RFC1939 POP3 protocol
22 * RFC2384 POP URL Scheme
23 * RFC2595 Using TLS with IMAP, POP3 and ACAP
24 *
25 ***************************************************************************/
26
27#include "setup.h"
28
29#ifndef CURL_DISABLE_POP3
30#include <stdio.h>
31#include <string.h>
32#include <stdlib.h>
33#include <stdarg.h>
34#include <ctype.h>
35
36#ifdef HAVE_UNISTD_H
37#include <unistd.h>
38#endif
39
40#ifdef HAVE_SYS_SOCKET_H
41#include <sys/socket.h>
42#endif
43#ifdef HAVE_NETINET_IN_H
44#include <netinet/in.h>
45#endif
46#ifdef HAVE_ARPA_INET_H
47#include <arpa/inet.h>
48#endif
49#ifdef HAVE_UTSNAME_H
50#include <sys/utsname.h>
51#endif
52#ifdef HAVE_NETDB_H
53#include <netdb.h>
54#endif
55#ifdef __VMS
56#include <in.h>
57#include <inet.h>
58#endif
59
60#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
61#undef in_addr_t
62#define in_addr_t unsigned long
63#endif
64
65#include <curl/curl.h>
66#include "urldata.h"
67#include "sendf.h"
68#include "easyif.h" /* for Curl_convert_... prototypes */
69
70#include "if2ip.h"
71#include "hostip.h"
72#include "progress.h"
73#include "transfer.h"
74#include "escape.h"
75#include "http.h" /* for HTTP proxy tunnel stuff */
76#include "socks.h"
77#include "pop3.h"
78
79#include "strtoofft.h"
80#include "strequal.h"
81#include "sslgen.h"
82#include "connect.h"
83#include "strerror.h"
84#include "select.h"
85#include "multiif.h"
86#include "url.h"
87#include "rawstr.h"
88#include "strtoofft.h"
89
90#define _MPRINTF_REPLACE /* use our functions only */
91#include <curl/mprintf.h>
92
93#include "curl_memory.h"
94/* The last #include file should be: */
95#include "memdebug.h"
96
97/* Local API functions */
98static CURLcode pop3_parse_url_path(struct connectdata *conn);
99static CURLcode pop3_regular_transfer(struct connectdata *conn, bool *done);
100static CURLcode pop3_do(struct connectdata *conn, bool *done);
101static CURLcode pop3_done(struct connectdata *conn,
102 CURLcode, bool premature);
103static CURLcode pop3_connect(struct connectdata *conn, bool *done);
104static CURLcode pop3_disconnect(struct connectdata *conn);
105static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done);
106static int pop3_getsock(struct connectdata *conn,
107 curl_socket_t *socks,
108 int numsocks);
109static CURLcode pop3_doing(struct connectdata *conn,
110 bool *dophase_done);
111static CURLcode pop3_setup_connection(struct connectdata * conn);
112
113/*
114 * POP3 protocol handler.
115 */
116
117const struct Curl_handler Curl_handler_pop3 = {
118 "POP3", /* scheme */
119 pop3_setup_connection, /* setup_connection */
120 pop3_do, /* do_it */
121 pop3_done, /* done */
122 ZERO_NULL, /* do_more */
123 pop3_connect, /* connect_it */
124 pop3_multi_statemach, /* connecting */
125 pop3_doing, /* doing */
126 pop3_getsock, /* proto_getsock */
127 pop3_getsock, /* doing_getsock */
128 ZERO_NULL, /* perform_getsock */
129 pop3_disconnect, /* disconnect */
130 PORT_POP3, /* defport */
131 PROT_POP3 /* protocol */
132};
133
134
135#ifdef USE_SSL
136/*
137 * POP3S protocol handler.
138 */
139
140const 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, /* perform_getsock */
152 pop3_disconnect, /* disconnect */
153 PORT_POP3S, /* defport */
154 PROT_POP3 | PROT_POP3S | PROT_SSL /* protocol */
155};
156#endif
157
158#ifndef CURL_DISABLE_HTTP
159/*
160 * HTTP-proxyed POP3 protocol handler.
161 */
162
163static const struct Curl_handler Curl_handler_pop3_proxy = {
164 "POP3", /* scheme */
165 ZERO_NULL, /* setup_connection */
166 Curl_http, /* do_it */
167 Curl_http_done, /* done */
168 ZERO_NULL, /* do_more */
169 ZERO_NULL, /* connect_it */
170 ZERO_NULL, /* connecting */
171 ZERO_NULL, /* doing */
172 ZERO_NULL, /* proto_getsock */
173 ZERO_NULL, /* doing_getsock */
174 ZERO_NULL, /* perform_getsock */
175 ZERO_NULL, /* disconnect */
176 PORT_POP3, /* defport */
177 PROT_HTTP /* protocol */
178};
179
180
181#ifdef USE_SSL
182/*
183 * HTTP-proxyed POP3S protocol handler.
184 */
185
186static const struct Curl_handler Curl_handler_pop3s_proxy = {
187 "POP3S", /* scheme */
188 ZERO_NULL, /* setup_connection */
189 Curl_http, /* do_it */
190 Curl_http_done, /* done */
191 ZERO_NULL, /* do_more */
192 ZERO_NULL, /* connect_it */
193 ZERO_NULL, /* connecting */
194 ZERO_NULL, /* doing */
195 ZERO_NULL, /* proto_getsock */
196 ZERO_NULL, /* doing_getsock */
197 ZERO_NULL, /* perform_getsock */
198 ZERO_NULL, /* disconnect */
199 PORT_POP3S, /* defport */
200 PROT_HTTP /* protocol */
201};
202#endif
203#endif
204
205
206/* function that checks for a pop3 status code at the start of the given
207 string */
208static int pop3_endofresp(struct pingpong *pp,
209 int *resp)
210{
211 char *line = pp->linestart_resp;
212 size_t len = pp->nread_resp;
213
214 if( ((len >= 3) && !memcmp("+OK", line, 3)) ||
215 ((len >= 4) && !memcmp("-ERR", line, 4)) ) {
216 *resp=line[1]; /* O or E */
217 return TRUE;
218 }
219
220 return FALSE; /* nothing for us */
221}
222
223/* This is the ONLY way to change POP3 state! */
224static void state(struct connectdata *conn,
225 pop3state newstate)
226{
227#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
228 /* for debug purposes */
229 static const char * const names[]={
230 "STOP",
231 "SERVERGREET",
232 "USER",
233 "PASS",
234 "STARTTLS",
235 "LIST",
236 "RETR",
237 "QUIT",
238 /* LAST */
239 };
240#endif
241 struct pop3_conn *pop3c = &conn->proto.pop3c;
242#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
243 if(pop3c->state != newstate)
244 infof(conn->data, "POP3 %p state change from %s to %s\n",
245 pop3c, names[pop3c->state], names[newstate]);
246#endif
247 pop3c->state = newstate;
248}
249
250static CURLcode pop3_state_user(struct connectdata *conn)
251{
252 CURLcode result;
253 struct FTP *pop3 = conn->data->state.proto.pop3;
254
255 /* send USER */
256 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s",
257 pop3->user?pop3->user:"");
258 if(result)
259 return result;
260
261 state(conn, POP3_USER);
262
263 return CURLE_OK;
264}
265
266/* For the POP3 "protocol connect" and "doing" phases only */
267static int pop3_getsock(struct connectdata *conn,
268 curl_socket_t *socks,
269 int numsocks)
270{
271 return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks);
272}
273
274/* for STARTTLS responses */
275static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
276 int pop3code,
277 pop3state instate)
278{
279 CURLcode result = CURLE_OK;
280 struct SessionHandle *data = conn->data;
281 (void)instate; /* no use for this yet */
282
283 if(pop3code != 'O') {
284 failf(data, "STARTTLS denied. %c", pop3code);
285 result = CURLE_LOGIN_DENIED;
286 }
287 else {
288 /* Curl_ssl_connect is BLOCKING */
289 result = Curl_ssl_connect(conn, FIRSTSOCKET);
290 if(CURLE_OK == result) {
291 conn->protocol |= PROT_POP3S;
292 result = pop3_state_user(conn);
293 }
294 }
295 state(conn, POP3_STOP);
296 return result;
297}
298
299/* for USER responses */
300static CURLcode pop3_state_user_resp(struct connectdata *conn,
301 int pop3code,
302 pop3state instate)
303{
304 CURLcode result = CURLE_OK;
305 struct SessionHandle *data = conn->data;
306 struct FTP *pop3 = data->state.proto.pop3;
307
308 (void)instate; /* no use for this yet */
309
310 if(pop3code != 'O') {
311 failf(data, "Access denied. %c", pop3code);
312 result = CURLE_LOGIN_DENIED;
313 }
314
315 /* send PASS */
316 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s",
317 pop3->passwd?pop3->passwd:"");
318 if(result)
319 return result;
320
321 state(conn, POP3_PASS);
322 return result;
323}
324
325/* for PASS responses */
326static CURLcode pop3_state_pass_resp(struct connectdata *conn,
327 int pop3code,
328 pop3state instate)
329{
330 CURLcode result = CURLE_OK;
331 struct SessionHandle *data = conn->data;
332 (void)instate; /* no use for this yet */
333
334 if(pop3code != 'O') {
335 failf(data, "Access denied. %c", pop3code);
336 result = CURLE_LOGIN_DENIED;
337 }
338
339 state(conn, POP3_STOP);
340 return result;
341}
342
343/* for the retr response */
344static CURLcode pop3_state_retr_resp(struct connectdata *conn,
345 int pop3code,
346 pop3state instate)
347{
348 CURLcode result = CURLE_OK;
349 struct SessionHandle *data = conn->data;
350 struct FTP *pop3 = data->state.proto.pop3;
351 struct pop3_conn *pop3c = &conn->proto.pop3c;
352 struct pingpong *pp = &pop3c->pp;
353
354 (void)instate; /* no use for this yet */
355
356 if('O' != pop3code) {
357 state(conn, POP3_STOP);
358 return CURLE_RECV_ERROR;
359 }
360
361 /* POP3 download */
362 result=Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE,
363 pop3->bytecountp,
364 -1, NULL); /* no upload here */
365
366 if(pp->cache) {
367 /* At this point there is a bunch of data in the header "cache" that is
368 actually body content, send it as body and then skip it. Do note
369 that there may even be additional "headers" after the body. */
370
371 /* we may get the EOB already here! */
372 result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
373 if(result)
374 return result;
375
376 /* cache is drained */
377 free(pp->cache);
378 pp->cache = NULL;
379 pp->cache_size = 0;
380 }
381
382 state(conn, POP3_STOP);
383 return result;
384}
385
386
387/* for the list response */
388static CURLcode pop3_state_list_resp(struct connectdata *conn,
389 int pop3code,
390 pop3state instate)
391{
392 CURLcode result = CURLE_OK;
393 struct SessionHandle *data = conn->data;
394 struct FTP *pop3 = data->state.proto.pop3;
395 struct pop3_conn *pop3c = &conn->proto.pop3c;
396 struct pingpong *pp = &pop3c->pp;
397
398 (void)instate; /* no use for this yet */
399
400 if('O' != pop3code) {
401 state(conn, POP3_STOP);
402 return CURLE_RECV_ERROR;
403 }
404
405 /* POP3 download */
406 result=Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE,
407 pop3->bytecountp,
408 -1, NULL); /* no upload here */
409
410 if(pp->cache) {
411 /* cache holds the email ID listing */
412
413 /* we may get the EOB already here! */
414 result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
415 if(result)
416 return result;
417
418 /* cache is drained */
419 free(pp->cache);
420 pp->cache = NULL;
421 pp->cache_size = 0;
422 }
423
424 state(conn, POP3_STOP);
425 return result;
426}
427
428/* start the DO phase for RETR */
429static CURLcode pop3_retr(struct connectdata *conn)
430{
431 CURLcode result = CURLE_OK;
432 struct pop3_conn *pop3c = &conn->proto.pop3c;
433
434 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "RETR %s", pop3c->mailbox);
435 if(result)
436 return result;
437
438 state(conn, POP3_RETR);
439 return result;
440}
441
442/* start the DO phase for LIST */
443static CURLcode pop3_list(struct connectdata *conn)
444{
445 CURLcode result = CURLE_OK;
446 struct pop3_conn *pop3c = &conn->proto.pop3c;
447
448 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "LIST %s", pop3c->mailbox);
449 if(result)
450 return result;
451
452 state(conn, POP3_LIST);
453 return result;
454}
455
456static CURLcode pop3_statemach_act(struct connectdata *conn)
457{
458 CURLcode result;
459 curl_socket_t sock = conn->sock[FIRSTSOCKET];
460 struct SessionHandle *data=conn->data;
461 int pop3code;
462 struct pop3_conn *pop3c = &conn->proto.pop3c;
463 struct pingpong *pp = &pop3c->pp;
464 size_t nread = 0;
465
466 if(pp->sendleft)
467 return Curl_pp_flushsend(pp);
468
469 /* we read a piece of response */
470 result = Curl_pp_readresp(sock, pp, &pop3code, &nread);
471 if(result)
472 return result;
473
474 if(pop3code) {
475 /* we have now received a full POP3 server response */
476 switch(pop3c->state) {
477 case POP3_SERVERGREET:
478 if(pop3code != 'O') {
479 failf(data, "Got unexpected pop3-server response");
480 return CURLE_FTP_WEIRD_SERVER_REPLY;
481 }
482
483 if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
484 /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
485 to TLS connection now */
486 result = Curl_pp_sendf(&pop3c->pp, "STARTTLS", NULL);
487 state(conn, POP3_STARTTLS);
488 }
489 else
490 result = pop3_state_user(conn);
491 if(result)
492 return result;
493 break;
494
495 case POP3_USER:
496 result = pop3_state_user_resp(conn, pop3code, pop3c->state);
497 break;
498
499 case POP3_PASS:
500 result = pop3_state_pass_resp(conn, pop3code, pop3c->state);
501 break;
502
503 case POP3_STARTTLS:
504 result = pop3_state_starttls_resp(conn, pop3code, pop3c->state);
505 break;
506
507 case POP3_RETR:
508 result = pop3_state_retr_resp(conn, pop3code, pop3c->state);
509 break;
510
511 case POP3_LIST:
512 result = pop3_state_list_resp(conn, pop3code, pop3c->state);
513 break;
514
515 case POP3_QUIT:
516 /* fallthrough, just stop! */
517 default:
518 /* internal error */
519 state(conn, POP3_STOP);
520 break;
521 }
522 }
523 return result;
524}
525
526/* called repeatedly until done from multi.c */
527static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done)
528{
529 struct pop3_conn *pop3c = &conn->proto.pop3c;
530 CURLcode result = Curl_pp_multi_statemach(&pop3c->pp);
531
532 *done = (bool)(pop3c->state == POP3_STOP);
533
534 return result;
535}
536
537static CURLcode pop3_easy_statemach(struct connectdata *conn)
538{
539 struct pop3_conn *pop3c = &conn->proto.pop3c;
540 struct pingpong *pp = &pop3c->pp;
541 CURLcode result = CURLE_OK;
542
543 while(pop3c->state != POP3_STOP) {
544 result = Curl_pp_easy_statemach(pp);
545 if(result)
546 break;
547 }
548
549 return result;
550}
551
552/*
553 * Allocate and initialize the struct POP3 for the current SessionHandle. If
554 * need be.
555 */
556static CURLcode pop3_init(struct connectdata *conn)
557{
558 struct SessionHandle *data = conn->data;
559 struct FTP *pop3 = data->state.proto.pop3;
560 if(!pop3) {
561 pop3 = data->state.proto.pop3 = calloc(sizeof(struct FTP), 1);
562 if(!pop3)
563 return CURLE_OUT_OF_MEMORY;
564 }
565
566 /* get some initial data into the pop3 struct */
567 pop3->bytecountp = &data->req.bytecount;
568
569 /* No need to duplicate user+password, the connectdata struct won't change
570 during a session, but we re-init them here since on subsequent inits
571 since the conn struct may have changed or been replaced.
572 */
573 pop3->user = conn->user;
574 pop3->passwd = conn->passwd;
575
576 return CURLE_OK;
577}
578
579/*
580 * pop3_connect() should do everything that is to be considered a part of
581 * the connection phase.
582 *
583 * The variable 'done' points to will be TRUE if the protocol-layer connect
584 * phase is done when this function returns, or FALSE is not. When called as
585 * a part of the easy interface, it will always be TRUE.
586 */
587static CURLcode pop3_connect(struct connectdata *conn,
588 bool *done) /* see description above */
589{
590 CURLcode result;
591 struct pop3_conn *pop3c = &conn->proto.pop3c;
592 struct SessionHandle *data=conn->data;
593 struct pingpong *pp = &pop3c->pp;
594
595 *done = FALSE; /* default to not done yet */
596
597 /* If there already is a protocol-specific struct allocated for this
598 sessionhandle, deal with it */
599 Curl_reset_reqproto(conn);
600
601 result = pop3_init(conn);
602 if(CURLE_OK != result)
603 return result;
604
605 /* We always support persistant connections on pop3 */
606 conn->bits.close = FALSE;
607
608 pp->response_time = RESP_TIMEOUT; /* set default response time-out */
609 pp->statemach_act = pop3_statemach_act;
610 pp->endofresp = pop3_endofresp;
611 pp->conn = conn;
612
613#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY)
614 if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
615 /* for POP3 over HTTP proxy */
616 struct HTTP http_proxy;
617 struct FTP *pop3_save;
618
619 /* BLOCKING */
620 /* We want "seamless" POP3 operations through HTTP proxy tunnel */
621
622 /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
623 * conn->proto.http; we want POP3 through HTTP and we have to change the
624 * member temporarily for connecting to the HTTP proxy. After
625 * Curl_proxyCONNECT we have to set back the member to the original struct
626 * POP3 pointer
627 */
628 pop3_save = data->state.proto.pop3;
629 memset(&http_proxy, 0, sizeof(http_proxy));
630 data->state.proto.http = &http_proxy;
631
632 result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
633 conn->host.name, conn->remote_port);
634
635 data->state.proto.pop3 = pop3_save;
636
637 if(CURLE_OK != result)
638 return result;
639 }
640#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */
641
642 if(conn->protocol & PROT_POP3S) {
643 /* BLOCKING */
644 /* POP3S is simply pop3 with SSL for the control channel */
645 /* now, perform the SSL initialization for this socket */
646 result = Curl_ssl_connect(conn, FIRSTSOCKET);
647 if(result)
648 return result;
649 }
650
651 Curl_pp_init(pp); /* init the response reader stuff */
652
653 /* When we connect, we start in the state where we await the server greet
654 response */
655 state(conn, POP3_SERVERGREET);
656
657 if(data->state.used_interface == Curl_if_multi)
658 result = pop3_multi_statemach(conn, done);
659 else {
660 result = pop3_easy_statemach(conn);
661 if(!result)
662 *done = TRUE;
663 }
664
665 return result;
666}
667
668/***********************************************************************
669 *
670 * pop3_done()
671 *
672 * The DONE function. This does what needs to be done after a single DO has
673 * performed.
674 *
675 * Input argument is already checked for validity.
676 */
677static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
678 bool premature)
679{
680 struct SessionHandle *data = conn->data;
681 struct FTP *pop3 = data->state.proto.pop3;
682 CURLcode result=CURLE_OK;
683 (void)premature;
684
685 if(!pop3)
686 /* When the easy handle is removed from the multi while libcurl is still
687 * trying to resolve the host name, it seems that the pop3 struct is not
688 * yet initialized, but the removal action calls Curl_done() which calls
689 * this function. So we simply return success if no pop3 pointer is set.
690 */
691 return CURLE_OK;
692
693 if(status) {
694 conn->bits.close = TRUE; /* marked for closure */
695 result = status; /* use the already set error code */
696 }
697
698 /* clear these for next connection */
699 pop3->transfer = FTPTRANSFER_BODY;
700
701 return result;
702}
703
704/***********************************************************************
705 *
706 * pop3_perform()
707 *
708 * This is the actual DO function for POP3. Get a file/directory according to
709 * the options previously setup.
710 */
711
712static
713CURLcode pop3_perform(struct connectdata *conn,
714 bool *connected, /* connect status after PASV / PORT */
715 bool *dophase_done)
716{
717 /* this is POP3 and no proxy */
718 CURLcode result=CURLE_OK;
719 struct pop3_conn *pop3c = &conn->proto.pop3c;
720
721 DEBUGF(infof(conn->data, "DO phase starts\n"));
722
723 if(conn->data->set.opt_no_body) {
724 /* requested no body means no transfer... */
725 struct FTP *pop3 = conn->data->state.proto.pop3;
726 pop3->transfer = FTPTRANSFER_INFO;
727 }
728
729 *dophase_done = FALSE; /* not done yet */
730
731 /* start the first command in the DO phase */
732 /* If mailbox is empty, then assume user wants listing for mail IDs,
733 * otherwise, attempt to retrieve the mail-id stored in mailbox
734 */
735 if (strlen(pop3c->mailbox))
736 result = pop3_retr(conn);
737 else
738 result = pop3_list(conn);
739 if(result)
740 return result;
741
742 /* run the state-machine */
743 if(conn->data->state.used_interface == Curl_if_multi)
744 result = pop3_multi_statemach(conn, dophase_done);
745 else {
746 result = pop3_easy_statemach(conn);
747 *dophase_done = TRUE; /* with the easy interface we are done here */
748 }
749 *connected = conn->bits.tcpconnect;
750
751 if(*dophase_done)
752 DEBUGF(infof(conn->data, "DO phase is complete\n"));
753
754 return result;
755}
756
757/***********************************************************************
758 *
759 * pop3_do()
760 *
761 * This function is registered as 'curl_do' function. It decodes the path
762 * parts etc as a wrapper to the actual DO function (pop3_perform).
763 *
764 * The input argument is already checked for validity.
765 */
766static CURLcode pop3_do(struct connectdata *conn, bool *done)
767{
768 CURLcode retcode = CURLE_OK;
769
770 *done = FALSE; /* default to false */
771
772 /*
773 Since connections can be re-used between SessionHandles, this might be a
774 connection already existing but on a fresh SessionHandle struct so we must
775 make sure we have a good 'struct POP3' to play with. For new connections,
776 the struct POP3 is allocated and setup in the pop3_connect() function.
777 */
778 Curl_reset_reqproto(conn);
779 retcode = pop3_init(conn);
780 if(retcode)
781 return retcode;
782
783 retcode = pop3_parse_url_path(conn);
784 if(retcode)
785 return retcode;
786
787 retcode = pop3_regular_transfer(conn, done);
788
789 return retcode;
790}
791
792/***********************************************************************
793 *
794 * pop3_quit()
795 *
796 * This should be called before calling sclose(). We should then wait for the
797 * response from the server before returning. The calling code should then try
798 * to close the connection.
799 *
800 */
801static CURLcode pop3_quit(struct connectdata *conn)
802{
803 CURLcode result = CURLE_OK;
804
805 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "QUIT", NULL);
806 if(result)
807 return result;
808 state(conn, POP3_QUIT);
809
810 result = pop3_easy_statemach(conn);
811
812 return result;
813}
814
815/***********************************************************************
816 *
817 * pop3_disconnect()
818 *
819 * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
820 * resources. BLOCKING.
821 */
822static CURLcode pop3_disconnect(struct connectdata *conn)
823{
824 struct pop3_conn *pop3c= &conn->proto.pop3c;
825
826 /* We cannot send quit unconditionally. If this connection is stale or
827 bad in any way, sending quit and waiting around here will make the
828 disconnect wait in vain and cause more problems than we need to.
829 */
830
831 /* The POP3 session may or may not have been allocated/setup at this
832 point! */
833 (void)pop3_quit(conn); /* ignore errors on the LOGOUT */
834
835
836 Curl_pp_disconnect(&pop3c->pp);
837
838 return CURLE_OK;
839}
840
841/***********************************************************************
842 *
843 * pop3_parse_url_path()
844 *
845 * Parse the URL path into separate path components.
846 *
847 */
848static CURLcode pop3_parse_url_path(struct connectdata *conn)
849{
850 /* the pop3 struct is already inited in pop3_connect() */
851 struct pop3_conn *pop3c = &conn->proto.pop3c;
852 struct SessionHandle *data = conn->data;
853 const char *path = data->state.path;
854 int len;
855
856 /* url decode the path and use this mailbox */
857 pop3c->mailbox = curl_easy_unescape(data, path, 0, &len);
858
859 return CURLE_OK;
860}
861
862/* call this when the DO phase has completed */
863static CURLcode pop3_dophase_done(struct connectdata *conn,
864 bool connected)
865{
866 CURLcode result = CURLE_OK;
867 struct FTP *pop3 = conn->data->state.proto.pop3;
868 struct pop3_conn *pop3c = &conn->proto.pop3c;
869 (void)connected;
870
871 if(pop3->transfer != FTPTRANSFER_BODY)
872 /* no data to transfer */
873 result=Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
874
875 free(pop3c->mailbox);
876
877 return result;
878}
879
880/* called from multi.c while DOing */
881static CURLcode pop3_doing(struct connectdata *conn,
882 bool *dophase_done)
883{
884 CURLcode result;
885 result = pop3_multi_statemach(conn, dophase_done);
886
887 if(*dophase_done) {
888 result = pop3_dophase_done(conn, FALSE /* not connected */);
889
890 DEBUGF(infof(conn->data, "DO phase is complete\n"));
891 }
892 return result;
893}
894
895/***********************************************************************
896 *
897 * pop3_regular_transfer()
898 *
899 * The input argument is already checked for validity.
900 *
901 * Performs all commands done before a regular transfer between a local and a
902 * remote host.
903 *
904 */
905static
906CURLcode pop3_regular_transfer(struct connectdata *conn,
907 bool *dophase_done)
908{
909 CURLcode result=CURLE_OK;
910 bool connected=FALSE;
911 struct SessionHandle *data = conn->data;
912 data->req.size = -1; /* make sure this is unknown at this point */
913
914 Curl_pgrsSetUploadCounter(data, 0);
915 Curl_pgrsSetDownloadCounter(data, 0);
916 Curl_pgrsSetUploadSize(data, 0);
917 Curl_pgrsSetDownloadSize(data, 0);
918
919 result = pop3_perform(conn,
920 &connected, /* have we connected after PASV/PORT */
921 dophase_done); /* all commands in the DO-phase done? */
922
923 if(CURLE_OK == result) {
924
925 if(!*dophase_done)
926 /* the DO phase has not completed yet */
927 return CURLE_OK;
928
929 result = pop3_dophase_done(conn, connected);
930 if(result)
931 return result;
932 }
933
934 return result;
935}
936
937static CURLcode pop3_setup_connection(struct connectdata * conn)
938{
939 struct SessionHandle *data = conn->data;
940
941 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
942 /* Unless we have asked to tunnel pop3 operations through the proxy, we
943 switch and use HTTP operations only */
944#ifndef CURL_DISABLE_HTTP
945 if(conn->handler == &Curl_handler_pop3)
946 conn->handler = &Curl_handler_pop3_proxy;
947 else {
948#ifdef USE_SSL
949 conn->handler = &Curl_handler_pop3s_proxy;
950#else
951 failf(data, "POP3S not supported!");
952 return CURLE_UNSUPPORTED_PROTOCOL;
953#endif
954 }
955 /*
956 * We explicitly mark this connection as persistent here as we're doing
957 * POP3 over HTTP and thus we accidentally avoid setting this value
958 * otherwise.
959 */
960 conn->bits.close = FALSE;
961#else
962 failf(data, "POP3 over http proxy requires HTTP support built-in!");
963 return CURLE_UNSUPPORTED_PROTOCOL;
964#endif
965 }
966
967 data->state.path++; /* don't include the initial slash */
968
969 return CURLE_OK;
970}
971
972/* this is the 5-bytes End-Of-Body marker for POP3 */
973#define POP3_EOB "\x0d\x0a\x2e\x0d\x0a"
974#define POP3_EOB_LEN 5
975
976/*
977 * This function scans the body after the end-of-body and writes everything
978 * until the end is found.
979 */
980CURLcode Curl_pop3_write(struct connectdata *conn,
981 char *str,
982 size_t nread)
983{
984 /* This code could be made into a special function in the handler struct. */
985 CURLcode result;
986 struct SessionHandle *data = conn->data;
987 struct SingleRequest *k = &data->req;
988
989 /* Detect the end-of-body marker, which is 5 bytes:
990 0d 0a 2e 0d 0a. This marker can of course be spread out
991 over up to 5 different data chunks. Deal with it! */
992 struct pop3_conn *pop3c = &conn->proto.pop3c;
993 size_t checkmax = (nread >= POP3_EOB_LEN?POP3_EOB_LEN:nread);
994 size_t checkleft = POP3_EOB_LEN-pop3c->eob;
995 size_t check = (checkmax >= checkleft?checkleft:checkmax);
996
997 if(!memcmp(POP3_EOB, &str[nread - check], check)) {
998 /* substring match */
999 pop3c->eob += check;
1000 if(pop3c->eob == POP3_EOB_LEN) {
1001 /* full match, the transfer is done! */
1002 str[nread - check] = '\0';
1003 nread -= check;
1004 k->keepon &= ~KEEP_RECV;
1005 pop3c->eob = 0;
1006 }
1007 }
1008 else if(pop3c->eob) {
1009 /* not a match, but we matched a piece before so we must now
1010 send that part as body first, before we move on and send
1011 this buffer */
1012 result = Curl_client_write(conn, CLIENTWRITE_BODY,
1013 (char *)POP3_EOB, pop3c->eob);
1014 if(result)
1015 return result;
1016 pop3c->eob = 0;
1017 }
1018
1019 result = Curl_client_write(conn, CLIENTWRITE_BODY, str, nread);
1020
1021 return result;
1022}
1023
1024#endif /* CURL_DISABLE_POP3 */