/* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "ioloop.h"
#include "net.h"
#include "istream.h"
#include "istream-chain.h"
#include "istream-dot.h"
#include "istream-seekable.h"
#include "ostream.h"
#include "iostream-rawlog.h"
#include "iostream-ssl.h"
#include "safe-mkstemp.h"
#include "base64.h"
#include "str.h"
#include "dns-lookup.h"
#include "pop3c-client.h"
#include <unistd.h>
enum pop3c_client_state {
/* No connection */
/* Trying to connect */
/* Connected, trying to authenticate */
/* Post-authentication, asking for capabilities */
/* Authenticated, ready to accept commands */
};
struct pop3c_client_sync_cmd_ctx {
char *reply;
};
struct pop3c_client_cmd {
bool reading_dot;
void *context;
};
struct pop3c_client {
int fd;
const char *auth_mech;
void *login_context;
const char *input_line;
};
static void
struct pop3c_client *client);
struct pop3c_client *
{
const char *error;
&error) < 0) {
i_error("pop3c(%s:%u): Couldn't initialize SSL context: %s",
}
}
return client;
}
static void
{
}
}
static void
{
!cmd->reading_dot) {
/* read the full input into seekable-istream before calling
the callback */
return;
}
}
if (running)
}
static void
{
"Disconnected");
}
{
"Disconnected");
}
{
}
{
}
{
i_error("pop3c(%s): connect(%s, %u) timed out after %u seconds",
break;
case POP3C_CLIENT_STATE_DONE:
i_error("pop3c(%s): Command timed out after %u seconds",
break;
default:
i_error("pop3c(%s): Authentication timed out after %u seconds",
break;
}
}
{
unsigned int ips_count;
int ret;
if (ret != 0) {
i_error("pop3c(%s): net_gethostbyname() failed: %s",
return -1;
}
} else {
&client->dns_lookup) < 0)
return -1;
}
return 0;
}
{
}
ioloop = io_loop_create();
/* we're connecting, start DNS lookup after our ioloop
is created */
if (pop3c_client_dns_lookup(client) < 0)
}
if (!failed) {
}
}
{
}
{
i_debug("pop3c(%s): Authenticating as '%s' (with USER+PASS)",
} else {
i_debug("pop3c(%s): Authenticating as master user '%s' for user '%s' (with SASL PLAIN)",
}
}
} else {
}
}
static const char *
{
} else {
}
}
{
}
static int
{
const char *reply;
if (!success) {
i_error("pop3c(%s): Server sent invalid banner: %s",
return -1;
}
else
break;
if (!success) {
i_error("pop3c(%s): STLS failed: %s",
return -1;
}
if (pop3c_client_ssl_init(client) < 0)
break;
case POP3C_CLIENT_STATE_USER:
if (!success) {
i_error("pop3c(%s): USER failed: %s",
return -1;
}
/* the PASS reply can take a long time.
switch to command timeout. */
break;
case POP3C_CLIENT_STATE_AUTH:
if (line[0] != '+') {
i_error("pop3c(%s): AUTH PLAIN failed: %s",
return -1;
}
break;
case POP3C_CLIENT_STATE_PASS:
line;
} else if (!success) {
i_error("pop3c(%s): Authentication via %s failed: %s",
}
if (!success)
return -1;
break;
case POP3C_CLIENT_STATE_CAPA:
/* CAPA command not supported. some commands still
support UIDL though. */
break;
break;
}
break;
case POP3C_CLIENT_STATE_DONE:
i_unreached();
}
return 0;
}
{
/* we need to read as much as we can with SSL streams to avoid
hanging */
return;
}
}
/* disconnected */
i_error("pop3c(%s): Server disconnected unexpectedly",
} else {
}
i_error("pop3c(%s): Server disconnected: %s",
}
}
}
{
const char *error;
i_debug("pop3c(%s): SSL handshake successful",
}
return 0;
i_debug("pop3c(%s): SSL handshake successful, "
"ignoring invalid certificate: %s",
}
return 0;
} else {
return -1;
}
}
{
const char *error;
return -1;
}
} else {
}
/* recreate rawlog after STARTTLS */
}
i_error("pop3c(%s): Couldn't initialize SSL client: %s",
return -1;
}
client);
return -1;
}
}
return 0;
}
{
int err;
if (err != 0) {
i_error("pop3c(%s): connect(%s, %u) failed: %s",
return;
}
if (pop3c_client_ssl_init(client) < 0)
}
}
{
return;
}
}
}
}
static void
struct pop3c_client *client)
{
i_error("pop3c(%s): dns_lookup() failed: %s",
return;
}
}
{
return;
}
}
{
}
enum pop3c_capability
{
return client->capabilities;
}
{
}
if (ret == 0)
return 0;
ret = 1;
if (ret > 0) {
/* currently we don't actually care about preserving the
+OK reply line for multi-line replies, so just return
it as empty */
return 1;
} else {
return -1;
}
}
static int
{
const char *line;
line += 3;
line += 4;
} else {
i_error("pop3c(%s): Server sent unrecognized line: %s",
}
if (line[0] == ' ')
line++;
i_error("pop3c(%s): Server sent line when no command was running: %s",
} else {
}
return 1;
}
{
int ret;
do {
/* continue reading the current multiline reply */
return;
} else {
}
} while (ret > 0);
if (ret < 0) {
i_error("pop3c(%s): Server disconnected unexpectedly",
}
}
{
}
const char **reply_r)
{
}
struct pop3c_client_cmd *
{
}
return cmd;
}
const char *cmdline)
{
}
{
int fd;
if (fd == -1) {
return -1;
}
/* we just want the fd, unlink it */
/* shouldn't happen.. */
i_close_fd(&fd);
return -1;
}
return fd;
}
{
const char *reply;
*error_r = "Disconnected from server";
return -1;
}
return 0;
return -1;
}
struct istream *
{
i_stream_unref(&inputs[0]);
}