static int pop3_mail_get_size(struct client *client, struct mail *mail, uoff_t *size_r) { int ret; if (!client->set->pop3_fast_size_lookups) return mail_get_virtual_size(mail, size_r); /* first try to get the virtual size */ mail->lookup_abort = MAIL_LOOKUP_ABORT_READ_MAIL; ret = mail_get_virtual_size(mail, size_r); mail->lookup_abort = MAIL_LOOKUP_ABORT_NEVER; if (ret == 0) return 0; if (mailbox_get_last_mail_error(mail->box) != MAIL_ERROR_NOTPOSSIBLE) return -1; /* virtual size not available with a fast lookup. fallback to trying the physical size */ mail->lookup_abort = MAIL_LOOKUP_ABORT_READ_MAIL; ret = mail_get_physical_size(mail, size_r); mail->lookup_abort = MAIL_LOOKUP_ABORT_NEVER; if (ret == 0) return 0; if (mailbox_get_last_mail_error(mail->box) != MAIL_ERROR_NOTPOSSIBLE) return -1; /* no way to quickly get the size. fallback to doing a slow virtual size lookup */ return mail_get_virtual_size(mail, size_r); }
static bool fts_mailbox_build_continue(struct mail_search_context *ctx) { struct fts_search_context *fctx = FTS_CONTEXT(ctx); int ret; ret = fts_indexer_more(fctx->indexer_ctx); if (ret == 0) return FALSE; /* indexing finished */ ctx->progress_hidden = FALSE; if (fts_indexer_deinit(&fctx->indexer_ctx) < 0) ret = -1; if (ret > 0) fts_search_lookup(fctx); if (ret < 0) { /* if indexing timed out, it probably means that the mailbox is still being indexed, but it's a large mailbox and it takes a while. in this situation we'll simply abort the search. if indexing failed for any other reason, just fallback to searching the slow way. */ fctx->indexing_timed_out = mailbox_get_last_mail_error(fctx->box) == MAIL_ERROR_INUSE; } return TRUE; }
static int fetch_binary_size(struct imap_fetch_context *ctx, struct mail *mail, struct imap_fetch_body_data *body) { string_t *str; uoff_t size; if (mail == NULL) { imap_msgpart_free(&body->msgpart); return 1; } if (imap_msgpart_size(mail, body->msgpart, &size) < 0) { if (mailbox_get_last_mail_error(mail->box) != MAIL_ERROR_INVALIDDATA) return -1; /* tried to do BINARY.SIZE fetch for a MIME part with broken content */ size = 0; } str = t_str_new(128); if (ctx->state.cur_first) ctx->state.cur_first = FALSE; else str_append_c(str, ' '); str_printfa(str, "%s %"PRIuUOFF_T, get_body_name(body), size); if (o_stream_send(ctx->client->output, str_data(str), str_len(str)) < 0) return -1; return 1; }
static int fetch_body_msgpart(struct imap_fetch_context *ctx, struct mail *mail, struct imap_fetch_body_data *body) { struct imap_msgpart_open_result result; string_t *str; if (mail == NULL) { imap_msgpart_free(&body->msgpart); return 1; } if (imap_msgpart_open(mail, body->msgpart, &result) < 0) { if (!body->binary || mailbox_get_last_mail_error(mail->box) != MAIL_ERROR_INVALIDDATA) return -1; /* tried to do BINARY fetch for a MIME part with broken content */ str = get_prefix(&ctx->state, body, (uoff_t)-1, FALSE); o_stream_nsend(ctx->client->output, str_data(str), str_len(str)); return 1; } ctx->state.cur_input = result.input; ctx->state.cur_size = result.size; ctx->state.cur_size_field = result.size_field; ctx->state.cur_human_name = get_body_human_name(ctx->ctx_pool, body); str = get_prefix(&ctx->state, body, ctx->state.cur_size, result.binary_decoded_input_has_nuls); o_stream_nsend(ctx->client->output, str_data(str), str_len(str)); ctx->state.cont_handler = fetch_stream_continue; return ctx->state.cont_handler(ctx); }
static int cmd_expunge_box(struct doveadm_mail_cmd_context *_ctx, const struct mailbox_info *info, struct mail_search_args *search_args) { struct expunge_cmd_context *ctx = (struct expunge_cmd_context *)_ctx; struct doveadm_mail_iter *iter; struct mailbox *box; struct mail *mail; enum mail_error error; int ret = 0; if (doveadm_mail_iter_init(_ctx, info, search_args, 0, NULL, FALSE, &iter) < 0) return -1; while (doveadm_mail_iter_next(iter, &mail)) { if (doveadm_debug) { i_debug("expunge: box=%s uid=%u", info->vname, mail->uid); } mail_expunge(mail); } if (doveadm_mail_iter_deinit_keep_box(&iter, &box) < 0) ret = -1; else if (mailbox_sync(box, 0) < 0) { i_error("Syncing mailbox '%s' failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_mailbox(_ctx, box); ret = -1; } if (ctx->delete_empty_mailbox && ret == 0) { if (mailbox_delete_empty(box) < 0) { error = mailbox_get_last_mail_error(box); if (error != MAIL_ERROR_EXISTS) { i_error("Deleting mailbox '%s' failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_mailbox(_ctx, box); ret = -1; } } else { if (mailbox_set_subscribed(box, FALSE) < 0) { i_error("Unsubscribing mailbox '%s' failed: %s", mailbox_get_vname(box), mailbox_get_last_internal_error(box, NULL)); doveadm_mail_failed_mailbox(_ctx, box); ret = -1; } } } mailbox_free(&box); return ret; }
static int quota_count_mailbox(struct quota_root *root, struct mail_namespace *ns, const char *vname, uint64_t *bytes_r, uint64_t *count_r) { struct quota_rule *rule; struct mailbox *box; struct mailbox_transaction_context *trans; struct mail_search_context *ctx; struct mail *mail; struct mail_search_args *search_args; enum mail_error error; uoff_t size; int ret = 0; 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 (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) { error = mailbox_get_last_mail_error(box); mailbox_free(&box); if (error == MAIL_ERROR_TEMP) return -1; /* non-temporary error, e.g. ACLs denied access. */ return 0; } trans = mailbox_transaction_begin(box, 0); search_args = mail_search_build_init(); mail_search_build_add_all(search_args); ctx = mailbox_search_init(trans, search_args, NULL, MAIL_FETCH_PHYSICAL_SIZE, NULL); mail_search_args_unref(&search_args); while (mailbox_search_next(ctx, &mail)) { if (mail_get_physical_size(mail, &size) == 0) *bytes_r += size; *count_r += 1; } if (mailbox_search_deinit(&ctx) < 0) ret = -1; if (ret < 0) mailbox_transaction_rollback(&trans); else (void)mailbox_transaction_commit(&trans); mailbox_free(&box); return ret; }
static time_t index_sort_program_set_date_failed(struct mail_search_sort_program *program, struct mail *mail) { index_sort_program_set_mail_failed(program, mail); if (mailbox_get_last_mail_error(mail->box) == MAIL_ERROR_LIMIT) { /* limit reached - sort the rest of the mails at the end of the list by their UIDs */ return LONG_MAX; } else { /* expunged / some other error - sort in the beginning */ return 0; } }
static void index_sort_program_set_mail_failed(struct mail_search_sort_program *program, struct mail *mail) { switch (mailbox_get_last_mail_error(mail->box)) { case MAIL_ERROR_EXPUNGED: break; case MAIL_ERROR_LOOKUP_ABORTED: /* just change the error message */ i_assert(program->slow_mails_left == 0); mail_storage_set_error(program->t->box->storage, MAIL_ERROR_LIMIT, "Requested sort would have taken too long."); /* fall through */ default: program->failed = TRUE; break; } }
static int index_mailbox_get_first_save_date(struct mailbox *box, struct mailbox_metadata *metadata_r) { const struct mail_index_header *hdr; struct mailbox_transaction_context *t; struct mail *mail; uint32_t seq; int ret = -1; hdr = mail_index_get_header(box->view); if (hdr->messages_count == 0) { metadata_r->first_save_date = (time_t)-1; return 0; } t = mailbox_transaction_begin(box, 0, __func__); mail = mail_alloc(t, 0, NULL); for (seq = 1; seq <= hdr->messages_count; seq++) { mail_set_seq(mail, seq); if (mail_get_save_date(mail, &metadata_r->first_save_date) == 0) { ret = 0; break; } if (mailbox_get_last_mail_error(box) != MAIL_ERROR_EXPUNGED) { /* failed */ break; } } mail_free(&mail); (void)mailbox_transaction_commit(&t); if (seq > hdr->messages_count) { /* all messages were expunged after all */ metadata_r->first_save_date = (time_t)-1; return 0; } return ret; }
struct mailbox_attribute_iter * index_storage_attribute_iter_init(struct mailbox *box, enum mail_attribute_type type, const char *prefix) { struct index_storage_attribute_iter *iter; struct dict *dict; const char *mailbox_prefix; iter = i_new(struct index_storage_attribute_iter, 1); iter->iter.box = box; if (index_storage_get_dict(box, type, &dict, &mailbox_prefix) < 0) { if (mailbox_get_last_mail_error(box) == MAIL_ERROR_NOTPOSSIBLE) iter->dict_disabled = TRUE; } else { iter->prefix = i_strdup(key_get_prefixed(type, mailbox_prefix, prefix)); iter->prefix_len = strlen(iter->prefix); iter->diter = dict_iterate_init(dict, iter->prefix, DICT_ITERATE_FLAG_RECURSE | DICT_ITERATE_FLAG_NO_VALUE); } return &iter->iter; }
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); dest_trans = mailbox_transaction_begin(destbox, MAILBOX_TRANSACTION_FLAG_EXTERNAL); 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 bool cmd_copy_full(struct client_command_context *cmd, bool move) { struct client *client = cmd->client; struct mail_storage *dest_storage; struct mailbox *destbox; struct mailbox_transaction_context *t, *src_trans; struct mail_search_args *search_args; const char *messageset, *mailbox, *src_uidset; enum mailbox_sync_flags sync_flags = 0; enum imap_sync_flags imap_flags = 0; struct mail_transaction_commit_changes changes; unsigned int copy_count; string_t *msg; int ret; /* <message set> <mailbox> */ if (!client_read_string_args(cmd, 2, &messageset, &mailbox)) return FALSE; if (!client_verify_open_mailbox(cmd)) return TRUE; ret = imap_search_get_seqset(cmd, messageset, cmd->uid, &search_args); if (ret <= 0) return ret < 0; if (client_open_save_dest_box(cmd, mailbox, &destbox) < 0) { mail_search_args_unref(&search_args); return TRUE; } t = mailbox_transaction_begin(destbox, MAILBOX_TRANSACTION_FLAG_EXTERNAL | MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS); ret = fetch_and_copy(client, move, t, &src_trans, search_args, &src_uidset, ©_count); mail_search_args_unref(&search_args); msg = t_str_new(256); if (ret <= 0) mailbox_transaction_rollback(&t); else if (mailbox_transaction_commit_get_changes(&t, &changes) < 0) { if (mailbox_get_last_mail_error(destbox) == MAIL_ERROR_EXPUNGED) { /* storage backend didn't notice the expunge until at commit time. */ ret = 0; } else { ret = -1; } } else if (copy_count == 0) { str_append(msg, "OK No messages found."); pool_unref(&changes.pool); } else if (seq_range_count(&changes.saved_uids) == 0 || changes.no_read_perm) { /* not supported by backend (virtual) or no read permissions for mailbox */ str_append(msg, move ? "OK Move completed." : "OK Copy completed."); pool_unref(&changes.pool); } else if (move) { i_assert(copy_count == seq_range_count(&changes.saved_uids)); copy_update_trashed(client, destbox, copy_count); str_printfa(msg, "* OK [COPYUID %u %s ", changes.uid_validity, src_uidset); imap_write_seq_range(msg, &changes.saved_uids); str_append(msg, "] Moved UIDs."); client_send_line(client, str_c(msg)); str_truncate(msg, 0); str_append(msg, "OK Move completed."); pool_unref(&changes.pool); } else { i_assert(copy_count == seq_range_count(&changes.saved_uids)); copy_update_trashed(client, destbox, copy_count); str_printfa(msg, "OK [COPYUID %u %s ", changes.uid_validity, src_uidset); imap_write_seq_range(msg, &changes.saved_uids); str_append(msg, "] Copy completed."); pool_unref(&changes.pool); } if (ret <= 0 && move) { /* move failed, don't expunge anything */ mailbox_transaction_rollback(&src_trans); } else { if (mailbox_transaction_commit(&src_trans) < 0) ret = -1; } dest_storage = mailbox_get_storage(destbox); if (destbox != client->mailbox) { if (move) sync_flags |= MAILBOX_SYNC_FLAG_EXPUNGE; else sync_flags |= MAILBOX_SYNC_FLAG_FAST; imap_flags |= IMAP_SYNC_FLAG_SAFE; mailbox_free(&destbox); } else if (move) { sync_flags |= MAILBOX_SYNC_FLAG_EXPUNGE; imap_flags |= IMAP_SYNC_FLAG_SAFE; } if (ret > 0) return cmd_sync(cmd, sync_flags, imap_flags, str_c(msg)); else if (ret == 0) { /* some messages were expunged, sync them */ return cmd_sync(cmd, 0, 0, "NO ["IMAP_RESP_CODE_EXPUNGEISSUED"] " "Some of the requested messages no longer exist."); } else { client_send_storage_error(cmd, dest_storage); return TRUE; } }
int dsync_brain_mailbox_tree_sync_change(struct dsync_brain *brain, const struct dsync_mailbox_tree_sync_change *change, enum mail_error *error_r) { struct mailbox *box = NULL, *destbox; const char *errstr, *func_name = NULL, *storage_name; enum mail_error error; int ret = -1; if (brain->backup_send) { i_assert(brain->no_backup_overwrite); return 0; } switch (change->type) { case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_BOX: /* make sure we're deleting the correct mailbox */ ret = dsync_brain_mailbox_alloc(brain, change->mailbox_guid, &box, &errstr, error_r); if (ret < 0) { i_error("Mailbox sync: Couldn't allocate mailbox GUID %s: %s", guid_128_to_string(change->mailbox_guid), errstr); return -1; } if (ret == 0) { if (brain->debug) { i_debug("brain %c: Change during sync: " "Mailbox GUID %s deletion conflict: %s", brain->master_brain ? 'M' : 'S', guid_128_to_string(change->mailbox_guid), errstr); } brain->changes_during_sync = TRUE; return 0; } break; case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_DIR: storage_name = mailbox_list_get_storage_name(change->ns->list, change->full_name); if (mailbox_list_delete_dir(change->ns->list, storage_name) == 0) return 0; errstr = mailbox_list_get_last_error(change->ns->list, &error); if (error == MAIL_ERROR_NOTFOUND || error == MAIL_ERROR_EXISTS) { if (brain->debug) { i_debug("brain %c: Change during sync: " "Mailbox %s mailbox_list_delete_dir conflict: %s", brain->master_brain ? 'M' : 'S', change->full_name, errstr); } brain->changes_during_sync = TRUE; return 0; } else { i_error("Mailbox sync: mailbox_list_delete_dir failed: %s", errstr); *error_r = error; return -1; } default: box = mailbox_alloc(change->ns->list, change->full_name, 0); break; } switch (change->type) { case DSYNC_MAILBOX_TREE_SYNC_TYPE_CREATE_BOX: ret = sync_create_box(brain, box, change->mailbox_guid, change->uid_validity, error_r); mailbox_free(&box); return ret; case DSYNC_MAILBOX_TREE_SYNC_TYPE_CREATE_DIR: ret = mailbox_create(box, NULL, TRUE); if (ret < 0 && mailbox_get_last_mail_error(box) == MAIL_ERROR_EXISTS) { /* it doesn't matter if somebody else created this directory or we automatically did while creating its child mailbox. it's there now anyway and we don't gain anything by treating this failure any differently from success. */ ret = 0; } func_name = "mailbox_create"; break; case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_BOX: ret = mailbox_delete(box); func_name = "mailbox_delete"; break; case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_DIR: i_unreached(); case DSYNC_MAILBOX_TREE_SYNC_TYPE_RENAME: destbox = mailbox_alloc(change->ns->list, change->rename_dest_name, 0); ret = mailbox_rename(box, destbox); func_name = "mailbox_rename"; mailbox_free(&destbox); break; case DSYNC_MAILBOX_TREE_SYNC_TYPE_SUBSCRIBE: ret = mailbox_set_subscribed(box, TRUE); func_name = "mailbox_set_subscribed"; break; case DSYNC_MAILBOX_TREE_SYNC_TYPE_UNSUBSCRIBE: ret = mailbox_set_subscribed(box, FALSE); func_name = "mailbox_set_subscribed"; break; } if (ret < 0) { errstr = mailbox_get_last_error(box, &error); if (error == MAIL_ERROR_EXISTS || error == MAIL_ERROR_NOTFOUND) { /* mailbox was already created or was already deleted. let the next sync figure out what to do */ if (brain->debug) { i_debug("brain %c: Change during sync: " "Mailbox %s %s conflict: %s", brain->master_brain ? 'M' : 'S', mailbox_get_vname(box), func_name, errstr); } brain->changes_during_sync = TRUE; ret = 0; } else { i_error("Mailbox %s sync: %s failed: %s", mailbox_get_vname(box), func_name, errstr); *error_r = error; } } mailbox_free(&box); return ret; }