imap-zlib-plugin.c revision 8552b0cad8ffe9ccb8270577ba28b8010c89af11
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen#include "imap-common.h"
47e0598840ecffa364ebed523e06939e22738f06Timo Sirainen#include "str.h"
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen#include "istream.h"
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen#include "ostream.h"
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen#include "module-context.h"
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen#include "imap-commands.h"
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen#include "zlib-plugin.h"
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen#include "imap-zlib-plugin.h"
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen#include <stdlib.h>
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen#define IMAP_COMPRESS_DEFAULT_LEVEL 6
1d738cce754bc64bbc66d3355ebdaf3f6eac55f1Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen#define IMAP_ZLIB_IMAP_CONTEXT(obj) \
bdd7a96c363346f7c38f389791be1487ca08775bTimo Sirainen MODULE_CONTEXT(obj, imap_zlib_imap_module)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen
63e2edd14ae7b1dc4a08e2e659501dbf519462f9Timo Sirainenstruct zlib_client {
63e2edd14ae7b1dc4a08e2e659501dbf519462f9Timo Sirainen union imap_module_context module_ctx;
63e2edd14ae7b1dc4a08e2e659501dbf519462f9Timo Sirainen
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen const struct zlib_handler *handler;
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen};
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainenconst char *imap_zlib_plugin_version = DOVECOT_VERSION;
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainenstatic struct module *imap_zlib_module;
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainenstatic void (*next_hook_client_created)(struct client **client);
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(imap_zlib_imap_module,
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen &imap_module_register);
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainenstatic void client_skip_line(struct client *client)
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen{
e925f007930955f10fa8728509a44f7a53d2b13eTimo Sirainen const unsigned char *data;
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen size_t data_size;
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen data = i_stream_get_data(client->input, &data_size);
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen i_assert(data_size > 0);
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen if (data[0] == '\n')
e925f007930955f10fa8728509a44f7a53d2b13eTimo Sirainen i_stream_skip(client->input, 1);
e925f007930955f10fa8728509a44f7a53d2b13eTimo Sirainen else if (data[0] == '\r' && data_size > 1 && data[1] == '\n')
e925f007930955f10fa8728509a44f7a53d2b13eTimo Sirainen i_stream_skip(client->input, 2);
e925f007930955f10fa8728509a44f7a53d2b13eTimo Sirainen else
e925f007930955f10fa8728509a44f7a53d2b13eTimo Sirainen i_unreached();
e925f007930955f10fa8728509a44f7a53d2b13eTimo Sirainen client->input_skip_line = FALSE;
e925f007930955f10fa8728509a44f7a53d2b13eTimo Sirainen}
e925f007930955f10fa8728509a44f7a53d2b13eTimo Sirainen
e925f007930955f10fa8728509a44f7a53d2b13eTimo Sirainenstatic void client_update_imap_parser_streams(struct client *client)
e925f007930955f10fa8728509a44f7a53d2b13eTimo Sirainen{
e925f007930955f10fa8728509a44f7a53d2b13eTimo Sirainen struct client_command_context *cmd;
e925f007930955f10fa8728509a44f7a53d2b13eTimo Sirainen
e925f007930955f10fa8728509a44f7a53d2b13eTimo Sirainen if (client->free_parser != NULL) {
e925f007930955f10fa8728509a44f7a53d2b13eTimo Sirainen imap_parser_set_streams(client->free_parser,
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen client->input, client->output);
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen }
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen for (cmd = client->command_queue; cmd != NULL; cmd = cmd->next) {
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen imap_parser_set_streams(cmd->parser,
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen client->input, client->output);
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen }
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen}
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenstatic bool cmd_compress(struct client_command_context *cmd)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen{
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen struct client *client = cmd->client;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen struct zlib_client *zclient = IMAP_ZLIB_IMAP_CONTEXT(client);
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen const struct zlib_handler *handler;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen const struct imap_arg *args;
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen struct istream *old_input;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen struct ostream *old_output;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen const char *mechanism, *value;
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen unsigned int level;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen
b397665e90fa0fc7c6a9156fdd6cf28b571e8e39Timo Sirainen /* <mechanism> */
b397665e90fa0fc7c6a9156fdd6cf28b571e8e39Timo Sirainen if (!client_read_args(cmd, 0, 0, &args))
b397665e90fa0fc7c6a9156fdd6cf28b571e8e39Timo Sirainen return FALSE;
b397665e90fa0fc7c6a9156fdd6cf28b571e8e39Timo Sirainen
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen if (!imap_arg_get_atom(args, &mechanism) ||
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen !IMAP_ARG_IS_EOL(&args[1])) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client_send_command_error(cmd, "Invalid arguments.");
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen return TRUE;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen }
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (zclient->handler != NULL) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client_send_tagline(cmd, t_strdup_printf(
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen "NO [COMPRESSIONACTIVE] COMPRESSION=%s already enabled.",
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen t_str_ucase(zclient->handler->name)));
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen return TRUE;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen }
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (client->tls_compression) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client_send_tagline(cmd,
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen "NO [COMPRESSIONACTIVE] TLS compression already enabled.");
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen return TRUE;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen }
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen handler = zlib_find_zlib_handler(t_str_lcase(mechanism));
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (handler == NULL || handler->create_istream == NULL) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen client_send_tagline(cmd, "NO Unknown compression mechanism.");
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen return TRUE;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen }
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen client_skip_line(client);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen client_send_tagline(cmd, "OK Begin compression.");
da4376093d4e1b26b14ea1e945689fb7056fe0a0Timo Sirainen
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen value = mail_user_plugin_getenv(client->user,
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen "imap_zlib_compress_level");
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (value == NULL || str_to_uint(value, &level) < 0 ||
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen level <= 0 || level > 9)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen level = IMAP_COMPRESS_DEFAULT_LEVEL;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen old_input = client->input;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen old_output = client->output;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client->input = handler->create_istream(old_input, FALSE);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client->output = handler->create_ostream(old_output, level);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen i_stream_unref(&old_input);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen o_stream_unref(&old_output);
0f66f12eb4cdbf47670975044c88d8f388bf92dfTimo Sirainen
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client_update_imap_parser_streams(client);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen zclient->handler = handler;
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen return TRUE;
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen}
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenstatic void imap_zlib_client_created(struct client **clientp)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen{
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen struct client *client = *clientp;
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen struct zlib_client *zclient;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (mail_user_is_plugin_loaded(client->user, imap_zlib_module) &&
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen zlib_find_zlib_handler("deflate") != NULL) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen zclient = p_new(client->pool, struct zlib_client, 1);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen MODULE_CONTEXT_SET(client, imap_zlib_imap_module, zclient);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen str_append(client->capability_string, " COMPRESS=DEFLATE");
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen }
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen if (next_hook_client_created != NULL)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen next_hook_client_created(clientp);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen}
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainenvoid imap_zlib_plugin_init(struct module *module)
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen{
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen command_register("COMPRESS", cmd_compress, 0);
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen imap_zlib_module = module;
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen next_hook_client_created = hook_client_created;
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen hook_client_created = imap_zlib_client_created;
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen}
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainenvoid imap_zlib_plugin_deinit(void)
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen{
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen command_unregister("COMPRESS");
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen hook_client_created = next_hook_client_created;
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen}
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainenconst char *imap_zlib_plugin_dependencies[] = { "zlib", NULL };
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenconst char imap_zlib_plugin_binary_dependency[] = "imap";
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen