bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen#include "imap-common.h"
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen#include "str.h"
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen#include "istream.h"
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen#include "ostream.h"
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen#include "module-context.h"
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen#include "imap-commands.h"
f052a448696b1246ee9fc36b7c727237aed56058Timo Sirainen#include "compression.h"
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen#include "imap-zlib-plugin.h"
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen#define IMAP_COMPRESS_DEFAULT_LEVEL 6
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen#define IMAP_ZLIB_IMAP_CONTEXT(obj) \
d355b7da3f24a62bb1458b8f489b9a545cda74faAki Tuomi MODULE_CONTEXT_REQUIRE(obj, imap_zlib_imap_module)
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainenstruct zlib_client {
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen union imap_module_context module_ctx;
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen int (*next_state_export)(struct client *client, bool internal,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen buffer_t *dest, const char **error_r);
f052a448696b1246ee9fc36b7c727237aed56058Timo Sirainen const struct compression_handler *handler;
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen};
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
bd63b5b860658b01b1f46f26d406e1e4a9dc019aTimo Sirainenconst char *imap_zlib_plugin_version = DOVECOT_ABI_VERSION;
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainenstatic struct module *imap_zlib_module;
c1faff067b29fb48426cb84260adba563e93189aTimo Sirainenstatic imap_client_created_func_t *next_hook_client_created;
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(imap_zlib_imap_module,
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen &imap_module_register);
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainenstatic void client_skip_line(struct client *client)
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen{
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen const unsigned char *data;
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen size_t data_size;
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen data = i_stream_get_data(client->input, &data_size);
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen i_assert(data_size > 0);
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen if (data[0] == '\n')
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen i_stream_skip(client->input, 1);
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen else if (data[0] == '\r' && data_size > 1 && data[1] == '\n')
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen i_stream_skip(client->input, 2);
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen else
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen i_unreached();
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen client->input_skip_line = FALSE;
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen}
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainenstatic void client_update_imap_parser_streams(struct client *client)
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen{
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen struct client_command_context *cmd;
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen if (client->free_parser != NULL) {
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen imap_parser_set_streams(client->free_parser,
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen client->input, client->output);
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen }
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen for (cmd = client->command_queue; cmd != NULL; cmd = cmd->next) {
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen imap_parser_set_streams(cmd->parser,
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen client->input, client->output);
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen }
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen}
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainenstatic bool cmd_compress(struct client_command_context *cmd)
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen{
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen struct client *client = cmd->client;
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen struct zlib_client *zclient = IMAP_ZLIB_IMAP_CONTEXT(client);
f052a448696b1246ee9fc36b7c727237aed56058Timo Sirainen const struct compression_handler *handler;
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen const struct imap_arg *args;
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen struct istream *old_input;
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen struct ostream *old_output;
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen const char *mechanism, *value;
271d1e8961a417bf5d069b8dfcff3e40993f0182Timo Sirainen unsigned int level;
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen /* <mechanism> */
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen if (!client_read_args(cmd, 0, 0, &args))
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen return FALSE;
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen if (!imap_arg_get_atom(args, &mechanism) ||
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen !IMAP_ARG_IS_EOL(&args[1])) {
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen client_send_command_error(cmd, "Invalid arguments.");
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen return TRUE;
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen }
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen if (zclient->handler != NULL) {
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen client_send_tagline(cmd, t_strdup_printf(
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen "NO [COMPRESSIONACTIVE] COMPRESSION=%s already enabled.",
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen t_str_ucase(zclient->handler->name)));
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen return TRUE;
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen }
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
f052a448696b1246ee9fc36b7c727237aed56058Timo Sirainen handler = compression_lookup_handler(t_str_lcase(mechanism));
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen if (handler == NULL || handler->create_istream == NULL) {
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen client_send_tagline(cmd, "NO Unknown compression mechanism.");
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen return TRUE;
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen }
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen client_skip_line(client);
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen client_send_tagline(cmd, "OK Begin compression.");
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen value = mail_user_plugin_getenv(client->user,
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen "imap_zlib_compress_level");
6e7148add3f1ae3c01085eec547d3920e4c05592Timo Sirainen if (value == NULL || str_to_uint(value, &level) < 0 ||
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen level <= 0 || level > 9)
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen level = IMAP_COMPRESS_DEFAULT_LEVEL;
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen old_input = client->input;
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen old_output = client->output;
ee818732611d9a7cf91e0c2dce2a636e0dcfac08Timo Sirainen client->input = handler->create_istream(old_input, FALSE);
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen client->output = handler->create_ostream(old_output, level);
ea06c6e6879161f8b9cdbe19689afceac4c7651dTimo Sirainen /* preserve output offset so that the bytes out counter in logout
ea06c6e6879161f8b9cdbe19689afceac4c7651dTimo Sirainen message doesn't get reset here */
ea06c6e6879161f8b9cdbe19689afceac4c7651dTimo Sirainen client->output->offset = old_output->offset;
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen i_stream_unref(&old_input);
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen o_stream_unref(&old_output);
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen client_update_imap_parser_streams(client);
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen zclient->handler = handler;
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen return TRUE;
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen}
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstatic int
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenimap_zlib_state_export(struct client *client, bool internal,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen buffer_t *dest, const char **error_r)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct zlib_client *zclient = IMAP_ZLIB_IMAP_CONTEXT(client);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (zclient->handler != NULL && internal) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = "COMPRESS enabled";
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return zclient->next_state_export(client, internal, dest, error_r);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainenstatic void imap_zlib_client_created(struct client **clientp)
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen{
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen struct client *client = *clientp;
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen struct zlib_client *zclient;
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen if (mail_user_is_plugin_loaded(client->user, imap_zlib_module) &&
f052a448696b1246ee9fc36b7c727237aed56058Timo Sirainen compression_lookup_handler("deflate") != NULL) {
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen zclient = p_new(client->pool, struct zlib_client, 1);
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen MODULE_CONTEXT_SET(client, imap_zlib_imap_module, zclient);
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen zclient->next_state_export = (*clientp)->v.state_export;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen (*clientp)->v.state_export = imap_zlib_state_export;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
2c719bcb92302f45df4badb71d1d97f57235d0ccTimo Sirainen client_add_capability(*clientp, "COMPRESS=DEFLATE");
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen }
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen if (next_hook_client_created != NULL)
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen next_hook_client_created(clientp);
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen}
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainenvoid imap_zlib_plugin_init(struct module *module)
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen{
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen command_register("COMPRESS", cmd_compress, 0);
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen imap_zlib_module = module;
ac0fed903142d28ae3a1d5d00d2097fdf161b138Timo Sirainen next_hook_client_created =
ac0fed903142d28ae3a1d5d00d2097fdf161b138Timo Sirainen imap_client_created_hook_set(imap_zlib_client_created);
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen}
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainenvoid imap_zlib_plugin_deinit(void)
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen{
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen command_unregister("COMPRESS");
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
b66d803de86bfb411165b3465b0d9ef64ecfe2a1Timo Sirainen imap_client_created_hook_set(next_hook_client_created);
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen}
383395aee82283b15d9ea8ea0ad55d1d8c988b60Timo Sirainen
8552b0cad8ffe9ccb8270577ba28b8010c89af11Timo Sirainenconst char imap_zlib_plugin_binary_dependency[] = "imap";