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