From d8aa10df6d1dae56d3aa485708a34d74e9e31e79 Mon Sep 17 00:00:00 2001 From: Stephan Bosch Date: Sun, 29 May 2016 19:14:58 +0200 Subject: [PATCH] imap: Implemented support for the LITERAL- capability. This replaces the LITERAL+ capability when the imap_literal_minus setting is enabled. --- configure.ac | 2 +- doc/example-config/conf.d/20-imap.conf | 3 +++ src/imap-login/imap-login-client.c | 17 +++++++++++++++-- src/imap-login/imap-login-settings.c | 4 +++- src/imap-login/imap-login-settings.h | 1 + src/imap/cmd-append.c | 12 ++++++++++-- src/imap/cmd-setmetadata.c | 2 ++ src/imap/imap-client.c | 8 ++++++++ src/imap/imap-settings.c | 2 ++ src/imap/imap-settings.h | 1 + src/lib-imap/imap-parser.c | 13 +++++++++++++ src/lib-imap/imap-parser.h | 4 ++++ 12 files changed, 63 insertions(+), 6 deletions(-) diff --git a/configure.ac b/configure.ac index 7e8d56fa9a..da03a919cf 100644 --- a/configure.ac +++ b/configure.ac @@ -778,7 +778,7 @@ dnl ** dnl IDLE doesn't really belong to banner. It's there just to make Blackberries dnl happy, because otherwise BIS server disables push email. -capability_banner="IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE" +capability_banner="IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE" capability="$capability_banner SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS THREAD=ORDEREDSUBJECT MULTIAPPEND URL-PARTIAL CATENATE UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS BINARY MOVE" AC_DEFINE_UNQUOTED(CAPABILITY_STRING, "$capability", [IMAP capabilities]) AC_DEFINE_UNQUOTED(CAPABILITY_BANNER_STRING, "$capability_banner", [IMAP capabilities advertised in banner]) diff --git a/doc/example-config/conf.d/20-imap.conf b/doc/example-config/conf.d/20-imap.conf index f10ae87f4a..dfae29d19c 100644 --- a/doc/example-config/conf.d/20-imap.conf +++ b/doc/example-config/conf.d/20-imap.conf @@ -68,6 +68,9 @@ # Host allowed in URLAUTH URLs sent by client. "*" allows all. #imap_urlauth_host = +# Enable IMAP LITERAL- extension (replaces LITERAL+) +#imap_literal_minus = no + protocol imap { # Space separated list of plugins to load (default is global mail_plugins). #mail_plugins = $mail_plugins diff --git a/src/imap-login/imap-login-client.c b/src/imap-login/imap-login-client.c index 8e539e7126..77258d5024 100644 --- a/src/imap-login/imap-login-client.c +++ b/src/imap-login/imap-login-client.c @@ -97,17 +97,26 @@ static const char *get_capability(struct client *client) { struct imap_client *imap_client = (struct imap_client *)client; string_t *cap_str = t_str_new(256); + bool explicit_capability = FALSE; if (*imap_client->set->imap_capability == '\0') str_append(cap_str, CAPABILITY_BANNER_STRING); - else if (*imap_client->set->imap_capability != '+') + else if (*imap_client->set->imap_capability != '+') { + explicit_capability = TRUE; str_append(cap_str, imap_client->set->imap_capability); - else { + } else { str_append(cap_str, CAPABILITY_BANNER_STRING); str_append_c(cap_str, ' '); str_append(cap_str, imap_client->set->imap_capability + 1); } + if (!explicit_capability) { + if (imap_client->set->imap_literal_minus) + str_append(cap_str, " LITERAL-"); + else + str_append(cap_str, " LITERAL+"); + } + if (client_is_tls_enabled(client) && !client->tls) str_append(cap_str, " STARTTLS"); if (is_login_cmd_disabled(client)) @@ -277,6 +286,8 @@ static int cmd_id(struct imap_client *client) id->parser = imap_parser_create(client->common.input, client->common.output, MAX_IMAP_LINE); + if (client->set->imap_literal_minus) + imap_parser_enable_literal_minus(id->parser); parser_flags = IMAP_PARSE_FLAG_STOP_AT_LIST; } else { id = client->cmd_id; @@ -543,6 +554,8 @@ static void imap_client_create(struct client *client, void **other_sets) 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(client->fd, IO_READ, client_input, client); } diff --git a/src/imap-login/imap-login-settings.c b/src/imap-login/imap-login-settings.c index a6d1043bf2..d0987163ae 100644 --- a/src/imap-login/imap-login-settings.c +++ b/src/imap-login/imap-login-settings.c @@ -57,6 +57,7 @@ static const struct setting_define imap_login_setting_defines[] = { DEF(SET_STR, imap_capability), DEF(SET_STR, imap_id_send), DEF(SET_STR, imap_id_log), + DEF(SET_BOOL, imap_literal_minus), SETTING_DEFINE_LIST_END }; @@ -64,7 +65,8 @@ static const struct setting_define imap_login_setting_defines[] = { static const struct imap_login_settings imap_login_default_settings = { .imap_capability = "", .imap_id_send = "name *", - .imap_id_log = "" + .imap_id_log = "", + .imap_literal_minus = FALSE }; static const struct setting_parser_info *imap_login_setting_dependencies[] = { diff --git a/src/imap-login/imap-login-settings.h b/src/imap-login/imap-login-settings.h index 08cc92561a..f1d250fb0e 100644 --- a/src/imap-login/imap-login-settings.h +++ b/src/imap-login/imap-login-settings.h @@ -5,6 +5,7 @@ struct imap_login_settings { const char *imap_capability; const char *imap_id_send; const char *imap_id_log; + bool imap_literal_minus; }; extern const struct setting_parser_info *imap_login_setting_roots[]; diff --git a/src/imap/cmd-append.c b/src/imap/cmd-append.c index 6bfac49141..2718734201 100644 --- a/src/imap/cmd-append.c +++ b/src/imap/cmd-append.c @@ -418,7 +418,10 @@ static bool cmd_append_continue_catenate(struct client_command_context *cmd) case IMAP_PARSE_ERROR_NONE: i_unreached(); case IMAP_PARSE_ERROR_LITERAL_TOO_BIG: - client_disconnect_with_error(client, msg); + client_send_line(client, t_strconcat("* BYE ", + (client->set->imap_literal_minus ? "[TOOBIG] " : ""), + msg, NULL)); + client_disconnect(client, msg); break; default: if (!ctx->failed) @@ -761,7 +764,10 @@ static bool cmd_append_parse_new_msg(struct client_command_context *cmd) case IMAP_PARSE_ERROR_NONE: i_unreached(); case IMAP_PARSE_ERROR_LITERAL_TOO_BIG: - client_disconnect_with_error(client, msg); + client_send_line(client, t_strconcat("* BYE ", + (client->set->imap_literal_minus ? "[TOOBIG] " : ""), + msg, NULL)); + client_disconnect(client, msg); break; default: client_send_command_error(cmd, msg); @@ -936,6 +942,8 @@ bool cmd_append(struct client_command_context *cmd) ctx->save_parser = imap_parser_create(client->input, client->output, client->set->imap_max_line_length); + if (client->set->imap_literal_minus) + imap_parser_enable_literal_minus(ctx->save_parser); cmd->func = cmd_append_parse_new_msg; cmd->context = ctx; diff --git a/src/imap/cmd-setmetadata.c b/src/imap/cmd-setmetadata.c index 652e13491a..98b9dc2307 100644 --- a/src/imap/cmd-setmetadata.c +++ b/src/imap/cmd-setmetadata.c @@ -278,6 +278,8 @@ cmd_setmetadata_start(struct imap_setmetadata_context *ctx) client->input_lock = cmd; ctx->parser = imap_parser_create(client->input, client->output, client->set->imap_max_line_length); + if (client->set->imap_literal_minus) + imap_parser_enable_literal_minus(ctx->parser); o_stream_unset_flush_callback(client->output); cmd->func = cmd_setmetadata_continue; diff --git a/src/imap/imap-client.c b/src/imap/imap-client.c index 7f48d983a7..0bbd1d55f2 100644 --- a/src/imap/imap-client.c +++ b/src/imap/imap-client.c @@ -139,6 +139,12 @@ struct client *client_create(int fd_in, int fd_out, const char *session_id, str_append_c(client->capability_string, ' '); str_append(client->capability_string, set->imap_capability + 1); } + if (!explicit_capability) { + if (client->set->imap_literal_minus) + str_append(client->capability_string, " LITERAL-"); + else + str_append(client->capability_string, " LITERAL+"); + } if (user->fuzzy_search && !explicit_capability) { /* Enable FUZZY capability only when it actually has a chance of working */ @@ -756,6 +762,8 @@ client_command_new(struct client *client) cmd->parser = imap_parser_create(client->input, client->output, client->set->imap_max_line_length); + if (client->set->imap_literal_minus) + imap_parser_enable_literal_minus(cmd->parser); } return cmd; } diff --git a/src/imap/imap-settings.c b/src/imap/imap-settings.c index 5355e8fe4d..58d348bd9f 100644 --- a/src/imap/imap-settings.c +++ b/src/imap/imap-settings.c @@ -71,6 +71,7 @@ static const struct setting_define imap_setting_defines[] = { DEF(SET_STR, imap_id_send), DEF(SET_STR, imap_id_log), DEF(SET_BOOL, imap_metadata), + DEF(SET_BOOL, imap_literal_minus), DEF(SET_TIME, imap_hibernate_timeout), DEF(SET_STR, imap_urlauth_host), @@ -96,6 +97,7 @@ static const struct imap_settings imap_default_settings = { .imap_id_send = "name *", .imap_id_log = "", .imap_metadata = FALSE, + .imap_literal_minus = FALSE, .imap_hibernate_timeout = 0, .imap_urlauth_host = "", diff --git a/src/imap/imap-settings.h b/src/imap/imap-settings.h index e4e70506c3..69e60d8948 100644 --- a/src/imap/imap-settings.h +++ b/src/imap/imap-settings.h @@ -25,6 +25,7 @@ struct imap_settings { const char *imap_id_send; const char *imap_id_log; bool imap_metadata; + bool imap_literal_minus; unsigned int imap_hibernate_timeout; /* imap urlauth: */ diff --git a/src/lib-imap/imap-parser.c b/src/lib-imap/imap-parser.c index 63474c3790..e3cad19e08 100644 --- a/src/lib-imap/imap-parser.c +++ b/src/lib-imap/imap-parser.c @@ -57,6 +57,7 @@ struct imap_parser { enum imap_parser_error error; const char *error_msg; + unsigned int literal_minus:1; unsigned int literal_skip_crlf:1; unsigned int literal_nonsync:1; unsigned int literal8:1; @@ -104,6 +105,11 @@ void imap_parser_unref(struct imap_parser **parser) *parser = NULL; } +void imap_parser_enable_literal_minus(struct imap_parser *parser) +{ + parser->literal_minus = TRUE; +} + void imap_parser_reset(struct imap_parser *parser) { p_clear(parser->pool); @@ -387,6 +393,13 @@ static int imap_parser_read_string(struct imap_parser *parser, static int imap_parser_literal_end(struct imap_parser *parser) { + if (parser->literal_minus && parser->literal_nonsync && + parser->literal_size > 4096) { + parser->error_msg = "Non-synchronizing literal size too large"; + parser->error = IMAP_PARSE_ERROR_LITERAL_TOO_BIG; + return FALSE; + } + if ((parser->flags & IMAP_PARSE_FLAG_LITERAL_SIZE) == 0) { if (parser->line_size >= parser->max_line_size || parser->literal_size > diff --git a/src/lib-imap/imap-parser.h b/src/lib-imap/imap-parser.h index 25dfda0672..e5d01c17f2 100644 --- a/src/lib-imap/imap-parser.h +++ b/src/lib-imap/imap-parser.h @@ -57,6 +57,10 @@ imap_parser_create(struct istream *input, struct ostream *output, void imap_parser_ref(struct imap_parser *parser); void imap_parser_unref(struct imap_parser **parser); +/* Enable LITERAL- parser semantics: non-synchronizing literals must not + exceed 4096 bytes */ +void imap_parser_enable_literal_minus(struct imap_parser *parser); + /* Reset the parser to initial state. */ void imap_parser_reset(struct imap_parser *parser);