summaryrefslogtreecommitdiffstats
path: root/src/plugins/virtual/virtual-mail.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/virtual/virtual-mail.c')
-rw-r--r--src/plugins/virtual/virtual-mail.c583
1 files changed, 583 insertions, 0 deletions
diff --git a/src/plugins/virtual/virtual-mail.c b/src/plugins/virtual/virtual-mail.c
new file mode 100644
index 0000000..fd8303f
--- /dev/null
+++ b/src/plugins/virtual/virtual-mail.c
@@ -0,0 +1,583 @@
+/* Copyright (c) 2008-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "index-mail.h"
+#include "virtual-storage.h"
+#include "virtual-transaction.h"
+
+struct virtual_mail {
+ struct index_mail imail;
+
+ enum mail_fetch_field wanted_fields;
+ struct mailbox_header_lookup_ctx *wanted_headers;
+
+ /* temp_wanted_fields for this mail. Used only when mail doesn't have
+ a backend mail yet. */
+ enum mail_fetch_field delayed_temp_fields;
+ struct mailbox_header_lookup_ctx *delayed_temp_headers;
+
+ /* currently active mail */
+ struct mail *cur_backend_mail;
+ struct virtual_mail_index_record cur_vrec;
+
+ /* all allocated mails */
+ ARRAY(struct mail *) backend_mails;
+
+ /* mail is lost if backend_mail doesn't point to correct mail */
+ bool cur_lost:1;
+};
+
+struct mail *
+virtual_mail_alloc(struct mailbox_transaction_context *t,
+ enum mail_fetch_field wanted_fields,
+ struct mailbox_header_lookup_ctx *wanted_headers)
+{
+ struct virtual_mailbox *mbox = (struct virtual_mailbox *)t->box;
+ struct virtual_mail *vmail;
+ pool_t mail_pool, data_pool;
+
+ mail_pool = pool_alloconly_create("vmail", 1024);
+ data_pool = pool_alloconly_create("virtual index_mail", 512);
+ vmail = p_new(mail_pool, struct virtual_mail, 1);
+ vmail->wanted_fields = wanted_fields;
+ vmail->wanted_headers = wanted_headers;
+ if (vmail->wanted_headers != NULL)
+ mailbox_header_lookup_ref(vmail->wanted_headers);
+ /* Do not pass wanted_fields or wanted_headers to index_mail_init.
+ It will just cause unwanted behaviour, as we only want these
+ to be passed to backend mails. */
+ index_mail_init(&vmail->imail, t, 0, NULL, mail_pool, data_pool);
+ vmail->imail.mail.v = virtual_mail_vfuncs;
+ i_array_init(&vmail->backend_mails, array_count(&mbox->backend_boxes));
+ return &vmail->imail.mail.mail;
+}
+
+static void virtual_mail_close(struct mail *mail)
+{
+ struct virtual_mail *vmail = (struct virtual_mail *)mail;
+ struct mail **mails;
+ unsigned int i, count;
+
+ if (mail->seq != 0) {
+ mailbox_header_lookup_unref(&vmail->delayed_temp_headers);
+ vmail->delayed_temp_fields = 0;
+ }
+
+ mails = array_get_modifiable(&vmail->backend_mails, &count);
+ for (i = 0; i < count; i++) {
+ struct mail_private *p = (struct mail_private *)mails[i];
+
+ if (vmail->imail.freeing)
+ mail_free(&mails[i]);
+ else
+ p->v.close(mails[i]);
+ }
+ if (vmail->imail.freeing) {
+ array_free(&vmail->backend_mails);
+ mailbox_header_lookup_unref(&vmail->wanted_headers);
+ }
+ index_mail_close(mail);
+}
+
+static struct mail *
+backend_mail_find(struct virtual_mail *vmail, struct mailbox *box)
+{
+ struct mail *const *mails;
+ unsigned int i, count;
+
+ mails = array_get(&vmail->backend_mails, &count);
+ for (i = 0; i < count; i++) {
+ if (mails[i]->box == box)
+ return mails[i];
+ }
+ return NULL;
+}
+
+static int backend_mail_get(struct virtual_mail *vmail,
+ struct mail **backend_mail_r)
+{
+ struct mail *mail = &vmail->imail.mail.mail;
+ struct virtual_mailbox *mbox = (struct virtual_mailbox *)mail->box;
+ struct virtual_backend_box *bbox;
+
+ *backend_mail_r = NULL;
+
+ if (vmail->cur_backend_mail != NULL) {
+ if (vmail->cur_lost) {
+ mail_set_expunged(&vmail->imail.mail.mail);
+ return -1;
+ }
+ *backend_mail_r = vmail->cur_backend_mail;
+ return 0;
+ }
+
+ if (!virtual_backend_box_lookup(mbox, vmail->cur_vrec.mailbox_id, &bbox))
+ i_unreached();
+
+ vmail->cur_backend_mail = backend_mail_find(vmail, bbox->box);
+ if (vmail->cur_backend_mail == NULL) {
+ if (!bbox->box->opened &&
+ virtual_backend_box_open(mbox, bbox) < 0) {
+ virtual_box_copy_error(mail->box, bbox->box);
+ return -1;
+ }
+ (void)virtual_mail_set_backend_mail(mail, bbox);
+ i_assert(vmail->cur_backend_mail != NULL);
+ }
+ virtual_backend_box_accessed(mbox, bbox);
+ vmail->cur_lost = !mail_set_uid(vmail->cur_backend_mail,
+ vmail->cur_vrec.real_uid);
+ mail->expunged = vmail->cur_lost || vmail->cur_backend_mail->expunged;
+ if (vmail->cur_lost) {
+ mail_set_expunged(&vmail->imail.mail.mail);
+ return -1;
+ }
+ /* headers need to be converted to backend-headers, so go through
+ the virtual add_temp_wanted_fields() again. */
+ mail_add_temp_wanted_fields(mail, vmail->delayed_temp_fields,
+ vmail->delayed_temp_headers);
+ *backend_mail_r = vmail->cur_backend_mail;
+ return 0;
+}
+
+struct mail *
+virtual_mail_set_backend_mail(struct mail *mail,
+ struct virtual_backend_box *bbox)
+{
+ struct virtual_mail *vmail = (struct virtual_mail *)mail;
+ struct mail_private *backend_pmail;
+ struct mailbox_transaction_context *backend_trans;
+ struct mailbox_header_lookup_ctx *backend_headers;
+
+ i_assert(bbox->box->opened);
+
+ backend_trans = virtual_transaction_get(mail->transaction, bbox->box);
+
+ backend_headers = vmail->wanted_headers == NULL ? NULL :
+ mailbox_header_lookup_init(bbox->box,
+ vmail->wanted_headers->name);
+ vmail->cur_backend_mail =
+ mail_alloc(backend_trans, vmail->wanted_fields, backend_headers);
+ mailbox_header_lookup_unref(&backend_headers);
+
+ backend_pmail = (struct mail_private *)vmail->cur_backend_mail;
+ backend_pmail->vmail = mail;
+ array_push_back(&vmail->backend_mails, &vmail->cur_backend_mail);
+ return vmail->cur_backend_mail;
+}
+
+void virtual_mail_set_unattached_backend_mail(struct mail *mail,
+ struct mail *backend_mail)
+{
+ struct virtual_mail *vmail = (struct virtual_mail *)mail;
+ struct mail_private *backend_pmail;
+
+ vmail->cur_backend_mail = backend_mail;
+
+ backend_pmail = (struct mail_private *)backend_mail;
+ backend_pmail->vmail = mail;
+}
+
+static void virtual_mail_set_seq(struct mail *mail, uint32_t seq, bool saving)
+{
+ struct virtual_mail *vmail = (struct virtual_mail *)mail;
+ struct virtual_mailbox *mbox = (struct virtual_mailbox *)mail->box;
+ const void *data;
+
+ i_assert(!saving);
+
+ mail_index_lookup_ext(mail->transaction->view, seq,
+ mbox->virtual_ext_id, &data, NULL);
+ memcpy(&vmail->cur_vrec, data, sizeof(vmail->cur_vrec));
+
+ index_mail_set_seq(mail, seq, saving);
+
+ vmail->cur_backend_mail = NULL;
+}
+
+static bool virtual_mail_set_uid(struct mail *mail, uint32_t uid)
+{
+ uint32_t seq;
+
+ if (!mail_index_lookup_seq(mail->transaction->view, uid, &seq))
+ return FALSE;
+
+ virtual_mail_set_seq(mail, seq, FALSE);
+ return TRUE;
+}
+
+static void virtual_mail_set_uid_cache_updates(struct mail *mail, bool set)
+{
+ struct virtual_mail *vmail = (struct virtual_mail *)mail;
+ struct mail *backend_mail;
+ struct mail_private *p;
+
+ if (backend_mail_get(vmail, &backend_mail) < 0)
+ return;
+ p = (struct mail_private *)backend_mail;
+ p->v.set_uid_cache_updates(backend_mail, set);
+}
+
+static bool virtual_mail_prefetch(struct mail *mail)
+{
+ struct virtual_mail *vmail = (struct virtual_mail *)mail;
+ struct mail *backend_mail;
+ struct mail_private *p;
+
+ if (backend_mail_get(vmail, &backend_mail) < 0)
+ return TRUE;
+ p = (struct mail_private *)backend_mail;
+ return p->v.prefetch(backend_mail);
+}
+
+static int virtual_mail_precache(struct mail *mail)
+{
+ struct virtual_mail *vmail = (struct virtual_mail *)mail;
+ struct mail *backend_mail;
+ struct mail_private *p;
+
+ if (backend_mail_get(vmail, &backend_mail) < 0)
+ return -1;
+ p = (struct mail_private *)backend_mail;
+ return p->v.precache(backend_mail);
+}
+
+static void
+virtual_mail_add_temp_wanted_fields(struct mail *mail,
+ enum mail_fetch_field fields,
+ struct mailbox_header_lookup_ctx *headers)
+{
+ struct virtual_mail *vmail = (struct virtual_mail *)mail;
+ struct mail *backend_mail;
+ struct mailbox_header_lookup_ctx *backend_headers, *new_headers;
+
+ if (mail->seq == 0) {
+ /* No mail set yet. Delay until it is set. */
+ vmail->delayed_temp_fields |= fields;
+ if (vmail->delayed_temp_headers == NULL)
+ vmail->delayed_temp_headers = headers;
+ else {
+ new_headers = mailbox_header_lookup_merge(
+ vmail->delayed_temp_headers, headers);
+ mailbox_header_lookup_unref(&vmail->delayed_temp_headers);
+ vmail->delayed_temp_headers = new_headers;
+ }
+ return;
+ }
+
+ if (backend_mail_get(vmail, &backend_mail) < 0)
+ return;
+ /* convert header indexes to backend mailbox's header indexes */
+ backend_headers = headers == NULL ? NULL :
+ mailbox_header_lookup_init(backend_mail->box, headers->name);
+ mail_add_temp_wanted_fields(backend_mail, fields, backend_headers);
+ mailbox_header_lookup_unref(&backend_headers);
+}
+
+static int
+virtual_mail_get_parts(struct mail *mail, struct message_part **parts_r)
+{
+ struct virtual_mail *vmail = (struct virtual_mail *)mail;
+ struct mail *backend_mail;
+
+ if (backend_mail_get(vmail, &backend_mail) < 0)
+ return -1;
+ if (mail_get_parts(backend_mail, parts_r) < 0) {
+ virtual_box_copy_error(mail->box, backend_mail->box);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+virtual_mail_get_date(struct mail *mail, time_t *date_r, int *timezone_r)
+{
+ struct virtual_mail *vmail = (struct virtual_mail *)mail;
+ struct mail *backend_mail;
+ int tz;
+
+ if (timezone_r == NULL)
+ timezone_r = &tz;
+
+ if (backend_mail_get(vmail, &backend_mail) < 0)
+ return -1;
+ if (mail_get_date(backend_mail, date_r, timezone_r) < 0) {
+ virtual_box_copy_error(mail->box, backend_mail->box);
+ return -1;
+ }
+ return 0;
+}
+
+static int virtual_mail_get_received_date(struct mail *mail, time_t *date_r)
+{
+ struct virtual_mail *vmail = (struct virtual_mail *)mail;
+ struct mail *backend_mail;
+
+ if (backend_mail_get(vmail, &backend_mail) < 0)
+ return -1;
+ if (mail_get_received_date(backend_mail, date_r) < 0) {
+ virtual_box_copy_error(mail->box, backend_mail->box);
+ return -1;
+ }
+ return 0;
+}
+
+static int virtual_mail_get_save_date(struct mail *mail, time_t *date_r)
+{
+ struct virtual_mail *vmail = (struct virtual_mail *)mail;
+ struct mail *backend_mail;
+ int ret;
+
+ if (backend_mail_get(vmail, &backend_mail) < 0)
+ return -1;
+ ret = mail_get_save_date(backend_mail, date_r);
+ if (ret < 0)
+ virtual_box_copy_error(mail->box, backend_mail->box);
+ return ret;
+}
+
+static int virtual_mail_get_virtual_mail_size(struct mail *mail, uoff_t *size_r)
+{
+ struct virtual_mail *vmail = (struct virtual_mail *)mail;
+ struct mail *backend_mail;
+
+ if (backend_mail_get(vmail, &backend_mail) < 0)
+ return -1;
+ if (mail_get_virtual_size(backend_mail, size_r) < 0) {
+ virtual_box_copy_error(mail->box, backend_mail->box);
+ return -1;
+ }
+ return 0;
+}
+
+static int virtual_mail_get_physical_size(struct mail *mail, uoff_t *size_r)
+{
+ struct virtual_mail *vmail = (struct virtual_mail *)mail;
+ struct mail *backend_mail;
+
+ if (backend_mail_get(vmail, &backend_mail) < 0)
+ return -1;
+ if (mail_get_physical_size(backend_mail, size_r) < 0) {
+ virtual_box_copy_error(mail->box, backend_mail->box);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+virtual_mail_get_first_header(struct mail *mail, const char *field,
+ bool decode_to_utf8, const char **value_r)
+{
+ struct virtual_mail *vmail = (struct virtual_mail *)mail;
+ struct mail *backend_mail;
+ struct mail_private *p;
+ int ret;
+
+ if (backend_mail_get(vmail, &backend_mail) < 0)
+ return -1;
+ p = (struct mail_private *)backend_mail;
+ ret = p->v.get_first_header(backend_mail, field,
+ decode_to_utf8, value_r);
+ if (ret < 0) {
+ virtual_box_copy_error(mail->box, backend_mail->box);
+ return -1;
+ }
+ return ret;
+}
+
+static int
+virtual_mail_get_headers(struct mail *mail, const char *field,
+ bool decode_to_utf8, const char *const **value_r)
+{
+ struct virtual_mail *vmail = (struct virtual_mail *)mail;
+ struct mail *backend_mail;
+ struct mail_private *p;
+
+ if (backend_mail_get(vmail, &backend_mail) < 0)
+ return -1;
+ p = (struct mail_private *)backend_mail;
+ if (p->v.get_headers(backend_mail, field, decode_to_utf8, value_r) < 0) {
+ virtual_box_copy_error(mail->box, backend_mail->box);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+virtual_mail_get_header_stream(struct mail *mail,
+ struct mailbox_header_lookup_ctx *headers,
+ struct istream **stream_r)
+{
+ struct virtual_mail *vmail = (struct virtual_mail *)mail;
+ struct mail *backend_mail;
+ struct mailbox_header_lookup_ctx *backend_headers;
+ int ret;
+
+ if (backend_mail_get(vmail, &backend_mail) < 0)
+ return -1;
+
+ backend_headers = mailbox_header_lookup_init(backend_mail->box,
+ headers->name);
+ ret = mail_get_header_stream(backend_mail, backend_headers, stream_r);
+ mailbox_header_lookup_unref(&backend_headers);
+ if (ret < 0) {
+ virtual_box_copy_error(mail->box, backend_mail->box);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+virtual_mail_get_stream(struct mail *mail, bool get_body,
+ struct message_size *hdr_size,
+ struct message_size *body_size,
+ struct istream **stream_r)
+{
+ struct virtual_mail *vmail = (struct virtual_mail *)mail;
+ struct mail_private *vp = (struct mail_private *)mail;
+ struct mail *backend_mail;
+ const char *reason = t_strdup_printf("virtual mailbox %s: Opened mail UID=%u: %s",
+ mailbox_get_vname(mail->box), mail->uid, vp->get_stream_reason);
+ int ret;
+
+ if (backend_mail_get(vmail, &backend_mail) < 0)
+ return -1;
+
+ if (get_body) {
+ ret = mail_get_stream_because(backend_mail, hdr_size, body_size,
+ reason, stream_r);
+ } else {
+ ret = mail_get_hdr_stream_because(backend_mail, hdr_size,
+ reason, stream_r);
+ }
+
+ if (ret < 0) {
+ virtual_box_copy_error(mail->box, backend_mail->box);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+virtual_mail_get_binary_stream(struct mail *mail,
+ const struct message_part *part,
+ bool include_hdr, uoff_t *size_r,
+ unsigned int *lines_r, bool *binary_r,
+ struct istream **stream_r)
+{
+ struct virtual_mail *vmail = (struct virtual_mail *)mail;
+ struct mail *backend_mail;
+
+ if (backend_mail_get(vmail, &backend_mail) < 0)
+ return -1;
+
+ struct mail_private *p = (struct mail_private *)backend_mail;
+ if (p->v.get_binary_stream(backend_mail, part, include_hdr,
+ size_r, lines_r, binary_r, stream_r) < 0) {
+ virtual_box_copy_error(mail->box, backend_mail->box);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+virtual_mail_get_special(struct mail *mail, enum mail_fetch_field field,
+ const char **value_r)
+{
+ struct virtual_mail *vmail = (struct virtual_mail *)mail;
+ struct mail *backend_mail;
+
+ if (backend_mail_get(vmail, &backend_mail) < 0)
+ return -1;
+ if (mail_get_special(backend_mail, field, value_r) < 0) {
+ virtual_box_copy_error(mail->box, backend_mail->box);
+ return -1;
+ }
+ return 0;
+}
+
+static int virtual_mail_get_backend_mail(struct mail *mail,
+ struct mail **real_mail_r)
+{
+ struct virtual_mail *vmail = (struct virtual_mail *)mail;
+ struct mail *backend_mail;
+
+ if (backend_mail_get(vmail, &backend_mail) < 0)
+ return -1;
+
+ if (mail_get_backend_mail(backend_mail, real_mail_r) < 0)
+ return -1;
+ return 0;
+}
+
+static void virtual_mail_update_pop3_uidl(struct mail *mail, const char *uidl)
+{
+ struct virtual_mail *vmail = (struct virtual_mail *)mail;
+ struct mail *backend_mail;
+
+ if (backend_mail_get(vmail, &backend_mail) < 0)
+ return;
+ mail_update_pop3_uidl(backend_mail, uidl);
+}
+
+static void virtual_mail_expunge(struct mail *mail)
+{
+ struct virtual_mail *vmail = (struct virtual_mail *)mail;
+ struct mail *backend_mail;
+
+ if (backend_mail_get(vmail, &backend_mail) < 0)
+ return;
+ mail_expunge(backend_mail);
+}
+
+static void
+virtual_mail_set_cache_corrupted(struct mail *mail,
+ enum mail_fetch_field field,
+ const char *reason)
+{
+ struct virtual_mail *vmail = (struct virtual_mail *)mail;
+ struct mail *backend_mail;
+
+ if (backend_mail_get(vmail, &backend_mail) < 0)
+ return;
+ mail_set_cache_corrupted(backend_mail, field, reason);
+}
+
+struct mail_vfuncs virtual_mail_vfuncs = {
+ virtual_mail_close,
+ index_mail_free,
+ virtual_mail_set_seq,
+ virtual_mail_set_uid,
+ virtual_mail_set_uid_cache_updates,
+ virtual_mail_prefetch,
+ virtual_mail_precache,
+ virtual_mail_add_temp_wanted_fields,
+
+ index_mail_get_flags,
+ index_mail_get_keywords,
+ index_mail_get_keyword_indexes,
+ index_mail_get_modseq,
+ index_mail_get_pvt_modseq,
+ virtual_mail_get_parts,
+ virtual_mail_get_date,
+ virtual_mail_get_received_date,
+ virtual_mail_get_save_date,
+ virtual_mail_get_virtual_mail_size,
+ virtual_mail_get_physical_size,
+ virtual_mail_get_first_header,
+ virtual_mail_get_headers,
+ virtual_mail_get_header_stream,
+ virtual_mail_get_stream,
+ virtual_mail_get_binary_stream,
+ virtual_mail_get_special,
+ virtual_mail_get_backend_mail,
+ index_mail_update_flags,
+ index_mail_update_keywords,
+ index_mail_update_modseq,
+ index_mail_update_pvt_modseq,
+ virtual_mail_update_pop3_uidl,
+ virtual_mail_expunge,
+ virtual_mail_set_cache_corrupted,
+ NULL,
+};