imap-zlib-plugin.c revision 6e7148add3f1ae3c01085eec547d3920e4c05592
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "imap-common.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "str.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "istream.h"
d39a04db2f4d0599cb9b5f03a9aa10a3c234453cTimo Sirainen#include "ostream.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "module-context.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "imap-commands.h"
a3fe8c0c54d87822f4b4f8f0d10caac611861b2bTimo Sirainen#include "zlib-plugin.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "imap-zlib-plugin.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
c99fe55d4535d839a6ad0735c4719e076a1adb2cTimo Sirainen#include <stdlib.h>
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
7891c8e6debdcfec552cb1beea2a0230fe89957bTimo Sirainen#define IMAP_COMPRESS_DEFAULT_LEVEL 6
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#define IMAP_ZLIB_IMAP_CONTEXT(obj) \
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen MODULE_CONTEXT(obj, imap_zlib_imap_module)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstruct zlib_client {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen union imap_module_context module_ctx;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const struct zlib_handler *handler;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen};
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenconst char *imap_zlib_plugin_version = DOVECOT_VERSION;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic struct module *imap_zlib_module;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic void (*next_hook_client_created)(struct client **client);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(imap_zlib_imap_module,
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen &imap_module_register);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainenstatic void client_skip_line(struct client *client)
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen{
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen const unsigned char *data;
86791365b10f45982c88e70f2eb94fd6c3fea151Timo Sirainen size_t data_size;
86791365b10f45982c88e70f2eb94fd6c3fea151Timo Sirainen
86791365b10f45982c88e70f2eb94fd6c3fea151Timo Sirainen data = i_stream_get_data(client->input, &data_size);
86791365b10f45982c88e70f2eb94fd6c3fea151Timo Sirainen i_assert(data_size > 0);
4d4d6d4745682790c20d759ba93dbea46b812c5dTimo Sirainen if (data[0] == '\n')
4d4d6d4745682790c20d759ba93dbea46b812c5dTimo Sirainen i_stream_skip(client->input, 1);
bd20ef9d5c639faf470912ab94e6e6627d3eaebaTimo Sirainen else if (data[0] == '\r' && data_size > 1 && data[1] == '\n')
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen i_stream_skip(client->input, 2);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen else
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_unreached();
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen client->input_skip_line = FALSE;
17fe695b985e9d6e9dc39c05b24e6b3c3b7e1ba1Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic void client_update_imap_parser_streams(struct client *client)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct client_command_context *cmd;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (client->free_parser != NULL) {
b437874782ad048daa155e0ac863c2326c3f5e43Timo Sirainen imap_parser_set_streams(client->free_parser,
b437874782ad048daa155e0ac863c2326c3f5e43Timo Sirainen client->input, client->output);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen for (cmd = client->command_queue; cmd != NULL; cmd = cmd->next) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen imap_parser_set_streams(cmd->parser,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen client->input, client->output);
b437874782ad048daa155e0ac863c2326c3f5e43Timo Sirainen }
b437874782ad048daa155e0ac863c2326c3f5e43Timo Sirainen}
b437874782ad048daa155e0ac863c2326c3f5e43Timo Sirainen
b437874782ad048daa155e0ac863c2326c3f5e43Timo Sirainenstatic bool cmd_compress(struct client_command_context *cmd)
b437874782ad048daa155e0ac863c2326c3f5e43Timo Sirainen{
b437874782ad048daa155e0ac863c2326c3f5e43Timo Sirainen struct client *client = cmd->client;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct zlib_client *zclient = IMAP_ZLIB_IMAP_CONTEXT(client);
b437874782ad048daa155e0ac863c2326c3f5e43Timo Sirainen const struct zlib_handler *handler;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const struct imap_arg *args;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct istream *old_input;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct ostream *old_output;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen const char *mechanism, *value;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen unsigned int level;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen /* <mechanism> */
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen if (!client_read_args(cmd, 0, 0, &args))
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen return FALSE;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen if (!imap_arg_get_atom(args, &mechanism) ||
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen !IMAP_ARG_IS_EOL(&args[1])) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen client_send_command_error(cmd, "Invalid arguments.");
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return TRUE;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (zclient->handler != NULL) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen client_send_tagline(cmd, t_strdup_printf(
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen "NO [COMPRESSIONACTIVE] COMPRESSION=%s already enabled.",
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen t_str_ucase(zclient->handler->name)));
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return TRUE;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (client->tls_compression) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen client_send_tagline(cmd,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen "NO [COMPRESSIONACTIVE] TLS compression already enabled.");
a3fe8c0c54d87822f4b4f8f0d10caac611861b2bTimo Sirainen return TRUE;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen handler = zlib_find_zlib_handler(t_str_lcase(mechanism));
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (handler == NULL || handler->create_istream == NULL) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen client_send_tagline(cmd, "NO Unknown compression mechanism.");
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return TRUE;
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen }
0c22bef8f5b35c645de8affd8746307fc53bd222Timo Sirainen
0c22bef8f5b35c645de8affd8746307fc53bd222Timo Sirainen client_skip_line(client);
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen client_send_tagline(cmd, "OK Begin compression.");
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen value = mail_user_plugin_getenv(client->user,
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen "imap_zlib_compress_level");
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen if (value == NULL || str_to_uint(value, &level) < 0 ||
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen level <= 0 || level > 9)
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen level = IMAP_COMPRESS_DEFAULT_LEVEL;
8451c4b5afc1ff5366438b2766f75b592c33e1ecTimo Sirainen
8451c4b5afc1ff5366438b2766f75b592c33e1ecTimo Sirainen old_input = client->input;
8451c4b5afc1ff5366438b2766f75b592c33e1ecTimo Sirainen old_output = client->output;
8451c4b5afc1ff5366438b2766f75b592c33e1ecTimo Sirainen client->input = handler->create_istream(old_input, FALSE);
8451c4b5afc1ff5366438b2766f75b592c33e1ecTimo Sirainen client->output = handler->create_ostream(old_output, level);
8451c4b5afc1ff5366438b2766f75b592c33e1ecTimo Sirainen i_stream_unref(&old_input);
8451c4b5afc1ff5366438b2766f75b592c33e1ecTimo Sirainen o_stream_unref(&old_output);
8451c4b5afc1ff5366438b2766f75b592c33e1ecTimo Sirainen
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen client_update_imap_parser_streams(client);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen zclient->handler = handler;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return TRUE;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic void imap_zlib_client_created(struct client **clientp)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct client *client = *clientp;
40a8e6948d662339c0c5e2c7abfb84ae7c1803fdTimo Sirainen struct zlib_client *zclient;
40a8e6948d662339c0c5e2c7abfb84ae7c1803fdTimo Sirainen
40a8e6948d662339c0c5e2c7abfb84ae7c1803fdTimo Sirainen if (mail_user_is_plugin_loaded(client->user, imap_zlib_module) &&
40a8e6948d662339c0c5e2c7abfb84ae7c1803fdTimo Sirainen zlib_find_zlib_handler("deflate") != NULL) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen zclient = p_new(client->pool, struct zlib_client, 1);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen MODULE_CONTEXT_SET(client, imap_zlib_imap_module, zclient);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
cd2ed64888b42b481cde6bb9548c8520516fa3e9Timo Sirainen str_append(client->capability_string, " COMPRESS=DEFLATE");
a3fe8c0c54d87822f4b4f8f0d10caac611861b2bTimo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
a3fe8c0c54d87822f4b4f8f0d10caac611861b2bTimo Sirainen if (next_hook_client_created != NULL)
a3fe8c0c54d87822f4b4f8f0d10caac611861b2bTimo Sirainen next_hook_client_created(clientp);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
e6b4168ba670d9e51ea7877661def039ae6b53c3Timo Sirainen
e6b4168ba670d9e51ea7877661def039ae6b53c3Timo Sirainenvoid imap_zlib_plugin_init(struct module *module)
e6b4168ba670d9e51ea7877661def039ae6b53c3Timo Sirainen{
e6b4168ba670d9e51ea7877661def039ae6b53c3Timo Sirainen command_register("COMPRESS", cmd_compress, 0);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen imap_zlib_module = module;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen next_hook_client_created = hook_client_created;
01230de017cd273de41143d88e9c18df1243ae8aTimo Sirainen hook_client_created = imap_zlib_client_created;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
b8efab7ea8876c0a33a73ee0d08eddada31320f8Timo Sirainenvoid imap_zlib_plugin_deinit(void)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
b8efab7ea8876c0a33a73ee0d08eddada31320f8Timo Sirainen command_unregister("COMPRESS");
b8efab7ea8876c0a33a73ee0d08eddada31320f8Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen hook_client_created = next_hook_client_created;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenconst char *imap_zlib_plugin_dependencies[] = { "zlib", NULL };
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen