lmtp-client.c revision 5f1d689131a75c39f064cbd4202373e7edf78f18
/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "ioloop.h"
#include "net.h"
#include "istream.h"
#include "ostream.h"
#include "str.h"
#include "dns-lookup.h"
#include "lmtp-client.h"
#include <ctype.h>
#define LMTP_MAX_LINE_LEN 1024
enum lmtp_input_state {
};
struct lmtp_rcpt {
const char *address;
void *context;
struct lmtp_recipient_params params;
bool data_called:1;
bool failed:1;
};
struct lmtp_client {
int refcount;
struct lmtp_client_settings set;
const char *host;
enum lmtp_client_protocol protocol;
enum lmtp_input_state input_state;
const char *global_fail_string;
const char **xclient_args;
struct dns_lookup *dns_lookup;
int fd;
void (*data_output_callback)(void *);
void *data_output_context;
void *finish_context;
const char *data_header;
unsigned int rcpt_next_receive_idx;
unsigned int rcpt_next_data_idx;
unsigned int rcpt_next_send_idx;
struct istream *data_input;
unsigned char output_last;
struct lmtp_client_times times;
bool running:1;
bool xclient_sent:1;
bool rcpt_to_successes:1;
bool output_finished:1;
bool finish_called:1;
bool global_remote_failure:1;
};
struct lmtp_client *
{
struct lmtp_client *client;
return client;
}
{
}
if (!client->finish_called) {
}
}
{
}
{
return;
}
{
}
{
switch (client->input_state) {
case LMTP_INPUT_STATE_GREET:
return "greeting";
case LMTP_INPUT_STATE_LHLO:
return "LHLO";
return "MAIL FROM";
case LMTP_INPUT_STATE_RCPT_TO:
return "RCPT TO";
return "DATA init";
case LMTP_INPUT_STATE_DATA:
if (client->output_finished)
return "DATA reply";
return t_strdup_printf(
} else {
}
case LMTP_INPUT_STATE_XCLIENT:
return "XCLIENT";
}
return "??";
}
static void
{
enum lmtp_client_result result;
struct lmtp_rcpt *recipients;
unsigned int i, count;
recipients[i].context);
}
if (!recipients[i].failed) {
recipients[i].context);
}
}
}
static void
{
}
{
}
static int
{
enum lmtp_client_result result;
if (result == LMTP_CLIENT_RESULT_OK)
if (client->rcpt_next_receive_idx >=
"451 4.5.0 Received unexpected reply: %s", line));
return -1;
}
return 0;
}
{
return 0;
return -1;
} else if (!client->rcpt_to_successes) {
/* This error string shouldn't become visible anywhere */
return -1;
} else {
client->input_state++;
return 0;
}
}
static int
{
unsigned int i, count;
enum lmtp_client_result result;
/* already called rcpt_to_callback with failure */
continue;
}
break;
}
return 0;
return -1;
}
{
const unsigned char *data;
unsigned char add;
bool sent_bytes = FALSE;
int ret;
if (client->output_finished)
return 0;
add = '\0';
for (i = 0; i < size; i++) {
if (data[i] == '\n') {
/* missing CR */
add = '\r';
break;
}
} else if (data[i] == '.' &&
/* escape the dot */
add = '.';
break;
}
}
if (i > 0) {
break;
sent_bytes = TRUE;
}
break;
if (ret == 0) {
/* continue later */
return 0;
}
}
if (add != '\0') {
break;
}
}
i_error("lmtp client: read(%s) failed: %s",
"451 4.3.0 Internal failure while reading DATA input");
return -1;
}
/* -2 can happen with tee istreams */
return 0;
}
/* didn't end with CRLF */
}
return 0;
}
{
t_strdup_printf("LHLO %s\r\n",
break;
t_strdup_printf("EHLO %s\r\n",
break;
}
}
{
return -1;
/* final reply */
return 1;
/* multiline reply. */
return 0;
} else {
/* invalid input */
return -1;
}
}
static void
{
const char *const *linep;
}
}
}
{
/* not supported */
return FALSE;
}
if (client->xclient_sent)
return FALSE;
return FALSE;
return TRUE;
}
{
int ret, reply_code = 0;
client->input_multiline)) <= 0) {
if (ret == 0)
return 0;
"451 4.5.0 Received invalid input: %s", line));
return -1;
}
switch (client->input_state) {
case LMTP_INPUT_STATE_GREET:
if (reply_code != 220) {
"451 4.5.0 Received invalid greeting: %s", line));
return -1;
}
break;
case LMTP_INPUT_STATE_XCLIENT:
if (reply_code != 220) {
"451 4.5.0 XCLIENT failed: %s", line));
return -1;
}
break;
case LMTP_INPUT_STATE_LHLO:
if (reply_code != 250) {
"451 4.5.0 LHLO failed: %s", line));
return -1;
}
if (lmtp_client_send_xclient(client)) {
break;
}
client->input_state++;
break;
if (reply_code != 250) {
"451 4.5.0 MAIL FROM failed: %s", line));
return -1;
}
client->input_state++;
break;
case LMTP_INPUT_STATE_RCPT_TO:
return -1;
break;
if (lmtp_client_send_data_cmd(client) < 0)
return -1;
break;
/* Start sending DATA */
return -1;
}
client->input_state++;
if (lmtp_client_send_data(client) < 0)
return -1;
break;
case LMTP_INPUT_STATE_DATA:
/* DATA replies */
return -1;
break;
}
return 1;
}
{
const char *line;
int ret;
T_BEGIN {
} T_END;
if (ret < 0) {
return;
}
if (ret > 0)
}
"501 5.5.4 Command reply line too long");
i_error("lmtp client: read() failed: %s",
" (read failure)");
" (disconnected in input)");
}
}
{
int err;
if (err != 0) {
i_error("lmtp client: connect(%s, %u) failed: %s",
" (connect)");
return;
}
}
{
i_error("lmtp client: connect(%s, %u) failed: Timed out in %u secs",
" (connect timeout)");
}
{
int ret;
" (disconnected in output)");
(void)lmtp_client_send_data(client);
return ret;
}
{
i_error("lmtp client: connect(%s, %u) failed: %m",
return -1;
}
/* we're already sending data in ostream, so can't use IO_WRITE here */
}
return 0;
}
struct lmtp_client *client)
{
i_error("lmtp client: DNS lookup of %s failed: %s",
" (DNS lookup)");
}
} else {
" (connect)");
}
}
}
enum lmtp_client_protocol protocol,
{
struct dns_lookup_settings dns_lookup_set;
unsigned int ips_count;
int ret;
if (*host == '\0') {
i_error("lmtp client: host not given");
return -1;
}
/* IP address */
/* no dns-client, use blocking lookup */
if (ret != 0) {
i_error("lmtp client: DNS lookup of %s failed: %s",
return -1;
}
} else {
&client->dns_lookup) < 0)
return -1;
return 0;
}
if (lmtp_client_connect(client) < 0)
return -1;
return 0;
}
{
}
{
unsigned int i;
for (i = 0; str[i] != '\0'; i++) {
else
}
}
{
unsigned int i, count;
str_truncate(str, 0);
}
}
client->rcpt_next_send_idx = i;
}
{
struct lmtp_recipient_params params;
}
const struct lmtp_recipient_params *params,
{
enum lmtp_client_result result;
/* we've already failed */
}
{
const char *line;
" (Timed out after %u secs while waiting for reply to %s)",
}
{
/* now we actually want to start doing I/O. start the timeout
handling. */
/* still waiting for connect to finish */
}
}
(void)lmtp_client_send_data_cmd(client);
}
{
(void)lmtp_client_send_data(client);
}
}
void (*callback)(void *),
void *context)
{
}
const struct lmtp_client_times *
{
}