commands.c revision bf7dc750b95039981c0e9d728f313d50cf38a156
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi#include "lib.h"
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi#include "ioloop.h"
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi#include "array.h"
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi#include "str.h"
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi#include "strescape.h"
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi#include "hostpid.h"
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi#include "istream.h"
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi#include "istream-concat.h"
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi#include "ostream.h"
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi#include "istream-dot.h"
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi#include "safe-mkstemp.h"
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi#include "hex-dec.h"
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi#include "time-util.h"
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi#include "var-expand.h"
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi#include "restrict-access.h"
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi#include "settings-parser.h"
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi#include "anvil-client.h"
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi#include "master-service.h"
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi#include "master-service-ssl.h"
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi#include "iostream-ssl.h"
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi#include "rfc822-parser.h"
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi#include "message-date.h"
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi#include "auth-master.h"
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi#include "mail-storage-service.h"
f207fb0f2fd3113aa9a9f7911fb12d94dce19dffAki Tuomi#include "index/raw/raw-storage.h"
f207fb0f2fd3113aa9a9f7911fb12d94dce19dffAki Tuomi#include "lda-settings.h"
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi#include "lmtp-settings.h"
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi#include "mail-namespace.h"
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi#include "mail-deliver.h"
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi#include "main.h"
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi#include "client.h"
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi#include "commands.h"
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi#include "lmtp-proxy.h"
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
03c6532fe7aa3ddae23c99ff6bec78d8dd2e8165Aki Tuomi#define ERRSTR_TEMP_MAILBOX_FAIL "451 4.3.0 <%s> Temporary internal error"
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi#define ERRSTR_TEMP_USERDB_FAIL_PREFIX "451 4.3.0 <%s> "
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi#define ERRSTR_TEMP_USERDB_FAIL \
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi ERRSTR_TEMP_USERDB_FAIL_PREFIX "Temporary user lookup failure"
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi#define LMTP_PROXY_DEFAULT_TIMEOUT_MSECS (1000*125)
03c6532fe7aa3ddae23c99ff6bec78d8dd2e8165Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomiint cmd_lhlo(struct client *client, const char *args)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi{
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi struct rfc822_parser_context parser;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi string_t *domain = t_str_new(128);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char *p;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi int ret = 0;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (*args == '\0') {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client_send_line(client, "501 Missing hostname");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return 0;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* domain / address-literal */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi rfc822_parser_init(&parser, (const unsigned char *)args, strlen(args),
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi NULL);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (*args != '[')
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi ret = rfc822_parse_dot_atom(&parser, domain);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi else {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi for (p = args+1; *p != ']'; p++) {
17541ea25593c656060199715051db2c1eef221dAki Tuomi if (*p == '\\' || *p == '[')
17541ea25593c656060199715051db2c1eef221dAki Tuomi break;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (strcmp(p, "]") != 0)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi ret = -1;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (ret < 0) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi str_truncate(domain, 0);
17541ea25593c656060199715051db2c1eef221dAki Tuomi str_append(domain, "invalid");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client_state_reset(client, "LHLO");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client_send_line(client, "250-%s", client->my_domain);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (master_service_ssl_is_enabled(master_service) &&
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client->ssl_iostream == NULL)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client_send_line(client, "250-STARTTLS");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (client_is_trusted(client))
03c6532fe7aa3ddae23c99ff6bec78d8dd2e8165Aki Tuomi client_send_line(client, "250-XCLIENT ADDR PORT TTL TIMEOUT");
03c6532fe7aa3ddae23c99ff6bec78d8dd2e8165Aki Tuomi client_send_line(client, "250-8BITMIME");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client_send_line(client, "250-ENHANCEDSTATUSCODES");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client_send_line(client, "250 PIPELINING");
7c9ae3d919ba59af5be3193a80ece4871a0d700cAki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi i_free(client->lhlo);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client->lhlo = i_strdup(str_c(domain));
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client_state_set(client, "LHLO", "");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return 0;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi}
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomiint cmd_starttls(struct client *client)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi{
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi struct ostream *plain_output = client->output;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char *error;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (client->ssl_iostream != NULL) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi o_stream_nsend_str(client->output,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi "443 5.5.1 TLS is already active.\r\n");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return 0;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (master_service_ssl_init(master_service,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi &client->input, &client->output,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi &client->ssl_iostream, &error) < 0) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi i_error("TLS initialization failed: %s", error);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi o_stream_nsend_str(client->output,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi "454 4.7.0 Internal error, TLS not available.\r\n");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return 0;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi o_stream_nsend_str(plain_output,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi "220 2.0.0 Begin TLS negotiation now.\r\n");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (ssl_iostream_handshake(client->ssl_iostream) < 0) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client_destroy(client, NULL, NULL);
5f1d689131a75c39f064cbd4202373e7edf78f18Josef 'Jeff' Sipek return -1;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return 0;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi}
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomistatic int parse_address(const char *str, const char **address_r,
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi const char **rest_r)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi{
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char *start;
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi if (*str++ != '<')
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return -1;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi start = str;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (*str == '"') {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* "quoted-string"@domain */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi for (str++; *str != '"'; str++) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (*str == '\\')
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi str++;
5f1d689131a75c39f064cbd4202373e7edf78f18Josef 'Jeff' Sipek if (*str == '\0')
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return -1;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi str++;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi for (; *str != '>'; str++) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (*str == '\0' || *str == ' ')
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return -1;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi *address_r = t_strdup_until(start, str);
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi if (*str++ != '>')
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi return -1;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (*str == ' ')
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi str++;
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi else if (*str != '\0')
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi return -1;
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi *rest_r = str;
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi return 0;
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi}
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomistatic const char *
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomiparse_xtext(struct client *client, const char *value)
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi{
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi const char *p;
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi string_t *str;
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi unsigned int i;
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi p = strchr(value, '+');
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi if (p == NULL)
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi return p_strdup(client->state_pool, value);
0d1b8b6bec79746c5d89d57dd8c1688946bd9237Josef 'Jeff' Sipek
5f1d689131a75c39f064cbd4202373e7edf78f18Josef 'Jeff' Sipek /*
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi hexchar = ASCII "+" immediately followed by two upper case
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi hexadecimal digits
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi */
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi str = t_str_new(128);
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi for (i = 0; value[i] != '\0'; i++) {
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi if (value[i] == '+' && value[i+1] != '\0' && value[i+2] != '\0') {
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi str_append_c(str, hex2dec((const void *)(value+i+1), 2));
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi i += 2;
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi } else {
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi str_append_c(str, value[i]);
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return p_strdup(client->state_pool, str_c(str));
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi}
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomistatic void lmtp_anvil_init(void)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi{
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (anvil == NULL) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char *path = t_strdup_printf("%s/anvil", base_dir);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi anvil = anvil_client_init(path, NULL, 0);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi}
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomiint cmd_mail(struct client *client, const char *args)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi{
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char *addr, *const *argv;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (client->state.mail_from != NULL) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client_send_line(client, "503 5.5.1 MAIL already given");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return 0;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (strncasecmp(args, "FROM:", 5) != 0 ||
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi parse_address(args + 5, &addr, &args) < 0) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client_send_line(client, "501 5.5.4 Invalid parameters");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return 0;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi argv = t_strsplit(args, " ");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi for (; *argv != NULL; argv++) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (strcasecmp(*argv, "BODY=7BIT") == 0)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client->state.mail_body_7bit = TRUE;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi else if (strcasecmp(*argv, "BODY=8BITMIME") == 0)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client->state.mail_body_8bitmime = TRUE;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi else {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client_send_line(client,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi "501 5.5.4 Unsupported options");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return 0;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client->state.mail_from = p_strdup(client->state_pool, addr);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi p_array_init(&client->state.rcpt_to, client->state_pool, 64);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client_send_line(client, "250 2.1.0 OK");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client_state_set(client, "MAIL FROM", client->state.mail_from);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (client->lmtp_set->lmtp_user_concurrency_limit > 0) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* connect to anvil before dropping privileges */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi lmtp_anvil_init();
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client->state.mail_from_timeval = ioloop_timeval;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return 0;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi}
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomistatic bool
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomiclient_proxy_rcpt_parse_fields(struct lmtp_proxy_rcpt_settings *set,
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi const char *const *args, const char **address)
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi{
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi const char *p, *key, *value;
66761ea6a8f00006f28743b4bd2f5339f78c10e0Timo Sirainen bool proxying = FALSE, port_set = FALSE;
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi for (; *args != NULL; args++) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi p = strchr(*args, '=');
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (p == NULL) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi key = *args;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi value = "";
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi } else {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi key = t_strdup_until(*args, p);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi value = p + 1;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (strcmp(key, "proxy") == 0)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi proxying = TRUE;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi else if (strcmp(key, "host") == 0)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi set->host = value;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi else if (strcmp(key, "port") == 0) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (net_str2port(value, &set->port) < 0) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi i_error("proxy: Invalid port number %s", value);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return FALSE;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi port_set = TRUE;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi } else if (strcmp(key, "proxy_timeout") == 0) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (str_to_uint(value, &set->timeout_msecs) < 0) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi i_error("proxy: Invalid proxy_timeout value %s", value);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return FALSE;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
e6ba63471b1451b48e1185492c33d7bf58be884dAki Tuomi set->timeout_msecs *= 1000;
e6ba63471b1451b48e1185492c33d7bf58be884dAki Tuomi } else if (strcmp(key, "protocol") == 0) {
e6ba63471b1451b48e1185492c33d7bf58be884dAki Tuomi if (strcmp(value, "lmtp") == 0) {
e6ba63471b1451b48e1185492c33d7bf58be884dAki Tuomi set->protocol = LMTP_CLIENT_PROTOCOL_LMTP;
66761ea6a8f00006f28743b4bd2f5339f78c10e0Timo Sirainen if (!port_set)
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi set->port = 24;
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi } else if (strcmp(value, "smtp") == 0) {
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi set->protocol = LMTP_CLIENT_PROTOCOL_SMTP;
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi if (!port_set)
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi set->port = 25;
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi } else {
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi i_error("proxy: Unknown protocol %s", value);
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi return FALSE;
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi }
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi } else if (strcmp(key, "user") == 0 ||
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi strcmp(key, "destuser") == 0) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* changing the username */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi *address = value;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi } else {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* just ignore it */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (proxying && set->host == NULL) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi i_error("proxy: host not given");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return FALSE;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return proxying;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi}
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomistatic bool
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomiclient_proxy_is_ourself(const struct client *client,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const struct lmtp_proxy_rcpt_settings *set)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi{
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi struct ip_addr ip;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (set->port != client->local_port)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return FALSE;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (net_addr2ip(set->host, &ip) < 0)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return FALSE;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (!net_ip_compare(&ip, &client->local_ip))
03c6532fe7aa3ddae23c99ff6bec78d8dd2e8165Aki Tuomi return FALSE;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return TRUE;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi}
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomistatic const char *
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomiaddress_add_detail(const char *username, char delim_c,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char *detail)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi{
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char *domain;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char delim[] = {delim_c, '\0'};
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi domain = strchr(username, '@');
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi if (domain == NULL)
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi return t_strconcat(username, delim, detail, NULL);
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi else {
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi username = t_strdup_until(username, domain);
66761ea6a8f00006f28743b4bd2f5339f78c10e0Timo Sirainen return t_strconcat(username, delim, detail, domain, NULL);
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi }
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi}
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomistatic bool client_proxy_rcpt(struct client *client, const char *address,
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi const char *username, const char *detail, char delim,
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi const struct lmtp_recipient_params *params)
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi{
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi struct auth_master_connection *auth_conn;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi struct lmtp_proxy_rcpt_settings set;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi struct auth_user_info info;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi struct mail_storage_service_input input;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char *args, *const *fields, *errstr, *orig_username = username;
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi pool_t pool;
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi int ret;
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi i_zero(&input);
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi input.module = input.service = "lmtp";
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi mail_storage_service_init_settings(storage_service, &input);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi i_zero(&info);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi info.service = master_service_get_name(master_service);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi info.local_ip = client->local_ip;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi info.remote_ip = client->remote_ip;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi info.local_port = client->local_port;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi info.remote_port = client->remote_port;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi pool = pool_alloconly_create("auth lookup", 1024);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi auth_conn = mail_storage_service_get_auth_conn(storage_service);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi ret = auth_master_pass_lookup(auth_conn, username, &info,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi pool, &fields);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (ret <= 0) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi errstr = ret < 0 && fields[0] != NULL ? t_strdup(fields[0]) :
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi t_strdup_printf(ERRSTR_TEMP_USERDB_FAIL, address);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi pool_unref(&pool);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (ret < 0) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client_send_line(client, "%s", errstr);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return TRUE;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi } else {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* user not found from passdb. try userdb also. */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return FALSE;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi i_zero(&set);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi set.port = client->local_port;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi set.protocol = LMTP_CLIENT_PROTOCOL_LMTP;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi set.timeout_msecs = LMTP_PROXY_DEFAULT_TIMEOUT_MSECS;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi set.params = *params;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (!client_proxy_rcpt_parse_fields(&set, fields, &username)) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* not proxying this user */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi pool_unref(&pool);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return FALSE;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (strcmp(username, orig_username) != 0) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* username changed. change the address as well */
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi if (*detail == '\0')
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi address = username;
9b7b07d70c25c51be54d3b070216872ddaab6e6eAki Tuomi else
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi address = address_add_detail(username, delim, detail);
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi } else if (client_proxy_is_ourself(client, &set)) {
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi i_error("Proxying to <%s> loops to itself", username);
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi client_send_line(client, "554 5.4.6 <%s> "
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi "Proxying loops to itself", address);
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi pool_unref(&pool);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return TRUE;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (client->proxy_ttl <= 1) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi i_error("Proxying to <%s> appears to be looping (TTL=0)",
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi username);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client_send_line(client, "554 5.4.6 <%s> "
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi "Proxying appears to be looping (TTL=0)",
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi username);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi pool_unref(&pool);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return TRUE;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (array_count(&client->state.rcpt_to) != 0) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client_send_line(client, "451 4.3.0 <%s> "
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi "Can't handle mixed proxy/non-proxy destinations",
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi address);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi pool_unref(&pool);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return TRUE;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (client->proxy == NULL) {
17541ea25593c656060199715051db2c1eef221dAki Tuomi struct lmtp_proxy_settings proxy_set;
0d1b8b6bec79746c5d89d57dd8c1688946bd9237Josef 'Jeff' Sipek
17541ea25593c656060199715051db2c1eef221dAki Tuomi i_zero(&proxy_set);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi proxy_set.my_hostname = client->my_domain;
17541ea25593c656060199715051db2c1eef221dAki Tuomi proxy_set.dns_client_socket_path = dns_client_socket_path;
17541ea25593c656060199715051db2c1eef221dAki Tuomi proxy_set.session_id = client->state.session_id;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi proxy_set.source_ip = client->remote_ip;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi proxy_set.source_port = client->remote_port;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi proxy_set.proxy_ttl = client->proxy_ttl-1;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client->proxy = lmtp_proxy_init(&proxy_set, client->output);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (client->state.mail_body_8bitmime)
03c6532fe7aa3ddae23c99ff6bec78d8dd2e8165Aki Tuomi args = " BODY=8BITMIME";
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi else if (client->state.mail_body_7bit)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi args = " BODY=7BIT";
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi else
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi args = "";
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi lmtp_proxy_mail_from(client->proxy, t_strdup_printf(
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi "<%s>%s", client->state.mail_from, args));
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi if (lmtp_proxy_add_rcpt(client->proxy, address, &set) < 0)
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi client_send_line(client, ERRSTR_TEMP_REMOTE_FAILURE);
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi else
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi client_send_line(client, "250 2.1.5 OK");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi pool_unref(&pool);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return TRUE;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi}
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomistatic const char *lmtp_unescape_address(const char *name)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi{
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi string_t *str;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char *p;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (*name != '"')
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return name;
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi /* quoted-string local-part. drop the quotes unless there's a
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi '@' character inside or there's an error. */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi str = t_str_new(128);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi for (p = name+1; *p != '"'; p++) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (*p == '\0')
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return name;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (*p == '\\') {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (p[1] == '\0') {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* error */
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi return name;
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi p++;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (*p == '@')
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return name;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi str_append_c(str, *p);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi p++;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (*p != '@' && *p != '\0')
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return name;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi str_append(str, p);
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi return str_c(str);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi}
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomistatic void rcpt_address_parse(struct client *client, const char *address,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char **username_r, char *delim_r,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char **detail_r)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi{
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char *p, *domain;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi size_t idx;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi *username_r = address;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi *detail_r = "";
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (*client->unexpanded_lda_set->recipient_delimiter == '\0')
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi domain = strchr(address, '@');
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* first character that matches the recipient_delimiter */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi idx = strcspn(address, client->unexpanded_lda_set->recipient_delimiter);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi p = address[idx] != '\0' ? address + idx : NULL;
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi if (p != NULL && (domain == NULL || p < domain)) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi *delim_r = *p;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* user+detail@domain */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi *username_r = t_strdup_until(*username_r, p);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (domain == NULL)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi *detail_r = p+1;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi else {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi *detail_r = t_strdup_until(p+1, domain);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi *username_r = t_strconcat(*username_r, domain, NULL);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi}
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomistatic void lmtp_address_translate(struct client *client, const char **address)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi{
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char *transpos = client->lmtp_set->lmtp_address_translate;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char *p, *nextstr, *addrpos = *address;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi size_t len;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi string_t *username, *domain, *dest = NULL;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (*transpos == '\0')
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi username = t_str_new(64);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi domain = t_str_new(64);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* check that string matches up to the first '%' */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi p = strchr(transpos, '%');
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi if (p == NULL)
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi len = strlen(transpos);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi else
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi len = p-transpos;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (strncmp(transpos, addrpos, len) != 0)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi transpos += len;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi addrpos += len;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi while (*transpos != '\0') {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi switch (transpos[1]) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi case 'n':
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi case 'u':
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi dest = username;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi break;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi case 'd':
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi dest = domain;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi break;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi default:
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi transpos += 2;
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi /* find where the next string starts */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (*transpos == '\0') {
03c6532fe7aa3ddae23c99ff6bec78d8dd2e8165Aki Tuomi str_append(dest, addrpos);
03c6532fe7aa3ddae23c99ff6bec78d8dd2e8165Aki Tuomi break;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi p = strchr(transpos, '%');
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (p == NULL)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi nextstr = transpos;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi else
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi nextstr = t_strdup_until(transpos, p);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi p = strstr(addrpos, nextstr);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (p == NULL)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi str_append_n(dest, addrpos, p-addrpos);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
03c6532fe7aa3ddae23c99ff6bec78d8dd2e8165Aki Tuomi len = strlen(nextstr);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi transpos += len;
03c6532fe7aa3ddae23c99ff6bec78d8dd2e8165Aki Tuomi addrpos = p + len;
03c6532fe7aa3ddae23c99ff6bec78d8dd2e8165Aki Tuomi }
03c6532fe7aa3ddae23c99ff6bec78d8dd2e8165Aki Tuomi str_append_c(username, '@');
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi if (domain != NULL)
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi str_append_str(username, domain);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi *address = str_c(username);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi}
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomistatic void
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomiclient_send_line_overquota(struct client *client,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const struct mail_recipient *rcpt, const char *error)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi{
f207fb0f2fd3113aa9a9f7911fb12d94dce19dffAki Tuomi struct lda_settings *lda_set =
f207fb0f2fd3113aa9a9f7911fb12d94dce19dffAki Tuomi mail_storage_service_user_get_set(rcpt->service_user)[1];
f207fb0f2fd3113aa9a9f7911fb12d94dce19dffAki Tuomi
f207fb0f2fd3113aa9a9f7911fb12d94dce19dffAki Tuomi client_send_line(client, "%s <%s> %s", lda_set->quota_full_tempfail ?
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi "452 4.2.2" : "552 5.2.2", rcpt->address, error);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi}
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomistatic int
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomilmtp_rcpt_to_is_over_quota(struct client *client,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const struct mail_recipient *rcpt)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi{
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi struct mail_user *user;
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi struct mail_namespace *ns;
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi struct mailbox *box;
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi struct mailbox_status status;
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi const char *errstr;
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi enum mail_error error;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi int ret;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (!client->lmtp_set->lmtp_rcpt_check_quota)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return 0;
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi
5f1d689131a75c39f064cbd4202373e7edf78f18Josef 'Jeff' Sipek ret = mail_storage_service_next(storage_service,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi rcpt->service_user, &user, &errstr);
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi if (ret < 0) {
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi i_error("Failed to initialize user %s: %s", rcpt->address, errstr);
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi return -1;
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi ns = mail_namespace_find_inbox(user->namespaces);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi box = mailbox_alloc(ns->list, "INBOX", 0);
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi ret = mailbox_get_status(box, STATUS_CHECK_OVER_QUOTA, &status);
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi if (ret < 0) {
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi errstr = mailbox_get_last_error(box, &error);
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi if (error == MAIL_ERROR_NOQUOTA) {
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi client_send_line_overquota(client, rcpt, errstr);
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi ret = 1;
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi } else {
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi i_error("mailbox_get_status(%s, STATUS_CHECK_OVER_QUOTA) "
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi "failed: %s",
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi mailbox_get_vname(box),
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi mailbox_get_last_internal_error(box, NULL));
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi }
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi }
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi mailbox_free(&box);
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi mail_user_unref(&user);
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi return ret;
204afc1f4f37a4f1cb53ff44b993a661cc45bf5dAki Tuomi}
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomistatic bool cmd_rcpt_finish(struct client *client, struct mail_recipient *rcpt)
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainen{
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainen int ret;
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainen
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainen if ((ret = lmtp_rcpt_to_is_over_quota(client, rcpt)) != 0) {
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi if (ret < 0) {
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainen rcpt->address);
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi }
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi mail_storage_service_user_unref(&rcpt->service_user);
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi return FALSE;
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainen }
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi array_append(&client->state.rcpt_to, &rcpt, 1);
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainen client_send_line(client, "250 2.1.5 OK");
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi return TRUE;
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi}
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainenstatic void rcpt_anvil_lookup_callback(const char *reply, void *context)
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi{
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi struct mail_recipient *rcpt = context;
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi struct client *client = rcpt->client;
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi const struct mail_storage_service_input *input;
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainen unsigned int parallel_count = 0;
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainen
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainen rcpt->anvil_query = NULL;
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi if (reply == NULL) {
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainen /* lookup failed */
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi } else if (str_to_uint(reply, &parallel_count) < 0) {
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi i_error("Invalid reply from anvil: %s", reply);
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi }
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainen
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi if (parallel_count >= client->lmtp_set->lmtp_user_concurrency_limit) {
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainen client_send_line(client, ERRSTR_TEMP_USERDB_FAIL_PREFIX
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainen "Too many concurrent deliveries for user",
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainen rcpt->address);
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainen mail_storage_service_user_unref(&rcpt->service_user);
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi } else if (cmd_rcpt_finish(client, rcpt)) {
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi rcpt->anvil_connect_sent = TRUE;
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi input = mail_storage_service_user_get_input(rcpt->service_user);
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi master_service_anvil_send(master_service, t_strconcat(
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi "CONNECT\t", my_pid, "\t", master_service_get_name(master_service),
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi "/", input->username, "\n", NULL));
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi }
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi client_io_reset(client);
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi client_input_handle(client);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi}
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi
004be038dfe290f71e3d4a4b14d88673e8b55fb2Timo Sirainenint cmd_rcpt(struct client *client, const char *args)
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi{
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi struct mail_recipient *rcpt;
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi struct mail_storage_service_input input;
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi const char *params, *address, *username, *detail;
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi const char *const *argv;
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi const char *error = NULL;
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi char delim = '\0';
004be038dfe290f71e3d4a4b14d88673e8b55fb2Timo Sirainen int ret = 0;
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi if (client->state.mail_from == NULL) {
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi client_send_line(client, "503 5.5.1 MAIL needed first");
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi return 0;
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi }
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi if (strncasecmp(args, "TO:", 3) != 0 ||
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi parse_address(args + 3, &address, &params) < 0) {
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi client_send_line(client, "501 5.5.4 Invalid parameters");
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi return 0;
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi }
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi rcpt = p_new(client->state_pool, struct mail_recipient, 1);
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi rcpt->client = client;
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi address = lmtp_unescape_address(address);
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi argv = t_strsplit(params, " ");
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi for (; *argv != NULL; argv++) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (strncasecmp(*argv, "ORCPT=", 6) == 0) {
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi rcpt->params.dsn_orcpt = parse_xtext(client, *argv + 6);
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi } else {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client_send_line(client, "501 5.5.4 Unsupported options");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi return 0;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi }
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi rcpt_address_parse(client, address, &username, &delim, &detail);
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi
67574bb43936e39d5be32626e986df6e787a296fAki Tuomi client_state_set(client, "RCPT TO", address);
67574bb43936e39d5be32626e986df6e787a296fAki Tuomi
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (client->lmtp_set->lmtp_proxy) {
if (client_proxy_rcpt(client, address, username, detail, delim,
&rcpt->params))
return 0;
}
/* Use a unique session_id for each mail delivery. This is especially
important for stats process to not see duplicate sessions. */
if (array_count(&client->state.rcpt_to) == 0)
rcpt->session_id = client->state.session_id;
else {
rcpt->session_id =
p_strdup_printf(client->state_pool, "%s:%u",
client->state.session_id,
array_count(&client->state.rcpt_to)+1);
}
i_zero(&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;
input.session_id = rcpt->session_id;
ret = mail_storage_service_lookup(storage_service, &input,
&rcpt->service_user, &error);
if (ret < 0) {
i_error("Failed to lookup user %s: %s", username, error);
client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL, address);
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);
mail_storage_service_user_unref(&rcpt->service_user);
return 0;
}
lmtp_address_translate(client, &address);
rcpt->address = p_strdup(client->state_pool, address);
rcpt->detail = p_strdup(client->state_pool, detail);
if (client->lmtp_set->lmtp_user_concurrency_limit == 0) {
(void)cmd_rcpt_finish(client, rcpt);
return 0;
} else {
/* NOTE: username may change as the result of the userdb
lookup. Look up the new one via service_user. */
const struct mail_storage_service_input *input =
mail_storage_service_user_get_input(rcpt->service_user);
const char *query = t_strconcat("LOOKUP\t",
master_service_get_name(master_service),
"/", str_tabescape(input->username), NULL);
io_remove(&client->io);
rcpt->anvil_query = anvil_client_query(anvil, query,
rcpt_anvil_lookup_callback, rcpt);
/* stop processing further commands while anvil query is
pending */
return rcpt->anvil_query == NULL ? 0 : -1;
}
}
int cmd_quit(struct client *client, const char *args ATTR_UNUSED)
{
client_send_line(client, "221 2.0.0 OK");
/* don't log the (state name) for successful QUITs */
i_info("Disconnect from %s: Successful quit", client_remote_id(client));
client->disconnected = TRUE;
client_destroy(client, NULL, NULL);
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, "RSET");
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 bool orcpt_get_valid_rfc822(const char *orcpt, const char **addr_r)
{
if (orcpt == NULL || strncasecmp(orcpt, "rfc822;", 7) != 0)
return FALSE;
/* FIXME: we should verify the address further */
*addr_r = orcpt + 7;
return TRUE;
}
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;
const struct mail_storage_settings *mail_set;
struct lda_settings *lda_set;
struct mail_namespace *ns;
struct setting_parser_context *set_parser;
struct timeval delivery_time_started;
void **sets;
const char *line, *error, *username;
string_t *str;
enum mail_error mail_error;
int ret;
input = mail_storage_service_user_get_input(rcpt->service_user);
username = t_strdup(input->username);
mail_set = mail_storage_service_user_get_mail_set(rcpt->service_user);
set_parser = mail_storage_service_user_get_settings_parser(rcpt->service_user);
if (client->proxy_timeout_secs > 0 &&
(mail_set->mail_max_lock_timeout == 0 ||
mail_set->mail_max_lock_timeout > client->proxy_timeout_secs)) {
/* set lock timeout waits to be less than when proxy has
advertised that it's going to timeout the connection.
this avoids duplicate deliveries in case the delivery
succeeds after the proxy has already disconnected from us. */
line = t_strdup_printf("mail_max_lock_timeout=%u",
client->proxy_timeout_secs <= 1 ? 1 :
client->proxy_timeout_secs-1);
if (settings_parse_line(set_parser, line) < 0)
i_unreached();
}
/* get the timestamp before user is created, since it starts the I/O */
io_loop_time_refresh();
delivery_time_started = ioloop_timeval;
client_state_set(client, "DATA", username);
i_set_failure_prefix("lmtp(%s, %s): ", my_pid, username);
if (mail_storage_service_next(storage_service, rcpt->service_user,
&client->state.dest_user, &error) < 0) {
i_error("Failed to initialize user: %s", error);
client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
rcpt->address);
return -1;
}
sets = mail_storage_service_user_get_set(rcpt->service_user);
lda_set = sets[1];
if (settings_var_expand(&lda_setting_parser_info, lda_set, client->pool,
mail_user_var_expand_table(client->state.dest_user), &error) <= 0) {
i_error("Failed to expand settings: %s", error);
client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
rcpt->address);
return -1;
}
str = t_str_new(256);
if (var_expand_with_funcs(str, client->state.dest_user->set->mail_log_prefix,
mail_user_var_expand_table(client->state.dest_user),
mail_user_var_expand_func_table,
client->state.dest_user, &error) <= 0) {
i_error("Failed to expand mail_log_prefix=%s: %s",
client->state.dest_user->set->mail_log_prefix, error);
client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
rcpt->address);
return -1;
}
i_set_failure_prefix("%s", str_c(str));
i_zero(&dctx);
dctx.session = session;
dctx.pool = session->pool;
dctx.set = lda_set;
dctx.timeout_secs = LDA_SUBMISSION_TIMEOUT_SECS;
dctx.session_id = rcpt->session_id;
dctx.src_mail = src_mail;
dctx.src_envelope_sender = client->state.mail_from;
dctx.dest_user = client->state.dest_user;
dctx.session_time_msecs =
timeval_diff_msecs(&client->state.data_end_timeval,
&client->state.mail_from_timeval);
dctx.delivery_time_started = delivery_time_started;
if (orcpt_get_valid_rfc822(rcpt->params.dsn_orcpt, &dctx.dest_addr)) {
/* used ORCPT */
} else 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, rcpt->session_id);
ret = 0;
} else if (dctx.tempfail_error != NULL) {
client_send_line(client, "451 4.2.0 <%s> %s",
rcpt->address, dctx.tempfail_error);
ret = -1;
} else if (storage != NULL) {
error = mail_storage_get_last_error(storage, &mail_error);
if (mail_error == MAIL_ERROR_NOQUOTA) {
client_send_line_overquota(client, rcpt, error);
} else {
client_send_line(client, "451 4.2.0 <%s> %s",
rcpt->address, error);
}
ret = -1;
} else {
/* This shouldn't happen */
i_error("BUG: Saving failed to unknown storage");
client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
rcpt->address);
ret = -1;
}
return ret;
}
static bool client_deliver_next(struct client *client, struct mail *src_mail,
struct mail_deliver_session *session)
{
struct mail_recipient *const *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);
client_state_set(client, "DATA", "");
i_set_failure_prefix("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)
{
struct mail_recipient *const *rcptp;
array_foreach(&client->state.rcpt_to, rcptp) {
client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
(*rcptp)->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);
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_set_name(cinput, "<lmtp DATA>");
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_internal_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);
if (chdir(base_dir) < 0)
i_error("chdir(%s) failed: %m", base_dir);
}
}
static void client_input_data_finish(struct client *client)
{
client_io_reset(client);
client_state_reset(client, "DATA finished");
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);
void **sets;
const struct lmtp_settings *lmtp_set;
const char *host, *rcpt_to = NULL;
if (array_count(&client->state.rcpt_to) == 1) {
struct mail_recipient *const *rcptp =
array_idx(&client->state.rcpt_to, 0);
sets = mail_storage_service_user_get_set((*rcptp)->service_user);
lmtp_set = sets[2];
switch (lmtp_set->parsed_lmtp_hdr_delivery_address) {
case LMTP_HDR_DELIVERY_ADDRESS_NONE:
break;
case LMTP_HDR_DELIVERY_ADDRESS_FINAL:
rcpt_to = (*rcptp)->address;
break;
case LMTP_HDR_DELIVERY_ADDRESS_ORIGINAL:
if (!orcpt_get_valid_rfc822((*rcptp)->params.dsn_orcpt,
&rcpt_to))
rcpt_to = (*rcptp)->address;
break;
}
}
/* 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);
host = net_ip2addr(&client->remote_ip);
if (host[0] != '\0')
str_printfa(str, " ([%s])", host);
str_append(str, "\r\n");
if (client->ssl_iostream != NULL) {
str_printfa(str, "\t(using %s)\r\n",
ssl_iostream_get_security_string(client->ssl_iostream));
}
str_printfa(str, "\tby %s 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 void client_input_data_write(struct client *client)
{
struct istream *input;
/* 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);
client->state.data_end_timeval = ioloop_timeval;
input = client_get_input(client);
if (array_count(&client->state.rcpt_to) != 0)
client_input_data_write_local(client, input);
if (client->proxy != NULL) {
client_state_set(client, "DATA", "proxying");
lmtp_proxy_start(client->proxy, input,
client_proxy_finish, client);
} else {
client_input_data_finish(client);
}
i_stream_unref(&input);
}
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;
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 (i_unlink(str_c(path)) < 0) {
/* shouldn't happen.. */
i_close_fd(&fd);
return -1;
}
state->mail_data_fd = fd;
state->mail_data_output = o_stream_create_fd_file(fd, 0, FALSE);
o_stream_set_name(state->mail_data_output, str_c(path));
o_stream_cork(state->mail_data_output);
o_stream_nsend(state->mail_data_output,
state->mail_data->data, state->mail_data->used);
o_stream_nsend(client->state.mail_data_output, data, size);
if (o_stream_nfinish(client->state.mail_data_output) < 0) {
i_error("write(%s) failed: %s", str_c(path),
o_stream_get_error(client->state.mail_data_output));
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->stream_errno != 0) {
/* client probably disconnected */
client_destroy(client, NULL, NULL);
return;
}
/* the ending "." line was seen. begin saving the mail. */
client_input_data_write(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");
/* send the DATA reply immediately before we start handling any data */
o_stream_uncork(client->output);
io_remove(&client->io);
client_state_set(client, "DATA", "");
client->io = io_add(client->fd_in, IO_READ, client_input_data, client);
client_input_data_handle(client);
return -1;
}
int cmd_xclient(struct client *client, const char *args)
{
const char *const *tmp;
struct ip_addr remote_ip;
in_port_t remote_port = 0;
unsigned int ttl = UINT_MAX, timeout_secs = 0;
bool args_ok = TRUE;
if (!client_is_trusted(client)) {
client_send_line(client, "550 You are not from trusted IP");
return 0;
}
remote_ip.family = 0;
for (tmp = t_strsplit(args, " "); *tmp != NULL; tmp++) {
if (strncasecmp(*tmp, "ADDR=", 5) == 0) {
if (net_addr2ip(*tmp + 5, &remote_ip) < 0)
args_ok = FALSE;
} else if (strncasecmp(*tmp, "PORT=", 5) == 0) {
if (net_str2port(*tmp + 5, &remote_port) < 0)
args_ok = FALSE;
} else if (strncasecmp(*tmp, "TTL=", 4) == 0) {
if (str_to_uint(*tmp + 4, &ttl) < 0)
args_ok = FALSE;
} else if (strncasecmp(*tmp, "TIMEOUT=", 8) == 0) {
if (str_to_uint(*tmp + 8, &timeout_secs) < 0)
args_ok = FALSE;
}
}
if (!args_ok) {
client_send_line(client, "501 Invalid parameters");
return 0;
}
/* args ok, set them and reset the state */
client_state_reset(client, "XCLIENT");
if (remote_ip.family != 0)
client->remote_ip = remote_ip;
if (remote_port != 0)
client->remote_port = remote_port;
if (ttl != UINT_MAX)
client->proxy_ttl = ttl;
client->proxy_timeout_secs = timeout_secs;
client_send_line(client, "220 %s %s", client->my_domain,
client->lmtp_set->login_greeting);
return 0;
}