Git init
[external/curl.git] / lib / pop3.c
1 /***************************************************************************
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 */
98 static CURLcode pop3_parse_url_path(struct connectdata *conn);
99 static CURLcode pop3_regular_transfer(struct connectdata *conn, bool *done);
100 static CURLcode pop3_do(struct connectdata *conn, bool *done);
101 static CURLcode pop3_done(struct connectdata *conn,
102                           CURLcode, bool premature);
103 static CURLcode pop3_connect(struct connectdata *conn, bool *done);
104 static CURLcode pop3_disconnect(struct connectdata *conn, bool dead_connection);
105 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done);
106 static int pop3_getsock(struct connectdata *conn,
107                         curl_socket_t *socks,
108                         int numsocks);
109 static CURLcode pop3_doing(struct connectdata *conn,
110                            bool *dophase_done);
111 static CURLcode pop3_setup_connection(struct connectdata * conn);
112
113 /*
114  * POP3 protocol handler.
115  */
116
117 const 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
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,                        /* 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
163 static 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
186 static 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 */
208 static 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! */
224 static 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
250 static 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 */
267 static 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 */
275 static 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 */
300 static 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   else
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 */
326 static 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 */
344 static 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   Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE,
363                       pop3->bytecountp, -1, NULL); /* no upload here */
364
365   if(pp->cache) {
366     /* At this point there is a bunch of data in the header "cache" that is
367        actually body content, send it as body and then skip it. Do note
368        that there may even be additional "headers" after the body. */
369
370     /* we may get the EOB already here! */
371     result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
372     if(result)
373       return result;
374
375     /* cache is drained */
376     free(pp->cache);
377     pp->cache = NULL;
378     pp->cache_size = 0;
379   }
380
381   state(conn, POP3_STOP);
382   return result;
383 }
384
385
386 /* for the list response */
387 static CURLcode pop3_state_list_resp(struct connectdata *conn,
388                                      int pop3code,
389                                      pop3state instate)
390 {
391   CURLcode result = CURLE_OK;
392   struct SessionHandle *data = conn->data;
393   struct FTP *pop3 = data->state.proto.pop3;
394   struct pop3_conn *pop3c = &conn->proto.pop3c;
395   struct pingpong *pp = &pop3c->pp;
396
397   (void)instate; /* no use for this yet */
398
399   if('O' != pop3code) {
400     state(conn, POP3_STOP);
401     return CURLE_RECV_ERROR;
402   }
403
404   /* POP3 download */
405   Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, pop3->bytecountp,
406                       -1, NULL); /* no upload here */
407
408   if(pp->cache) {
409     /* cache holds the email ID listing */
410
411     /* we may get the EOB already here! */
412     result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
413     if(result)
414       return result;
415
416     /* cache is drained */
417     free(pp->cache);
418     pp->cache = NULL;
419     pp->cache_size = 0;
420   }
421
422   state(conn, POP3_STOP);
423   return result;
424 }
425
426 /* start the DO phase for RETR */
427 static CURLcode pop3_retr(struct connectdata *conn)
428 {
429   CURLcode result = CURLE_OK;
430   struct pop3_conn *pop3c = &conn->proto.pop3c;
431
432   result = Curl_pp_sendf(&conn->proto.pop3c.pp, "RETR %s", pop3c->mailbox);
433   if(result)
434     return result;
435
436   state(conn, POP3_RETR);
437   return result;
438 }
439
440 /* start the DO phase for LIST */
441 static CURLcode pop3_list(struct connectdata *conn)
442 {
443   CURLcode result = CURLE_OK;
444   struct pop3_conn *pop3c = &conn->proto.pop3c;
445
446   result = Curl_pp_sendf(&conn->proto.pop3c.pp, "LIST %s", pop3c->mailbox);
447   if(result)
448     return result;
449
450   state(conn, POP3_LIST);
451   return result;
452 }
453
454 static CURLcode pop3_statemach_act(struct connectdata *conn)
455 {
456   CURLcode result;
457   curl_socket_t sock = conn->sock[FIRSTSOCKET];
458   struct SessionHandle *data=conn->data;
459   int pop3code;
460   struct pop3_conn *pop3c = &conn->proto.pop3c;
461   struct pingpong *pp = &pop3c->pp;
462   size_t nread = 0;
463
464   if(pp->sendleft)
465     return Curl_pp_flushsend(pp);
466
467   /* we read a piece of response */
468   result = Curl_pp_readresp(sock, pp, &pop3code, &nread);
469   if(result)
470     return result;
471
472   if(pop3code) {
473     /* we have now received a full POP3 server response */
474     switch(pop3c->state) {
475     case POP3_SERVERGREET:
476       if(pop3code != 'O') {
477         failf(data, "Got unexpected pop3-server response");
478         return CURLE_FTP_WEIRD_SERVER_REPLY;
479       }
480
481       if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
482         /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
483            to TLS connection now */
484         result = Curl_pp_sendf(&pop3c->pp, "STARTTLS", NULL);
485         state(conn, POP3_STARTTLS);
486       }
487       else
488         result = pop3_state_user(conn);
489       if(result)
490         return result;
491       break;
492
493     case POP3_USER:
494       result = pop3_state_user_resp(conn, pop3code, pop3c->state);
495       break;
496
497     case POP3_PASS:
498       result = pop3_state_pass_resp(conn, pop3code, pop3c->state);
499       break;
500
501     case POP3_STARTTLS:
502       result = pop3_state_starttls_resp(conn, pop3code, pop3c->state);
503       break;
504
505     case POP3_RETR:
506       result = pop3_state_retr_resp(conn, pop3code, pop3c->state);
507       break;
508
509     case POP3_LIST:
510       result = pop3_state_list_resp(conn, pop3code, pop3c->state);
511       break;
512
513     case POP3_QUIT:
514       /* fallthrough, just stop! */
515     default:
516       /* internal error */
517       state(conn, POP3_STOP);
518       break;
519     }
520   }
521   return result;
522 }
523
524 /* called repeatedly until done from multi.c */
525 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done)
526 {
527   struct pop3_conn *pop3c = &conn->proto.pop3c;
528   CURLcode result = Curl_pp_multi_statemach(&pop3c->pp);
529
530   *done = (bool)(pop3c->state == POP3_STOP);
531
532   return result;
533 }
534
535 static CURLcode pop3_easy_statemach(struct connectdata *conn)
536 {
537   struct pop3_conn *pop3c = &conn->proto.pop3c;
538   struct pingpong *pp = &pop3c->pp;
539   CURLcode result = CURLE_OK;
540
541   while(pop3c->state != POP3_STOP) {
542     result = Curl_pp_easy_statemach(pp);
543     if(result)
544       break;
545   }
546
547   return result;
548 }
549
550 /*
551  * Allocate and initialize the struct POP3 for the current SessionHandle.  If
552  * need be.
553  */
554 static CURLcode pop3_init(struct connectdata *conn)
555 {
556   struct SessionHandle *data = conn->data;
557   struct FTP *pop3 = data->state.proto.pop3;
558   if(!pop3) {
559     pop3 = data->state.proto.pop3 = calloc(sizeof(struct FTP), 1);
560     if(!pop3)
561       return CURLE_OUT_OF_MEMORY;
562   }
563
564   /* get some initial data into the pop3 struct */
565   pop3->bytecountp = &data->req.bytecount;
566
567   /* No need to duplicate user+password, the connectdata struct won't change
568      during a session, but we re-init them here since on subsequent inits
569      since the conn struct may have changed or been replaced.
570   */
571   pop3->user = conn->user;
572   pop3->passwd = conn->passwd;
573
574   return CURLE_OK;
575 }
576
577 /*
578  * pop3_connect() should do everything that is to be considered a part of
579  * the connection phase.
580  *
581  * The variable 'done' points to will be TRUE if the protocol-layer connect
582  * phase is done when this function returns, or FALSE is not. When called as
583  * a part of the easy interface, it will always be TRUE.
584  */
585 static CURLcode pop3_connect(struct connectdata *conn,
586                                  bool *done) /* see description above */
587 {
588   CURLcode result;
589   struct pop3_conn *pop3c = &conn->proto.pop3c;
590   struct SessionHandle *data=conn->data;
591   struct pingpong *pp = &pop3c->pp;
592
593   *done = FALSE; /* default to not done yet */
594
595   /* If there already is a protocol-specific struct allocated for this
596      sessionhandle, deal with it */
597   Curl_reset_reqproto(conn);
598
599   result = pop3_init(conn);
600   if(CURLE_OK != result)
601     return result;
602
603   /* We always support persistant connections on pop3 */
604   conn->bits.close = FALSE;
605
606   pp->response_time = RESP_TIMEOUT; /* set default response time-out */
607   pp->statemach_act = pop3_statemach_act;
608   pp->endofresp = pop3_endofresp;
609   pp->conn = conn;
610
611 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY)
612   if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
613     /* for POP3 over HTTP proxy */
614     struct HTTP http_proxy;
615     struct FTP *pop3_save;
616
617     /* BLOCKING */
618     /* We want "seamless" POP3 operations through HTTP proxy tunnel */
619
620     /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
621      * conn->proto.http; we want POP3 through HTTP and we have to change the
622      * member temporarily for connecting to the HTTP proxy. After
623      * Curl_proxyCONNECT we have to set back the member to the original struct
624      * POP3 pointer
625      */
626     pop3_save = data->state.proto.pop3;
627     memset(&http_proxy, 0, sizeof(http_proxy));
628     data->state.proto.http = &http_proxy;
629
630     result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
631                                conn->host.name, conn->remote_port);
632
633     data->state.proto.pop3 = pop3_save;
634
635     if(CURLE_OK != result)
636       return result;
637   }
638 #endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */
639
640   if(conn->protocol & PROT_POP3S) {
641     /* BLOCKING */
642     /* POP3S is simply pop3 with SSL for the control channel */
643     /* now, perform the SSL initialization for this socket */
644     result = Curl_ssl_connect(conn, FIRSTSOCKET);
645     if(result)
646       return result;
647   }
648
649   Curl_pp_init(pp); /* init the response reader stuff */
650
651   /* When we connect, we start in the state where we await the server greet
652      response */
653   state(conn, POP3_SERVERGREET);
654
655   if(data->state.used_interface == Curl_if_multi)
656     result = pop3_multi_statemach(conn, done);
657   else {
658     result = pop3_easy_statemach(conn);
659     if(!result)
660       *done = TRUE;
661   }
662
663   return result;
664 }
665
666 /***********************************************************************
667  *
668  * pop3_done()
669  *
670  * The DONE function. This does what needs to be done after a single DO has
671  * performed.
672  *
673  * Input argument is already checked for validity.
674  */
675 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
676                           bool premature)
677 {
678   struct SessionHandle *data = conn->data;
679   struct FTP *pop3 = data->state.proto.pop3;
680   CURLcode result=CURLE_OK;
681   (void)premature;
682
683   if(!pop3)
684     /* When the easy handle is removed from the multi while libcurl is still
685      * trying to resolve the host name, it seems that the pop3 struct is not
686      * yet initialized, but the removal action calls Curl_done() which calls
687      * this function. So we simply return success if no pop3 pointer is set.
688      */
689     return CURLE_OK;
690
691   if(status) {
692     conn->bits.close = TRUE; /* marked for closure */
693     result = status;      /* use the already set error code */
694   }
695
696   /* clear these for next connection */
697   pop3->transfer = FTPTRANSFER_BODY;
698
699   return result;
700 }
701
702 /***********************************************************************
703  *
704  * pop3_perform()
705  *
706  * This is the actual DO function for POP3. Get a file/directory according to
707  * the options previously setup.
708  */
709
710 static
711 CURLcode pop3_perform(struct connectdata *conn,
712                      bool *connected,  /* connect status after PASV / PORT */
713                      bool *dophase_done)
714 {
715   /* this is POP3 and no proxy */
716   CURLcode result=CURLE_OK;
717   struct pop3_conn *pop3c = &conn->proto.pop3c;
718
719   DEBUGF(infof(conn->data, "DO phase starts\n"));
720
721   if(conn->data->set.opt_no_body) {
722     /* requested no body means no transfer... */
723     struct FTP *pop3 = conn->data->state.proto.pop3;
724     pop3->transfer = FTPTRANSFER_INFO;
725   }
726
727   *dophase_done = FALSE; /* not done yet */
728
729   /* start the first command in the DO phase */
730   /* If mailbox is empty, then assume user wants listing for mail IDs,
731    * otherwise, attempt to retrieve the mail-id stored in mailbox
732    */
733   if (strlen(pop3c->mailbox))
734     result = pop3_retr(conn);
735   else
736     result = pop3_list(conn);
737   if(result)
738     return result;
739
740   /* run the state-machine */
741   if(conn->data->state.used_interface == Curl_if_multi)
742     result = pop3_multi_statemach(conn, dophase_done);
743   else {
744     result = pop3_easy_statemach(conn);
745     *dophase_done = TRUE; /* with the easy interface we are done here */
746   }
747   *connected = conn->bits.tcpconnect;
748
749   if(*dophase_done)
750     DEBUGF(infof(conn->data, "DO phase is complete\n"));
751
752   return result;
753 }
754
755 /***********************************************************************
756  *
757  * pop3_do()
758  *
759  * This function is registered as 'curl_do' function. It decodes the path
760  * parts etc as a wrapper to the actual DO function (pop3_perform).
761  *
762  * The input argument is already checked for validity.
763  */
764 static CURLcode pop3_do(struct connectdata *conn, bool *done)
765 {
766   CURLcode retcode = CURLE_OK;
767
768   *done = FALSE; /* default to false */
769
770   /*
771     Since connections can be re-used between SessionHandles, this might be a
772     connection already existing but on a fresh SessionHandle struct so we must
773     make sure we have a good 'struct POP3' to play with. For new connections,
774     the struct POP3 is allocated and setup in the pop3_connect() function.
775   */
776   Curl_reset_reqproto(conn);
777   retcode = pop3_init(conn);
778   if(retcode)
779     return retcode;
780
781   retcode = pop3_parse_url_path(conn);
782   if(retcode)
783     return retcode;
784
785   retcode = pop3_regular_transfer(conn, done);
786
787   return retcode;
788 }
789
790 /***********************************************************************
791  *
792  * pop3_quit()
793  *
794  * This should be called before calling sclose().  We should then wait for the
795  * response from the server before returning. The calling code should then try
796  * to close the connection.
797  *
798  */
799 static CURLcode pop3_quit(struct connectdata *conn)
800 {
801   CURLcode result = CURLE_OK;
802
803   result = Curl_pp_sendf(&conn->proto.pop3c.pp, "QUIT", NULL);
804   if(result)
805     return result;
806   state(conn, POP3_QUIT);
807
808   result = pop3_easy_statemach(conn);
809
810   return result;
811 }
812
813 /***********************************************************************
814  *
815  * pop3_disconnect()
816  *
817  * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
818  * resources. BLOCKING.
819  */
820 static CURLcode pop3_disconnect(struct connectdata *conn, bool dead_connection)
821 {
822   struct pop3_conn *pop3c= &conn->proto.pop3c;
823
824   /* We cannot send quit unconditionally. If this connection is stale or
825      bad in any way, sending quit and waiting around here will make the
826      disconnect wait in vain and cause more problems than we need to.
827   */
828
829   /* The POP3 session may or may not have been allocated/setup at this
830      point! */
831   if(!dead_connection && pop3c->pp.conn)
832     (void)pop3_quit(conn); /* ignore errors on the LOGOUT */
833
834
835   Curl_pp_disconnect(&pop3c->pp);
836
837   return CURLE_OK;
838 }
839
840 /***********************************************************************
841  *
842  * pop3_parse_url_path()
843  *
844  * Parse the URL path into separate path components.
845  *
846  */
847 static CURLcode pop3_parse_url_path(struct connectdata *conn)
848 {
849   /* the pop3 struct is already inited in pop3_connect() */
850   struct pop3_conn *pop3c = &conn->proto.pop3c;
851   struct SessionHandle *data = conn->data;
852   const char *path = data->state.path;
853
854   /* url decode the path and use this mailbox */
855   pop3c->mailbox = curl_easy_unescape(data, path, 0, NULL);
856   if (!pop3c->mailbox)
857     return CURLE_OUT_OF_MEMORY;
858
859   return CURLE_OK;
860 }
861
862 /* call this when the DO phase has completed */
863 static CURLcode pop3_dophase_done(struct connectdata *conn,
864                                   bool connected)
865 {
866   struct FTP *pop3 = conn->data->state.proto.pop3;
867   struct pop3_conn *pop3c = &conn->proto.pop3c;
868   (void)connected;
869
870   if(pop3->transfer != FTPTRANSFER_BODY)
871     /* no data to transfer */
872     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
873
874   free(pop3c->mailbox);
875
876   return CURLE_OK;
877 }
878
879 /* called from multi.c while DOing */
880 static CURLcode pop3_doing(struct connectdata *conn,
881                                bool *dophase_done)
882 {
883   CURLcode result;
884   result = pop3_multi_statemach(conn, dophase_done);
885
886   if(*dophase_done) {
887     result = pop3_dophase_done(conn, FALSE /* not connected */);
888
889     DEBUGF(infof(conn->data, "DO phase is complete\n"));
890   }
891   return result;
892 }
893
894 /***********************************************************************
895  *
896  * pop3_regular_transfer()
897  *
898  * The input argument is already checked for validity.
899  *
900  * Performs all commands done before a regular transfer between a local and a
901  * remote host.
902  *
903  */
904 static
905 CURLcode pop3_regular_transfer(struct connectdata *conn,
906                               bool *dophase_done)
907 {
908   CURLcode result=CURLE_OK;
909   bool connected=FALSE;
910   struct SessionHandle *data = conn->data;
911   data->req.size = -1; /* make sure this is unknown at this point */
912
913   Curl_pgrsSetUploadCounter(data, 0);
914   Curl_pgrsSetDownloadCounter(data, 0);
915   Curl_pgrsSetUploadSize(data, 0);
916   Curl_pgrsSetDownloadSize(data, 0);
917
918   result = pop3_perform(conn,
919                         &connected, /* have we connected after PASV/PORT */
920                         dophase_done); /* all commands in the DO-phase done? */
921
922   if(CURLE_OK == result) {
923
924     if(!*dophase_done)
925       /* the DO phase has not completed yet */
926       return CURLE_OK;
927
928     result = pop3_dophase_done(conn, connected);
929     if(result)
930       return result;
931   }
932
933   return result;
934 }
935
936 static CURLcode pop3_setup_connection(struct connectdata * conn)
937 {
938   struct SessionHandle *data = conn->data;
939
940   if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
941     /* Unless we have asked to tunnel pop3 operations through the proxy, we
942        switch and use HTTP operations only */
943 #ifndef CURL_DISABLE_HTTP
944     if(conn->handler == &Curl_handler_pop3)
945       conn->handler = &Curl_handler_pop3_proxy;
946     else {
947 #ifdef USE_SSL
948       conn->handler = &Curl_handler_pop3s_proxy;
949 #else
950       failf(data, "POP3S not supported!");
951       return CURLE_UNSUPPORTED_PROTOCOL;
952 #endif
953     }
954     /*
955      * We explicitly mark this connection as persistent here as we're doing
956      * POP3 over HTTP and thus we accidentally avoid setting this value
957      * otherwise.
958      */
959     conn->bits.close = FALSE;
960 #else
961     failf(data, "POP3 over http proxy requires HTTP support built-in!");
962     return CURLE_UNSUPPORTED_PROTOCOL;
963 #endif
964   }
965
966   data->state.path++;   /* don't include the initial slash */
967
968   return CURLE_OK;
969 }
970
971 /* this is the 5-bytes End-Of-Body marker for POP3 */
972 #define POP3_EOB "\x0d\x0a\x2e\x0d\x0a"
973 #define POP3_EOB_LEN 5
974
975 /*
976  * This function scans the body after the end-of-body and writes everything
977  * until the end is found.
978  */
979 CURLcode Curl_pop3_write(struct connectdata *conn,
980                          char *str,
981                          size_t nread)
982 {
983   /* This code could be made into a special function in the handler struct. */
984   CURLcode result;
985   struct SessionHandle *data = conn->data;
986   struct SingleRequest *k = &data->req;
987
988   /* Detect the end-of-body marker, which is 5 bytes:
989      0d 0a 2e 0d 0a. This marker can of course be spread out
990      over up to 5 different data chunks. Deal with it! */
991   struct pop3_conn *pop3c = &conn->proto.pop3c;
992   size_t checkmax = (nread >= POP3_EOB_LEN?POP3_EOB_LEN:nread);
993   size_t checkleft = POP3_EOB_LEN-pop3c->eob;
994   size_t check = (checkmax >= checkleft?checkleft:checkmax);
995
996   if(!memcmp(POP3_EOB, &str[nread - check], check)) {
997     /* substring match */
998     pop3c->eob += check;
999     if(pop3c->eob == POP3_EOB_LEN) {
1000       /* full match, the transfer is done! */
1001       str[nread - check] = '\0';
1002       nread -= check;
1003       k->keepon &= ~KEEP_RECV;
1004       pop3c->eob = 0;
1005     }
1006   }
1007   else if(pop3c->eob) {
1008     /* not a match, but we matched a piece before so we must now
1009        send that part as body first, before we move on and send
1010        this buffer */
1011     result = Curl_client_write(conn, CLIENTWRITE_BODY,
1012                                (char *)POP3_EOB, pop3c->eob);
1013     if(result)
1014       return result;
1015     pop3c->eob = 0;
1016   }
1017
1018   result = Curl_client_write(conn, CLIENTWRITE_BODY, str, nread);
1019
1020   return result;
1021 }
1022
1023 #endif /* CURL_DISABLE_POP3 */