static int
index_storage_attribute_get_dict_trans(struct mailbox_transaction_context *t,
				       enum mail_attribute_type type,
				       struct dict_transaction_context **dtrans_r,
				       const char **mailbox_prefix_r)
{
	struct dict_transaction_context **dtransp = NULL;
	struct dict *dict;
	struct mailbox_metadata metadata;

	switch (type) {
	case MAIL_ATTRIBUTE_TYPE_PRIVATE:
		dtransp = &t->attr_pvt_trans;
		break;
	case MAIL_ATTRIBUTE_TYPE_SHARED:
		dtransp = &t->attr_shared_trans;
		break;
	}
	i_assert(dtransp != NULL);

	if (*dtransp != NULL) {
		/* transaction already created */
		if (mailbox_get_metadata(t->box, MAILBOX_METADATA_GUID,
					 &metadata) < 0)
			return -1;
		*mailbox_prefix_r = guid_128_to_string(metadata.guid);
		*dtrans_r = *dtransp;
		return 0;
	}

	if (index_storage_get_dict(t->box, type, &dict, mailbox_prefix_r) < 0)
		return -1;
	*dtransp = *dtrans_r = dict_transaction_begin(dict);
	return 0;
}
static int
index_storage_get_dict(struct mailbox *box, enum mail_attribute_type type,
		       struct dict **dict_r, const char **mailbox_prefix_r)
{
	struct mail_storage *storage = box->storage;
	struct mail_namespace *ns;
	struct mailbox_metadata metadata;
	struct dict_settings set;
	const char *error;

	if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) < 0)
		return -1;
	*mailbox_prefix_r = guid_128_to_string(metadata.guid);

	ns = mailbox_get_namespace(box);
	if (type == MAIL_ATTRIBUTE_TYPE_PRIVATE) {
		/* private attributes are stored in user's own dict */
		return index_storage_get_user_dict(storage, storage->user, dict_r);
	} else if (ns->user == ns->owner) {
		/* user owns the mailbox. shared attributes are stored in
		   the same dict. */
		return index_storage_get_user_dict(storage, storage->user, dict_r);
	} else if (ns->owner != NULL) {
		/* accessing shared attribute of a shared mailbox.
		   use the owner's dict. */
		return index_storage_get_user_dict(storage, ns->owner, dict_r);
	}

	/* accessing shared attributes of a public mailbox. no user owns it,
	   so use the storage's dict. */
	if (storage->_shared_attr_dict != NULL) {
		*dict_r = storage->_shared_attr_dict;
		return 0;
	}
	if (*storage->set->mail_attribute_dict == '\0') {
		mail_storage_set_error(storage, MAIL_ERROR_NOTPOSSIBLE,
				       "Mailbox attributes not enabled");
		return -1;
	}
	if (storage->shared_attr_dict_failed) {
		mail_storage_set_internal_error(storage);
		return -1;
	}

	memset(&set, 0, sizeof(set));
	set.username = storage->user->username;
	set.base_dir = storage->user->set->base_dir;
	if (mail_user_get_home(storage->user, &set.home_dir) <= 0)
		set.home_dir = NULL;
	if (dict_init_full(storage->set->mail_attribute_dict, &set,
			   &storage->_shared_attr_dict, &error) < 0) {
		mail_storage_set_critical(storage,
			"mail_attribute_dict: dict_init(%s) failed: %s",
			storage->set->mail_attribute_dict, error);
		storage->shared_attr_dict_failed = TRUE;
		return -1;
	}
	*dict_r = storage->_shared_attr_dict;
	return 0;
}
Exemple #3
0
static int
index_mailbox_precache(struct master_connection *conn, struct mailbox *box)
{
    struct mail_storage *storage = mailbox_get_storage(box);
    const char *username = mail_storage_get_user(storage)->username;
    const char *box_vname = mailbox_get_vname(box);
    struct mailbox_status status;
    struct mailbox_transaction_context *trans;
    struct mail_search_args *search_args;
    struct mail_search_context *ctx;
    struct mail *mail;
    struct mailbox_metadata metadata;
    uint32_t seq;
    char percentage_str[2+1+1];
    unsigned int counter = 0, max, percentage, percentage_sent = 0;
    int ret = 0;

    if (mailbox_get_metadata(box, MAILBOX_METADATA_PRECACHE_FIELDS,
                             &metadata) < 0 ||
            mailbox_get_status(box, STATUS_MESSAGES | STATUS_LAST_CACHED_SEQ,
                               &status) < 0)
        return -1;
    seq = status.last_cached_seq + 1;

    trans = mailbox_transaction_begin(box, MAILBOX_TRANSACTION_FLAG_NO_CACHE_DEC);
    search_args = mail_search_build_init();
    mail_search_build_add_seqset(search_args, seq, status.messages);
    ctx = mailbox_search_init(trans, search_args, NULL,
                              metadata.precache_fields, NULL);
    mail_search_args_unref(&search_args);

    max = status.messages - seq + 1;
    while (mailbox_search_next(ctx, &mail)) {
        mail_precache(mail);
        if (++counter % 100 == 0) {
            percentage = counter*100 / max;
            if (percentage != percentage_sent && percentage < 100) {
                percentage_sent = percentage;
                if (i_snprintf(percentage_str,
                               sizeof(percentage_str), "%u\n",
                               percentage) < 0)
                    i_unreached();
                (void)write_full(conn->fd, percentage_str,
                                 strlen(percentage_str));
            }
            indexer_worker_refresh_proctitle(username, box_vname,
                                             counter, max);
        }
    }
    if (mailbox_search_deinit(&ctx) < 0)
        ret = -1;
    if (mailbox_transaction_commit(&trans) < 0)
        ret = -1;
    if (ret == 0) {
        i_info("Indexed %u messages in %s",
               counter, mailbox_get_vname(box));
    }
    return ret;
}
Exemple #4
0
void mailbox_guid_cache_refresh(struct mailbox_list *list)
{
	struct mailbox_list_iterate_context *ctx;
	const struct mailbox_info *info;
	struct mailbox *box;
	struct mailbox_metadata metadata;
	struct mailbox_guid_cache_rec *rec;
	uint8_t *guid_p;

	if (!hash_table_is_created(list->guid_cache)) {
		list->guid_cache_pool =
			pool_alloconly_create("guid cache", 1024*16);
		hash_table_create(&list->guid_cache, list->guid_cache_pool, 0,
				  guid_128_hash, guid_128_cmp);
	} else {
		hash_table_clear(list->guid_cache, TRUE);
		p_clear(list->guid_cache_pool);
	}
	list->guid_cache_invalidated = FALSE;
	list->guid_cache_updated = FALSE;
	list->guid_cache_errors = FALSE;

	ctx = mailbox_list_iter_init(list, "*",
				     MAILBOX_LIST_ITER_SKIP_ALIASES |
				     MAILBOX_LIST_ITER_NO_AUTO_BOXES);
	while ((info = mailbox_list_iter_next(ctx)) != NULL) {
		if ((info->flags &
		     (MAILBOX_NOSELECT | MAILBOX_NONEXISTENT)) != 0)
			continue;

		box = mailbox_alloc(list, info->vname, 0);
		if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID,
					 &metadata) < 0) {
			i_error("Couldn't get mailbox %s GUID: %s",
				info->vname, mailbox_get_last_internal_error(box, NULL));
			list->guid_cache_errors = TRUE;
		} else if ((rec = hash_table_lookup(list->guid_cache,
				(const uint8_t *)metadata.guid)) != NULL) {
			i_warning("Mailbox %s has duplicate GUID with %s: %s",
				  info->vname, rec->vname,
				  guid_128_to_string(metadata.guid));
		} else {
			rec = p_new(list->guid_cache_pool,
				    struct mailbox_guid_cache_rec, 1);
			memcpy(rec->guid, metadata.guid, sizeof(rec->guid));
			rec->vname = p_strdup(list->guid_cache_pool, info->vname);
			guid_p = rec->guid;
			hash_table_insert(list->guid_cache, guid_p, rec);
		}
		mailbox_free(&box);
	}
	if (mailbox_list_iter_deinit(&ctx) < 0)
		list->guid_cache_errors = TRUE;
}
static int dsync_box_get(struct mailbox *box, struct dsync_mailbox *dsync_box_r)
{
	const enum mailbox_status_items status_items =
		STATUS_UIDVALIDITY | STATUS_UIDNEXT | STATUS_MESSAGES |
		STATUS_FIRST_RECENT_UID | STATUS_HIGHESTMODSEQ |
		STATUS_HIGHESTPVTMODSEQ;
	const enum mailbox_metadata_items metadata_items =
		MAILBOX_METADATA_CACHE_FIELDS | MAILBOX_METADATA_GUID;
	struct mailbox_status status;
	struct mailbox_metadata metadata;
	const char *errstr;
	enum mail_error error;

	/* get metadata first, since it may autocreate the mailbox */
	if (mailbox_get_metadata(box, metadata_items, &metadata) < 0 ||
	    mailbox_get_status(box, status_items, &status) < 0) {
		errstr = mailbox_get_last_error(box, &error);
		if (error == MAIL_ERROR_NOTFOUND ||
		    error == MAIL_ERROR_NOTPOSSIBLE) {
			/* Mailbox isn't selectable, try the next one. We
			   should have already caught \Noselect mailboxes, but
			   check them anyway here. The NOTPOSSIBLE check is
			   mainly for invalid mbox files. */
			return 0;
		}
		i_error("Failed to access mailbox %s: %s",
			mailbox_get_vname(box), errstr);
		return -1;
	}

	i_assert(status.uidvalidity != 0 || status.messages == 0);

	memset(dsync_box_r, 0, sizeof(*dsync_box_r));
	memcpy(dsync_box_r->mailbox_guid, metadata.guid,
	       sizeof(dsync_box_r->mailbox_guid));
	dsync_box_r->uid_validity = status.uidvalidity;
	dsync_box_r->uid_next = status.uidnext;
	dsync_box_r->messages_count = status.messages;
	dsync_box_r->first_recent_uid = status.first_recent_uid;
	dsync_box_r->highest_modseq = status.highest_modseq;
	dsync_box_r->highest_pvt_modseq = status.highest_pvt_modseq;
	dsync_box_r->cache_fields = *metadata.cache_fields;
	dsync_box_r->have_guids = status.have_guids;
	dsync_box_r->have_save_guids = status.have_save_guids;
	dsync_box_r->have_only_guid128 = status.have_only_guid128;
	return 1;
}
static int
quota_count_mailbox(struct quota_root *root, struct mail_namespace *ns,
		    const char *vname, uint64_t *bytes, uint64_t *count)
{
	struct quota_rule *rule;
	struct mailbox *box;
	struct mailbox_metadata metadata;
	struct mailbox_status status;
	enum mail_error error;
	const char *errstr;
	int ret;

	rule = quota_root_rule_find(root->set, vname);
	if (rule != NULL && rule->ignore) {
		/* mailbox not included in quota */
		return 0;
	}

	box = mailbox_alloc(ns->list, vname, MAILBOX_FLAG_READONLY);
	if ((box->storage->class_flags & MAIL_STORAGE_CLASS_FLAG_NOQUOTA) != 0) {
		/* quota doesn't exist for this mailbox/storage */
		ret = 0;
	} else if (mailbox_get_metadata(box, root->quota->set->vsizes ?
					MAILBOX_METADATA_VIRTUAL_SIZE :
					MAILBOX_METADATA_PHYSICAL_SIZE,
					&metadata) < 0 ||
	    mailbox_get_status(box, STATUS_MESSAGES, &status) < 0) {
		errstr = mailbox_get_last_error(box, &error);
		if (error == MAIL_ERROR_TEMP) {
			i_error("quota: Couldn't get size of mailbox %s: %s",
				vname, errstr);
			ret = -1;
		} else {
			/* non-temporary error, e.g. ACLs denied access. */
			ret = 0;
		}
	} else {
		ret = 1;
		*bytes += root->quota->set->vsizes ?
			metadata.virtual_size : metadata.physical_size;
		*count += status.messages;
	}
	mailbox_free(&box);
	return ret;
}
static int
status_mailbox(struct status_cmd_context *ctx, const struct mailbox_info *info)
{
	struct mailbox *box;
	struct mailbox_status status;
	struct mailbox_metadata metadata;

	box = doveadm_mailbox_find(ctx->ctx.cur_mail_user, info->vname);
	if (mailbox_get_status(box, ctx->status_items, &status) < 0 ||
	    mailbox_get_metadata(box, ctx->metadata_items, &metadata) < 0) {
		doveadm_mail_failed_mailbox(&ctx->ctx, box);
		mailbox_free(&box);
		return -1;
	}
	if (!ctx->total_sum)
		status_output(ctx, box, &status, &metadata);
	else
		status_sum(ctx, &status, &metadata);
	mailbox_free(&box);
	return 0;
}
Exemple #8
0
int imap_status_get(struct client_command_context *cmd,
		    struct mail_namespace *ns, const char *mailbox,
		    const struct imap_status_items *items,
		    struct imap_status_result *result_r)
{
	struct client *client = cmd->client;
	struct mailbox *box;
	const char *errstr;
	int ret = 0;

	if (client->mailbox != NULL &&
	    mailbox_equals(client->mailbox, ns, mailbox)) {
		/* this mailbox is selected */
		box = client->mailbox;
	} else {
		/* open the mailbox */
		box = mailbox_alloc(ns->list, mailbox, MAILBOX_FLAG_READONLY);
		mailbox_set_reason(box, "STATUS");
		if (client->enabled_features != 0)
			(void)mailbox_enable(box, client->enabled_features);
	}

	if ((items->status & STATUS_HIGHESTMODSEQ) != 0)
		(void)client_enable(client, MAILBOX_FEATURE_CONDSTORE);

	ret = mailbox_get_status(box, items->status, &result_r->status);
	if (items->metadata != 0 && ret == 0) {
		ret = mailbox_get_metadata(box, items->metadata,
					   &result_r->metadata);
	}

	if (ret < 0) {
		errstr = mailbox_get_last_error(box, &result_r->error);
		result_r->errstr = imap_get_error_string(cmd, errstr,
							 result_r->error);
	}
	if (box != client->mailbox)
		mailbox_free(&box);
	return ret;
}
static int
dsync_mailbox_tree_get_selectable(struct mailbox *box,
				  struct mailbox_metadata *metadata_r,
				  struct mailbox_status *status_r)
{
	/* try the fast path */
	if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, metadata_r) < 0)
		return -1;
	if (mailbox_get_status(box, STATUS_UIDVALIDITY | STATUS_UIDNEXT, status_r) < 0)
		return -1;

	i_assert(!guid_128_is_empty(metadata_r->guid));
	if (status_r->uidvalidity != 0)
		return 0;

	/* no UIDVALIDITY assigned yet. syncing a mailbox should add it. */
	if (mailbox_sync(box, 0) < 0)
		return -1;
	if (mailbox_get_status(box, STATUS_UIDVALIDITY | STATUS_UIDNEXT, status_r) < 0)
		return -1;
	i_assert(status_r->uidvalidity != 0);
	return 0;
}
Exemple #10
0
static int
index_mailbox_precache(struct master_connection *conn, struct mailbox *box)
{
	struct mail_storage *storage = mailbox_get_storage(box);
	const char *username = mail_storage_get_user(storage)->username;
	const char *box_vname = mailbox_get_vname(box);
	struct mailbox_status status;
	struct mailbox_transaction_context *trans;
	struct mail_search_args *search_args;
	struct mail_search_context *ctx;
	struct mail *mail;
	struct mailbox_metadata metadata;
	uint32_t seq, first_uid = 0, last_uid = 0;
	char percentage_str[2+1+1];
	unsigned int counter = 0, max, percentage, percentage_sent = 0;
	int ret = 0;

	if (mailbox_get_metadata(box, MAILBOX_METADATA_PRECACHE_FIELDS,
				 &metadata) < 0) {
		i_error("Mailbox %s: Precache-fields lookup failed: %s",
			mailbox_get_vname(box),
			mailbox_get_last_internal_error(box, NULL));
		return -1;
	}
	if (mailbox_get_status(box, STATUS_MESSAGES | STATUS_LAST_CACHED_SEQ,
			       &status) < 0) {
		i_error("Mailbox %s: Status lookup failed: %s",
			mailbox_get_vname(box),
			mailbox_get_last_internal_error(box, NULL));
		return -1;
	}
	seq = status.last_cached_seq + 1;

	trans = mailbox_transaction_begin(box, MAILBOX_TRANSACTION_FLAG_NO_CACHE_DEC,
					  "indexing");
	search_args = mail_search_build_init();
	mail_search_build_add_seqset(search_args, seq, status.messages);
	ctx = mailbox_search_init(trans, search_args, NULL,
				  metadata.precache_fields, NULL);
	mail_search_args_unref(&search_args);

	max = status.messages + 1 - seq;
	while (mailbox_search_next(ctx, &mail)) {
		if (first_uid == 0)
			first_uid = mail->uid;
		last_uid = mail->uid;

		mail_precache(mail);
		if (++counter % 100 == 0) {
			percentage = counter*100 / max;
			if (percentage != percentage_sent && percentage < 100) {
				percentage_sent = percentage;
				if (i_snprintf(percentage_str,
					       sizeof(percentage_str), "%u\n",
					       percentage) < 0)
					i_unreached();
				(void)write_full(conn->fd, percentage_str,
						 strlen(percentage_str));
			}
			indexer_worker_refresh_proctitle(username, box_vname,
							 counter, max);
		}
	}
	if (mailbox_search_deinit(&ctx) < 0) {
		i_error("Mailbox %s: Mail search failed: %s",
			mailbox_get_vname(box),
			mailbox_get_last_internal_error(box, NULL));
		ret = -1;
	}
	const char *uids = first_uid == 0 ? "" :
		t_strdup_printf(" (UIDs %u..%u)", first_uid, last_uid);
	if (mailbox_transaction_commit(&trans) < 0) {
		i_error("Mailbox %s: Transaction commit failed: %s"
			" (attempted to index %u messages%s)",
			mailbox_get_vname(box),
			mailbox_get_last_internal_error(box, NULL),
			counter, uids);
		ret = -1;
	} else {
		i_info("Indexed %u messages in %s%s",
		       counter, mailbox_get_vname(box), uids);
	}
	return ret;
}
static int
sync_create_box(struct dsync_brain *brain, struct mailbox *box,
		const guid_128_t mailbox_guid, uint32_t uid_validity,
		enum mail_error *error_r)
{
	struct mailbox_metadata metadata;
	struct mailbox_update update;
	enum mail_error error;
	const char *errstr;
	int ret;

	memset(&update, 0, sizeof(update));
	memcpy(update.mailbox_guid, mailbox_guid, sizeof(update.mailbox_guid));
	update.uid_validity = uid_validity;

	if (mailbox_create(box, &update, FALSE) < 0) {
		errstr = mailbox_get_last_error(box, &error);
		if (error != MAIL_ERROR_EXISTS) {
			i_error("Can't create mailbox %s: %s",
				mailbox_get_vname(box), errstr);
			*error_r = error;
			return -1;
		}
	}
	if (brain->no_mail_sync) {
		/* trust that create worked, we can't actually open it
		   and verify. */
		return 0;
	}
	/* sync the mailbox so we can look up its latest status */
	if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) {
		i_error("Can't sync mailbox %s: %s",
			mailbox_get_vname(box),
			mailbox_get_last_error(box, error_r));
		return -1;
	}

	/* verify that the GUID is what we wanted. if it's not, it probably
	   means that the mailbox had already been created. then we'll use the
	   GUID that is higher.

	   mismatching UIDVALIDITY is handled later, because we choose it by
	   checking which mailbox has more messages */
	if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) < 0) {
		i_error("Can't get mailbox GUID %s: %s",
			mailbox_get_vname(box),
			mailbox_get_last_error(box, error_r));
		return -1;
	}

	ret = memcmp(mailbox_guid, metadata.guid, sizeof(metadata.guid));
	if (ret > 0) {
		if (brain->debug) {
			i_debug("brain %c: Changing mailbox %s GUID %s -> %s",
				brain->master_brain ? 'M' : 'S',
				mailbox_get_vname(box),
				guid_128_to_string(metadata.guid),
				guid_128_to_string(mailbox_guid));
		}
		memset(&update, 0, sizeof(update));
		memcpy(update.mailbox_guid, mailbox_guid,
		       sizeof(update.mailbox_guid));
		if (mailbox_update(box, &update) < 0) {
			i_error("Can't update mailbox GUID %s: %s",
				mailbox_get_vname(box),
				mailbox_get_last_error(box, error_r));
			return -1;
		}
		/* verify that the update worked */
		if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID,
					 &metadata) < 0) {
			i_error("Can't get mailbox GUID %s: %s",
				mailbox_get_vname(box),
				mailbox_get_last_error(box, error_r));
			return -1;
		}
		if (memcmp(mailbox_guid, metadata.guid,
			   sizeof(metadata.guid)) != 0) {
			i_error("Backend didn't update mailbox %s GUID",
				mailbox_get_vname(box));
			*error_r = MAIL_ERROR_TEMP;
			return -1;
		}
	} else if (ret < 0) {
		if (brain->debug) {
			i_debug("brain %c: Other brain should change mailbox "
				"%s GUID %s -> %s",
				brain->master_brain ? 'M' : 'S',
				mailbox_get_vname(box),
				guid_128_to_string(mailbox_guid),
				guid_128_to_string(metadata.guid));
		}
	}
	return 0;
}