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