~ubuntu-branches/ubuntu/trusty/dovecot/trusty-updates

« back to all changes in this revision

Viewing changes to src/doveadm/dsync/dsync-mailbox-tree-fill.c

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2014-01-08 09:35:49 UTC
  • mfrom: (1.15.3) (96.1.1 trusty-proposed)
  • Revision ID: package-import@ubuntu.com-20140108093549-814nkqdcxfbvgktg
Tags: 1:2.2.9-1ubuntu1
* Merge from Debian unstable, remaining changes:
  + Add mail-stack-delivery package:
    - Update d/rules
    - d/control: convert existing dovecot-postfix package to a dummy
      package and add new mail-stack-delivery package.
    - Update maintainer scripts.
    - Rename d/dovecot-postfix.* to debian/mail-stack-delivery.*
    - d/mail-stack-delivery.preinst: Move previously installed backups and
      config files to a new package namespace.
    - d/mail-stack-delivery.prerm: Added to handle downgrades.
  + Use Snakeoil SSL certificates by default:
    - d/control: Depend on ssl-cert.
    - d/dovecot-core.postinst: Relax grep for SSL_* a bit.
  + Add autopkgtest to debian/tests/*.
  + Add ufw integration:
    - d/dovecot-core.ufw.profile: new ufw profile.
    - d/rules: install profile in dovecot-core.
    - d/control: dovecot-core - suggest ufw.
  + d/dovecot-core.dirs: Added usr/share/doc/dovecot-core
  + Add apport hook:
    - d/rules, d/source_dovecot.py
  + Add upstart job:
    - d/rules, d/dovecot-core.dovecot.upstart, d/control,
      d/dovecot-core.dirs, dovecot-imapd.{postrm, postinst, prerm},
      d/dovecot-pop3d.{postinst, postrm, prerm}.
      d/mail-stack-deliver.postinst: Convert init script to upstart.
  + Use the autotools-dev dh addon to update config.guess/config.sub for
    arm64.
* Dropped changes, included in Debian:
  - Update Dovecot name to reflect distribution in login greeting.
  - Update Drac plugin for >= 2.0.0 support.
* d/control: Drop dovecot-postfix package as its no longer required.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
 
2
 
 
3
#include "lib.h"
 
4
#include "array.h"
 
5
#include "hash.h"
 
6
#include "guid.h"
 
7
#include "str.h"
 
8
#include "wildcard-match.h"
 
9
#include "mailbox-log.h"
 
10
#include "mail-namespace.h"
 
11
#include "mail-storage.h"
 
12
#include "mailbox-list-iter.h"
 
13
#include "dsync-mailbox-tree-private.h"
 
14
 
 
15
static int
 
16
dsync_mailbox_tree_add_node(struct dsync_mailbox_tree *tree,
 
17
                            const struct mailbox_info *info,
 
18
                            struct dsync_mailbox_node **node_r)
 
19
{
 
20
        struct dsync_mailbox_node *node;
 
21
 
 
22
        node = dsync_mailbox_tree_get(tree, info->vname);
 
23
        if (node->ns != info->ns) {
 
24
                i_assert(node->ns != NULL);
 
25
 
 
26
                i_error("Mailbox '%s' exists in two namespaces: '%s' and '%s'",
 
27
                        info->vname, node->ns->prefix, info->ns->prefix);
 
28
                return -1;
 
29
        }
 
30
        *node_r = node;
 
31
        return 0;
 
32
}
 
33
 
 
34
static int
 
35
dsync_mailbox_tree_add_exists_node(struct dsync_mailbox_tree *tree,
 
36
                                   const struct mailbox_info *info,
 
37
                                   struct dsync_mailbox_node **node_r)
 
38
{
 
39
        if (dsync_mailbox_tree_add_node(tree, info, node_r) < 0)
 
40
                return -1;
 
41
        (*node_r)->existence = DSYNC_MAILBOX_NODE_EXISTS;
 
42
        return 0;
 
43
}
 
44
 
 
45
static int
 
46
dsync_mailbox_tree_get_selectable(struct mailbox *box,
 
47
                                  struct mailbox_metadata *metadata_r,
 
48
                                  struct mailbox_status *status_r)
 
49
{
 
50
        /* try the fast path */
 
51
        if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, metadata_r) < 0)
 
52
                return -1;
 
53
        if (mailbox_get_status(box, STATUS_UIDVALIDITY | STATUS_UIDNEXT, status_r) < 0)
 
54
                return -1;
 
55
 
 
56
        i_assert(!guid_128_is_empty(metadata_r->guid));
 
57
        if (status_r->uidvalidity != 0)
 
58
                return 0;
 
59
 
 
60
        /* no UIDVALIDITY assigned yet. syncing a mailbox should add it. */
 
61
        if (mailbox_sync(box, 0) < 0)
 
62
                return -1;
 
63
        if (mailbox_get_status(box, STATUS_UIDVALIDITY | STATUS_UIDNEXT, status_r) < 0)
 
64
                return -1;
 
65
        i_assert(status_r->uidvalidity != 0);
 
66
        return 0;
 
67
}
 
68
 
 
69
static int dsync_mailbox_tree_add(struct dsync_mailbox_tree *tree,
 
70
                                  const struct mailbox_info *info,
 
71
                                  const guid_128_t box_guid)
 
72
{
 
73
        struct dsync_mailbox_node *node;
 
74
        struct mailbox *box;
 
75
        struct mailbox_metadata metadata;
 
76
        struct mailbox_status status;
 
77
        const char *errstr;
 
78
        enum mail_error error;
 
79
        int ret = 0;
 
80
 
 
81
        if ((info->flags & MAILBOX_NONEXISTENT) != 0)
 
82
                return 0;
 
83
        if ((info->flags & MAILBOX_NOSELECT) != 0) {
 
84
                return !guid_128_is_empty(box_guid) ? 0 :
 
85
                        dsync_mailbox_tree_add_exists_node(tree, info, &node);
 
86
        }
 
87
 
 
88
        /* get GUID and UIDVALIDITY for selectable mailbox */
 
89
        box = mailbox_alloc(info->ns->list, info->vname, 0);
 
90
        if (dsync_mailbox_tree_get_selectable(box, &metadata, &status) < 0) {
 
91
                errstr = mailbox_get_last_error(box, &error);
 
92
                switch (error) {
 
93
                case MAIL_ERROR_NOTFOUND:
 
94
                        /* mailbox was just deleted? */
 
95
                        break;
 
96
                case MAIL_ERROR_NOTPOSSIBLE:
 
97
                        /* invalid mbox files? ignore */
 
98
                        break;
 
99
                default:
 
100
                        i_error("Failed to access mailbox %s: %s",
 
101
                                info->vname, errstr);
 
102
                        ret = -1;
 
103
                }
 
104
                mailbox_free(&box);
 
105
                return ret;
 
106
        }
 
107
        mailbox_free(&box);
 
108
 
 
109
        if (!guid_128_is_empty(box_guid) &&
 
110
            !guid_128_equals(box_guid, metadata.guid)) {
 
111
                /* unwanted mailbox */
 
112
                return 0;
 
113
        }
 
114
        if (dsync_mailbox_tree_add_exists_node(tree, info, &node) < 0)
 
115
                return -1;
 
116
        memcpy(node->mailbox_guid, metadata.guid,
 
117
               sizeof(node->mailbox_guid));
 
118
        node->uid_validity = status.uidvalidity;
 
119
        node->uid_next = status.uidnext;
 
120
        return 0;
 
121
}
 
122
 
 
123
static struct dsync_mailbox_node *
 
124
dsync_mailbox_tree_find_sha(struct dsync_mailbox_tree *tree,
 
125
                            struct mail_namespace *ns, const guid_128_t sha128)
 
126
{
 
127
        struct dsync_mailbox_node *node;
 
128
 
 
129
        if (!hash_table_is_created(tree->name128_hash))
 
130
                dsync_mailbox_tree_build_name128_hash(tree);
 
131
 
 
132
        node = hash_table_lookup(tree->name128_hash, sha128);
 
133
        return node == NULL || node->ns != ns ? NULL : node;
 
134
}
 
135
 
 
136
static int
 
137
dsync_mailbox_tree_add_change_timestamps(struct dsync_mailbox_tree *tree,
 
138
                                         struct mail_namespace *ns)
 
139
{
 
140
        struct dsync_mailbox_node *node;
 
141
        struct dsync_mailbox_delete *del;
 
142
        struct mailbox_log *log;
 
143
        struct mailbox_log_iter *iter;
 
144
        const struct mailbox_log_record *rec;
 
145
        const uint8_t *guid_p;
 
146
        time_t timestamp;
 
147
 
 
148
        log = mailbox_list_get_changelog(ns->list);
 
149
        if (log == NULL)
 
150
                return 0;
 
151
 
 
152
        iter = mailbox_log_iter_init(log);
 
153
        while ((rec = mailbox_log_iter_next(iter)) != NULL) {
 
154
                node = rec->type == MAILBOX_LOG_RECORD_DELETE_MAILBOX ? NULL :
 
155
                        dsync_mailbox_tree_find_sha(tree, ns, rec->mailbox_guid);
 
156
 
 
157
                timestamp = mailbox_log_record_get_timestamp(rec);
 
158
                switch (rec->type) {
 
159
                case MAILBOX_LOG_RECORD_DELETE_MAILBOX:
 
160
                        guid_p = rec->mailbox_guid;
 
161
                        if (hash_table_lookup(tree->guid_hash, guid_p) != NULL) {
 
162
                                /* mailbox still exists. maybe it was restored
 
163
                                   from backup or something. */
 
164
                                break;
 
165
                        }
 
166
                        del = array_append_space(&tree->deletes);
 
167
                        del->type = DSYNC_MAILBOX_DELETE_TYPE_MAILBOX;
 
168
                        del->timestamp = timestamp;
 
169
                        memcpy(del->guid, rec->mailbox_guid, sizeof(del->guid));
 
170
                        break;
 
171
                case MAILBOX_LOG_RECORD_DELETE_DIR:
 
172
                        if (node != NULL) {
 
173
                                /* directory exists again, skip it */
 
174
                                break;
 
175
                        }
 
176
                        /* we don't know what directory name was deleted,
 
177
                           just its hash. if the name still exists on the other
 
178
                           dsync side, it can match this deletion to the
 
179
                           name. */
 
180
                        del = array_append_space(&tree->deletes);
 
181
                        del->type = DSYNC_MAILBOX_DELETE_TYPE_DIR;
 
182
                        del->timestamp = timestamp;
 
183
                        memcpy(del->guid, rec->mailbox_guid, sizeof(del->guid));
 
184
                        break;
 
185
                case MAILBOX_LOG_RECORD_CREATE_DIR:
 
186
                        if (node == NULL) {
 
187
                                /* directory has been deleted again, skip it */
 
188
                                break;
 
189
                        }
 
190
                        /* notify the remote that we want to keep this
 
191
                           directory created (unless remote has a newer delete
 
192
                           timestamp) */
 
193
                        node->last_renamed_or_created = timestamp;
 
194
                        break;
 
195
                case MAILBOX_LOG_RECORD_RENAME:
 
196
                        if (node != NULL)
 
197
                                node->last_renamed_or_created = timestamp;
 
198
                        break;
 
199
                case MAILBOX_LOG_RECORD_SUBSCRIBE:
 
200
                        if (node != NULL)
 
201
                                node->last_subscription_change = timestamp;
 
202
                        break;
 
203
                case MAILBOX_LOG_RECORD_UNSUBSCRIBE:
 
204
                        if (node != NULL) {
 
205
                                node->last_subscription_change = timestamp;
 
206
                                break;
 
207
                        }
 
208
                        /* The mailbox is already deleted, but it may still
 
209
                           exist on the other side (even the subscription
 
210
                           alone). */
 
211
                        del = array_append_space(&tree->deletes);
 
212
                        del->type = DSYNC_MAILBOX_DELETE_TYPE_UNSUBSCRIBE;
 
213
                        del->timestamp = timestamp;
 
214
                        memcpy(del->guid, rec->mailbox_guid, sizeof(del->guid));
 
215
                        break;
 
216
                }
 
217
        }
 
218
        if (mailbox_log_iter_deinit(&iter) < 0) {
 
219
                i_error("Mailbox log iteration for namespace '%s' failed",
 
220
                        ns->prefix);
 
221
                return -1;
 
222
        }
 
223
        return 0;
 
224
}
 
225
 
 
226
static int
 
227
dsync_mailbox_tree_fix_guid_duplicate(struct dsync_mailbox_tree *tree,
 
228
                                      struct dsync_mailbox_node *node1,
 
229
                                      struct dsync_mailbox_node *node2)
 
230
{
 
231
        struct mailbox *box;
 
232
        struct mailbox_update update;
 
233
        struct dsync_mailbox_node *change_node;
 
234
        int ret = 0;
 
235
 
 
236
        memset(&update, 0, sizeof(update));
 
237
        guid_128_generate(update.mailbox_guid);
 
238
 
 
239
        /* just in case the duplication exists in both sides,
 
240
           make them choose the same node */
 
241
        if (strcmp(dsync_mailbox_node_get_full_name(tree, node1),
 
242
                   dsync_mailbox_node_get_full_name(tree, node2)) <= 0)
 
243
                change_node = node1;
 
244
        else
 
245
                change_node = node2;
 
246
 
 
247
        i_error("Duplicate mailbox GUID %s for mailboxes %s and %s - "
 
248
                "giving a new GUID %s to %s",
 
249
                guid_128_to_string(node1->mailbox_guid),
 
250
                dsync_mailbox_node_get_full_name(tree, node1),
 
251
                dsync_mailbox_node_get_full_name(tree, node2),
 
252
                guid_128_to_string(update.mailbox_guid),
 
253
                dsync_mailbox_node_get_full_name(tree, change_node));
 
254
 
 
255
        i_assert(node1->ns != NULL && node2->ns != NULL);
 
256
        box = mailbox_alloc(change_node->ns->list, change_node->name, 0);
 
257
        if (mailbox_update(box, &update) < 0) {
 
258
                i_error("Couldn't update mailbox %s GUID: %s",
 
259
                        change_node->name, mailbox_get_last_error(box, NULL));
 
260
                ret = -1;
 
261
        } else {
 
262
                memcpy(change_node->mailbox_guid, update.mailbox_guid,
 
263
                       sizeof(change_node->mailbox_guid));
 
264
        }
 
265
        mailbox_free(&box);
 
266
        return ret;
 
267
}
 
268
 
 
269
static bool
 
270
dsync_mailbox_info_is_excluded(const struct mailbox_info *info,
 
271
                               const char *const *exclude_mailboxes)
 
272
{
 
273
        const char *const *info_specialuses;
 
274
        unsigned int i;
 
275
 
 
276
        if (exclude_mailboxes == NULL)
 
277
                return FALSE;
 
278
 
 
279
        info_specialuses = info->special_use == NULL ? NULL :
 
280
                t_strsplit(info->special_use, " ");
 
281
        for (i = 0; exclude_mailboxes[i] != NULL; i++) {
 
282
                const char *exclude = exclude_mailboxes[i];
 
283
 
 
284
                if (exclude[0] == '\\') {
 
285
                        /* special-use */
 
286
                        if (info_specialuses != NULL &&
 
287
                            str_array_icase_find(info_specialuses, exclude))
 
288
                                return TRUE;
 
289
                } else {
 
290
                        /* mailbox with wildcards */
 
291
                        if (wildcard_match(info->vname, exclude))
 
292
                                return TRUE;
 
293
                }
 
294
        }
 
295
        return FALSE;
 
296
}
 
297
 
 
298
int dsync_mailbox_tree_fill(struct dsync_mailbox_tree *tree,
 
299
                            struct mail_namespace *ns, const char *box_name,
 
300
                            const guid_128_t box_guid,
 
301
                            const char *const *exclude_mailboxes)
 
302
{
 
303
        const enum mailbox_list_iter_flags list_flags =
 
304
                /* FIXME: we'll skip symlinks, because we can't handle them
 
305
                   currently. in future we could detect them and create them
 
306
                   by creating the symlink. */
 
307
                MAILBOX_LIST_ITER_SKIP_ALIASES |
 
308
                MAILBOX_LIST_ITER_NO_AUTO_BOXES;
 
309
        const enum mailbox_list_iter_flags subs_list_flags =
 
310
                MAILBOX_LIST_ITER_NO_AUTO_BOXES |
 
311
                MAILBOX_LIST_ITER_SELECT_SUBSCRIBED |
 
312
                MAILBOX_LIST_ITER_RETURN_NO_FLAGS;
 
313
        struct mailbox_list_iterate_context *iter;
 
314
        struct dsync_mailbox_node *node, *dup_node1, *dup_node2;
 
315
        const struct mailbox_info *info;
 
316
        const char *list_pattern = box_name != NULL ? box_name : "*";
 
317
        int ret = 0;
 
318
 
 
319
        i_assert(mail_namespace_get_sep(ns) == tree->sep);
 
320
 
 
321
        /* assign namespace to its root, so it gets copied to children */
 
322
        if (ns->prefix_len > 0) {
 
323
                node = dsync_mailbox_tree_get(tree,
 
324
                        t_strndup(ns->prefix, ns->prefix_len-1));
 
325
                node->ns = ns;
 
326
        } else {
 
327
                tree->root.ns = ns;
 
328
        }
 
329
 
 
330
        /* first add all of the existing mailboxes */
 
331
        iter = mailbox_list_iter_init(ns->list, list_pattern, list_flags);
 
332
        while ((info = mailbox_list_iter_next(iter)) != NULL) T_BEGIN {
 
333
                if (!dsync_mailbox_info_is_excluded(info, exclude_mailboxes)) {
 
334
                        if (dsync_mailbox_tree_add(tree, info, box_guid) < 0)
 
335
                                ret = -1;
 
336
                }
 
337
        } T_END;
 
338
        if (mailbox_list_iter_deinit(&iter) < 0) {
 
339
                i_error("Mailbox listing for namespace '%s' failed", ns->prefix);
 
340
                ret = -1;
 
341
        }
 
342
 
 
343
        /* add subscriptions */
 
344
        iter = mailbox_list_iter_init(ns->list, list_pattern, subs_list_flags);
 
345
        while ((info = mailbox_list_iter_next(iter)) != NULL) {
 
346
                if (dsync_mailbox_tree_add_node(tree, info, &node) < 0)
 
347
                        ret = -1;
 
348
                else
 
349
                        node->subscribed = TRUE;
 
350
        }
 
351
        if (mailbox_list_iter_deinit(&iter) < 0) {
 
352
                i_error("Mailbox listing for namespace '%s' failed", ns->prefix);
 
353
                ret = -1;
 
354
        }
 
355
        if (ret < 0)
 
356
                return -1;
 
357
 
 
358
        while (dsync_mailbox_tree_build_guid_hash(tree, &dup_node1,
 
359
                                                  &dup_node2) < 0) {
 
360
                if (dsync_mailbox_tree_fix_guid_duplicate(tree, dup_node1, dup_node2) < 0)
 
361
                        return -1;
 
362
        }
 
363
 
 
364
        /* add timestamps */
 
365
        if (dsync_mailbox_tree_add_change_timestamps(tree, ns) < 0)
 
366
                return -1;
 
367
        return 0;
 
368
}