imap-login-client.c revision 06704acb3bfbee17e463e16f98361be706855a04
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "login-common.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "buffer.h"
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen#include "ioloop.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "istream.h"
a35cbba04d0a2823da98e693bd09a051addffdb2Timo Sirainen#include "ostream.h"
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen#include "safe-memset.h"
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen#include "str.h"
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen#include "imap-parser.h"
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen#include "imap-id.h"
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen#include "imap-resp-code.h"
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen#include "master-service.h"
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen#include "master-service-ssl-settings.h"
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen#include "master-auth.h"
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen#include "imap-login-client.h"
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen#include "client-authenticate.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "auth-client.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "imap-proxy.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "imap-quote.h"
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen#include "imap-login-commands.h"
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen#include "imap-login-settings.h"
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen#if LOGIN_MAX_INBUF_SIZE < 1024+2
a35cbba04d0a2823da98e693bd09a051addffdb2Timo Sirainen# error LOGIN_MAX_INBUF_SIZE too short to fit all ID command parameters
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen#endif
a35cbba04d0a2823da98e693bd09a051addffdb2Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen/* maximum length for IMAP command line. */
a35cbba04d0a2823da98e693bd09a051addffdb2Timo Sirainen#define MAX_IMAP_LINE 8192
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen/* Disconnect client when it sends too many bad commands */
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen#define CLIENT_MAX_BAD_COMMANDS 3
a35cbba04d0a2823da98e693bd09a051addffdb2Timo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenstatic const char *const imap_login_reserved_id_keys[] = {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen "x-originating-ip",
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen "x-originating-port",
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen "x-connected-ip",
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen "x-connected-port",
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen "x-proxy-ttl",
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen "x-session-id",
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen "x-session-ext-id",
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen NULL
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen};
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen
fc84f8af4794f4bb6caf6e5ec3fb1f8cebd0462aTimo Sirainen/* Skip incoming data until newline is found,
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainen returns TRUE if newline was found. */
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainenbool client_skip_line(struct imap_client *client)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen{
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen const unsigned char *data;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen size_t i, data_size;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen data = i_stream_get_data(client->common.input, &data_size);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen for (i = 0; i < data_size; i++) {
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen if (data[i] == '\n') {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_stream_skip(client->common.input, i+1);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return TRUE;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen }
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return FALSE;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen}
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenstatic bool client_handle_parser_error(struct imap_client *client,
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen struct imap_parser *parser)
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen{
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen const char *msg;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen enum imap_parser_error parse_error;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen msg = imap_parser_get_error(parser, &parse_error);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen switch (parse_error) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen case IMAP_PARSE_ERROR_NONE:
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_unreached();
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen case IMAP_PARSE_ERROR_LITERAL_TOO_BIG:
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_send_reply(&client->common,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen IMAP_CMD_REPLY_BYE, msg);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client_destroy(&client->common,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen t_strconcat("Disconnected: ", msg, NULL));
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return FALSE;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen default:
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen break;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_send_reply(&client->common, IMAP_CMD_REPLY_BAD, msg);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client->cmd_finished = TRUE;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client->skip_line = TRUE;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return TRUE;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen}
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic bool is_login_cmd_disabled(struct client *client)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (client->secured) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (auth_client_find_mech(auth_client, "PLAIN") == NULL) {
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen /* no PLAIN authentication, can't use LOGIN command */
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen return TRUE;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return FALSE;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (client->set->disable_plaintext_auth)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return TRUE;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen if (strcmp(client->ssl_set->ssl, "required") == 0)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return TRUE;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return FALSE;
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen}
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic const char *get_capability(struct client *client)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct imap_client *imap_client = (struct imap_client *)client;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen string_t *cap_str = t_str_new(256);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen bool explicit_capability = FALSE;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (*imap_client->set->imap_capability == '\0')
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen str_append(cap_str, CAPABILITY_BANNER_STRING);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen else if (*imap_client->set->imap_capability != '+') {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen explicit_capability = TRUE;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen str_append(cap_str, imap_client->set->imap_capability);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen } else {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen str_append(cap_str, CAPABILITY_BANNER_STRING);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen str_append_c(cap_str, ' ');
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen str_append(cap_str, imap_client->set->imap_capability + 1);
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainen }
26a8b7deb3a5b6f26f9c4d71538e1248f680e4beTimo Sirainen
26a8b7deb3a5b6f26f9c4d71538e1248f680e4beTimo Sirainen if (!explicit_capability) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (imap_client->set->imap_literal_minus)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen str_append(cap_str, " LITERAL-");
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen else
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen str_append(cap_str, " LITERAL+");
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (client_is_tls_enabled(client) && !client->tls)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen str_append(cap_str, " STARTTLS");
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (is_login_cmd_disabled(client))
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen str_append(cap_str, " LOGINDISABLED");
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client_authenticate_get_capabilities(client, cap_str);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen return str_c(cap_str);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenstatic int cmd_capability(struct imap_client *imap_client,
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen const struct imap_arg *args ATTR_UNUSED)
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct client *client = &imap_client->common;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* Client is required to send CAPABILITY after STARTTLS, so the
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen capability resp-code workaround checks only pre-STARTTLS
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen CAPABILITY commands. */
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen if (!client->starttls)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen imap_client->client_ignores_capability_resp_code = TRUE;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_send_raw(client, t_strconcat(
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen "* CAPABILITY ", get_capability(client), "\r\n", NULL));
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen client_send_reply(client, IMAP_CMD_REPLY_OK,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen "Pre-login capabilities listed, post-login capabilities have more.");
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return 1;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic int cmd_starttls(struct imap_client *client,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen const struct imap_arg *args ATTR_UNUSED)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_cmd_starttls(&client->common);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen return 1;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen}
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic void
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenimap_client_notify_starttls(struct client *client,
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen bool success, const char *text)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (success)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client_send_reply(client, IMAP_CMD_REPLY_OK, text);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen else
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_send_reply(client, IMAP_CMD_REPLY_BAD, text);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic bool
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenclient_update_info(struct imap_client *client,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen const char *key, const char *value)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_assert(value != NULL);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen /* SYNC WITH imap_login_reserved_id_keys */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (strcasecmp(key, "x-originating-ip") == 0) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen (void)net_addr2ip(value, &client->common.ip);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen } else if (strcasecmp(key, "x-originating-port") == 0) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen (void)net_str2port(value, &client->common.remote_port);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen } else if (strcasecmp(key, "x-connected-ip") == 0) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen (void)net_addr2ip(value, &client->common.local_ip);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen } else if (strcasecmp(key, "x-connected-port") == 0) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen (void)net_str2port(value, &client->common.local_port);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen } else if (strcasecmp(key, "x-proxy-ttl") == 0) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (str_to_uint(value, &client->common.proxy_ttl) < 0) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* nothing */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen } else if (strcasecmp(key, "x-session-id") == 0 ||
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen strcasecmp(key, "x-session-ext-id") == 0) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (strlen(value) <= LOGIN_MAX_SESSION_ID_LEN) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client->common.session_id =
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen p_strdup(client->common.pool, value);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen } else if (strncasecmp(key, "x-forward-", 10) == 0) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* handle extra field */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_add_forward_field(&client->common, key+10, value);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen } else {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return FALSE;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return TRUE;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen}
fc84f8af4794f4bb6caf6e5ec3fb1f8cebd0462aTimo Sirainen
fc84f8af4794f4bb6caf6e5ec3fb1f8cebd0462aTimo Sirainenstatic bool client_id_reserved_word(const char *key)
fc84f8af4794f4bb6caf6e5ec3fb1f8cebd0462aTimo Sirainen{
fc84f8af4794f4bb6caf6e5ec3fb1f8cebd0462aTimo Sirainen i_assert(key != NULL);
fc84f8af4794f4bb6caf6e5ec3fb1f8cebd0462aTimo Sirainen return (strncasecmp(key, "x-forward-", 10) == 0 ||
fc84f8af4794f4bb6caf6e5ec3fb1f8cebd0462aTimo Sirainen str_array_icase_find(imap_login_reserved_id_keys, key));
fc84f8af4794f4bb6caf6e5ec3fb1f8cebd0462aTimo Sirainen}
fc84f8af4794f4bb6caf6e5ec3fb1f8cebd0462aTimo Sirainen
fc84f8af4794f4bb6caf6e5ec3fb1f8cebd0462aTimo Sirainenstatic void cmd_id_handle_keyvalue(struct imap_client *client,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen const char *key, const char *value)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen bool client_id_str;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* length of key + length of value (NIL for NULL) and two set of
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen quotes and space */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen size_t kvlen = strlen(key) + 2 + 1 +
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen (value == NULL ? 3 : strlen(value)) + 2;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (client->common.trusted && !client->id_logged) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (value == NULL) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* do not try to process NIL values as client-info,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen but store them for non-reserved keys */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_id_str = !client_id_reserved_word(key);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen } else {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_id_str = !client_update_info(client, key, value);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_assert(client_id_str == !client_id_reserved_word(key));
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen } else {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_id_str = !client_id_reserved_word(key);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (client->set->imap_id_retain && client_id_str &&
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen (client->common.client_id == NULL ||
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen str_len(client->common.client_id) + kvlen < LOGIN_MAX_CLIENT_ID_LEN)) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (client->common.client_id == NULL) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client->common.client_id = str_new(client->common.preproxy_pool, 64);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen } else {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen str_append_c(client->common.client_id, ' ');
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen imap_append_quoted(client->common.client_id, key);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen str_append_c(client->common.client_id, ' ');
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen if (value == NULL)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen str_append(client->common.client_id, "NIL");
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen else
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen imap_append_quoted(client->common.client_id, value);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen if (client->cmd_id->log_reply != NULL &&
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen (client->cmd_id->log_keys == NULL ||
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen str_array_icase_find((void *)client->cmd_id->log_keys, key)))
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen imap_id_log_reply_append(client->cmd_id->log_reply, key, value);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen}
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic int cmd_id_handle_args(struct imap_client *client,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen const struct imap_arg *arg)
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct imap_client_cmd_id *id = client->cmd_id;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen const char *key, *value;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen switch (id->state) {
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen case IMAP_CLIENT_ID_STATE_LIST:
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (arg->type == IMAP_ARG_NIL)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return 1;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (arg->type != IMAP_ARG_LIST)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return -1;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (client->set->imap_id_log[0] == '\0') {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* no ID logging */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen } else if (client->id_logged) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* already logged the ID reply */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen } else {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen id->log_reply = str_new(default_pool, 64);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (strcmp(client->set->imap_id_log, "*") == 0) {
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen /* log all keys */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen } else {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* log only specified keys */
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen id->log_keys = p_strsplit_spaces(default_pool,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client->set->imap_id_log, " ");
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen id->state = IMAP_CLIENT_ID_STATE_KEY;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen break;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen case IMAP_CLIENT_ID_STATE_KEY:
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (!imap_arg_get_string(arg, &key))
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return -1;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (i_strocpy(id->key, key, sizeof(id->key)) < 0)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return -1;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen id->state = IMAP_CLIENT_ID_STATE_VALUE;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen break;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen case IMAP_CLIENT_ID_STATE_VALUE:
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (!imap_arg_get_nstring(arg, &value))
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return -1;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen cmd_id_handle_keyvalue(client, id->key, value);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen id->state = IMAP_CLIENT_ID_STATE_KEY;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen break;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return 0;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen}
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic void cmd_id_finish(struct imap_client *client)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen{
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen /* finished handling the parameters */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (!client->id_logged) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client->id_logged = TRUE;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (client->cmd_id->log_reply != NULL) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_log(&client->common, t_strdup_printf(
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen "ID sent: %s", str_c(client->cmd_id->log_reply)));
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen }
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_send_raw(&client->common,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen t_strdup_printf("* ID %s\r\n",
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen imap_id_reply_generate(client->set->imap_id_send)));
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_send_reply(&client->common, IMAP_CMD_REPLY_OK, "ID completed.");
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen}
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenstatic void cmd_id_free(struct imap_client *client)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen{
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen struct imap_client_cmd_id *id = client->cmd_id;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (id->log_reply != NULL)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen str_free(&id->log_reply);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (id->log_keys != NULL)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen p_strsplit_free(default_pool, id->log_keys);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen imap_parser_unref(&id->parser);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_free_and_null(client->cmd_id);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client->skip_line = TRUE;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainenstatic int cmd_id(struct imap_client *client)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen{
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen struct imap_client_cmd_id *id;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen enum imap_parser_flags parser_flags;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen const struct imap_arg *args;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen int ret;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (client->common.client_id != NULL)
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen str_truncate(client->common.client_id, 0);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen if (client->cmd_id == NULL) {
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen client->cmd_id = id = i_new(struct imap_client_cmd_id, 1);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen id->parser = imap_parser_create(client->common.input,
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen client->common.output,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen MAX_IMAP_LINE);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (client->set->imap_literal_minus)
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen imap_parser_enable_literal_minus(id->parser);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen parser_flags = IMAP_PARSE_FLAG_STOP_AT_LIST;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen } else {
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen id = client->cmd_id;
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen parser_flags = IMAP_PARSE_FLAG_INSIDE_LIST;
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen while ((ret = imap_parser_read_args(id->parser, 1, parser_flags, &args)) > 0) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_assert(ret == 1);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen if ((ret = cmd_id_handle_args(client, args)) < 0) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_send_reply(&client->common,
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen IMAP_CMD_REPLY_BAD,
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen "Invalid ID parameters");
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen cmd_id_free(client);
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen return -1;
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen }
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen if (ret > 0) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* NIL parameter */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen ret = 0;
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen break;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen imap_parser_reset(id->parser);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen parser_flags = IMAP_PARSE_FLAG_INSIDE_LIST;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (ret == 0) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* finished the line */
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen cmd_id_finish(client);
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen cmd_id_free(client);
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen return 1;
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen } else if (ret == -1) {
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen if (!client_handle_parser_error(client, id->parser))
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return 0;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen cmd_id_free(client);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return -1;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen } else {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_assert(ret == -2);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return 0;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen}
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic int cmd_noop(struct imap_client *client,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen const struct imap_arg *args ATTR_UNUSED)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_send_reply(&client->common, IMAP_CMD_REPLY_OK,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen "NOOP completed.");
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return 1;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen}
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic int cmd_logout(struct imap_client *client,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen const struct imap_arg *args ATTR_UNUSED)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_send_reply(&client->common, IMAP_CMD_REPLY_BYE, "Logging out");
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen client_send_reply(&client->common, IMAP_CMD_REPLY_OK,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen "Logout completed.");
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_destroy(&client->common, "Aborted login");
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen return 1;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen}
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic int cmd_enable(struct imap_client *client,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen const struct imap_arg *args ATTR_UNUSED)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_send_raw(&client->common, "* ENABLED\r\n");
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen client_send_reply(&client->common, IMAP_CMD_REPLY_OK,
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen "ENABLE ignored in non-authenticated state.");
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen return 1;
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen}
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainenstatic int client_command_execute(struct imap_client *client, const char *cmd,
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen const struct imap_arg *args)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct imap_login_command *login_cmd;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen login_cmd = imap_login_command_lookup(cmd);
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen if (login_cmd == NULL)
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen return -2;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return login_cmd->func(client, args);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen}
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic bool imap_is_valid_tag(const char *tag)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen for (; *tag != '\0'; tag++) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen switch (*tag) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen case '+':
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* atom-specials: */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen case '(':
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen case ')':
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen case '{':
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen case '/':
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen case ' ':
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* list-wildcards: */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen case '%':
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen case '*':
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* quoted-specials: */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen case '"':
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen case '\\':
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return FALSE;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen default:
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (*tag < ' ') /* CTL */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return FALSE;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen break;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return TRUE;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen}
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic int client_parse_command(struct imap_client *client,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen const struct imap_arg **args_r)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen switch (imap_parser_read_args(client->parser, 0, 0, args_r)) {
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen case -1:
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* error */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (!client_handle_parser_error(client, client->parser)) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* client destroyed */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return 0;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return -1;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen case -2:
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen /* not enough data */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return 0;
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen default:
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen /* we read the entire line - skip over the CRLF */
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen if (!client_skip_line(client))
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen i_unreached();
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return 1;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen}
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainenstatic bool client_handle_input(struct imap_client *client)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_assert(!client->common.authenticating);
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen if (client->cmd_finished) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* clear the previous command from memory. don't do this
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen immediately after handling command since we need the
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen cmd_tag to stay some time after authentication commands. */
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen client->cmd_tag = NULL;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client->cmd_name = NULL;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen imap_parser_reset(client->parser);
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen /* remove \r\n */
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen if (client->skip_line) {
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen if (!client_skip_line(client))
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen return FALSE;
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen client->skip_line = FALSE;
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client->cmd_finished = FALSE;
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen if (client->cmd_tag == NULL) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client->cmd_tag = imap_parser_read_word(client->parser);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (client->cmd_tag == NULL)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return FALSE; /* need more data */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (!imap_is_valid_tag(client->cmd_tag) ||
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen strlen(client->cmd_tag) > IMAP_TAG_MAX_LEN) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* the tag is invalid, don't allow it and don't
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen send it back. this attempts to prevent any
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen potentially dangerous replies in case someone tries
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen to access us using HTTP protocol. */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client->cmd_tag = "";
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen if (client->cmd_name == NULL) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client->cmd_name = imap_parser_read_word(client->parser);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (client->cmd_name == NULL)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return FALSE; /* need more data */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return client->common.v.input_next_cmd(&client->common);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen}
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainenstatic bool imap_client_input_next_cmd(struct client *_client)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen{
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen struct imap_client *client = (struct imap_client *)_client;
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen const struct imap_arg *args;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen bool parsed;
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen int ret;
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen if (strcasecmp(client->cmd_name, "AUTHENTICATE") == 0) {
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen /* SASL-IR may need more space than input buffer's size,
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen so we'll handle it as a special case. */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen ret = cmd_authenticate(client, &parsed);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (ret == 0 && !parsed)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return FALSE;
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen } else if (strcasecmp(client->cmd_name, "ID") == 0) {
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen /* ID extensions allows max. 30 parameters,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen each max. 1024 bytes long. that brings us over the input
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen buffer's size, so handle the parameters one at a time */
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen ret = cmd_id(client);
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen if (ret == 0)
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen return FALSE;
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen if (ret < 0)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen ret = 1; /* don't send the error reply again */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen } else {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen ret = client_parse_command(client, &args);
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen if (ret < 0)
return TRUE;
if (ret == 0)
return FALSE;
ret = *client->cmd_tag == '\0' ? -1 :
client_command_execute(client, client->cmd_name, args);
}
client->cmd_finished = TRUE;
if (ret == -2 && strcasecmp(client->cmd_tag, "LOGIN") == 0) {
client_send_reply(&client->common, IMAP_CMD_REPLY_BAD,
"First parameter in line is IMAP's command tag, "
"not the command name. Add that before the command, "
"like: a login user pass");
} else if (ret < 0) {
if (*client->cmd_tag == '\0')
client->cmd_tag = "*";
if (++client->common.bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
client_send_reply(&client->common, IMAP_CMD_REPLY_BYE,
"Too many invalid IMAP commands.");
client_destroy(&client->common,
"Disconnected: Too many invalid commands");
return FALSE;
}
client_send_reply(&client->common, IMAP_CMD_REPLY_BAD,
"Error in IMAP command received by server.");
}
return ret != 0 && !client->common.destroyed;
}
static void imap_client_input(struct client *client)
{
struct imap_client *imap_client = (struct imap_client *)client;
if (!client_read(client))
return;
client_ref(client);
o_stream_cork(imap_client->common.output);
for (;;) {
if (!auth_client_is_connected(auth_client)) {
/* we're not currently connected to auth process -
don't allow any commands */
client_notify_status(client, FALSE,
AUTH_SERVER_WAITING_MSG);
timeout_remove(&client->to_auth_waiting);
client->input_blocked = TRUE;
break;
} else {
if (!client_handle_input(imap_client))
break;
}
}
o_stream_uncork(imap_client->common.output);
client_unref(&client);
}
static struct client *imap_client_alloc(pool_t pool)
{
struct imap_client *imap_client;
imap_client = p_new(pool, struct imap_client, 1);
return &imap_client->common;
}
static void imap_client_create(struct client *client, void **other_sets)
{
struct imap_client *imap_client = (struct imap_client *)client;
imap_client->set = other_sets[0];
imap_client->parser =
imap_parser_create(imap_client->common.input,
imap_client->common.output, MAX_IMAP_LINE);
if (imap_client->set->imap_literal_minus)
imap_parser_enable_literal_minus(imap_client->parser);
client->io = io_add_istream(client->input, client_input, client);
}
static void imap_client_destroy(struct client *client)
{
struct imap_client *imap_client = (struct imap_client *)client;
i_free_and_null(imap_client->proxy_backend_capability);
imap_parser_unref(&imap_client->parser);
}
static void imap_client_notify_auth_ready(struct client *client)
{
string_t *greet;
greet = t_str_new(128);
str_append(greet, "* OK ");
str_printfa(greet, "[CAPABILITY %s] ", get_capability(client));
str_append(greet, client->set->login_greeting);
str_append(greet, "\r\n");
client_send_raw(client, str_c(greet));
client->banner_sent = TRUE;
}
static void imap_client_starttls(struct client *client)
{
struct imap_client *imap_client = (struct imap_client *)client;
imap_parser_unref(&imap_client->parser);
imap_client->parser =
imap_parser_create(imap_client->common.input,
imap_client->common.output, MAX_IMAP_LINE);
/* CRLF is lost from buffer when streams are reopened. */
imap_client->skip_line = FALSE;
}
static void ATTR_NULL(3)
client_send_reply_raw(struct client *client,
const char *prefix, const char *resp_code,
const char *text, bool tagged)
{
struct imap_client *imap_client = (struct imap_client *)client;
T_BEGIN {
string_t *line = t_str_new(256);
if (tagged)
str_append(line, imap_client->cmd_tag);
else
str_append_c(line, '*');
str_append_c(line, ' ');
str_append(line, prefix);
str_append_c(line, ' ');
if (resp_code != NULL)
str_printfa(line, "[%s] ", resp_code);
str_append(line, text);
str_append(line, "\r\n");
client_send_raw_data(client, str_data(line), str_len(line));
} T_END;
}
void client_send_reply_code(struct client *client, enum imap_cmd_reply reply,
const char *resp_code, const char *text)
{
const char *prefix = "NO";
bool tagged = TRUE;
switch (reply) {
case IMAP_CMD_REPLY_OK:
prefix = "OK";
break;
case IMAP_CMD_REPLY_NO:
break;
case IMAP_CMD_REPLY_BAD:
prefix = "BAD";
break;
case IMAP_CMD_REPLY_BYE:
prefix = "BYE";
tagged = FALSE;
break;
}
client_send_reply_raw(client, prefix, resp_code, text, tagged);
}
void client_send_reply(struct client *client, enum imap_cmd_reply reply,
const char *text)
{
client_send_reply_code(client, reply, NULL, text);
}
static void
imap_client_notify_status(struct client *client, bool bad, const char *text)
{
if (bad)
client_send_reply_raw(client, "BAD", "ALERT", text, FALSE);
else
client_send_reply_raw(client, "OK", NULL, text, FALSE);
}
static void
imap_client_notify_disconnect(struct client *client,
enum client_disconnect_reason reason,
const char *text)
{
if (reason == CLIENT_DISCONNECT_INTERNAL_ERROR) {
client_send_reply_code(client, IMAP_CMD_REPLY_BYE,
IMAP_RESP_CODE_UNAVAILABLE, text);
} else {
client_send_reply_code(client, IMAP_CMD_REPLY_BYE, NULL, text);
}
}
static void imap_login_preinit(void)
{
login_set_roots = imap_login_setting_roots;
}
static const struct imap_login_command imap_login_commands[] = {
{ "LOGIN", cmd_login },
{ "CAPABILITY", cmd_capability },
{ "STARTTLS", cmd_starttls },
{ "NOOP", cmd_noop },
{ "LOGOUT", cmd_logout },
{ "ENABLE", cmd_enable }
};
static void imap_login_init(void)
{
imap_login_commands_init();
imap_login_commands_register(imap_login_commands,
N_ELEMENTS(imap_login_commands));
}
static void imap_login_deinit(void)
{
clients_destroy_all();
imap_login_commands_deinit();
}
static struct client_vfuncs imap_client_vfuncs = {
imap_client_alloc,
imap_client_create,
imap_client_destroy,
imap_client_notify_auth_ready,
imap_client_notify_disconnect,
imap_client_notify_status,
imap_client_notify_starttls,
imap_client_starttls,
imap_client_input,
NULL,
NULL,
imap_client_auth_result,
imap_proxy_reset,
imap_proxy_parse_line,
imap_proxy_error,
imap_proxy_get_state,
client_common_send_raw_data,
imap_client_input_next_cmd,
client_common_default_free,
};
static const struct login_binary imap_login_binary = {
.protocol = "imap",
.process_name = "imap-login",
.default_port = 143,
.default_ssl_port = 993,
.client_vfuncs = &imap_client_vfuncs,
.preinit = imap_login_preinit,
.init = imap_login_init,
.deinit = imap_login_deinit,
.sasl_support_final_reply = FALSE
};
int main(int argc, char *argv[])
{
return login_binary_run(&imap_login_binary, argc, argv);
}