commands.c revision a795ff053b41be95cb94a33cd5501f8d2e843e20
45312f52ff3a3d4c137447be4c7556500c2f8bf2Timo Sirainen/* Copyright (c) 2009-2012 Dovecot authors, see the included COPYING file */
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#include "lib.h"
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#include "ioloop.h"
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#include "array.h"
c2ee17c9c263efdc9c0a339c4836c3d43f5cd3d9Sascha Wilde#include "str.h"
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#include "strescape.h"
cda217260716cfd8d8ec5e56f91708c64c140538Timo Sirainen#include "hostpid.h"
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#include "istream.h"
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#include "istream-concat.h"
c2ee17c9c263efdc9c0a339c4836c3d43f5cd3d9Sascha Wilde#include "ostream.h"
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen#include "istream-dot.h"
c2ee17c9c263efdc9c0a339c4836c3d43f5cd3d9Sascha Wilde#include "safe-mkstemp.h"
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#include "restrict-access.h"
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#include "master-service.h"
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#include "rfc822-parser.h"
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#include "message-date.h"
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#include "auth-master.h"
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#include "mail-storage-service.h"
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#include "index/raw/raw-storage.h"
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#include "lda-settings.h"
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#include "lmtp-settings.h"
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#include "mail-namespace.h"
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#include "mail-deliver.h"
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#include "main.h"
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#include "client.h"
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#include "commands.h"
f325d795b52ce2053f914072b22ebca9c4f0dc7eTimo Sirainen#include "lmtp-proxy.h"
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#include <stdlib.h>
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
f1743785713e7632459d623d5df2108f4b93accbTimo Sirainen#define ERRSTR_TEMP_MAILBOX_FAIL "451 4.3.0 <%s> Temporary internal error"
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#define ERRSTR_TEMP_USERDB_FAIL_PREFIX "451 4.3.0 <%s> "
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen#define ERRSTR_TEMP_USERDB_FAIL \
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen ERRSTR_TEMP_USERDB_FAIL_PREFIX "Temporary user lookup failure"
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen#define LMTP_PROXY_DEFAULT_TIMEOUT_MSECS (1000*30)
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainenint cmd_lhlo(struct client *client, const char *args)
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen{
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen struct rfc822_parser_context parser;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen string_t *domain = t_str_new(128);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen const char *p;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen int ret = 0;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (*args == '\0') {
c8cf8a605e0ddea7cb36fe04551aeca5090e684bTimo Sirainen client_send_line(client, "501 Missing hostname");
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen return 0;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen /* domain / address-literal */
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen rfc822_parser_init(&parser, (const unsigned char *)args, strlen(args),
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen NULL);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (*args != '[')
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen ret = rfc822_parse_dot_atom(&parser, domain);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen else {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen for (p = args+1; *p != ']'; p++) {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (*p == '\\' || *p == '[')
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen break;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (strcmp(p, "]") != 0)
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen ret = -1;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (ret < 0) {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen str_truncate(domain, 0);
c8cf8a605e0ddea7cb36fe04551aeca5090e684bTimo Sirainen str_append(domain, "invalid");
c8cf8a605e0ddea7cb36fe04551aeca5090e684bTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen client_state_reset(client);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen client_send_line(client, "250-%s", client->my_domain);
c8cf8a605e0ddea7cb36fe04551aeca5090e684bTimo Sirainen client_send_line(client, "250-8BITMIME");
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen client_send_line(client, "250-ENHANCEDSTATUSCODES");
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen client_send_line(client, "250 PIPELINING");
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen i_free(client->lhlo);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen client->lhlo = i_strdup(str_c(domain));
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen client->state.name = "LHLO";
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen return 0;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen}
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
33a9c31657771606be5942b55f5f91a5606b2199Timo Sirainenstatic int parse_address(const char *str, const char **address_r,
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen const char **rest_r)
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen{
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen const char *start;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (*str++ != '<')
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen return -1;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen start = str;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (*str == '"') {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen /* "quoted-string"@domain */
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen for (str++; *str != '"'; str++) {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (*str == '\\')
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen str++;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (*str == '\0')
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return -1;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen str++;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen for (; *str != '>'; str++) {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (*str == '\0' || *str == ' ')
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen return -1;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen *address_r = t_strdup_until(start, str);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (*str++ != '>')
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return -1;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (*str == ' ')
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen str++;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen else if (*str != '\0')
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen return -1;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen *rest_r = str;
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen return 0;
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen}
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainenint cmd_mail(struct client *client, const char *args)
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen{
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen const char *addr, *const *argv;
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen if (client->state.mail_from != NULL) {
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen client_send_line(client, "503 5.5.1 MAIL already given");
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen return 0;
c2ee17c9c263efdc9c0a339c4836c3d43f5cd3d9Sascha Wilde }
c2ee17c9c263efdc9c0a339c4836c3d43f5cd3d9Sascha Wilde
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (strncasecmp(args, "FROM:", 5) != 0 ||
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen parse_address(args + 5, &addr, &args) < 0) {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen client_send_line(client, "501 5.5.4 Invalid parameters");
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen return 0;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen argv = t_strsplit(args, " ");
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen for (; *argv != NULL; argv++) {
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen if (strcasecmp(*argv, "BODY=7BIT") == 0)
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen client->state.mail_body_7bit = TRUE;
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen else if (strcasecmp(*argv, "BODY=8BITMIME") == 0)
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen client->state.mail_body_8bitmime = TRUE;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen else {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen client_send_line(client,
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen "501 5.5.4 Unsupported options");
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return 0;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen }
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen client->state.mail_from = p_strdup(client->state_pool, addr);
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen p_array_init(&client->state.rcpt_to, client->state_pool, 64);
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen client_send_line(client, "250 2.1.0 OK");
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen client->state.name = "MAIL FROM";
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen return 0;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen}
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainenstatic bool
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainenclient_proxy_rcpt_parse_fields(struct lmtp_proxy_settings *set,
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen const char *const *args, const char **address)
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen{
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen const char *p, *key, *value;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen bool proxying = FALSE, port_set = FALSE;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen for (; *args != NULL; args++) {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen p = strchr(*args, '=');
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (p == NULL) {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen key = *args;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen value = "";
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen } else {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen key = t_strdup_until(*args, p);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen value = p + 1;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (strcmp(key, "proxy") == 0)
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen proxying = TRUE;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen else if (strcmp(key, "host") == 0)
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen set->host = value;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen else if (strcmp(key, "port") == 0) {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen set->port = atoi(value);
35083063d0e432d0cf78206b5929750e613ad772Timo Sirainen port_set = TRUE;
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen } else if (strcmp(key, "proxy_timeout") == 0)
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen set->timeout_msecs = atoi(value)*1000;
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen else if (strcmp(key, "protocol") == 0) {
35083063d0e432d0cf78206b5929750e613ad772Timo Sirainen if (strcmp(value, "lmtp") == 0)
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen set->protocol = LMTP_CLIENT_PROTOCOL_LMTP;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen else if (strcmp(value, "smtp") == 0) {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen set->protocol = LMTP_CLIENT_PROTOCOL_SMTP;
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen if (!port_set)
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen set->port = 25;
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen } else {
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen i_error("proxy: Unknown protocol %s", value);
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen return FALSE;
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen }
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen } else if (strcmp(key, "user") == 0 ||
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen strcmp(key, "destuser") == 0) {
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen /* changing the username */
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen *address = value;
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen } else {
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen /* just ignore it */
d927bc5618696157fc55eb1f11b5cab05400ed52Timo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (proxying && set->host == NULL) {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen i_error("proxy: host not given");
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen return FALSE;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen return proxying;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen}
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainenstatic bool
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainenclient_proxy_is_ourself(const struct client *client,
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen const struct lmtp_proxy_settings *set)
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen{
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen struct ip_addr ip;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
d927bc5618696157fc55eb1f11b5cab05400ed52Timo Sirainen if (set->port != client->local_port)
d927bc5618696157fc55eb1f11b5cab05400ed52Timo Sirainen return FALSE;
d927bc5618696157fc55eb1f11b5cab05400ed52Timo Sirainen
d927bc5618696157fc55eb1f11b5cab05400ed52Timo Sirainen if (net_addr2ip(set->host, &ip) < 0)
d927bc5618696157fc55eb1f11b5cab05400ed52Timo Sirainen return FALSE;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (!net_ip_compare(&ip, &client->local_ip))
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen return FALSE;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen return TRUE;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen}
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainenstatic const char *
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainenaddress_add_detail(struct client *client, const char *username,
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen const char *detail)
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen{
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen const char *delim = client->set->recipient_delimiter;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen const char *domain;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen domain = strchr(username, '@');
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (domain == NULL)
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen return t_strconcat(username, delim, detail, NULL);
c7194d1d3872ffb2901737e1df337cc227a3fa77Timo Sirainen else {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen username = t_strdup_until(username, domain);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen return t_strconcat(username, delim, detail, domain, NULL);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainenstatic bool client_proxy_rcpt(struct client *client, const char *address,
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen const char *username, const char *detail)
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen{
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen struct auth_master_connection *auth_conn;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen struct lmtp_proxy_settings set;
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen struct auth_user_info info;
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen struct mail_storage_service_input input;
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen const char *args, *const *fields, *errstr, *orig_username = username;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen pool_t pool;
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen int ret;
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen memset(&input, 0, sizeof(input));
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen input.module = input.service = "lmtp";
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen mail_storage_service_init_settings(storage_service, &input);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen memset(&info, 0, sizeof(info));
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen info.service = master_service_get_name(master_service);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen info.local_ip = client->local_ip;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen info.remote_ip = client->remote_ip;
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen info.local_port = client->local_port;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen info.remote_port = client->remote_port;
c8cf8a605e0ddea7cb36fe04551aeca5090e684bTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen pool = pool_alloconly_create("auth lookup", 1024);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen auth_conn = mail_storage_service_get_auth_conn(storage_service);
c8cf8a605e0ddea7cb36fe04551aeca5090e684bTimo Sirainen ret = auth_master_pass_lookup(auth_conn, username, &info,
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen pool, &fields);
9762e4f86950549c8186c7d3d4fa4a6b533ea848Timo Sirainen if (ret <= 0) {
9762e4f86950549c8186c7d3d4fa4a6b533ea848Timo Sirainen errstr = ret < 0 && fields[0] != NULL ? t_strdup(fields[0]) :
e930af34580510d2fe58628d270dbaf786e86248Timo Sirainen t_strdup_printf(ERRSTR_TEMP_USERDB_FAIL, address);
c7194d1d3872ffb2901737e1df337cc227a3fa77Timo Sirainen pool_unref(&pool);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (ret < 0) {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen client_send_line(client, "%s", errstr);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen return TRUE;
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen } else {
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen /* user not found from passdb. try userdb also. */
60b42c6dfdf9edcca8a96b380ef9a0adc60c2464Timo Sirainen return FALSE;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen }
60b42c6dfdf9edcca8a96b380ef9a0adc60c2464Timo Sirainen }
60b42c6dfdf9edcca8a96b380ef9a0adc60c2464Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen memset(&set, 0, sizeof(set));
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen set.port = client->local_port;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen set.protocol = LMTP_CLIENT_PROTOCOL_LMTP;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen set.timeout_msecs = LMTP_PROXY_DEFAULT_TIMEOUT_MSECS;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (!client_proxy_rcpt_parse_fields(&set, fields, &username)) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen /* not proxying this user */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen pool_unref(&pool);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return FALSE;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (strcmp(username, orig_username) != 0) {
0d16525a729011f4fced989a3da74d755ea49e6dTimo Sirainen /* username changed. change the address as well */
0d16525a729011f4fced989a3da74d755ea49e6dTimo Sirainen if (*detail == '\0')
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen address = username;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen else
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen address = address_add_detail(client, username, detail);
4ac2d38239cea8090154e17faefd77de5a71d882Timo Sirainen } else if (client_proxy_is_ourself(client, &set)) {
c7194d1d3872ffb2901737e1df337cc227a3fa77Timo Sirainen i_error("Proxying to <%s> loops to itself", username);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen client_send_line(client, "554 5.4.6 <%s> "
94ce7e7700cda14a8342cb08e7285507b4b531daTimo Sirainen "Proxying loops to itself", address);
94ce7e7700cda14a8342cb08e7285507b4b531daTimo Sirainen pool_unref(&pool);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen return TRUE;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (array_count(&client->state.rcpt_to) != 0) {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen client_send_line(client, "451 4.3.0 <%s> "
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen "Can't handle mixed proxy/non-proxy destinations",
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen address);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen pool_unref(&pool);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen return TRUE;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (client->proxy == NULL) {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen client->proxy = lmtp_proxy_init(client->set->hostname,
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen dns_client_socket_path,
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen client->state.session_id,
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen client->output);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (client->state.mail_body_8bitmime)
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen args = " BODY=8BITMIME";
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen else if (client->state.mail_body_7bit)
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen args = " BODY=7BIT";
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen else
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen args = "";
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen lmtp_proxy_mail_from(client->proxy, t_strdup_printf(
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen "<%s>%s", client->state.mail_from, args));
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (lmtp_proxy_add_rcpt(client->proxy, address, &set) < 0)
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen client_send_line(client, ERRSTR_TEMP_REMOTE_FAILURE);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen else
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen client_send_line(client, "250 2.1.5 OK");
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen pool_unref(&pool);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen return TRUE;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen}
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainenstatic const char *lmtp_unescape_address(const char *name)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen{
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen string_t *str;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen const char *p;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (*name != '"')
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen return name;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
77c462c3a415536f9c87028ee34546ee96fd1445Timo Sirainen /* quoted-string local-part. drop the quotes unless there's a
3e564425db51f3921ce4de11859777135fdedd15Timo Sirainen '@' character inside or there's an error. */
3e564425db51f3921ce4de11859777135fdedd15Timo Sirainen str = t_str_new(128);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen for (p = name+1; *p != '"'; p++) {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (*p == '\0')
return name;
if (*p == '\\') {
if (p[1] == '\0') {
/* error */
return name;
}
p++;
}
if (*p == '@')
return name;
str_append_c(str, *p);
}
p++;
if (*p != '@' && *p != '\0')
return name;
str_append(str, p);
return str_c(str);
}
static void rcpt_address_parse(struct client *client, const char *address,
const char **username_r, const char **detail_r)
{
const char *p, *domain;
*username_r = address;
*detail_r = "";
if (*client->set->recipient_delimiter == '\0')
return;
domain = strchr(address, '@');
p = strstr(address, client->set->recipient_delimiter);
if (p != NULL && (domain == NULL || p < domain)) {
/* user+detail@domain */
*username_r = t_strdup_until(*username_r, p);
if (domain == NULL)
*detail_r = p+1;
else {
*detail_r = t_strdup_until(p+1, domain);
*username_r = t_strconcat(*username_r, domain, NULL);
}
}
}
static void lmtp_address_translate(struct client *client, const char **address)
{
const char *transpos = client->lmtp_set->lmtp_address_translate;
const char *p, *nextstr, *addrpos = *address;
unsigned int len;
string_t *username, *domain, *dest = NULL;
if (*transpos == '\0')
return;
username = t_str_new(64);
domain = t_str_new(64);
/* check that string matches up to the first '%' */
p = strchr(transpos, '%');
if (p == NULL)
len = strlen(transpos);
else
len = p-transpos;
if (strncmp(transpos, addrpos, len) != 0)
return;
transpos += len;
addrpos += len;
while (*transpos != '\0') {
switch (transpos[1]) {
case 'n':
case 'u':
dest = username;
break;
case 'd':
dest = domain;
break;
default:
return;
}
transpos += 2;
/* find where the next string starts */
if (*transpos == '\0') {
str_append(dest, addrpos);
break;
}
p = strchr(transpos, '%');
if (p == NULL)
nextstr = transpos;
else
nextstr = t_strdup_until(transpos, p);
p = strstr(addrpos, nextstr);
if (p == NULL)
return;
str_append_n(dest, addrpos, p-addrpos);
len = strlen(nextstr);
transpos += len;
addrpos = p + len;
}
str_append_c(username, '@');
if (domain != NULL)
str_append_str(username, domain);
*address = str_c(username);
}
int cmd_rcpt(struct client *client, const char *args)
{
struct mail_recipient rcpt;
struct mail_storage_service_input input;
const char *address, *username, *detail, *prefix;
const char *error = NULL;
int ret = 0;
client->state.name = "RCPT TO";
if (client->state.mail_from == NULL) {
client_send_line(client, "503 5.5.1 MAIL needed first");
return 0;
}
if (strncasecmp(args, "TO:", 3) != 0 ||
parse_address(args + 3, &address, &args) < 0) {
client_send_line(client, "501 5.5.4 Invalid parameters");
return 0;
}
memset(&rcpt, 0, sizeof(rcpt));
address = lmtp_unescape_address(address);
if (*args != '\0') {
client_send_line(client, "501 5.5.4 Unsupported options");
return 0;
}
rcpt_address_parse(client, address, &username, &detail);
if (client->lmtp_set->lmtp_proxy) {
if (client_proxy_rcpt(client, address, username, detail))
return 0;
}
memset(&input, 0, sizeof(input));
input.module = input.service = "lmtp";
input.username = username;
input.local_ip = client->local_ip;
input.remote_ip = client->remote_ip;
input.local_port = client->local_port;
input.remote_port = client->remote_port;
ret = mail_storage_service_lookup(storage_service, &input,
&rcpt.service_user, &error);
if (ret < 0) {
prefix = t_strdup_printf(ERRSTR_TEMP_USERDB_FAIL_PREFIX,
username);
client_send_line(client, "%s%s", prefix, error);
return 0;
}
if (ret == 0) {
client_send_line(client,
"550 5.1.1 <%s> User doesn't exist: %s",
address, username);
return 0;
}
if (client->proxy != NULL) {
/* NOTE: if this restriction is ever removed, we'll also need
to send different message bodies to local and proxy
(with and without Return-Path: header) */
client_send_line(client, "451 4.3.0 <%s> "
"Can't handle mixed proxy/non-proxy destinations",
address);
return 0;
}
lmtp_address_translate(client, &address);
rcpt.address = p_strdup(client->state_pool, address);
rcpt.detail = p_strdup(client->state_pool, detail);
array_append(&client->state.rcpt_to, &rcpt, 1);
client_send_line(client, "250 2.1.5 OK");
return 0;
}
int cmd_quit(struct client *client, const char *args ATTR_UNUSED)
{
client_destroy(client, "221 2.0.0", "Client quit");
return -1;
}
int cmd_vrfy(struct client *client, const char *args ATTR_UNUSED)
{
client_send_line(client, "252 2.3.3 Try RCPT instead");
return 0;
}
int cmd_rset(struct client *client, const char *args ATTR_UNUSED)
{
client_state_reset(client);
client_send_line(client, "250 2.0.0 OK");
return 0;
}
int cmd_noop(struct client *client, const char *args ATTR_UNUSED)
{
client_send_line(client, "250 2.0.0 OK");
return 0;
}
static int
client_deliver(struct client *client, const struct mail_recipient *rcpt,
struct mail *src_mail, struct mail_deliver_session *session)
{
struct mail_deliver_context dctx;
struct mail_storage *storage;
const struct mail_storage_service_input *input;
struct mail_namespace *ns;
void **sets;
const char *error, *username;
enum mail_error mail_error;
int ret;
input = mail_storage_service_user_get_input(rcpt->service_user);
username = t_strdup(input->username);
i_set_failure_prefix(t_strdup_printf("lmtp(%s, %s): ",
my_pid, username));
if (mail_storage_service_next(storage_service, rcpt->service_user,
&client->state.dest_user) < 0) {
client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
rcpt->address);
return -1;
}
sets = mail_storage_service_user_get_set(rcpt->service_user);
memset(&dctx, 0, sizeof(dctx));
dctx.session = session;
dctx.pool = session->pool;
dctx.set = sets[1];
dctx.session_id = client->state.session_id;
dctx.src_mail = src_mail;
dctx.src_envelope_sender = client->state.mail_from;
dctx.dest_user = client->state.dest_user;
if (*dctx.set->lda_original_recipient_header != '\0') {
dctx.dest_addr = mail_deliver_get_address(src_mail,
dctx.set->lda_original_recipient_header);
}
if (dctx.dest_addr == NULL)
dctx.dest_addr = rcpt->address;
dctx.final_dest_addr = rcpt->address;
if (*rcpt->detail == '\0' ||
!client->lmtp_set->lmtp_save_to_detail_mailbox)
dctx.dest_mailbox_name = "INBOX";
else {
ns = mail_namespace_find_inbox(dctx.dest_user->namespaces);
dctx.dest_mailbox_name =
t_strconcat(ns->prefix, rcpt->detail, NULL);
}
dctx.save_dest_mail = array_count(&client->state.rcpt_to) > 1 &&
client->state.first_saved_mail == NULL;
if (mail_deliver(&dctx, &storage) == 0) {
if (dctx.dest_mail != NULL) {
i_assert(client->state.first_saved_mail == NULL);
client->state.first_saved_mail = dctx.dest_mail;
}
client_send_line(client, "250 2.0.0 <%s> %s Saved",
rcpt->address, client->state.session_id);
ret = 0;
} else if (storage == NULL) {
/* This shouldn't happen */
i_error("BUG: Saving failed to unknown storage");
client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
rcpt->address);
ret = -1;
} else {
error = mail_storage_get_last_error(storage, &mail_error);
if (mail_error == MAIL_ERROR_NOSPACE) {
client_send_line(client, "%s <%s> %s",
dctx.set->quota_full_tempfail ?
"452 4.2.2" : "552 5.2.2",
rcpt->address, error);
} else {
client_send_line(client, "451 4.2.0 <%s> %s",
rcpt->address, error);
}
ret = -1;
}
return ret;
}
static bool client_deliver_next(struct client *client, struct mail *src_mail,
struct mail_deliver_session *session)
{
const struct mail_recipient *rcpts;
unsigned int count;
int ret;
rcpts = array_get(&client->state.rcpt_to, &count);
while (client->state.rcpt_idx < count) {
ret = client_deliver(client, &rcpts[client->state.rcpt_idx],
src_mail, session);
i_set_failure_prefix(t_strdup_printf("lmtp(%s): ", my_pid));
client->state.rcpt_idx++;
if (ret == 0)
return TRUE;
/* failed. try the next one. */
if (client->state.dest_user != NULL)
mail_user_unref(&client->state.dest_user);
}
return FALSE;
}
static void client_rcpt_fail_all(struct client *client)
{
const struct mail_recipient *rcpt;
array_foreach(&client->state.rcpt_to, rcpt) {
client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
rcpt->address);
}
}
static struct istream *client_get_input(struct client *client)
{
struct client_state *state = &client->state;
struct istream *cinput, *inputs[3];
inputs[0] = i_stream_create_from_data(state->added_headers,
strlen(state->added_headers));
if (state->mail_data_output != NULL) {
o_stream_unref(&state->mail_data_output);
inputs[1] = i_stream_create_fd(state->mail_data_fd,
MAIL_READ_FULL_BLOCK_SIZE,
FALSE);
i_stream_set_init_buffer_size(inputs[1],
MAIL_READ_FULL_BLOCK_SIZE);
} else {
inputs[1] = i_stream_create_from_data(state->mail_data->data,
state->mail_data->used);
}
inputs[2] = NULL;
cinput = i_stream_create_concat(inputs);
i_stream_unref(&inputs[0]);
i_stream_unref(&inputs[1]);
return cinput;
}
static int client_open_raw_mail(struct client *client, struct istream *input)
{
static const char *wanted_headers[] = {
"From", "To", "Message-ID", "Subject", "Return-Path",
NULL
};
struct mailbox *box;
struct mailbox_transaction_context *trans;
struct mailbox_header_lookup_ctx *headers_ctx;
enum mail_error error;
if (raw_mailbox_alloc_stream(client->raw_mail_user, input,
(time_t)-1, client->state.mail_from,
&box) < 0) {
i_error("Can't open delivery mail as raw: %s",
mailbox_get_last_error(box, &error));
mailbox_free(&box);
client_rcpt_fail_all(client);
return -1;
}
trans = mailbox_transaction_begin(box, 0);
headers_ctx = mailbox_header_lookup_init(box, wanted_headers);
client->state.raw_mail = mail_alloc(trans, 0, headers_ctx);
mailbox_header_lookup_unref(&headers_ctx);
mail_set_seq(client->state.raw_mail, 1);
return 0;
}
static void
client_input_data_write_local(struct client *client, struct istream *input)
{
struct mail_deliver_session *session;
struct mail *src_mail;
uid_t old_uid, first_uid = (uid_t)-1;
if (client_open_raw_mail(client, input) < 0)
return;
session = mail_deliver_session_init();
old_uid = geteuid();
src_mail = client->state.raw_mail;
while (client_deliver_next(client, src_mail, session)) {
if (client->state.first_saved_mail == NULL ||
client->state.first_saved_mail == src_mail)
mail_user_unref(&client->state.dest_user);
else {
/* use the first saved message to save it elsewhere too.
this might allow hard linking the files. */
client->state.dest_user = NULL;
src_mail = client->state.first_saved_mail;
first_uid = geteuid();
i_assert(first_uid != 0);
}
}
mail_deliver_session_deinit(&session);
if (client->state.first_saved_mail != NULL) {
struct mail *mail = client->state.first_saved_mail;
struct mailbox_transaction_context *trans = mail->transaction;
struct mailbox *box = trans->box;
struct mail_user *user = box->storage->user;
/* just in case these functions are going to write anything,
change uid back to user's own one */
if (first_uid != old_uid) {
if (seteuid(0) < 0)
i_fatal("seteuid(0) failed: %m");
if (seteuid(first_uid) < 0)
i_fatal("seteuid() failed: %m");
}
mail_free(&mail);
mailbox_transaction_rollback(&trans);
mailbox_free(&box);
mail_user_unref(&user);
}
if (old_uid == 0) {
/* switch back to running as root, since that's what we're
practically doing anyway. it's also important in case we
lose e.g. config connection and need to reconnect to it. */
if (seteuid(0) < 0)
i_fatal("seteuid(0) failed: %m");
/* enable core dumping again. we need to chdir also to
root-owned directory to get core dumps. */
restrict_access_allow_coredumps(TRUE);
(void)chdir(base_dir);
}
}
static void client_input_data_finish(struct client *client)
{
client_io_reset(client);
client_state_reset(client);
if (i_stream_have_bytes_left(client->input))
client_input_handle(client);
}
static void client_proxy_finish(void *context)
{
struct client *client = context;
lmtp_proxy_deinit(&client->proxy);
client_input_data_finish(client);
}
static const char *client_get_added_headers(struct client *client)
{
string_t *str = t_str_new(200);
const char *host, *rcpt_to = NULL;
if (array_count(&client->state.rcpt_to) == 1) {
const struct mail_recipient *rcpt =
array_idx(&client->state.rcpt_to, 0);
rcpt_to = rcpt->address;
}
/* don't set Return-Path when proxying so it won't get added twice */
if (array_count(&client->state.rcpt_to) > 0) {
str_printfa(str, "Return-Path: <%s>\r\n",
client->state.mail_from);
if (rcpt_to != NULL)
str_printfa(str, "Delivered-To: <%s>\r\n", rcpt_to);
}
str_printfa(str, "Received: from %s", client->lhlo);
if ((host = net_ip2addr(&client->remote_ip)) != NULL)
str_printfa(str, " ([%s])", host);
str_printfa(str, "\r\n\tby %s ("PACKAGE_NAME") with LMTP id %s",
client->my_domain, client->state.session_id);
str_append(str, "\r\n\t");
if (rcpt_to != NULL)
str_printfa(str, "for <%s>", rcpt_to);
str_printfa(str, "; %s\r\n", message_date_create(ioloop_time));
return str_c(str);
}
static bool client_input_data_write(struct client *client)
{
struct istream *input;
bool ret = TRUE;
/* stop handling client input until saving/proxying is finished */
if (client->to_idle != NULL)
timeout_remove(&client->to_idle);
io_remove(&client->io);
i_stream_destroy(&client->dot_input);
input = client_get_input(client);
if (array_count(&client->state.rcpt_to) != 0)
client_input_data_write_local(client, input);
if (client->proxy != NULL) {
lmtp_proxy_start(client->proxy, input, NULL,
client_proxy_finish, client);
ret = FALSE;
}
i_stream_unref(&input);
return ret;
}
static int client_input_add_file(struct client *client,
const unsigned char *data, size_t size)
{
struct client_state *state = &client->state;
string_t *path;
ssize_t ret;
int fd;
if (state->mail_data_output != NULL) {
/* continue writing to file */
if (o_stream_send(state->mail_data_output,
data, size) != (ssize_t)size)
return -1;
return 0;
}
/* move everything to a temporary file. */
path = t_str_new(256);
mail_user_set_get_temp_prefix(path, client->raw_mail_user->set);
fd = safe_mkstemp_hostpid(path, 0600, (uid_t)-1, (gid_t)-1);
if (fd == -1) {
i_error("Temp file creation to %s failed: %m", str_c(path));
return -1;
}
/* we just want the fd, unlink it */
if (unlink(str_c(path)) < 0) {
/* shouldn't happen.. */
i_error("unlink(%s) failed: %m", str_c(path));
(void)close(fd);
return -1;
}
state->mail_data_fd = fd;
state->mail_data_output = o_stream_create_fd_file(fd, 0, FALSE);
o_stream_cork(state->mail_data_output);
ret = o_stream_send(state->mail_data_output,
state->mail_data->data, state->mail_data->used);
if (ret != (ssize_t)state->mail_data->used)
return -1;
if (o_stream_send(client->state.mail_data_output,
data, size) != (ssize_t)size)
return -1;
return 0;
}
static int
client_input_add(struct client *client, const unsigned char *data, size_t size)
{
if (client->state.mail_data->used + size <=
CLIENT_MAIL_DATA_MAX_INMEMORY_SIZE &&
client->state.mail_data_output == NULL) {
buffer_append(client->state.mail_data, data, size);
return 0;
} else {
return client_input_add_file(client, data, size);
}
}
static void client_input_data_handle(struct client *client)
{
const unsigned char *data;
size_t size;
ssize_t ret;
while ((ret = i_stream_read(client->dot_input)) > 0 || ret == -2) {
data = i_stream_get_data(client->dot_input, &size);
if (client_input_add(client, data, size) < 0) {
client_destroy(client, "451 4.3.0",
"Temporary internal failure");
return;
}
i_stream_skip(client->dot_input, size);
}
if (ret == 0)
return;
if (!client->dot_input->eof) {
/* client probably disconnected */
client_destroy(client, NULL, NULL);
return;
}
if (client_input_data_write(client))
client_input_data_finish(client);
}
static void client_input_data(struct client *client)
{
if (client_input_read(client) < 0)
return;
client_input_data_handle(client);
}
int cmd_data(struct client *client, const char *args ATTR_UNUSED)
{
if (client->state.mail_from == NULL) {
client_send_line(client, "503 5.5.1 MAIL needed first");
return 0;
}
if (array_count(&client->state.rcpt_to) == 0 && client->proxy == NULL) {
client_send_line(client, "554 5.5.1 No valid recipients");
return 0;
}
client->state.added_headers =
p_strdup(client->state_pool, client_get_added_headers(client));
i_assert(client->state.mail_data == NULL);
client->state.mail_data = buffer_create_dynamic(default_pool, 1024*64);
i_assert(client->dot_input == NULL);
client->dot_input = i_stream_create_dot(client->input, TRUE);
client_send_line(client, "354 OK");
io_remove(&client->io);
client->state.name = "DATA";
client->io = io_add(client->fd_in, IO_READ, client_input_data, client);
client_input_data_handle(client);
return -1;
}