zlib-plugin.c revision 5d202fa7788e500f5b2bab2aa6f6327c8c7461dc
a8c5a86d183db25a57bf193c06b41e092ec2e151Timo Sirainen/* Copyright (c) 2005-2010 Dovecot authors, see the included COPYING file */
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen union mailbox_transaction_module_context module_ctx;
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainenconst char *zlib_plugin_version = DOVECOT_VERSION;
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(zlib_user_module,
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(zlib_storage_module,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(zlib_mail_module, &mail_module_register);
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainenstatic bool is_compressed_zlib(struct istream *input)
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen const unsigned char *data;
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen /* Peek in to the stream and see if it looks like it's compressed
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen (based on its header). This also means that users can try to exploit
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen security holes in the uncompression library by APPENDing a specially
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen crafted mail. So let's hope zlib is free of holes. */
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen if (i_stream_read_data(input, &data, &size, 1) <= 0)
c28f6aa0b70af4811c9ace9114fe827c2f503455Timo Sirainenstatic bool is_compressed_bzlib(struct istream *input)
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen const unsigned char *data;
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen if (i_stream_read_data(input, &data, &size, 4+6 - 1) <= 0)
c28f6aa0b70af4811c9ace9114fe827c2f503455Timo Sirainen return memcmp(data + 4, "\x31\x41\x59\x26\x53\x59", 6) == 0;
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainenconst struct zlib_handler *zlib_find_zlib_handler(const char *name)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen unsigned int i;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen for (i = 0; zlib_handlers[i].name != NULL; i++) {
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainenstatic const struct zlib_handler *zlib_get_zlib_handler(struct istream *input)
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen unsigned int i;
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen for (i = 0; zlib_handlers[i].name != NULL; i++) {
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainenstatic const struct zlib_handler *zlib_get_zlib_handler_ext(const char *name)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen for (i = 0; zlib_handlers[i].name != NULL; i++) {
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen strcmp(name + name_len - len, zlib_handlers[i].ext) == 0)
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainenstatic int zlib_permail_get_stream(struct mail *_mail,
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen struct zlib_user *zuser = ZLIB_USER_CONTEXT(_mail->box->storage->user);
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen struct mail_private *mail = (struct mail_private *)_mail;
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen struct index_mail *imail = (struct index_mail *)mail;
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen union mail_module_context *zmail = ZLIB_MAIL_CONTEXT(mail);
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen /* don't uncompress input when we are reading a mail that we're just
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen in the middle of saving, and we didn't do the compression ourself.
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen in such situation we're probably checking if the user-given input
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen looks compressed */
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen (_mail->uid == 0 && zuser->save_handler == NULL)) {
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen return zmail->super.get_stream(_mail, hdr_size, body_size,
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen if (zmail->super.get_stream(_mail, NULL, NULL, &input) < 0)
a2738cdb6d2733fb3e186331d68009421a19ea00Timo Sirainen handler = zlib_get_zlib_handler(imail->data.stream);
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen mail_storage_set_critical(_mail->box->storage,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen "zlib plugin: Detected %s compression "
0892446b45c195461bb7be6599f02d97e1e2c9b2Timo Sirainen imail->data.stream = handler->create_istream(input, TRUE);
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen return index_mail_init_stream(imail, hdr_size, body_size, stream_r);
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainenstatic struct mail *
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainenzlib_permail_mail_alloc(struct mailbox_transaction_context *t,
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen struct mailbox_header_lookup_ctx *wanted_headers)
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen union mailbox_module_context *zbox = ZLIB_CONTEXT(t->box);
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen _mail = zbox->super.mail_alloc(t, wanted_fields, wanted_headers);
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen zmail = p_new(mail->pool, union mail_module_context, 1);
2524ef7b34965a1b1895d6140fd8296bf57c78d2Timo Sirainen MODULE_CONTEXT_SET_SELF(mail, zlib_mail_module, zmail);
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainenzlib_mailbox_transaction_begin(struct mailbox *box,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen union mailbox_module_context *zbox = ZLIB_CONTEXT(box);
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen t = zbox->super.transaction_begin(box, flags);
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen zt = i_new(struct zlib_transaction_context, 1);
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen MODULE_CONTEXT_SET(t, zlib_storage_module, zt);
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainenzlib_mailbox_transaction_rollback(struct mailbox_transaction_context *t)
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen union mailbox_module_context *zbox = ZLIB_CONTEXT(t->box);
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen struct zlib_transaction_context *zt = ZLIB_CONTEXT(t);
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainenzlib_mailbox_transaction_commit(struct mailbox_transaction_context *t,
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen struct mail_transaction_commit_changes *changes_r)
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen union mailbox_module_context *zbox = ZLIB_CONTEXT(t->box);
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen struct zlib_transaction_context *zt = ZLIB_CONTEXT(t);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen ret = zbox->super.transaction_commit(t, changes_r);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenzlib_mail_save_begin(struct mail_save_context *ctx, struct istream *input)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen struct mailbox_transaction_context *t = ctx->transaction;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen struct zlib_transaction_context *zt = ZLIB_CONTEXT(t);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen union mailbox_module_context *zbox = ZLIB_CONTEXT(t->box);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen zt->tmp_mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE,
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainenstatic int zlib_mail_save_finish(struct mail_save_context *ctx)
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen union mailbox_module_context *zbox = ZLIB_CONTEXT(box);
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen if (mail_get_stream(ctx->dest_mail, NULL, NULL, &input) < 0)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE,
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen "Saving mails compressed by client isn't supported");
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainenzlib_mail_save_compress_begin(struct mail_save_context *ctx,
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen struct zlib_user *zuser = ZLIB_USER_CONTEXT(box->storage->user);
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen union mailbox_module_context *zbox = ZLIB_CONTEXT(box);
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen output = zuser->save_handler->create_ostream(ctx->output,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic void zlib_permail_alloc_init(struct mailbox *box)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen struct zlib_user *zuser = ZLIB_USER_CONTEXT(box->storage->user);
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen box->v.transaction_begin = zlib_mailbox_transaction_begin;
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen box->v.transaction_rollback = zlib_mailbox_transaction_rollback;
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen box->v.transaction_commit = zlib_mailbox_transaction_commit;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen box->v.save_begin = zlib_mail_save_compress_begin;
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainenstatic int zlib_mailbox_open_input(struct mailbox *box)
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen handler = zlib_get_zlib_handler_ext(box->name);
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen if (handler == NULL || handler->create_istream == NULL)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (mail_storage_is_mailbox_file(box->storage)) {
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen /* looks like a compressed single file mailbox. we should be
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen able to handle this. */
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE);
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen box->input = handler->create_istream(input, TRUE);
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainenstatic int zlib_mailbox_open(struct mailbox *box)
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen union mailbox_module_context *zbox = ZLIB_CONTEXT(box);
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainenstatic void zlib_mailbox_allocated(struct mailbox *box)
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen zbox = p_new(box->pool, union mailbox_module_context, 1);
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen MODULE_CONTEXT_SET_SELF(box, zlib_storage_module, zbox);
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen if (strcmp(box->storage->name, "maildir") == 0 ||
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainenstatic void zlib_mail_user_created(struct mail_user *user)
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen zuser = p_new(user->pool, struct zlib_user, 1);
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen name = mail_user_plugin_getenv(user, "zlib_save");
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen zuser->save_handler = zlib_find_zlib_handler(name);
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen i_error("zlib_save: Unknown handler: %s", name);
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen name = mail_user_plugin_getenv(user, "zlib_save_level");
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen if (str_to_uint(name, &zuser->save_level) < 0 ||
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen zuser->save_level < 1 || zuser->save_level > 9) {
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen i_error("zlib_save_level: Level must be between 1..9");
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen zuser->save_level = ZLIB_PLUGIN_DEFAULT_LEVEL;
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen MODULE_CONTEXT_SET(user, zlib_user_module, zuser);
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainenstatic struct mail_storage_hooks zlib_mail_storage_hooks = {
df16c7e87511fed827e6890a2a47d13ca48716deTimo Sirainen mail_storage_hooks_add(module, &zlib_mail_storage_hooks);
7b42d6cbee8186195d8c5e66078043a0fa1f25c1Timo Sirainen mail_storage_hooks_remove(&zlib_mail_storage_hooks);