maildir-keywords.c revision a75d470c9223a75801418fcdda258885c36317e0
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen/* Copyright (c) 2005-2012 Dovecot authors, see the included COPYING file */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen/* note that everything here depends on uidlist file being locked the whole
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen time. that's why we don't have any locking of our own, or that we do things
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen that would be racy otherwise. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen/* how many seconds to wait before overriding dovecot-keywords.lock */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen HASH_TABLE(char *, void *) hash; /* name -> idx+1 */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int chridx_to_idx[MAILDIR_MAX_KEYWORDS];
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstruct maildir_keywords *maildir_keywords_init(struct maildir_mailbox *mbox)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mk = maildir_keywords_init_readonly(&mbox->box);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenmaildir_keywords_init_readonly(struct mailbox *box)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char *dir;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen dir = mailbox_list_get_path(box->list, box->name,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mk->path = i_strconcat(dir, "/" MAILDIR_KEYWORDS_NAME, NULL);
bd63b5b860658b01b1f46f26d406e1e4a9dc019aTimo Sirainen mk->pool = pool_alloconly_create("maildir keywords", 512);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_array_init(&mk->list, MAILDIR_MAX_KEYWORDS);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen hash_table_create(&mk->hash, mk->pool, 0, strcase_hash, strcasecmp);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mk->dotlock_settings.stale_timeout = KEYWORDS_LOCK_STALE_TIMEOUT;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenvoid maildir_keywords_deinit(struct maildir_keywords **_mk)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic void maildir_keywords_clear(struct maildir_keywords *mk)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int maildir_keywords_sync(struct maildir_keywords *mk)
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen const char **strp;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen unsigned int idx;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* Remember that we rely on uidlist file locking in here. That's why
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen we rely on stat()'s timestamp and don't bother handling ESTALE
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* file is updated only by replacing it, no need to flush
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen attribute cache */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* hasn't changed */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen while ((line = i_stream_read_next_line(input)) != NULL) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* note that when converting .customflags file this
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen case happens in the first line. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* shouldn't happen */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* save it */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen hash_table_insert(mk->hash, new_name, POINTER_CAST(idx + 1));
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenmaildir_keywords_lookup(struct maildir_keywords *mk, const char *name,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int *chridx_r)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen *chridx_r = POINTER_CAST_TO(value, unsigned int)-1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenmaildir_keywords_create(struct maildir_keywords *mk, const char *name,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int chridx)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char **strp;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen hash_table_insert(mk->hash, new_name, POINTER_CAST(chridx + 1));
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen strp = array_idx_modifiable(&mk->list, chridx);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenmaildir_keywords_lookup_or_create(struct maildir_keywords *mk, const char *name,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int *chridx_r)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char *const *keywords;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int i, count;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if ((ret = maildir_keywords_lookup(mk, name, chridx_r)) != 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* see if we are full */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen for (i = 0; i < count; i++) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (i == count && count >= MAILDIR_MAX_KEYWORDS)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (!maildir_uidlist_is_locked(mk->mbox->uidlist))
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic const char *
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenmaildir_keywords_idx(struct maildir_keywords *mk, unsigned int idx)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char *const *keywords;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int count;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int maildir_keywords_write_fd(struct maildir_keywords *mk,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const struct mailbox_permissions *perm = mailbox_get_permissions(box);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char *const *keywords;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int i, count;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen for (i = 0; i < count; i++) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (write_full(fd, str_data(str), str_len(str)) < 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (fchown(fd, (uid_t)-1, perm->file_create_gid) < 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* mtime must grow every time */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mk->synced_mtime = ioloop_time <= mk->synced_mtime ?
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int maildir_keywords_commit(struct maildir_keywords *mk)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen lock_path = t_strconcat(mk->path, ".lock", NULL);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen perm = mailbox_get_permissions(&mk->mbox->box);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen for (i = 0;; i++) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* we could just create the temp file directly, but doing it
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen this ways avoids potential problems with overwriting
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen contents in malicious symlinks */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen old_mask = umask(0777 & ~perm->file_create_mode);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen fd = file_dotlock_open(&mk->dotlock_settings, mk->path,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (errno != ENOENT || i == MAILDIR_DELETE_RETRY_COUNT) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "file_dotlock_open(%s) failed: %m", mk->path);
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen /* the control dir doesn't exist. create it unless the whole
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen mailbox was just deleted. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (maildir_keywords_write_fd(mk, lock_path, fd) < 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "file_dotlock_replace(%s) failed: %m", mk->path);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenmaildir_keywords_sync_init(struct maildir_keywords *mk,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx = i_new(struct maildir_keywords_sync_ctx, 1);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->keywords = mail_index_get_keywords(index);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_array_init(&ctx->idx_to_chr, MAILDIR_MAX_KEYWORDS);
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainenmaildir_keywords_sync_init_readonly(struct maildir_keywords *mk,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenvoid maildir_keywords_sync_deinit(struct maildir_keywords_sync_ctx **_ctx)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct maildir_keywords_sync_ctx *ctx = *_ctx;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenunsigned int maildir_keywords_char_idx(struct maildir_keywords_sync_ctx *ctx,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* lookup / create */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* name is lost. just generate one ourself. */
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen while (maildir_keywords_lookup(ctx->mk, name, &idx) > 0) {
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen /* don't create a duplicate name.
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen keep changing the name until it doesn't exist */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen maildir_keywords_create(ctx->mk, name, chridx);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_index_keyword_lookup_or_create(ctx->index, name, &idx);
0e7d5ff38f28d8c85e197a031bbb66b322ff89e6Timo Sirainenchar maildir_keywords_idx_char(struct maildir_keywords_sync_ctx *ctx,
0e7d5ff38f28d8c85e197a031bbb66b322ff89e6Timo Sirainen unsigned int idx)
0e7d5ff38f28d8c85e197a031bbb66b322ff89e6Timo Sirainen const char *const *name_p;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int chridx;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen chr_p = array_idx_modifiable(&ctx->idx_to_chr, idx);
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen maildir_keywords_lookup_or_create(ctx->mk, *name_p, &chridx) :