diff options
Diffstat (limited to '')
-rw-r--r-- | src/plugins/imap-zlib/imap-zlib-plugin.c | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/src/plugins/imap-zlib/imap-zlib-plugin.c b/src/plugins/imap-zlib/imap-zlib-plugin.c new file mode 100644 index 0000000..df5508b --- /dev/null +++ b/src/plugins/imap-zlib/imap-zlib-plugin.c @@ -0,0 +1,184 @@ +/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */ + +#include "imap-common.h" +#include "str.h" +#include "istream.h" +#include "ostream.h" +#include "module-context.h" +#include "imap-commands.h" +#include "compression.h" +#include "imap-zlib-plugin.h" + + +#define IMAP_COMPRESS_DEFAULT_LEVEL 6 + +#define IMAP_ZLIB_IMAP_CONTEXT(obj) \ + MODULE_CONTEXT_REQUIRE(obj, imap_zlib_imap_module) + +struct zlib_client { + union imap_module_context module_ctx; + + int (*next_state_export)(struct client *client, bool internal, + buffer_t *dest, const char **error_r); + const struct compression_handler *handler; +}; + +const char *imap_zlib_plugin_version = DOVECOT_ABI_VERSION; + +static struct module *imap_zlib_module; +static imap_client_created_func_t *next_hook_client_created; + +static MODULE_CONTEXT_DEFINE_INIT(imap_zlib_imap_module, + &imap_module_register); + +static void client_skip_line(struct client *client) +{ + const unsigned char *data; + size_t data_size; + + data = i_stream_get_data(client->input, &data_size); + i_assert(data_size > 0); + if (data[0] == '\n') + i_stream_skip(client->input, 1); + else if (data[0] == '\r' && data_size > 1 && data[1] == '\n') + i_stream_skip(client->input, 2); + else + i_unreached(); + client->input_skip_line = FALSE; +} + +static void client_update_imap_parser_streams(struct client *client) +{ + struct client_command_context *cmd; + + if (client->free_parser != NULL) { + imap_parser_set_streams(client->free_parser, + client->input, client->output); + } + + for (cmd = client->command_queue; cmd != NULL; cmd = cmd->next) { + imap_parser_set_streams(cmd->parser, + client->input, client->output); + } +} + +static bool cmd_compress(struct client_command_context *cmd) +{ + struct client *client = cmd->client; + struct zlib_client *zclient = IMAP_ZLIB_IMAP_CONTEXT(client); + const struct compression_handler *handler; + const struct imap_arg *args; + struct istream *old_input; + struct ostream *old_output; + const char *mechanism, *value; + int level; + int ret; + + /* <mechanism> */ + if (!client_read_args(cmd, 0, 0, &args)) + return FALSE; + + if (!imap_arg_get_atom(args, &mechanism) || + !IMAP_ARG_IS_EOL(&args[1])) { + client_send_command_error(cmd, "Invalid arguments."); + return TRUE; + } + if (zclient->handler != NULL) { + client_send_tagline(cmd, t_strdup_printf( + "NO [COMPRESSIONACTIVE] COMPRESSION=%s already enabled.", + t_str_ucase(zclient->handler->name))); + return TRUE; + } + + ret = compression_lookup_handler(t_str_lcase(mechanism), &handler); + if (ret <= 0) { + const char * tagline = + t_strdup_printf("NO %s compression mechanism", + ret == 0 ? "Unsupported" : "Unknown"); + client_send_tagline(cmd, tagline); + return TRUE; + } + + client_skip_line(client); + client_send_tagline(cmd, "OK Begin compression."); + + const char *setting = t_strdup_printf("imap_compress_%s_level", + handler->name); + value = mail_user_plugin_getenv(client->user, setting); + if (value == NULL) { + level = handler->get_default_level(); + } else if (str_to_int(value, &level) < 0 || + level < handler->get_min_level() || + level > handler->get_max_level()) { + i_error("%s: Level must be between %d..%d", + setting, + handler->get_min_level(), + handler->get_max_level()); + level = handler->get_default_level(); + } + old_input = client->input; + old_output = client->output; + client->input = handler->create_istream(old_input); + client->output = handler->create_ostream(old_output, level); + /* preserve output offset so that the bytes out counter in logout + message doesn't get reset here */ + client->output->offset = old_output->offset; + i_stream_unref(&old_input); + o_stream_unref(&old_output); + + client_update_imap_parser_streams(client); + zclient->handler = handler; + return TRUE; +} + +static int +imap_zlib_state_export(struct client *client, bool internal, + buffer_t *dest, const char **error_r) +{ + struct zlib_client *zclient = IMAP_ZLIB_IMAP_CONTEXT(client); + + if (zclient->handler != NULL && internal) { + *error_r = "COMPRESS enabled"; + return 0; + } + return zclient->next_state_export(client, internal, dest, error_r); +} + +static void imap_zlib_client_created(struct client **clientp) +{ + struct client *client = *clientp; + struct zlib_client *zclient; + const struct compression_handler *handler; + + if (mail_user_is_plugin_loaded(client->user, imap_zlib_module) && + compression_lookup_handler("deflate", &handler) > 0) { + zclient = p_new(client->pool, struct zlib_client, 1); + MODULE_CONTEXT_SET(client, imap_zlib_imap_module, zclient); + + zclient->next_state_export = (*clientp)->v.state_export; + (*clientp)->v.state_export = imap_zlib_state_export; + + client_add_capability(*clientp, "COMPRESS=DEFLATE"); + } + + if (next_hook_client_created != NULL) + next_hook_client_created(clientp); +} + +void imap_zlib_plugin_init(struct module *module) +{ + command_register("COMPRESS", cmd_compress, 0); + + imap_zlib_module = module; + next_hook_client_created = + imap_client_created_hook_set(imap_zlib_client_created); +} + +void imap_zlib_plugin_deinit(void) +{ + command_unregister("COMPRESS"); + + imap_client_created_hook_set(next_hook_client_created); +} + +const char imap_zlib_plugin_binary_dependency[] = "imap"; |