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