Exemple #1
0
static int
driver_mysql_transaction_commit_s(struct sql_transaction_context *_ctx,
				  const char **error_r)
{
	struct mysql_transaction_context *ctx =
		(struct mysql_transaction_context *)_ctx;
	struct mysql_db *db = (struct mysql_db *)_ctx->db;
	int ret = 1;

	*error_r = NULL;

	if (_ctx->head != NULL) {
		ret = driver_mysql_try_commit_s(ctx);
		*error_r = t_strdup(ctx->error);
		if (ret == 0) {
			i_info("%s: Disconnected from database, "
			       "retrying commit", db->dbname);
			if (sql_connect(_ctx->db) >= 0) {
				ctx->failed = FALSE;
				ret = driver_mysql_try_commit_s(ctx);
			}
		}
	}
	sql_transaction_rollback(&_ctx);
	return ret <= 0 ? -1 : 0;
}
static void client_input(struct client *client)
{
	struct istream *input;
	struct ostream *output;
	unsigned char buf[1024];
	ssize_t ret;

	ret = read(STDIN_FILENO, buf, sizeof(buf));
	if (ret == 0) {
		if (client->compressed) {
			master_service_stop(master_service);
			return;
		}
		/* start compression */
		i_info("<Compression started>");
		input = i_stream_create_deflate(client->input, TRUE);
		output = o_stream_create_deflate(client->output, 6);
		i_stream_unref(&client->input);
		o_stream_unref(&client->output);
		client->input = input;
		client->output = output;
		client->compressed = TRUE;
		return;
	}
	if (ret < 0)
		i_fatal("read(stdin) failed: %m");

	o_stream_nsend(client->output, buf, ret);
}
Exemple #3
0
static int
index_mailbox_precache(struct master_connection *conn, struct mailbox *box)
{
    struct mail_storage *storage = mailbox_get_storage(box);
    const char *username = mail_storage_get_user(storage)->username;
    const char *box_vname = mailbox_get_vname(box);
    struct mailbox_status status;
    struct mailbox_transaction_context *trans;
    struct mail_search_args *search_args;
    struct mail_search_context *ctx;
    struct mail *mail;
    struct mailbox_metadata metadata;
    uint32_t seq;
    char percentage_str[2+1+1];
    unsigned int counter = 0, max, percentage, percentage_sent = 0;
    int ret = 0;

    if (mailbox_get_metadata(box, MAILBOX_METADATA_PRECACHE_FIELDS,
                             &metadata) < 0 ||
            mailbox_get_status(box, STATUS_MESSAGES | STATUS_LAST_CACHED_SEQ,
                               &status) < 0)
        return -1;
    seq = status.last_cached_seq + 1;

    trans = mailbox_transaction_begin(box, MAILBOX_TRANSACTION_FLAG_NO_CACHE_DEC);
    search_args = mail_search_build_init();
    mail_search_build_add_seqset(search_args, seq, status.messages);
    ctx = mailbox_search_init(trans, search_args, NULL,
                              metadata.precache_fields, NULL);
    mail_search_args_unref(&search_args);

    max = status.messages - seq + 1;
    while (mailbox_search_next(ctx, &mail)) {
        mail_precache(mail);
        if (++counter % 100 == 0) {
            percentage = counter*100 / max;
            if (percentage != percentage_sent && percentage < 100) {
                percentage_sent = percentage;
                if (i_snprintf(percentage_str,
                               sizeof(percentage_str), "%u\n",
                               percentage) < 0)
                    i_unreached();
                (void)write_full(conn->fd, percentage_str,
                                 strlen(percentage_str));
            }
            indexer_worker_refresh_proctitle(username, box_vname,
                                             counter, max);
        }
    }
    if (mailbox_search_deinit(&ctx) < 0)
        ret = -1;
    if (mailbox_transaction_commit(&trans) < 0)
        ret = -1;
    if (ret == 0) {
        i_info("Indexed %u messages in %s",
               counter, mailbox_get_vname(box));
    }
    return ret;
}
Exemple #4
0
static void
fts_tika_parser_response(const struct http_response *response,
			 struct tika_fts_parser *parser)
{
	i_assert(parser->payload == NULL);

	switch (response->status) {
	case 200:
		/* read response */
		if (response->payload == NULL)
			parser->payload = i_stream_create_from_data("", 0);
		else {
			i_stream_ref(response->payload);
			parser->payload = response->payload;
		}
		break;
	case 204: /* empty response */
	case 415: /* Unsupported Media Type */
	case 422: /* Unprocessable Entity */
		if (parser->user->mail_debug) {
			i_debug("fts_tika: PUT %s failed: %u %s",
				mail_user_plugin_getenv(parser->user, "fts_tika"),
				response->status, response->reason);
		}
		parser->payload = i_stream_create_from_data("", 0);
		break;
	case 500:
		/* Server Error - the problem could be anything (in Tika or
		   HTTP server or proxy) and might be retriable, but Tika has
		   trouble processing some documents and throws up this error
		   every time for those documents.

		   Unfortunately we can't easily re-send the request here,
		   because we would have to re-send the entire payload, which
		   isn't available anymore here. So we'd need to indicate
		   in fts_parser_deinit() that we want to retry.
		   FIXME: do this in v2.3. For now we'll just ignore it. */
		i_info("fts_tika: PUT %s failed: %u %s - ignoring",
		       mail_user_plugin_getenv(parser->user, "fts_tika"),
		       response->status, response->reason);
		parser->payload = i_stream_create_from_data("", 0);
		break;

	default:
		i_error("fts_tika: PUT %s failed: %u %s",
			mail_user_plugin_getenv(parser->user, "fts_tika"),
			response->status, response->reason);
		parser->failed = TRUE;
		break;
	}
	parser->http_req = NULL;
	io_loop_stop(current_ioloop);
}
Exemple #5
0
static inline void
http_server_request_client_error(struct http_server_request *req,
	const char *format, ...)
{
	va_list args;

	va_start(args, format);
	i_info("http-server: request %s: %s",
		http_server_request_label(req),
		t_strdup_vprintf(format, args));
	va_end(args);
}
Exemple #6
0
static void
fts_tika_parser_response(const struct http_response *response,
                         struct tika_fts_parser *parser)
{
    i_assert(parser->payload == NULL);

    switch (response->status) {
    case 200:
        /* read response */
        if (response->payload == NULL)
            parser->payload = i_stream_create_from_data("", 0);
        else {
            i_stream_ref(response->payload);
            parser->payload = response->payload;
        }
        break;
    case 204: /* empty response */
    case 415: /* Unsupported Media Type */
    case 422: /* Unprocessable Entity */
        if (parser->user->mail_debug) {
            i_debug("fts_tika: PUT %s failed: %u %s",
                    mail_user_plugin_getenv(parser->user, "fts_tika"),
                    response->status, response->reason);
        }
        parser->payload = i_stream_create_from_data("", 0);
        break;
    case 500:
        /* Server Error - the problem could be anything (in Tika or
           HTTP server or proxy) and might be retriable, but Tika has
           trouble processing some documents and throws up this error
           every time for those documents. So we try retrying this a
           couple of times, but if that doesn't work we'll just ignore
           it. */
        if (http_client_request_try_retry(parser->http_req))
            return;
        i_info("fts_tika: PUT %s failed: %u %s - ignoring",
               mail_user_plugin_getenv(parser->user, "fts_tika"),
               response->status, response->reason);
        parser->payload = i_stream_create_from_data("", 0);
        break;

    default:
        i_error("fts_tika: PUT %s failed: %u %s",
                mail_user_plugin_getenv(parser->user, "fts_tika"),
                response->status, response->reason);
        parser->failed = TRUE;
        break;
    }
    parser->http_req = NULL;
    io_loop_stop(current_ioloop);
}
Exemple #7
0
static void login_access_callback(bool success, void *context)
{
	struct login_access_lookup *lookup = context;

	if (!success) {
		i_info("access(%s): Client refused (rip=%s)",
		       *lookup->next_socket,
		       net_ip2addr(&lookup->conn.remote_ip));
		login_access_lookup_free(lookup);
	} else {
		lookup->next_socket++;
		login_access_lookup_next(lookup);
	}
}
Exemple #8
0
static int
openssl_iostream_verify_client_cert(int preverify_ok, X509_STORE_CTX *ctx)
{
	int ssl_extidx = SSL_get_ex_data_X509_STORE_CTX_idx();
	SSL *ssl;
	struct ssl_iostream *ssl_io;
	char certname[1024];
	X509_NAME *subject;

	ssl = X509_STORE_CTX_get_ex_data(ctx, ssl_extidx);
	ssl_io = SSL_get_ex_data(ssl, dovecot_ssl_extdata_index);
	ssl_io->cert_received = TRUE;

	subject = X509_get_subject_name(X509_STORE_CTX_get_current_cert(ctx));
	if (subject == NULL ||
	    X509_NAME_oneline(subject, certname, sizeof(certname)) == NULL)
		certname[0] = '\0';
	else
		certname[sizeof(certname)-1] = '\0'; /* just in case.. */
	if (preverify_ok == 0) {
		openssl_iostream_set_error(ssl_io, t_strdup_printf(
			"Received invalid SSL certificate: %s: %s",
			X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx)), certname));
		if (ssl_io->verbose_invalid_cert)
			i_info("%s", ssl_io->last_error);
	} else if (ssl_io->verbose) {
		i_info("Received valid SSL certificate: %s", certname);
	}
	if (preverify_ok == 0) {
		ssl_io->cert_broken = TRUE;
		if (!ssl_io->allow_invalid_cert) {
			ssl_io->handshake_failed = TRUE;
			return 0;
		}
	}
	return 1;
}
Exemple #9
0
static int
openssl_iostream_verify_client_cert(int preverify_ok, X509_STORE_CTX *ctx)
{
    int ssl_extidx = SSL_get_ex_data_X509_STORE_CTX_idx();
    SSL *ssl;
    struct ssl_iostream *ssl_io;

    ssl = X509_STORE_CTX_get_ex_data(ctx, ssl_extidx);
    ssl_io = SSL_get_ex_data(ssl, dovecot_ssl_extdata_index);
    ssl_io->cert_received = TRUE;

    if (ssl_io->verbose ||
            (ssl_io->verbose_invalid_cert && !preverify_ok)) {
        char buf[1024];
        X509_NAME *subject;

        subject = X509_get_subject_name(ctx->current_cert);
        if (subject == NULL ||
                X509_NAME_oneline(subject, buf, sizeof(buf)) == NULL)
            buf[0] = '\0';
        else
            buf[sizeof(buf)-1] = '\0'; /* just in case.. */
        if (!preverify_ok) {
            i_info("Invalid certificate: %s: %s",
                   X509_verify_cert_error_string(ctx->error), buf);
        } else {
            i_info("Valid certificate: %s", buf);
        }
    }
    if (!preverify_ok) {
        ssl_io->cert_broken = TRUE;
        if (ssl_io->require_valid_cert)
            return 0;
    }
    return 1;
}
Exemple #10
0
static void client_input_error(struct login_access_lookup *lookup)
{
	char c;
	int ret;

	ret = recv(lookup->conn.fd, &c, 1, MSG_PEEK);
	if (ret <= 0) {
		i_info("access(%s): Client disconnected during lookup (rip=%s)",
		       *lookup->next_socket,
		       net_ip2addr(&lookup->conn.remote_ip));
		login_access_lookup_free(lookup);
	} else {
		/* actual input. stop listening until lookup is done. */
		io_remove(&lookup->io);
	}
}
void client_disconnect(struct client *client, const char *reason)
{
	i_assert(reason != NULL);

	if (client->disconnected)
		return;

	i_info("Disconnected: %s %s", reason, client_stats(client));
	client->disconnected = TRUE;
	o_stream_nflush(client->output);
	o_stream_uncork(client->output);

	i_stream_close(client->input);
	o_stream_close(client->output);

	if (client->to_idle != NULL)
		timeout_remove(&client->to_idle);
	client->to_idle = timeout_add(0, client_destroy_timeout, client);
}
void client_proxy_log_failure(struct client *client, const char *line)
{
	string_t *str = t_str_new(128);

	str_printfa(str, "proxy(%s): Login failed to %s:%u",
		    client->virtual_user,
		    login_proxy_get_host(client->login_proxy),
		    login_proxy_get_port(client->login_proxy));
	if (strcmp(client->virtual_user, client->proxy_user) != 0) {
		/* remote username is different, log it */
		str_append_c(str, '/');
		str_append(str, client->proxy_user);
	}
	if (client->proxy_master_user != NULL)
		str_printfa(str, " (master %s)", client->proxy_master_user);
	str_append(str, ": ");
	str_append(str, line);
	i_info("%s", str_c(str));
}
static int xaps_register(const char *aps_account_id, const char *aps_device_token, const char *aps_subtopic, const char *dovecot_username, const struct imap_arg *dovecot_mailboxes)
{
  /*
   * Construct our request.
   */

  string_t *req = t_str_new(2048);
  str_append(req, "XAPS REGISTER ");
  str_append(req, "{\"aps-account-id\":\"");
  json_append_escaped(req, aps_account_id);
  str_append(req, "\",\"aps-device-token\":\"");
  json_append_escaped(req, aps_device_token);
  str_append(req, "\",\"aps-subtopic\":\"");
  json_append_escaped(req, aps_subtopic);
  str_append(req, "\",\"dovecot-username\":\"");
  json_append_escaped(req, dovecot_username);
  str_append(req, "\",");

  if (dovecot_mailboxes == NULL) {
    str_append(req, "\"dovecot-mailboxes\":[\"INBOX\"]");
  } else {
    str_append(req, "\"dovecot-mailboxes\":[");
    int next = 0;
    for (; !IMAP_ARG_IS_EOL(dovecot_mailboxes); dovecot_mailboxes++) {
      const char *mailbox;
      if (!imap_arg_get_astring(&dovecot_mailboxes[0], &mailbox)) {
        return -1;
      }
      if (next) {
        str_append(req, ",");
      }
      str_append(req, "\"");
      json_append_escaped(req, mailbox);
      str_append(req, "\"");
      next = 1;
    }
    str_append(req, "]");
  }
  str_append(req, "}");
  i_info(str_c(req));
  return 0;
}
static void stats_dump(const char *path, const char *cmd)
{
	struct istream *input;
	const char *const *args;
	unsigned int i;
	int fd;

	fd = doveadm_connect(path);
	net_set_nonblock(fd, FALSE);

	input = i_stream_create_fd(fd, (size_t)-1, TRUE);
	if (write_full(fd, cmd, strlen(cmd)) < 0)
		i_fatal("write(%s) failed: %m", path);

	/* read header */
	args = read_next_line(input);
	if (args == NULL)
		i_fatal("read(%s) unexpectedly disconnected", path);
	if (*args == NULL)
		i_info("no statistics available");
	else {
		for (; *args != NULL; args++)
			doveadm_print_header_simple(*args);

		/* read lines */
		do {
			T_BEGIN {
				args = read_next_line(input);
				if (args != NULL && args[0] == NULL)
					args = NULL;
				if (args != NULL) {
					for (i = 0; args[i] != NULL; i++)
						doveadm_print(args[i]);
				}
			} T_END;
		} while (args != NULL);
	}
	if (input->stream_errno != 0)
		i_fatal("read(%s) failed: %m", path);
	i_stream_destroy(&input);
}
Exemple #15
0
static void server_input(struct client *client)
{
	const unsigned char *data;
	size_t size;

	if (i_stream_read(client->input) == -1) {
		if (client->input->stream_errno != 0) {
			i_fatal("read(server) failed: %s",
				i_stream_get_error(client->input));
		}

		i_info("Server disconnected");
		master_service_stop(master_service);
		return;
	}

	data = i_stream_get_data(client->input, &size);
	if (write(STDOUT_FILENO, data, size) < 0)
		i_fatal("write(stdout) failed: %m");
	i_stream_skip(client->input, size);
}
Exemple #16
0
static void cmd_zlibconnect(int argc ATTR_UNUSED, char *argv[])
{
	struct client client;
	struct ip_addr *ips;
	unsigned int ips_count;
	in_port_t port = 143;
	int fd, ret;

	if (argv[1] == NULL ||
	    (argv[2] != NULL && net_str2port(argv[2], &port) < 0))
		help(&doveadm_cmd_zlibconnect);

	ret = net_gethostbyname(argv[1], &ips, &ips_count);
	if (ret != 0) {
		i_fatal("Host %s lookup failed: %s", argv[1],
			net_gethosterror(ret));
	}

	if ((fd = net_connect_ip(&ips[0], port, NULL)) == -1)
		i_fatal("connect(%s, %u) failed: %m", argv[1], port);

	i_info("Connected to %s port %u. Ctrl-D starts compression",
	       net_ip2addr(&ips[0]), port);

	memset(&client, 0, sizeof(client));
	client.fd = fd;
	client.input = i_stream_create_fd(fd, (size_t)-1);
	client.output = o_stream_create_fd(fd, 0);
	o_stream_set_no_error_handling(client.output, TRUE);
	client.io_client = io_add(STDIN_FILENO, IO_READ, client_input, &client);
	client.io_server = io_add(fd, IO_READ, server_input, &client);
	master_service_run(master_service, NULL);
	io_remove(&client.io_client);
	io_remove(&client.io_server);
	i_stream_unref(&client.input);
	o_stream_unref(&client.output);
	if (close(fd) < 0)
		i_fatal("close() failed: %m");
}
Exemple #17
0
static int
master_service_haproxy_read(struct master_service_haproxy_conn *hpconn)
{
	/* reasonable max size for haproxy data */
	unsigned char rbuf[1500];
	const char *error;
	static union {
		unsigned char v1_data[HAPROXY_V1_MAX_HEADER_SIZE];
		struct {
			const struct haproxy_header_v2 hdr;
			const struct haproxy_data_v2 data;
		} v2;
	} buf;
	struct ip_addr *real_remote_ip = &hpconn->conn.remote_ip;
	int fd = hpconn->conn.fd;
	struct ip_addr local_ip, remote_ip;
	in_port_t local_port, remote_port;
	size_t size,i,want;
	ssize_t ret;
	enum haproxy_version_t version;

	/* the protocol specification explicitly states that the protocol header
	   must be sent as one TCP frame, meaning that we will get it in full
	   with the first recv() call.
	 */
	i_zero(&buf);
	i_zero(rbuf);

	/* see if there is a HAPROXY protocol command waiting */
	if ((ret = master_service_haproxy_recv(fd, &buf, sizeof(buf), MSG_PEEK))<=0) {
		if (ret < 0)
			i_info("haproxy: Client disconnected (rip=%s): %m",
			       net_ip2addr(real_remote_ip));
		return ret;
	/* see if there is a haproxy command, 8 is used later on as well */
	} else if (ret >= 8 && memcmp(buf.v1_data, "PROXY", 5) == 0) {
		/* fine */
		version = HAPROXY_VERSION_1;
	} else if ((size_t)ret >= sizeof(buf.v2.hdr) &&
		   memcmp(buf.v2.hdr.sig, haproxy_v2sig, sizeof(haproxy_v2sig)) == 0) {
		want = ntohs(buf.v2.hdr.len) + sizeof(buf.v2.hdr);
		if (want > sizeof(rbuf)) {
			i_error("haproxy: Client disconnected: Too long header (rip=%s)",
				net_ip2addr(real_remote_ip));
			return -1;
		}

		if ((ret = master_service_haproxy_recv(fd, rbuf, want, MSG_WAITALL))<=0) {
			if (ret < 0)
				i_info("haproxy: Client disconnected (rip=%s): %m",
				       net_ip2addr(real_remote_ip));
			return ret;
		}

		if (ret != (ssize_t)want) {
			i_info("haproxy: Client disconnected: Failed to read full header (rip=%s)",
				net_ip2addr(real_remote_ip));
			return -1;
		}
		memcpy(&buf, rbuf, sizeof(buf));
		version = HAPROXY_VERSION_2;
	} else {
		/* it wasn't haproxy data */
		i_error("haproxy: Client disconnected: "
			"Failed to read valid HAproxy data (rip=%s)",
			net_ip2addr(real_remote_ip));
		return -1;
	}

	/* don't update true connection data until we succeed */
	local_ip = hpconn->conn.local_ip;
	remote_ip = hpconn->conn.remote_ip;
	local_port = hpconn->conn.local_port;
	remote_port = hpconn->conn.remote_port;

	/* protocol version 2 */
	if (version == HAPROXY_VERSION_2) {
		const struct haproxy_header_v2 *hdr = &buf.v2.hdr;
		const struct haproxy_data_v2 *data = &buf.v2.data;
		size_t hdr_len;

		i_assert(ret >= (ssize_t)sizeof(buf.v2.hdr));

		if ((hdr->ver_cmd & 0xf0) != 0x20) {
			i_error("haproxy: Client disconnected: "
				"Unsupported protocol version (version=%02x, rip=%s)",
				(hdr->ver_cmd & 0xf0) >> 4,
				net_ip2addr(real_remote_ip));
			return -1;
		}

		hdr_len = ntohs(hdr->len);
		size = sizeof(*hdr) + hdr_len;
		/* keep tab of how much address data there really is because
		   because TLVs begin after that. */
		i = 0;

		if (ret < (ssize_t)size) {
			i_error("haproxy(v2): Client disconnected: "
				"Protocol payload length does not match header "
				"(got=%"PRIuSIZE_T", expect=%"PRIuSIZE_T", rip=%s)",
				(size_t)ret, size, net_ip2addr(real_remote_ip));
			return -1;
		}

		i += sizeof(*hdr);

		switch (hdr->ver_cmd & 0x0f) {
		case HAPROXY_CMD_LOCAL:
			/* keep local connection address for LOCAL */
			/*i_debug("haproxy(v2): Local connection (rip=%s)",
				net_ip2addr(real_remote_ip));*/
			break;
		case HAPROXY_CMD_PROXY:
			if ((hdr->fam & 0x0f) != HAPROXY_SOCK_STREAM) {
				/* UDP makes no sense currently */
				i_error("haproxy(v2): Client disconnected: "
					"Not using TCP (type=%02x, rip=%s)",
					(hdr->fam & 0x0f), net_ip2addr(real_remote_ip));
				return -1;
			}
			switch ((hdr->fam & 0xf0) >> 4) {
			case HAPROXY_AF_INET:
				/* IPv4 */
				if (hdr_len < sizeof(data->addr.ip4)) {
					i_error("haproxy(v2): Client disconnected: "
						"IPv4 data is incomplete (rip=%s)",
						net_ip2addr(real_remote_ip));
					return -1;
				}
				local_ip.family = AF_INET;
				local_ip.u.ip4.s_addr = data->addr.ip4.dst_addr;
				local_port = ntohs(data->addr.ip4.dst_port);
				remote_ip.family = AF_INET;
				remote_ip.u.ip4.s_addr = data->addr.ip4.src_addr;
				remote_port = ntohs(data->addr.ip4.src_port);
				i += sizeof(data->addr.ip4);
				break;
			case HAPROXY_AF_INET6:
				/* IPv6 */
				if (hdr_len < sizeof(data->addr.ip6)) {
					i_error("haproxy(v2): Client disconnected: "
						"IPv6 data is incomplete (rip=%s)",
						net_ip2addr(real_remote_ip));
					return -1;
				}
				local_ip.family = AF_INET6;
				memcpy(&local_ip.u.ip6.s6_addr, data->addr.ip6.dst_addr, 16);
				local_port = ntohs(data->addr.ip6.dst_port);
				remote_ip.family = AF_INET6;
				memcpy(&remote_ip.u.ip6.s6_addr, data->addr.ip6.src_addr, 16);
				remote_port = ntohs(data->addr.ip6.src_port);
				i += sizeof(data->addr.ip6);
				break;
			case HAPROXY_AF_UNSPEC:
			case HAPROXY_AF_UNIX:
				/* unsupported; ignored */
				i_error("haproxy(v2): Unsupported address family "
					"(family=%02x, rip=%s)", (hdr->fam & 0xf0) >> 4,
					net_ip2addr(real_remote_ip));
				break;
			default:
				/* unsupported; error */
				i_error("haproxy(v2): Client disconnected: "
					"Unknown address family "
					"(family=%02x, rip=%s)", (hdr->fam & 0xf0) >> 4,
					net_ip2addr(real_remote_ip));
				return -1;
			}
			break;
		default:
			i_error("haproxy(v2): Client disconnected: "
				"Invalid command (cmd=%02x, rip=%s)",
				(hdr->ver_cmd & 0x0f),
				net_ip2addr(real_remote_ip));
			return -1; /* not a supported command */
		}

		if (master_service_haproxy_parse_tlv(hpconn, rbuf+i, size-i, &error) < 0) {
			i_error("haproxy(v2): Client disconnected: "
				"Invalid TLV: %s (cmd=%02x, rip=%s)",
				error,
				(hdr->ver_cmd & 0x0f),
				net_ip2addr(real_remote_ip));
			return -1;
		}
	/* protocol version 1 (soon obsolete) */
	} else if (version == HAPROXY_VERSION_1) {
static bool cmd_xapplepushservice(struct client_command_context *cmd)
{
  /*
   * Parse arguments. We expect four key value pairs. We only take
   * those that we understand for version 2 of this extension.
   *
   * TODO: We are ignoring the mailboxes parameter for now and just
   * default to INBOX always.
   */

  const struct imap_arg *args;
  const char *arg_key, *arg_val;
  const char *aps_version = NULL, *aps_account_id = NULL, *aps_device_token = NULL, *aps_subtopic = NULL;

  if (!client_read_args(cmd, 0, 0, &args)) {
    client_send_command_error(cmd, "Invalid arguments.");
    return FALSE;
  }

  for (int i = 0; i < 4; i++) {
    if (!imap_arg_get_astring(&args[i*2+0], &arg_key)) {
      client_send_command_error(cmd, "Invalid arguments.");
      return FALSE;
    }

    if (!imap_arg_get_astring(&args[i*2+1], &arg_val)) {
      client_send_command_error(cmd, "Invalid arguments.");
      return FALSE;
    }

    if (strcasecmp(arg_key, "aps-version") == 0) {
      aps_version = arg_val;
    } else if (strcasecmp(arg_key, "aps-account-id") == 0) {
      aps_account_id = arg_val;
    } else if (strcasecmp(arg_key, "aps-device-token") == 0) {
      aps_device_token = arg_val;
    } else if (strcasecmp(arg_key, "aps-subtopic") == 0) {
      aps_subtopic = arg_val;
    }
  }

  /*
   * Check if this is a version we expect
   */

  if (!aps_version || (strcmp(aps_version, "1") != 0 && strcmp(aps_version, "2") != 0)) {
    client_send_command_error(cmd, "Unknown aps-version.");
    return FALSE;
  }

  /*
   * If this is version 2 then also need to grab the mailboxes, which
   * is a list of mailbox names.
   */

  const struct imap_arg *mailboxes = NULL;

  if (strcmp(aps_version, "2") == 0) {
    if (!imap_arg_get_astring(&args[8], &arg_key)) {
      client_send_command_error(cmd, "Invalid arguments.");
      return FALSE;
    }

    if (strcmp(arg_key, "mailboxes") != 0) {
      client_send_command_error(cmd, "Invalid arguments. (Expected mailboxes)");
      return FALSE;
    }

    if (!imap_arg_get_list(&args[9], &mailboxes)) {
      client_send_command_error(cmd, "Invalid arguments.");
      return FALSE;
    }
  }

  /*
   * Check if all of the parameters are there.
   */

  if (!aps_account_id || strlen(aps_account_id) == 0) {
    client_send_command_error(cmd, "Incomplete or empty aps-account-id parameter.");
    return FALSE;
  }

  if (!aps_device_token || strlen(aps_device_token) == 0) {
    client_send_command_error(cmd, "Incomplete or empty aps-device-token parameter.");
    return FALSE;
  }

  if (!aps_subtopic || strlen(aps_subtopic) == 0) {
    client_send_command_error(cmd, "Incomplete or empty aps-subtopic parameter.");
    return FALSE;
  }

  /*
   * Forward to the helper daemon. The helper will return the
   * aps-topic, which in reality is the subject of the certificate. I
   * think it is only used to make sure that the binding between the
   * client and the APS server and the IMAP server stays current.
   */

  struct client *client = cmd->client;
  struct mail_user *user = client->user;

  const char *aps_topic = mail_user_plugin_getenv(user, "xaps_topic");

  if (xaps_register(aps_account_id, aps_device_token, aps_subtopic, user->username, mailboxes) != 0) {
    client_send_command_error(cmd, "Registration failed.");
i_info("Registration failed.");
    return FALSE;
  }

  /*
   * Return success. We assume that aps_version and aps_topic do not
   * contain anything that needs to be escaped.
   */
  client_send_line(cmd->client,
    t_strdup_printf("* XAPPLEPUSHSERVICE aps-version \"%s\" aps-topic \"%s\"", aps_version, aps_topic));
  client_send_tagline(cmd, "OK XAPPLEPUSHSERVICE Registration successful.");


  return TRUE;
}
static int program_client_local_disconnect
(struct program_client *pclient, bool force)
{
	struct program_client_local *slclient = 
		(struct program_client_local *) pclient;
	pid_t pid = slclient->pid, ret;
	time_t runtime, timeout = 0;
	int status;
	
	if ( pid < 0 ) {
		/* program never started */
		pclient->exit_code = 0;
		return 0;
	}

	slclient->pid = -1;

	/* Calculate timeout */
	runtime = ioloop_time - pclient->start_time;
	if ( !force && pclient->set.input_idle_timeout_secs > 0 &&
		runtime < (time_t)pclient->set.input_idle_timeout_secs )
		timeout = pclient->set.input_idle_timeout_secs - runtime;

	if ( pclient->debug ) {
		i_debug("waiting for program `%s' to finish after %llu seconds",
			pclient->path, (unsigned long long int)runtime);
	}

	/* Wait for child to exit */
	force = force ||
		(timeout == 0 && pclient->set.input_idle_timeout_secs > 0);
	if ( !force ) {
		alarm(timeout);
		ret = waitpid(pid, &status, 0);
		alarm(0);
	}
	if ( force || ret < 0 ) {
		if ( !force && errno != EINTR ) {
			i_error("waitpid(%s) failed: %m", pclient->path);
			(void)kill(pid, SIGKILL);
			return -1;
		}

		/* Timed out */
		force = TRUE;
		if ( pclient->error == PROGRAM_CLIENT_ERROR_NONE )
			pclient->error = PROGRAM_CLIENT_ERROR_RUN_TIMEOUT;
		if ( pclient->debug ) {
			i_debug("program `%s' execution timed out after %llu seconds: "
				"sending TERM signal", pclient->path,
				(unsigned long long int)pclient->set.input_idle_timeout_secs);
		}

		/* Kill child gently first */
		if ( kill(pid, SIGTERM) < 0 ) {
			i_error("failed to send SIGTERM signal to program `%s'", pclient->path);
			(void)kill(pid, SIGKILL);
			return -1;
		} 
			
		/* Wait for it to die (give it some more time) */
		alarm(5);
		ret = waitpid(pid, &status, 0);
		alarm(0);
		if ( ret < 0 ) {
			if ( errno != EINTR ) {
				i_error("waitpid(%s) failed: %m", pclient->path);
				(void)kill(pid, SIGKILL); 
				return -1;
			}

			/* Timed out again */
			if ( pclient->debug ) {
				i_debug("program `%s' execution timed out: sending KILL signal",
					pclient->path);
			}

			/* Kill it brutally now */
			if ( kill(pid, SIGKILL) < 0 ) {
				i_error("failed to send SIGKILL signal to program `%s'",
					pclient->path);
				return -1;
			}

			/* Now it will die immediately */
			if ( waitpid(pid, &status, 0) < 0 ) {
				i_error("waitpid(%s) failed: %m", pclient->path);
				return -1;
			}
		}
	}
	
	/* Evaluate child exit status */
	pclient->exit_code = -1;
	if ( WIFEXITED(status) ) {
		/* Exited */
		int exit_code = WEXITSTATUS(status);
				
		if ( exit_code != 0 ) {
			i_info("program `%s' terminated with non-zero exit code %d", 
				pclient->path, exit_code);
			pclient->exit_code = 0;
			return 0;
		}

		pclient->exit_code = 1;
		return 1;	

	} else if ( WIFSIGNALED(status) ) {
		/* Killed with a signal */
		
		if ( force ) {
			i_error("program `%s' was forcibly terminated with signal %d",
				pclient->path, WTERMSIG(status));
		} else {
			i_error("program `%s' terminated abnormally, signal %d",
				pclient->path, WTERMSIG(status));
		}
		return -1;

	} else if ( WIFSTOPPED(status) ) {
		/* Stopped */
		i_error("program `%s' stopped, signal %d",
			pclient->path, WSTOPSIG(status));
		return -1;
	} 

	/* Something else */
	i_error("program `%s' terminated abnormally, return status %d",
		pclient->path, status);
	return -1;
}
int mail_send_rejection(struct mail_deliver_context *ctx, const char *recipient,
			const char *reason)
{
    struct mail *mail = ctx->src_mail;
    struct istream *input;
    struct smtp_client *smtp_client;
    struct ostream *output;
    const char *return_addr, *hdr;
    const char *value, *msgid, *orig_msgid, *boundary;
    string_t *str;
    int ret;

    if (mail_get_first_header(mail, "Message-ID", &orig_msgid) < 0)
	    orig_msgid = NULL;

    if (mail_get_first_header(mail, "Auto-Submitted", &value) > 0 &&
	strcasecmp(value, "no") != 0) {
	    i_info("msgid=%s: Auto-submitted message discarded: %s",
		   orig_msgid == NULL ? "" : str_sanitize(orig_msgid, 80),
		   str_sanitize(reason, 512));
	    return 0;
    }

    return_addr = mail_deliver_get_return_address(ctx);
    if (return_addr == NULL) {
	    i_info("msgid=%s: Return-Path missing, rejection reason: %s",
		   orig_msgid == NULL ? "" : str_sanitize(orig_msgid, 80),
		   str_sanitize(reason, 512));
	    return 0;
    }

    if (mailbox_get_settings(mail->box)->mail_debug) {
	    i_debug("Sending a rejection to %s: %s", recipient,
		    str_sanitize(reason, 512));
    }

    smtp_client = smtp_client_open(ctx->set, return_addr, NULL, &output);

    msgid = mail_deliver_get_new_message_id(ctx);
    boundary = t_strdup_printf("%s/%s", my_pid, ctx->set->hostname);

    str = t_str_new(512);
    str_printfa(str, "Message-ID: %s\r\n", msgid);
    str_printfa(str, "Date: %s\r\n", message_date_create(ioloop_time));
    str_printfa(str, "From: Mail Delivery Subsystem <%s>\r\n",
		ctx->set->postmaster_address);
    str_printfa(str, "To: <%s>\r\n", return_addr);
    str_append(str, "MIME-Version: 1.0\r\n");
    str_printfa(str, "Content-Type: "
		"multipart/report; report-type=%s;\r\n"
		"\tboundary=\"%s\"\r\n",
		ctx->dsn ? "delivery-status" : "disposition-notification",
		boundary);
    str_append(str, "Subject: ");
    var_expand(str, ctx->set->rejection_subject,
	       get_var_expand_table(mail, reason, recipient));
    str_append(str, "\r\n");

    str_append(str, "Auto-Submitted: auto-replied (rejected)\r\n");
    str_append(str, "Precedence: bulk\r\n");
    str_append(str, "\r\nThis is a MIME-encapsulated message\r\n\r\n");

    /* human readable status report */
    str_printfa(str, "--%s\r\n", boundary);
    str_append(str, "Content-Type: text/plain; charset=utf-8\r\n");
    str_append(str, "Content-Disposition: inline\r\n");
    str_append(str, "Content-Transfer-Encoding: 8bit\r\n\r\n");

    var_expand(str, ctx->set->rejection_reason,
	       get_var_expand_table(mail, reason, recipient));
    str_append(str, "\r\n");

    if (ctx->dsn) {
	    /* DSN status report: For LDA rejects. currently only used when
	       user is out of quota */
	    str_printfa(str, "--%s\r\n"
			"Content-Type: message/delivery-status\r\n\r\n",
			boundary);
	    str_printfa(str, "Reporting-MTA: dns; %s\r\n", ctx->set->hostname);
	    if (mail_get_first_header(mail, "Original-Recipient", &hdr) > 0)
		    str_printfa(str, "Original-Recipient: rfc822; %s\r\n", hdr);
	    str_printfa(str, "Final-Recipient: rfc822; %s\r\n", recipient);
	    str_append(str, "Action: failed\r\n");
	    str_printfa(str, "Status: %s\r\n", ctx->mailbox_full ? "5.2.2" : "5.2.0");
    } else {
	    /* MDN status report: For Sieve "reject" */
	    str_printfa(str, "--%s\r\n"
			"Content-Type: message/disposition-notification\r\n\r\n",
			boundary);
	    str_printfa(str, "Reporting-UA: %s; Dovecot Mail Delivery Agent\r\n",
			ctx->set->hostname);
	    if (mail_get_first_header(mail, "Original-Recipient", &hdr) > 0)
		    str_printfa(str, "Original-Recipient: rfc822; %s\r\n", hdr);
	    str_printfa(str, "Final-Recipient: rfc822; %s\r\n", recipient);

	    if (orig_msgid != NULL)
		    str_printfa(str, "Original-Message-ID: %s\r\n", orig_msgid);
	    str_append(str, "Disposition: "
		       "automatic-action/MDN-sent-automatically; deleted\r\n");
    }
    str_append(str, "\r\n");

    /* original message's headers */
    str_printfa(str, "--%s\r\nContent-Type: message/rfc822\r\n\r\n", boundary);
    o_stream_nsend(output, str_data(str), str_len(str));

    if (mail_get_hdr_stream(mail, NULL, &input) == 0) {
	    /* Note: If you add more headers, they need to be sorted.
	       We'll drop Content-Type because we're not including the message
	       body, and having a multipart Content-Type may confuse some
	       MIME parsers when they don't see the message boundaries. */
	    static const char *const exclude_headers[] = {
		    "Content-Type"
	    };

	    input = i_stream_create_header_filter(input,
	    		HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR |
			HEADER_FILTER_HIDE_BODY, exclude_headers,
			N_ELEMENTS(exclude_headers),
			*null_header_filter_callback, (void *)NULL);

	    ret = o_stream_send_istream(output, input);
	    i_stream_unref(&input);

	    i_assert(ret != 0);
    }

    str_truncate(str, 0);
    str_printfa(str, "\r\n\r\n--%s--\r\n", boundary);
    o_stream_nsend(output, str_data(str), str_len(str));
    return smtp_client_close(smtp_client);
}
Exemple #21
0
static void
ssl_params_if_unchanged(const char *path, time_t mtime,
			unsigned int ssl_dh_parameters_length ATTR_UNUSED)
{
	const char *temp_path, *error;
	struct file_lock *lock;
	struct stat st, st2;
	mode_t old_mask;
	int fd, ret;
	buffer_t *buf;

#ifdef HAVE_SETPRIORITY
	if (setpriority(PRIO_PROCESS, 0, SSL_PARAMS_PRIORITY) < 0)
		i_error("setpriority(%d) failed: %m", SSL_PARAMS_PRIORITY);
#endif

	temp_path = t_strconcat(path, ".tmp", NULL);

	old_mask = umask(0);
	fd = open(temp_path, O_WRONLY | O_CREAT, 0644);
	umask(old_mask);

	if (fd == -1)
		i_fatal("creat(%s) failed: %m", temp_path);

	/* If multiple dovecot instances are running, only one of them needs
	   to regenerate this file. */
	ret = file_wait_lock(fd, temp_path, F_WRLCK,
			     FILE_LOCK_METHOD_FCNTL,
			     SSL_BUILD_PARAM_TIMEOUT_SECS, &lock);
	if (ret < 0)
		i_fatal("file_try_lock(%s) failed: %m", temp_path);
	if (ret == 0) {
		/* someone else is writing this */
		i_fatal("Timeout while waiting for %s generation to complete",
			path);
	}

	/* make sure the .tmp file is still the one we created */
	if (fstat(fd, &st) < 0)
		i_fatal("fstat(%s) failed: %m", temp_path);
	if (stat(temp_path, &st2) < 0) {
		if (errno != ENOENT)
			i_fatal("stat(%s) failed: %m", temp_path);
		st2.st_ino = st.st_ino+1;
	}
	if (st.st_ino != st2.st_ino) {
		/* nope. so someone else just generated the file. */
		i_close_fd(&fd);
		return;
	}

	/* check that the parameters file is still the same */
	if (stat(path, &st) == 0) {
		if (st.st_mtime != mtime) {
			i_close_fd(&fd);
			return;
		}
	} else if (errno != ENOENT)
		i_fatal("stat(%s) failed: %m", path);

	/* ok, we really want to generate it. */
	if (ftruncate(fd, 0) < 0)
		i_fatal("ftruncate(%s) failed: %m", temp_path);

	i_info("Generating SSL parameters");

	buf = buffer_create_dynamic(pool_datastack_create(), 1024);
	if (ssl_iostream_generate_params(buf, ssl_dh_parameters_length,
					 &error) < 0) {
		i_fatal("ssl_iostream_generate_params(%u) failed: %s",
			ssl_dh_parameters_length, error);
	}
	if (write_full(fd, buf->data, buf->used) < 0)
		i_fatal("write(%s) failed: %m", temp_path);

	if (rename(temp_path, path) < 0)
		i_fatal("rename(%s, %s) failed: %m", temp_path, path);
	if (close(fd) < 0)
		i_fatal("close(%s) failed: %m", temp_path);
	file_lock_free(&lock);

	i_info("SSL parameters regeneration completed");
}
Exemple #22
0
static int
index_mailbox_precache(struct master_connection *conn, struct mailbox *box)
{
	struct mail_storage *storage = mailbox_get_storage(box);
	const char *username = mail_storage_get_user(storage)->username;
	const char *box_vname = mailbox_get_vname(box);
	struct mailbox_status status;
	struct mailbox_transaction_context *trans;
	struct mail_search_args *search_args;
	struct mail_search_context *ctx;
	struct mail *mail;
	struct mailbox_metadata metadata;
	uint32_t seq, first_uid = 0, last_uid = 0;
	char percentage_str[2+1+1];
	unsigned int counter = 0, max, percentage, percentage_sent = 0;
	int ret = 0;

	if (mailbox_get_metadata(box, MAILBOX_METADATA_PRECACHE_FIELDS,
				 &metadata) < 0) {
		i_error("Mailbox %s: Precache-fields lookup failed: %s",
			mailbox_get_vname(box),
			mailbox_get_last_internal_error(box, NULL));
		return -1;
	}
	if (mailbox_get_status(box, STATUS_MESSAGES | STATUS_LAST_CACHED_SEQ,
			       &status) < 0) {
		i_error("Mailbox %s: Status lookup failed: %s",
			mailbox_get_vname(box),
			mailbox_get_last_internal_error(box, NULL));
		return -1;
	}
	seq = status.last_cached_seq + 1;

	trans = mailbox_transaction_begin(box, MAILBOX_TRANSACTION_FLAG_NO_CACHE_DEC,
					  "indexing");
	search_args = mail_search_build_init();
	mail_search_build_add_seqset(search_args, seq, status.messages);
	ctx = mailbox_search_init(trans, search_args, NULL,
				  metadata.precache_fields, NULL);
	mail_search_args_unref(&search_args);

	max = status.messages + 1 - seq;
	while (mailbox_search_next(ctx, &mail)) {
		if (first_uid == 0)
			first_uid = mail->uid;
		last_uid = mail->uid;

		mail_precache(mail);
		if (++counter % 100 == 0) {
			percentage = counter*100 / max;
			if (percentage != percentage_sent && percentage < 100) {
				percentage_sent = percentage;
				if (i_snprintf(percentage_str,
					       sizeof(percentage_str), "%u\n",
					       percentage) < 0)
					i_unreached();
				(void)write_full(conn->fd, percentage_str,
						 strlen(percentage_str));
			}
			indexer_worker_refresh_proctitle(username, box_vname,
							 counter, max);
		}
	}
	if (mailbox_search_deinit(&ctx) < 0) {
		i_error("Mailbox %s: Mail search failed: %s",
			mailbox_get_vname(box),
			mailbox_get_last_internal_error(box, NULL));
		ret = -1;
	}
	const char *uids = first_uid == 0 ? "" :
		t_strdup_printf(" (UIDs %u..%u)", first_uid, last_uid);
	if (mailbox_transaction_commit(&trans) < 0) {
		i_error("Mailbox %s: Transaction commit failed: %s"
			" (attempted to index %u messages%s)",
			mailbox_get_vname(box),
			mailbox_get_last_internal_error(box, NULL),
			counter, uids);
		ret = -1;
	} else {
		i_info("Indexed %u messages in %s%s",
		       counter, mailbox_get_vname(box), uids);
	}
	return ret;
}
static void client_default_destroy(struct client *client, const char *reason)
{
	struct client_command_context *cmd;
	const char *cmd_status = "";

	i_assert(!client->destroyed);
	client->destroyed = TRUE;

	if (!client->disconnected) {
		client->disconnected = TRUE;
		if (reason == NULL) {
			reason = io_stream_get_disconnect_reason(client->input,
								 client->output);
			cmd_status = client_get_commands_status(client);
		}
		i_info("%s%s %s", reason, cmd_status, client_stats(client));
	}

	i_stream_close(client->input);
	o_stream_close(client->output);

	/* finish off all the queued commands. */
	if (client->output_cmd_lock != NULL)
		client_command_cancel(&client->output_cmd_lock);
	while (client->command_queue != NULL) {
		cmd = client->command_queue;
		client_command_cancel(&cmd);
	}
	/* handle the input_lock command last. it might have been waiting on
	   other queued commands (although we probably should just drop the
	   command at that point since it hasn't started running. but this may
	   change in future). */
	if (client->input_lock != NULL)
		client_command_cancel(&client->input_lock);

	if (client->mailbox != NULL) {
		client_search_updates_free(client);
		mailbox_free(&client->mailbox);
	}
	if (client->notify_ctx != NULL)
		imap_notify_deinit(&client->notify_ctx);
	if (client->urlauth_ctx != NULL)
		imap_urlauth_deinit(&client->urlauth_ctx);
	if (client->anvil_sent) {
		master_service_anvil_send(master_service, t_strconcat(
			"DISCONNECT\t", my_pid, "\timap/",
			mail_user_get_anvil_userip_ident(client->user),
			"\n", NULL));
	}
	mail_user_unref(&client->user);

	if (client->free_parser != NULL)
		imap_parser_unref(&client->free_parser);
	if (client->io != NULL)
		io_remove(&client->io);
	if (client->to_idle_output != NULL)
		timeout_remove(&client->to_idle_output);
	if (client->to_delayed_input != NULL)
		timeout_remove(&client->to_delayed_input);
	timeout_remove(&client->to_idle);

	i_stream_destroy(&client->input);
	o_stream_destroy(&client->output);

	net_disconnect(client->fd_in);
	if (client->fd_in != client->fd_out)
		net_disconnect(client->fd_out);

	if (array_is_created(&client->search_saved_uidset))
		array_free(&client->search_saved_uidset);
	if (array_is_created(&client->search_updates))
		array_free(&client->search_updates);
	pool_unref(&client->command_pool);
	mail_storage_service_user_free(&client->service_user);

	imap_client_count--;
	DLLIST_REMOVE(&imap_clients, client);
	pool_unref(&client->pool);

	master_service_client_connection_destroyed(master_service);
	imap_refresh_proctitle();
}
Exemple #24
0
static int
index_mailbox(struct master_connection *conn, struct mail_user *user,
	      const char *mailbox, unsigned int max_recent_msgs,
	      const char *what)
{
	struct mail_namespace *ns;
	struct mailbox *box;
	struct mailbox_status status;
	const char *path, *errstr;
	enum mail_error error;
	enum mailbox_sync_flags sync_flags = MAILBOX_SYNC_FLAG_FULL_READ;
	int ret;

	ns = mail_namespace_find(user->namespaces, mailbox);
	box = mailbox_alloc(ns->list, mailbox, 0);
	mailbox_set_reason(box, "indexing");
	ret = mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_INDEX, &path);
	if (ret < 0) {
		i_error("Getting path to mailbox %s failed: %s",
			mailbox, mailbox_get_last_internal_error(box, NULL));
		mailbox_free(&box);
		return -1;
	}
	if (ret == 0) {
		i_info("Indexes disabled for mailbox %s, skipping", mailbox);
		mailbox_free(&box);
		return 0;
	}
	ret = 0;

	if (max_recent_msgs != 0) {
		/* index only if there aren't too many recent messages.
		   don't bother syncing the mailbox, that alone can take a
		   while with large maildirs. */
		if (mailbox_open(box) < 0) {
			i_error("Opening mailbox %s failed: %s", mailbox,
				mailbox_get_last_internal_error(box, NULL));
			ret = -1;
		} else {
			mailbox_get_open_status(box, STATUS_RECENT, &status);
		}
		if (ret < 0 || status.recent > max_recent_msgs) {
			mailbox_free(&box);
			return ret;
		}
	}

	if (strchr(what, 'o') != NULL)
		sync_flags |= MAILBOX_SYNC_FLAG_OPTIMIZE;

	if (mailbox_sync(box, sync_flags) < 0) {
		errstr = mailbox_get_last_internal_error(box, &error);
		if (error != MAIL_ERROR_NOTFOUND) {
			i_error("Syncing mailbox %s failed: %s",
				mailbox, errstr);
		} else if (user->mail_debug) {
			i_debug("Syncing mailbox %s failed: %s",
				mailbox, errstr);
		}
		ret = -1;
	} else if (strchr(what, 'i') != NULL) {
		if (index_mailbox_precache(conn, box) < 0)
			ret = -1;
	}
	mailbox_free(&box);
	return ret;
}
static int
master_service_haproxy_read(struct master_service_haproxy_conn *hpconn)
{
	static union {
		unsigned char v1_data[HAPROXY_V1_MAX_HEADER_SIZE];
		struct {
			const struct haproxy_header_v2 hdr;
			const struct haproxy_data_v2 data;
		} v2;
	} buf;
	struct ip_addr *real_remote_ip = &hpconn->conn.remote_ip;
	int fd = hpconn->conn.fd;
	struct ip_addr local_ip, remote_ip;
	in_port_t local_port, remote_port;
	size_t size;
	ssize_t ret;

	/* the protocol specification explicitly states that the protocol header
	   must be sent as one TCP frame, meaning that we will get it in full
	   with the first recv() call.
	   FIXME: still, it would be cleaner to allow reading it incrementally.
	 */
	do {
		ret = recv(fd, &buf, sizeof(buf), MSG_PEEK);
	} while (ret < 0 && errno == EINTR);

	if (ret < 0 && errno == EAGAIN)
		return 0;
	if (ret <= 0) {
		i_info("haproxy: Client disconnected (rip=%s)",
		       net_ip2addr(real_remote_ip));
		return -1;
	}

	/* don't update true connection data until we succeed */
	local_ip = hpconn->conn.local_ip;
	remote_ip = hpconn->conn.remote_ip;
	local_port = hpconn->conn.local_port;
	remote_port = hpconn->conn.remote_port;

	/* protocol version 2 */
	if (ret >= (ssize_t)sizeof(buf.v2.hdr) &&
	    memcmp(buf.v2.hdr.sig, haproxy_v2sig,
		   sizeof(buf.v2.hdr.sig)) == 0) {
		const struct haproxy_header_v2 *hdr = &buf.v2.hdr;
		const struct haproxy_data_v2 *data = &buf.v2.data;
		size_t hdr_len;

		if ((hdr->ver_cmd & 0xf0) != 0x20) {
			i_error("haproxy: Client disconnected: "
				"Unsupported protocol version (version=%02x, rip=%s)",
				(hdr->ver_cmd & 0xf0) >> 4,
				net_ip2addr(real_remote_ip));
			return -1;
		}

		hdr_len = ntohs(hdr->len);
		size = sizeof(*hdr) + hdr_len;
		if (ret < (ssize_t)size) {
			i_error("haproxy(v2): Client disconnected: "
				"Protocol payload length does not match header "
				"(got=%"PRIuSIZE_T", expect=%"PRIuSIZE_T", rip=%s)",
				(size_t)ret, size, net_ip2addr(real_remote_ip));
			return -1;
		}

		switch (hdr->ver_cmd & 0x0f) {
		case HAPROXY_CMD_LOCAL:
			/* keep local connection address for LOCAL */
			/*i_debug("haproxy(v2): Local connection (rip=%s)",
				net_ip2addr(real_remote_ip));*/
			break;
		case HAPROXY_CMD_PROXY:
			if ((hdr->fam & 0x0f) != HAPROXY_SOCK_STREAM) {
				/* UDP makes no sense currently */
				i_error("haproxy(v2): Client disconnected: "
					"Not using TCP (type=%02x, rip=%s)",
					(hdr->fam & 0x0f), net_ip2addr(real_remote_ip));
				return -1;
			}
			switch ((hdr->fam & 0xf0) >> 4) {
			case HAPROXY_AF_INET:
				/* IPv4 */
				if (hdr_len < sizeof(data->addr.ip4)) {
					i_error("haproxy(v2): Client disconnected: "
						"IPv4 data is incomplete (rip=%s)",
						net_ip2addr(real_remote_ip));
					return -1;
				}
				local_ip.family = AF_INET;
				local_ip.u.ip4.s_addr = data->addr.ip4.dst_addr;
				local_port = ntohs(data->addr.ip4.dst_port);
				remote_ip.family = AF_INET;
				remote_ip.u.ip4.s_addr = data->addr.ip4.src_addr;
				remote_port = ntohs(data->addr.ip4.src_port);
				break;
			case HAPROXY_AF_INET6:
				/* IPv6 */
				if (hdr_len < sizeof(data->addr.ip6)) {
					i_error("haproxy(v2): Client disconnected: "
						"IPv6 data is incomplete (rip=%s)",
						net_ip2addr(real_remote_ip));
					return -1;
				}
				local_ip.family = AF_INET6;
				memcpy(&local_ip.u.ip6.s6_addr, data->addr.ip6.dst_addr, 16);
				local_port = ntohs(data->addr.ip6.dst_port);
				remote_ip.family = AF_INET6;
				memcpy(&remote_ip.u.ip6.s6_addr, data->addr.ip6.src_addr, 16);
				remote_port = ntohs(data->addr.ip6.src_port);
				break;
			case HAPROXY_AF_UNSPEC:
			case HAPROXY_AF_UNIX:
				/* unsupported; ignored */
				i_error("haproxy(v2): Unsupported address family "
					"(family=%02x, rip=%s)", (hdr->fam & 0xf0) >> 4,
					net_ip2addr(real_remote_ip));
				break;
			default:
				/* unsupported; error */
				i_error("haproxy(v2): Client disconnected: "
					"Unknown address family "
					"(family=%02x, rip=%s)", (hdr->fam & 0xf0) >> 4,
					net_ip2addr(real_remote_ip));
				return -1;
			}
			break;
		default:
			i_error("haproxy(v2): Client disconnected: "
				"Invalid command (cmd=%02x, rip=%s)",
				(hdr->ver_cmd & 0x0f),
				net_ip2addr(real_remote_ip));
			return -1; /* not a supported command */
		}

		// FIXME: TLV vectors are ignored
		//         (useful to see whether proxied client is using SSL)

	/* protocol version 1 (soon obsolete) */
	} else if (ret >= 8 && memcmp(buf.v1_data, "PROXY", 5) == 0) {