960 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			960 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/***************************************************************************
 | 
						|
 *                                  _   _ ____  _
 | 
						|
 *  Project                     ___| | | |  _ \| |
 | 
						|
 *                             / __| | | | |_) | |
 | 
						|
 *                            | (__| |_| |  _ <| |___
 | 
						|
 *                             \___|\___/|_| \_\_____|
 | 
						|
 *
 | 
						|
 * Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
 | 
						|
 *
 | 
						|
 * This software is licensed as described in the file COPYING, which
 | 
						|
 * you should have received as part of this distribution. The terms
 | 
						|
 * are also available at http://curl.haxx.se/docs/copyright.html.
 | 
						|
 *
 | 
						|
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
 | 
						|
 * copies of the Software, and permit persons to whom the Software is
 | 
						|
 * furnished to do so, under the terms of the COPYING file.
 | 
						|
 *
 | 
						|
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 | 
						|
 * KIND, either express or implied.
 | 
						|
 *
 | 
						|
 * RFC1939 POP3 protocol
 | 
						|
 * RFC2384 POP URL Scheme
 | 
						|
 * RFC2595 Using TLS with IMAP, POP3 and ACAP
 | 
						|
 *
 | 
						|
 * $Id$
 | 
						|
 ***************************************************************************/
 | 
						|
 | 
						|
#include "setup.h"
 | 
						|
 | 
						|
#ifndef CURL_DISABLE_POP3
 | 
						|
#include <stdio.h>
 | 
						|
#include <string.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <stdarg.h>
 | 
						|
#include <ctype.h>
 | 
						|
 | 
						|
#ifdef HAVE_UNISTD_H
 | 
						|
#include <unistd.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef HAVE_SYS_SOCKET_H
 | 
						|
#include <sys/socket.h>
 | 
						|
#endif
 | 
						|
#ifdef HAVE_NETINET_IN_H
 | 
						|
#include <netinet/in.h>
 | 
						|
#endif
 | 
						|
#ifdef HAVE_ARPA_INET_H
 | 
						|
#include <arpa/inet.h>
 | 
						|
#endif
 | 
						|
#ifdef HAVE_UTSNAME_H
 | 
						|
#include <sys/utsname.h>
 | 
						|
#endif
 | 
						|
#ifdef HAVE_NETDB_H
 | 
						|
#include <netdb.h>
 | 
						|
#endif
 | 
						|
#ifdef  VMS
 | 
						|
#include <in.h>
 | 
						|
#include <inet.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
 | 
						|
#undef in_addr_t
 | 
						|
#define in_addr_t unsigned long
 | 
						|
#endif
 | 
						|
 | 
						|
#include <curl/curl.h>
 | 
						|
#include "urldata.h"
 | 
						|
#include "sendf.h"
 | 
						|
#include "easyif.h" /* for Curl_convert_... prototypes */
 | 
						|
 | 
						|
#include "if2ip.h"
 | 
						|
#include "hostip.h"
 | 
						|
#include "progress.h"
 | 
						|
#include "transfer.h"
 | 
						|
#include "escape.h"
 | 
						|
#include "http.h" /* for HTTP proxy tunnel stuff */
 | 
						|
#include "socks.h"
 | 
						|
#include "pop3.h"
 | 
						|
 | 
						|
#include "strtoofft.h"
 | 
						|
#include "strequal.h"
 | 
						|
#include "sslgen.h"
 | 
						|
#include "connect.h"
 | 
						|
#include "strerror.h"
 | 
						|
#include "select.h"
 | 
						|
#include "multiif.h"
 | 
						|
#include "url.h"
 | 
						|
#include "rawstr.h"
 | 
						|
#include "strtoofft.h"
 | 
						|
 | 
						|
#define _MPRINTF_REPLACE /* use our functions only */
 | 
						|
#include <curl/mprintf.h>
 | 
						|
 | 
						|
#include "curl_memory.h"
 | 
						|
/* The last #include file should be: */
 | 
						|
#include "memdebug.h"
 | 
						|
 | 
						|
/* Local API functions */
 | 
						|
static CURLcode pop3_parse_url_path(struct connectdata *conn);
 | 
						|
static CURLcode pop3_regular_transfer(struct connectdata *conn, bool *done);
 | 
						|
static CURLcode pop3_do(struct connectdata *conn, bool *done);
 | 
						|
static CURLcode pop3_done(struct connectdata *conn,
 | 
						|
                          CURLcode, bool premature);
 | 
						|
static CURLcode pop3_connect(struct connectdata *conn, bool *done);
 | 
						|
static CURLcode pop3_disconnect(struct connectdata *conn);
 | 
						|
static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done);
 | 
						|
static int pop3_getsock(struct connectdata *conn,
 | 
						|
                        curl_socket_t *socks,
 | 
						|
                        int numsocks);
 | 
						|
static CURLcode pop3_doing(struct connectdata *conn,
 | 
						|
                           bool *dophase_done);
 | 
						|
static CURLcode pop3_setup_connection(struct connectdata * conn);
 | 
						|
 | 
						|
/*
 | 
						|
 * POP3 protocol handler.
 | 
						|
 */
 | 
						|
 | 
						|
const struct Curl_handler Curl_handler_pop3 = {
 | 
						|
  "POP3",                           /* scheme */
 | 
						|
  pop3_setup_connection,            /* setup_connection */
 | 
						|
  pop3_do,                          /* do_it */
 | 
						|
  pop3_done,                        /* done */
 | 
						|
  ZERO_NULL,                        /* do_more */
 | 
						|
  pop3_connect,                     /* connect_it */
 | 
						|
  pop3_multi_statemach,             /* connecting */
 | 
						|
  pop3_doing,                       /* doing */
 | 
						|
  pop3_getsock,                     /* proto_getsock */
 | 
						|
  pop3_getsock,                     /* doing_getsock */
 | 
						|
  ZERO_NULL,                        /* perform_getsock */
 | 
						|
  pop3_disconnect,                  /* disconnect */
 | 
						|
  PORT_POP3,                        /* defport */
 | 
						|
  PROT_POP3                         /* protocol */
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
#ifdef USE_SSL
 | 
						|
/*
 | 
						|
 * POP3S protocol handler.
 | 
						|
 */
 | 
						|
 | 
						|
const struct Curl_handler Curl_handler_pop3s = {
 | 
						|
  "POP3S",                          /* scheme */
 | 
						|
  pop3_setup_connection,            /* setup_connection */
 | 
						|
  pop3_do,                          /* do_it */
 | 
						|
  pop3_done,                        /* done */
 | 
						|
  ZERO_NULL,                        /* do_more */
 | 
						|
  pop3_connect,                     /* connect_it */
 | 
						|
  pop3_multi_statemach,             /* connecting */
 | 
						|
  pop3_doing,                       /* doing */
 | 
						|
  pop3_getsock,                     /* proto_getsock */
 | 
						|
  pop3_getsock,                     /* doing_getsock */
 | 
						|
  ZERO_NULL,                        /* perform_getsock */
 | 
						|
  pop3_disconnect,                  /* disconnect */
 | 
						|
  PORT_POP3S,                       /* defport */
 | 
						|
  PROT_POP3 | PROT_POP3S | PROT_SSL  /* protocol */
 | 
						|
};
 | 
						|
#endif
 | 
						|
 | 
						|
#ifndef CURL_DISABLE_HTTP
 | 
						|
/*
 | 
						|
 * HTTP-proxyed POP3 protocol handler.
 | 
						|
 */
 | 
						|
 | 
						|
const struct Curl_handler Curl_handler_pop3_proxy = {
 | 
						|
  "POP3",                               /* scheme */
 | 
						|
  ZERO_NULL,                            /* setup_connection */
 | 
						|
  Curl_http,                            /* do_it */
 | 
						|
  Curl_http_done,                       /* done */
 | 
						|
  ZERO_NULL,                            /* do_more */
 | 
						|
  ZERO_NULL,                            /* connect_it */
 | 
						|
  ZERO_NULL,                            /* connecting */
 | 
						|
  ZERO_NULL,                            /* doing */
 | 
						|
  ZERO_NULL,                            /* proto_getsock */
 | 
						|
  ZERO_NULL,                            /* doing_getsock */
 | 
						|
  ZERO_NULL,                            /* perform_getsock */
 | 
						|
  ZERO_NULL,                            /* disconnect */
 | 
						|
  PORT_POP3,                            /* defport */
 | 
						|
  PROT_HTTP                             /* protocol */
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
#ifdef USE_SSL
 | 
						|
/*
 | 
						|
 * HTTP-proxyed POP3S protocol handler.
 | 
						|
 */
 | 
						|
 | 
						|
const struct Curl_handler Curl_handler_pop3s_proxy = {
 | 
						|
  "POP3S",                              /* scheme */
 | 
						|
  ZERO_NULL,                            /* setup_connection */
 | 
						|
  Curl_http,                            /* do_it */
 | 
						|
  Curl_http_done,                       /* done */
 | 
						|
  ZERO_NULL,                            /* do_more */
 | 
						|
  ZERO_NULL,                            /* connect_it */
 | 
						|
  ZERO_NULL,                            /* connecting */
 | 
						|
  ZERO_NULL,                            /* doing */
 | 
						|
  ZERO_NULL,                            /* proto_getsock */
 | 
						|
  ZERO_NULL,                            /* doing_getsock */
 | 
						|
  ZERO_NULL,                            /* perform_getsock */
 | 
						|
  ZERO_NULL,                            /* disconnect */
 | 
						|
  PORT_POP3S,                           /* defport */
 | 
						|
  PROT_HTTP                             /* protocol */
 | 
						|
};
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
/* function that checks for a pop3 status code at the start of the given
 | 
						|
   string */
 | 
						|
static int pop3_endofresp(struct pingpong *pp,
 | 
						|
                          int *resp)
 | 
						|
{
 | 
						|
  char *line = pp->linestart_resp;
 | 
						|
  size_t len = pp->nread_resp;
 | 
						|
 | 
						|
  if( ((len >= 3) && !memcmp("+OK", line, 3)) ||
 | 
						|
      ((len >= 4) && !memcmp("-ERR", line, 4)) ) {
 | 
						|
    *resp=line[1]; /* O or E */
 | 
						|
    return TRUE;
 | 
						|
  }
 | 
						|
 | 
						|
  return FALSE; /* nothing for us */
 | 
						|
}
 | 
						|
 | 
						|
/* This is the ONLY way to change POP3 state! */
 | 
						|
static void state(struct connectdata *conn,
 | 
						|
                  pop3state newstate)
 | 
						|
{
 | 
						|
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
 | 
						|
  /* for debug purposes */
 | 
						|
  static const char * const names[]={
 | 
						|
    "STOP",
 | 
						|
    "SERVERGREET",
 | 
						|
    "USER",
 | 
						|
    "PASS",
 | 
						|
    "STARTTLS",
 | 
						|
    "RETR",
 | 
						|
    "QUIT",
 | 
						|
    /* LAST */
 | 
						|
  };
 | 
						|
#endif
 | 
						|
  struct pop3_conn *pop3c = &conn->proto.pop3c;
 | 
						|
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
 | 
						|
  if(pop3c->state != newstate)
 | 
						|
    infof(conn->data, "POP3 %p state change from %s to %s\n",
 | 
						|
          pop3c, names[pop3c->state], names[newstate]);
 | 
						|
#endif
 | 
						|
  pop3c->state = newstate;
 | 
						|
}
 | 
						|
 | 
						|
static CURLcode pop3_state_user(struct connectdata *conn)
 | 
						|
{
 | 
						|
  CURLcode result;
 | 
						|
  struct FTP *pop3 = conn->data->state.proto.pop3;
 | 
						|
 | 
						|
  /* send USER */
 | 
						|
  result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s",
 | 
						|
                         pop3->user?pop3->user:"");
 | 
						|
  if(result)
 | 
						|
    return result;
 | 
						|
 | 
						|
  state(conn, POP3_USER);
 | 
						|
 | 
						|
  return CURLE_OK;
 | 
						|
}
 | 
						|
 | 
						|
/* For the POP3 "protocol connect" and "doing" phases only */
 | 
						|
static int pop3_getsock(struct connectdata *conn,
 | 
						|
                        curl_socket_t *socks,
 | 
						|
                        int numsocks)
 | 
						|
{
 | 
						|
  return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks);
 | 
						|
}
 | 
						|
 | 
						|
/* for STARTTLS responses */
 | 
						|
static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
 | 
						|
                                         int pop3code,
 | 
						|
                                         pop3state instate)
 | 
						|
{
 | 
						|
  CURLcode result = CURLE_OK;
 | 
						|
  struct SessionHandle *data = conn->data;
 | 
						|
  (void)instate; /* no use for this yet */
 | 
						|
 | 
						|
  if(pop3code != 'O') {
 | 
						|
    failf(data, "STARTTLS denied. %c", pop3code);
 | 
						|
    result = CURLE_LOGIN_DENIED;
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    /* Curl_ssl_connect is BLOCKING */
 | 
						|
    result = Curl_ssl_connect(conn, FIRSTSOCKET);
 | 
						|
    if(CURLE_OK == result) {
 | 
						|
      conn->protocol |= PROT_POP3S;
 | 
						|
      result = pop3_state_user(conn);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  state(conn, POP3_STOP);
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
/* for USER responses */
 | 
						|
static CURLcode pop3_state_user_resp(struct connectdata *conn,
 | 
						|
                                     int pop3code,
 | 
						|
                                     pop3state instate)
 | 
						|
{
 | 
						|
  CURLcode result = CURLE_OK;
 | 
						|
  struct SessionHandle *data = conn->data;
 | 
						|
  struct FTP *pop3 = data->state.proto.pop3;
 | 
						|
 | 
						|
  (void)instate; /* no use for this yet */
 | 
						|
 | 
						|
  if(pop3code != 'O') {
 | 
						|
    failf(data, "Access denied. %c", pop3code);
 | 
						|
    result = CURLE_LOGIN_DENIED;
 | 
						|
  }
 | 
						|
 | 
						|
  /* send PASS */
 | 
						|
  result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s",
 | 
						|
                         pop3->passwd?pop3->passwd:"");
 | 
						|
  if(result)
 | 
						|
    return result;
 | 
						|
 | 
						|
  state(conn, POP3_PASS);
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
/* for PASS responses */
 | 
						|
static CURLcode pop3_state_pass_resp(struct connectdata *conn,
 | 
						|
                                     int pop3code,
 | 
						|
                                     pop3state instate)
 | 
						|
{
 | 
						|
  CURLcode result = CURLE_OK;
 | 
						|
  struct SessionHandle *data = conn->data;
 | 
						|
  (void)instate; /* no use for this yet */
 | 
						|
 | 
						|
  if(pop3code != 'O') {
 | 
						|
    failf(data, "Access denied. %c", pop3code);
 | 
						|
    result = CURLE_LOGIN_DENIED;
 | 
						|
  }
 | 
						|
 | 
						|
  state(conn, POP3_STOP);
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
/* for the retr response */
 | 
						|
static CURLcode pop3_state_retr_resp(struct connectdata *conn,
 | 
						|
                                     int pop3code,
 | 
						|
                                     pop3state instate)
 | 
						|
{
 | 
						|
  CURLcode result = CURLE_OK;
 | 
						|
  struct SessionHandle *data = conn->data;
 | 
						|
  struct FTP *pop3 = data->state.proto.pop3;
 | 
						|
  struct pop3_conn *pop3c = &conn->proto.pop3c;
 | 
						|
  struct pingpong *pp = &pop3c->pp;
 | 
						|
 | 
						|
  (void)instate; /* no use for this yet */
 | 
						|
 | 
						|
  if('O' != pop3code) {
 | 
						|
    state(conn, POP3_STOP);
 | 
						|
    return CURLE_RECV_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  /* POP3 download */
 | 
						|
  result=Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE,
 | 
						|
                             pop3->bytecountp,
 | 
						|
                             -1, NULL); /* no upload here */
 | 
						|
 | 
						|
  if(pp->cache) {
 | 
						|
    /* At this point there is a bunch of data in the header "cache" that is
 | 
						|
       actually body content, send it as body and then skip it. Do note
 | 
						|
       that there may even be additional "headers" after the body. */
 | 
						|
 | 
						|
    /* we may get the EOB already here! */
 | 
						|
    result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
 | 
						|
    if(result)
 | 
						|
      return result;
 | 
						|
 | 
						|
    /* cache is drained */
 | 
						|
    free(pp->cache);
 | 
						|
    pp->cache = NULL;
 | 
						|
    pp->cache_size = 0;
 | 
						|
  }
 | 
						|
 | 
						|
  state(conn, POP3_STOP);
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
/* start the DO phase */
 | 
						|
static CURLcode pop3_retr(struct connectdata *conn)
 | 
						|
{
 | 
						|
  CURLcode result = CURLE_OK;
 | 
						|
  struct pop3_conn *pop3c = &conn->proto.pop3c;
 | 
						|
 | 
						|
  result = Curl_pp_sendf(&conn->proto.pop3c.pp, "RETR %s", pop3c->mailbox);
 | 
						|
  if(result)
 | 
						|
    return result;
 | 
						|
 | 
						|
  state(conn, POP3_RETR);
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
static CURLcode pop3_statemach_act(struct connectdata *conn)
 | 
						|
{
 | 
						|
  CURLcode result;
 | 
						|
  curl_socket_t sock = conn->sock[FIRSTSOCKET];
 | 
						|
  struct SessionHandle *data=conn->data;
 | 
						|
  int pop3code;
 | 
						|
  struct pop3_conn *pop3c = &conn->proto.pop3c;
 | 
						|
  struct pingpong *pp = &pop3c->pp;
 | 
						|
  size_t nread = 0;
 | 
						|
 | 
						|
  if(pp->sendleft)
 | 
						|
    return Curl_pp_flushsend(pp);
 | 
						|
 | 
						|
  /* we read a piece of response */
 | 
						|
  result = Curl_pp_readresp(sock, pp, &pop3code, &nread);
 | 
						|
  if(result)
 | 
						|
    return result;
 | 
						|
 | 
						|
  if(pop3code) {
 | 
						|
    /* we have now received a full POP3 server response */
 | 
						|
    switch(pop3c->state) {
 | 
						|
    case POP3_SERVERGREET:
 | 
						|
      if(pop3code != 'O') {
 | 
						|
        failf(data, "Got unexpected pop3-server response");
 | 
						|
        return CURLE_FTP_WEIRD_SERVER_REPLY;
 | 
						|
      }
 | 
						|
 | 
						|
      if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
 | 
						|
        /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
 | 
						|
           to TLS connection now */
 | 
						|
        result = Curl_pp_sendf(&pop3c->pp, "STARTTLS", NULL);
 | 
						|
        state(conn, POP3_STARTTLS);
 | 
						|
      }
 | 
						|
      else
 | 
						|
        result = pop3_state_user(conn);
 | 
						|
      if(result)
 | 
						|
        return result;
 | 
						|
      break;
 | 
						|
 | 
						|
    case POP3_USER:
 | 
						|
      result = pop3_state_user_resp(conn, pop3code, pop3c->state);
 | 
						|
      break;
 | 
						|
 | 
						|
    case POP3_PASS:
 | 
						|
      result = pop3_state_pass_resp(conn, pop3code, pop3c->state);
 | 
						|
      break;
 | 
						|
 | 
						|
    case POP3_STARTTLS:
 | 
						|
      result = pop3_state_starttls_resp(conn, pop3code, pop3c->state);
 | 
						|
      break;
 | 
						|
 | 
						|
    case POP3_RETR:
 | 
						|
      result = pop3_state_retr_resp(conn, pop3code, pop3c->state);
 | 
						|
      break;
 | 
						|
 | 
						|
    case POP3_QUIT:
 | 
						|
      /* fallthrough, just stop! */
 | 
						|
    default:
 | 
						|
      /* internal error */
 | 
						|
      state(conn, POP3_STOP);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
/* called repeatedly until done from multi.c */
 | 
						|
static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done)
 | 
						|
{
 | 
						|
  struct pop3_conn *pop3c = &conn->proto.pop3c;
 | 
						|
  CURLcode result = Curl_pp_multi_statemach(&pop3c->pp);
 | 
						|
 | 
						|
  *done = (bool)(pop3c->state == POP3_STOP);
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
static CURLcode pop3_easy_statemach(struct connectdata *conn)
 | 
						|
{
 | 
						|
  struct pop3_conn *pop3c = &conn->proto.pop3c;
 | 
						|
  struct pingpong *pp = &pop3c->pp;
 | 
						|
  CURLcode result = CURLE_OK;
 | 
						|
 | 
						|
  while(pop3c->state != POP3_STOP) {
 | 
						|
    result = Curl_pp_easy_statemach(pp);
 | 
						|
    if(result)
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Allocate and initialize the struct POP3 for the current SessionHandle.  If
 | 
						|
 * need be.
 | 
						|
 */
 | 
						|
static CURLcode pop3_init(struct connectdata *conn)
 | 
						|
{
 | 
						|
  struct SessionHandle *data = conn->data;
 | 
						|
  struct FTP *pop3 = data->state.proto.pop3;
 | 
						|
  if(!pop3) {
 | 
						|
    pop3 = data->state.proto.pop3 = calloc(sizeof(struct FTP), 1);
 | 
						|
    if(!pop3)
 | 
						|
      return CURLE_OUT_OF_MEMORY;
 | 
						|
  }
 | 
						|
 | 
						|
  /* get some initial data into the pop3 struct */
 | 
						|
  pop3->bytecountp = &data->req.bytecount;
 | 
						|
 | 
						|
  /* No need to duplicate user+password, the connectdata struct won't change
 | 
						|
     during a session, but we re-init them here since on subsequent inits
 | 
						|
     since the conn struct may have changed or been replaced.
 | 
						|
  */
 | 
						|
  pop3->user = conn->user;
 | 
						|
  pop3->passwd = conn->passwd;
 | 
						|
 | 
						|
  return CURLE_OK;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * pop3_connect() should do everything that is to be considered a part of
 | 
						|
 * the connection phase.
 | 
						|
 *
 | 
						|
 * The variable 'done' points to will be TRUE if the protocol-layer connect
 | 
						|
 * phase is done when this function returns, or FALSE is not. When called as
 | 
						|
 * a part of the easy interface, it will always be TRUE.
 | 
						|
 */
 | 
						|
static CURLcode pop3_connect(struct connectdata *conn,
 | 
						|
                                 bool *done) /* see description above */
 | 
						|
{
 | 
						|
  CURLcode result;
 | 
						|
  struct pop3_conn *pop3c = &conn->proto.pop3c;
 | 
						|
  struct SessionHandle *data=conn->data;
 | 
						|
  struct pingpong *pp = &pop3c->pp;
 | 
						|
 | 
						|
  *done = FALSE; /* default to not done yet */
 | 
						|
 | 
						|
  /* If there already is a protocol-specific struct allocated for this
 | 
						|
     sessionhandle, deal with it */
 | 
						|
  Curl_reset_reqproto(conn);
 | 
						|
 | 
						|
  result = pop3_init(conn);
 | 
						|
  if(CURLE_OK != result)
 | 
						|
    return result;
 | 
						|
 | 
						|
  /* We always support persistant connections on pop3 */
 | 
						|
  conn->bits.close = FALSE;
 | 
						|
 | 
						|
  pp->response_time = RESP_TIMEOUT; /* set default response time-out */
 | 
						|
  pp->statemach_act = pop3_statemach_act;
 | 
						|
  pp->endofresp = pop3_endofresp;
 | 
						|
  pp->conn = conn;
 | 
						|
 | 
						|
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY)
 | 
						|
  if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
 | 
						|
    /* for POP3 over HTTP proxy */
 | 
						|
    struct HTTP http_proxy;
 | 
						|
    struct FTP *pop3_save;
 | 
						|
 | 
						|
    /* BLOCKING */
 | 
						|
    /* We want "seamless" POP3 operations through HTTP proxy tunnel */
 | 
						|
 | 
						|
    /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
 | 
						|
     * conn->proto.http; we want POP3 through HTTP and we have to change the
 | 
						|
     * member temporarily for connecting to the HTTP proxy. After
 | 
						|
     * Curl_proxyCONNECT we have to set back the member to the original struct
 | 
						|
     * POP3 pointer
 | 
						|
     */
 | 
						|
    pop3_save = data->state.proto.pop3;
 | 
						|
    memset(&http_proxy, 0, sizeof(http_proxy));
 | 
						|
    data->state.proto.http = &http_proxy;
 | 
						|
 | 
						|
    result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
 | 
						|
                               conn->host.name, conn->remote_port);
 | 
						|
 | 
						|
    data->state.proto.pop3 = pop3_save;
 | 
						|
 | 
						|
    if(CURLE_OK != result)
 | 
						|
      return result;
 | 
						|
  }
 | 
						|
#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */
 | 
						|
 | 
						|
  if(conn->protocol & PROT_POP3S) {
 | 
						|
    /* BLOCKING */
 | 
						|
    /* POP3S is simply pop3 with SSL for the control channel */
 | 
						|
    /* now, perform the SSL initialization for this socket */
 | 
						|
    result = Curl_ssl_connect(conn, FIRSTSOCKET);
 | 
						|
    if(result)
 | 
						|
      return result;
 | 
						|
  }
 | 
						|
 | 
						|
  Curl_pp_init(pp); /* init the response reader stuff */
 | 
						|
 | 
						|
  /* When we connect, we start in the state where we await the server greet
 | 
						|
     response */
 | 
						|
  state(conn, POP3_SERVERGREET);
 | 
						|
 | 
						|
  if(data->state.used_interface == Curl_if_multi)
 | 
						|
    result = pop3_multi_statemach(conn, done);
 | 
						|
  else {
 | 
						|
    result = pop3_easy_statemach(conn);
 | 
						|
    if(!result)
 | 
						|
      *done = TRUE;
 | 
						|
  }
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
/***********************************************************************
 | 
						|
 *
 | 
						|
 * pop3_done()
 | 
						|
 *
 | 
						|
 * The DONE function. This does what needs to be done after a single DO has
 | 
						|
 * performed.
 | 
						|
 *
 | 
						|
 * Input argument is already checked for validity.
 | 
						|
 */
 | 
						|
static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
 | 
						|
                          bool premature)
 | 
						|
{
 | 
						|
  struct SessionHandle *data = conn->data;
 | 
						|
  struct FTP *pop3 = data->state.proto.pop3;
 | 
						|
  CURLcode result=CURLE_OK;
 | 
						|
  (void)premature;
 | 
						|
 | 
						|
  if(!pop3)
 | 
						|
    /* When the easy handle is removed from the multi while libcurl is still
 | 
						|
     * trying to resolve the host name, it seems that the pop3 struct is not
 | 
						|
     * yet initialized, but the removal action calls Curl_done() which calls
 | 
						|
     * this function. So we simply return success if no pop3 pointer is set.
 | 
						|
     */
 | 
						|
    return CURLE_OK;
 | 
						|
 | 
						|
  if(status) {
 | 
						|
    conn->bits.close = TRUE; /* marked for closure */
 | 
						|
    result = status;      /* use the already set error code */
 | 
						|
  }
 | 
						|
 | 
						|
  /* clear these for next connection */
 | 
						|
  pop3->transfer = FTPTRANSFER_BODY;
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
/***********************************************************************
 | 
						|
 *
 | 
						|
 * pop3_perform()
 | 
						|
 *
 | 
						|
 * This is the actual DO function for POP3. Get a file/directory according to
 | 
						|
 * the options previously setup.
 | 
						|
 */
 | 
						|
 | 
						|
static
 | 
						|
CURLcode pop3_perform(struct connectdata *conn,
 | 
						|
                     bool *connected,  /* connect status after PASV / PORT */
 | 
						|
                     bool *dophase_done)
 | 
						|
{
 | 
						|
  /* this is POP3 and no proxy */
 | 
						|
  CURLcode result=CURLE_OK;
 | 
						|
 | 
						|
  DEBUGF(infof(conn->data, "DO phase starts\n"));
 | 
						|
 | 
						|
  if(conn->data->set.opt_no_body) {
 | 
						|
    /* requested no body means no transfer... */
 | 
						|
    struct FTP *pop3 = conn->data->state.proto.pop3;
 | 
						|
    pop3->transfer = FTPTRANSFER_INFO;
 | 
						|
  }
 | 
						|
 | 
						|
  *dophase_done = FALSE; /* not done yet */
 | 
						|
 | 
						|
  /* start the first command in the DO phase */
 | 
						|
  result = pop3_retr(conn);
 | 
						|
  if(result)
 | 
						|
    return result;
 | 
						|
 | 
						|
  /* run the state-machine */
 | 
						|
  if(conn->data->state.used_interface == Curl_if_multi)
 | 
						|
    result = pop3_multi_statemach(conn, dophase_done);
 | 
						|
  else {
 | 
						|
    result = pop3_easy_statemach(conn);
 | 
						|
    *dophase_done = TRUE; /* with the easy interface we are done here */
 | 
						|
  }
 | 
						|
  *connected = conn->bits.tcpconnect;
 | 
						|
 | 
						|
  if(*dophase_done)
 | 
						|
    DEBUGF(infof(conn->data, "DO phase is complete\n"));
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
/***********************************************************************
 | 
						|
 *
 | 
						|
 * pop3_do()
 | 
						|
 *
 | 
						|
 * This function is registered as 'curl_do' function. It decodes the path
 | 
						|
 * parts etc as a wrapper to the actual DO function (pop3_perform).
 | 
						|
 *
 | 
						|
 * The input argument is already checked for validity.
 | 
						|
 */
 | 
						|
static CURLcode pop3_do(struct connectdata *conn, bool *done)
 | 
						|
{
 | 
						|
  CURLcode retcode = CURLE_OK;
 | 
						|
 | 
						|
  *done = FALSE; /* default to false */
 | 
						|
 | 
						|
  /*
 | 
						|
    Since connections can be re-used between SessionHandles, this might be a
 | 
						|
    connection already existing but on a fresh SessionHandle struct so we must
 | 
						|
    make sure we have a good 'struct POP3' to play with. For new connections,
 | 
						|
    the struct POP3 is allocated and setup in the pop3_connect() function.
 | 
						|
  */
 | 
						|
  Curl_reset_reqproto(conn);
 | 
						|
  retcode = pop3_init(conn);
 | 
						|
  if(retcode)
 | 
						|
    return retcode;
 | 
						|
 | 
						|
  retcode = pop3_parse_url_path(conn);
 | 
						|
  if(retcode)
 | 
						|
    return retcode;
 | 
						|
 | 
						|
  retcode = pop3_regular_transfer(conn, done);
 | 
						|
 | 
						|
  return retcode;
 | 
						|
}
 | 
						|
 | 
						|
/***********************************************************************
 | 
						|
 *
 | 
						|
 * pop3_quit()
 | 
						|
 *
 | 
						|
 * This should be called before calling sclose().  We should then wait for the
 | 
						|
 * response from the server before returning. The calling code should then try
 | 
						|
 * to close the connection.
 | 
						|
 *
 | 
						|
 */
 | 
						|
static CURLcode pop3_quit(struct connectdata *conn)
 | 
						|
{
 | 
						|
  CURLcode result = CURLE_OK;
 | 
						|
 | 
						|
  result = Curl_pp_sendf(&conn->proto.pop3c.pp, "QUIT", NULL);
 | 
						|
  if(result)
 | 
						|
    return result;
 | 
						|
  state(conn, POP3_QUIT);
 | 
						|
 | 
						|
  result = pop3_easy_statemach(conn);
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
/***********************************************************************
 | 
						|
 *
 | 
						|
 * pop3_disconnect()
 | 
						|
 *
 | 
						|
 * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
 | 
						|
 * resources. BLOCKING.
 | 
						|
 */
 | 
						|
static CURLcode pop3_disconnect(struct connectdata *conn)
 | 
						|
{
 | 
						|
  struct pop3_conn *pop3c= &conn->proto.pop3c;
 | 
						|
 | 
						|
  /* We cannot send quit unconditionally. If this connection is stale or
 | 
						|
     bad in any way, sending quit and waiting around here will make the
 | 
						|
     disconnect wait in vain and cause more problems than we need to.
 | 
						|
  */
 | 
						|
 | 
						|
  /* The POP3 session may or may not have been allocated/setup at this
 | 
						|
     point! */
 | 
						|
  (void)pop3_quit(conn); /* ignore errors on the LOGOUT */
 | 
						|
 | 
						|
 | 
						|
  Curl_pp_disconnect(&pop3c->pp);
 | 
						|
 | 
						|
  return CURLE_OK;
 | 
						|
}
 | 
						|
 | 
						|
/***********************************************************************
 | 
						|
 *
 | 
						|
 * pop3_parse_url_path()
 | 
						|
 *
 | 
						|
 * Parse the URL path into separate path components.
 | 
						|
 *
 | 
						|
 */
 | 
						|
static CURLcode pop3_parse_url_path(struct connectdata *conn)
 | 
						|
{
 | 
						|
  /* the pop3 struct is already inited in pop3_connect() */
 | 
						|
  struct pop3_conn *pop3c = &conn->proto.pop3c;
 | 
						|
  struct SessionHandle *data = conn->data;
 | 
						|
  const char *path = data->state.path;
 | 
						|
  int len;
 | 
						|
 | 
						|
  if(!*path)
 | 
						|
    path = "INBOX";
 | 
						|
 | 
						|
  /* url decode the path and use this mailbox */
 | 
						|
  pop3c->mailbox = curl_easy_unescape(data, path, 0, &len);
 | 
						|
 | 
						|
  return CURLE_OK;
 | 
						|
}
 | 
						|
 | 
						|
/* call this when the DO phase has completed */
 | 
						|
static CURLcode pop3_dophase_done(struct connectdata *conn,
 | 
						|
                                  bool connected)
 | 
						|
{
 | 
						|
  CURLcode result = CURLE_OK;
 | 
						|
  struct FTP *pop3 = conn->data->state.proto.pop3;
 | 
						|
  struct pop3_conn *pop3c = &conn->proto.pop3c;
 | 
						|
 (void)connected;
 | 
						|
 | 
						|
  if(pop3->transfer != FTPTRANSFER_BODY)
 | 
						|
    /* no data to transfer */
 | 
						|
    result=Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
 | 
						|
 | 
						|
  free(pop3c->mailbox);
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
/* called from multi.c while DOing */
 | 
						|
static CURLcode pop3_doing(struct connectdata *conn,
 | 
						|
                               bool *dophase_done)
 | 
						|
{
 | 
						|
  CURLcode result;
 | 
						|
  result = pop3_multi_statemach(conn, dophase_done);
 | 
						|
 | 
						|
  if(*dophase_done) {
 | 
						|
    result = pop3_dophase_done(conn, FALSE /* not connected */);
 | 
						|
 | 
						|
    DEBUGF(infof(conn->data, "DO phase is complete\n"));
 | 
						|
  }
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
/***********************************************************************
 | 
						|
 *
 | 
						|
 * pop3_regular_transfer()
 | 
						|
 *
 | 
						|
 * The input argument is already checked for validity.
 | 
						|
 *
 | 
						|
 * Performs all commands done before a regular transfer between a local and a
 | 
						|
 * remote host.
 | 
						|
 *
 | 
						|
 */
 | 
						|
static
 | 
						|
CURLcode pop3_regular_transfer(struct connectdata *conn,
 | 
						|
                              bool *dophase_done)
 | 
						|
{
 | 
						|
  CURLcode result=CURLE_OK;
 | 
						|
  bool connected=FALSE;
 | 
						|
  struct SessionHandle *data = conn->data;
 | 
						|
  data->req.size = -1; /* make sure this is unknown at this point */
 | 
						|
 | 
						|
  Curl_pgrsSetUploadCounter(data, 0);
 | 
						|
  Curl_pgrsSetDownloadCounter(data, 0);
 | 
						|
  Curl_pgrsSetUploadSize(data, 0);
 | 
						|
  Curl_pgrsSetDownloadSize(data, 0);
 | 
						|
 | 
						|
  result = pop3_perform(conn,
 | 
						|
                        &connected, /* have we connected after PASV/PORT */
 | 
						|
                        dophase_done); /* all commands in the DO-phase done? */
 | 
						|
 | 
						|
  if(CURLE_OK == result) {
 | 
						|
 | 
						|
    if(!*dophase_done)
 | 
						|
      /* the DO phase has not completed yet */
 | 
						|
      return CURLE_OK;
 | 
						|
 | 
						|
    result = pop3_dophase_done(conn, connected);
 | 
						|
    if(result)
 | 
						|
      return result;
 | 
						|
  }
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
static CURLcode pop3_setup_connection(struct connectdata * conn)
 | 
						|
{
 | 
						|
  struct SessionHandle *data = conn->data;
 | 
						|
 | 
						|
  if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
 | 
						|
    /* Unless we have asked to tunnel pop3 operations through the proxy, we
 | 
						|
       switch and use HTTP operations only */
 | 
						|
#ifndef CURL_DISABLE_HTTP
 | 
						|
    if(conn->handler == &Curl_handler_pop3)
 | 
						|
      conn->handler = &Curl_handler_pop3_proxy;
 | 
						|
    else {
 | 
						|
#ifdef USE_SSL
 | 
						|
      conn->handler = &Curl_handler_pop3s_proxy;
 | 
						|
#else
 | 
						|
      failf(data, "POP3S not supported!");
 | 
						|
      return CURLE_UNSUPPORTED_PROTOCOL;
 | 
						|
#endif
 | 
						|
    }
 | 
						|
    /*
 | 
						|
     * We explicitly mark this connection as persistent here as we're doing
 | 
						|
     * POP3 over HTTP and thus we accidentally avoid setting this value
 | 
						|
     * otherwise.
 | 
						|
     */
 | 
						|
    conn->bits.close = FALSE;
 | 
						|
#else
 | 
						|
    failf(data, "POP3 over http proxy requires HTTP support built-in!");
 | 
						|
    return CURLE_UNSUPPORTED_PROTOCOL;
 | 
						|
#endif
 | 
						|
  }
 | 
						|
 | 
						|
  data->state.path++;   /* don't include the initial slash */
 | 
						|
 | 
						|
  return CURLE_OK;
 | 
						|
}
 | 
						|
 | 
						|
/* this is the 5-bytes End-Of-Body marker for POP3 */
 | 
						|
#define POP3_EOB "\x0d\x0a\x2e\x0d\x0a"
 | 
						|
#define POP3_EOB_LEN 5
 | 
						|
 | 
						|
/*
 | 
						|
 * This function scans the body after the end-of-body and writes everything
 | 
						|
 * until the end is found.
 | 
						|
 */
 | 
						|
CURLcode Curl_pop3_write(struct connectdata *conn,
 | 
						|
                         char *str,
 | 
						|
                         size_t nread)
 | 
						|
{
 | 
						|
  /* This code could be made into a special function in the handler struct. */
 | 
						|
  CURLcode result;
 | 
						|
  struct SessionHandle *data = conn->data;
 | 
						|
  struct SingleRequest *k = &data->req;
 | 
						|
 | 
						|
  /* Detect the end-of-body marker, which is 5 bytes:
 | 
						|
     0d 0a 2e 0d 0a. This marker can of course be spread out
 | 
						|
     over up to 5 different data chunks. Deal with it! */
 | 
						|
  struct pop3_conn *pop3c = &conn->proto.pop3c;
 | 
						|
  int checkmax = (nread >= POP3_EOB_LEN?POP3_EOB_LEN:nread);
 | 
						|
  int checkleft = POP3_EOB_LEN-pop3c->eob;
 | 
						|
  int check = (checkmax >= checkleft?checkleft:checkmax);
 | 
						|
 | 
						|
  if(!memcmp(POP3_EOB, &str[nread - check], check)) {
 | 
						|
    /* substring match */
 | 
						|
    pop3c->eob += check;
 | 
						|
    if(pop3c->eob == POP3_EOB_LEN) {
 | 
						|
      /* full match, the transfer is done! */
 | 
						|
      nread -= check;
 | 
						|
      k->keepon &= ~KEEP_RECV;
 | 
						|
      pop3c->eob = 0;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else if(pop3c->eob) {
 | 
						|
    /* not a match, but we matched a piece before so we must now
 | 
						|
       send that part as body first, before we move on and send
 | 
						|
       this buffer */
 | 
						|
    result = Curl_client_write(conn, CLIENTWRITE_BODY,
 | 
						|
                               (char *)POP3_EOB, pop3c->eob);
 | 
						|
    if(result)
 | 
						|
      return result;
 | 
						|
    pop3c->eob = 0;
 | 
						|
  }
 | 
						|
 | 
						|
  result = Curl_client_write(conn, CLIENTWRITE_BODY, str, nread);
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
#endif /* CURL_DISABLE_POP3 */
 |