-
Notifications
You must be signed in to change notification settings - Fork 271
/
snarf-plugin.c
253 lines (212 loc) · 6.79 KB
/
snarf-plugin.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "unichar.h"
#include "mail-namespace.h"
#include "mail-search-build.h"
#include "mail-storage-private.h"
#include "snarf-plugin.h"
#define SNARF_CONTEXT(obj) \
MODULE_CONTEXT(obj, snarf_storage_module)
#define SNARF_CONTEXT_REQUIRE(obj) \
MODULE_CONTEXT_REQUIRE(obj, snarf_storage_module)
struct snarf_mail_storage {
union mail_storage_module_context module_ctx;
const char *snarf_path;
bool snarfing_disabled;
};
struct snarf_mailbox {
union mailbox_module_context module_ctx;
struct mailbox *snarf_box;
};
const char *snarf_plugin_version = DOVECOT_ABI_VERSION;
static MODULE_CONTEXT_DEFINE_INIT(snarf_storage_module,
&mail_storage_module_register);
static int snarf(struct mailbox *srcbox, struct mailbox *destbox)
{
struct mail_search_args *search_args;
struct mail_search_context *search_ctx;
struct mailbox_transaction_context *src_trans, *dest_trans;
struct mail_save_context *save_ctx;
struct mail *mail;
enum mail_error error;
int ret;
/* make sure the destination mailbox has been opened.
note that this locks the mailbox. */
if (mailbox_open(destbox) < 0)
return -1;
if (mailbox_sync(srcbox, MAILBOX_SYNC_FLAG_FULL_READ) < 0)
return -1;
src_trans = mailbox_transaction_begin(srcbox, 0, "snarf src_trans");
dest_trans = mailbox_transaction_begin(destbox,
MAILBOX_TRANSACTION_FLAG_EXTERNAL,
"snarf dest_trans");
search_args = mail_search_build_init();
mail_search_build_add_all(search_args);
search_ctx = mailbox_search_init(src_trans, search_args, NULL,
MAIL_FETCH_STREAM_HEADER |
MAIL_FETCH_STREAM_BODY, NULL);
mail_search_args_unref(&search_args);
ret = 0;
while (mailbox_search_next(search_ctx, &mail)) {
if (mail->expunged)
continue;
save_ctx = mailbox_save_alloc(dest_trans);
if (mailbox_copy(&save_ctx, mail) < 0 && !mail->expunged) {
error = mailbox_get_last_mail_error(destbox);
/* if we failed because of out of disk space, just
move those messages we managed to move so far. */
if (error != MAIL_ERROR_NOQUOTA)
ret = -1;
break;
}
mail_expunge(mail);
}
if (mailbox_search_deinit(&search_ctx) < 0)
ret = -1;
/* commit the copied messages to the destination mailbox. if we crash
between that and between expunging the messages from the source
mailbox, we're left with duplicates. */
if (ret < 0)
mailbox_transaction_rollback(&dest_trans);
else if (mailbox_transaction_commit(&dest_trans) < 0)
ret = -1;
if (ret < 0)
mailbox_transaction_rollback(&src_trans);
else {
if (mailbox_transaction_commit(&src_trans) < 0)
ret = -1;
}
if (ret == 0) {
if (mailbox_sync(srcbox, 0) < 0)
ret = -1;
}
return ret;
}
static struct mailbox_sync_context *
snarf_sync_init(struct mailbox *box, enum mailbox_sync_flags flags)
{
struct snarf_mailbox *sbox = SNARF_CONTEXT_REQUIRE(box);
(void)snarf(sbox->snarf_box, box);
/* close the mailbox so that we don't have to keep it locked */
(void)mailbox_close(sbox->snarf_box);
return sbox->module_ctx.super.sync_init(box, flags);
}
static void snarf_mailbox_free(struct mailbox *box)
{
struct snarf_mailbox *sbox = SNARF_CONTEXT_REQUIRE(box);
mailbox_free(&sbox->snarf_box);
sbox->module_ctx.super.free(box);
}
static bool
snarf_box_find(struct mail_user *user, struct mailbox_list **list_r,
const char **name_r)
{
struct mail_namespace *snarf_ns;
const char *snarf_name;
snarf_name = mail_user_plugin_getenv(user, "snarf");
if (snarf_name == NULL)
return FALSE;
if (!uni_utf8_str_is_valid(snarf_name)) {
i_error("snarf: Mailbox name not UTF-8: %s", snarf_name);
return FALSE;
}
snarf_ns = mail_namespace_find(user->namespaces, snarf_name);
*list_r = snarf_ns->list;
*name_r = snarf_name;
return TRUE;
}
static void snarf_mailbox_allocated(struct mailbox *box)
{
struct snarf_mail_storage *sstorage = SNARF_CONTEXT(box->storage);
struct mailbox_vfuncs *v = box->vlast;
struct snarf_mailbox *sbox;
struct mailbox_list *snarf_list;
const char *snarf_name;
if (!box->inbox_user)
return;
if (sstorage != NULL && sstorage->snarfing_disabled)
return;
if (!snarf_box_find(box->storage->user, &snarf_list, &snarf_name))
return;
sbox = p_new(box->pool, struct snarf_mailbox, 1);
sbox->module_ctx.super = *v;
box->vlast = &sbox->module_ctx.super;
sbox->snarf_box = mailbox_alloc(snarf_list, snarf_name,
MAILBOX_FLAG_KEEP_LOCKED);
v->sync_init = snarf_sync_init;
v->free = snarf_mailbox_free;
MODULE_CONTEXT_SET(box, snarf_storage_module, sbox);
}
static struct mailbox *
snarf_mailbox_alloc(struct mail_storage *storage,
struct mailbox_list *list,
const char *vname, enum mailbox_flags flags)
{
struct snarf_mail_storage *sstorage = SNARF_CONTEXT_REQUIRE(storage);
struct mail_namespace *ns = mailbox_list_get_namespace(list);
struct mailbox *box;
struct mailbox_list *snarf_list;
const char *snarf_name;
struct stat st;
if (strcmp(vname, "INBOX") == 0 &&
(ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) {
if (stat(sstorage->snarf_path, &st) == 0)
sstorage->snarfing_disabled = FALSE;
else {
if (errno != ENOENT) {
mail_storage_set_critical(storage,
"stat(%s) failed: %m",
sstorage->snarf_path);
}
sstorage->snarfing_disabled = TRUE;
/* use the snarf box as our real INBOX */
if (snarf_box_find(storage->user, &snarf_list,
&snarf_name)) {
list = snarf_list;
vname = snarf_name;
}
}
}
box = sstorage->module_ctx.super.
mailbox_alloc(storage, list, vname, flags);
if (sstorage->snarfing_disabled) {
box->inbox_user = TRUE;
box->inbox_any = TRUE;
}
return box;
}
static void
snarf_mail_storage_create(struct mail_storage *storage, const char *path)
{
struct snarf_mail_storage *mstorage;
struct mail_storage_vfuncs *v = storage->vlast;
path = mail_user_home_expand(storage->user, path);
mstorage = p_new(storage->pool, struct snarf_mail_storage, 1);
mstorage->snarf_path = p_strdup(storage->pool, path);
mstorage->module_ctx.super = *v;
storage->vlast = &mstorage->module_ctx.super;
v->mailbox_alloc = snarf_mailbox_alloc;
MODULE_CONTEXT_SET(storage, snarf_storage_module, mstorage);
}
static void snarf_mail_storage_created(struct mail_storage *storage)
{
const char *path;
/* snarfing is optional: do it only if the path specified
by mbox_snarf exists */
path = mail_user_plugin_getenv(storage->user, "mbox_snarf");
if (path != NULL)
snarf_mail_storage_create(storage, path);
}
static struct mail_storage_hooks snarf_mail_storage_hooks = {
.mailbox_allocated = snarf_mailbox_allocated,
.mail_storage_created = snarf_mail_storage_created
};
void snarf_plugin_init(struct module *module)
{
mail_storage_hooks_add(module, &snarf_mail_storage_hooks);
}
void snarf_plugin_deinit(void)
{
mail_storage_hooks_remove(&snarf_mail_storage_hooks);
}