static int dest_mailbox_open_or_create(struct import_cmd_context *ctx, struct mail_user *user, const char *name, struct mailbox **box_r) { struct mail_namespace *ns; struct mailbox *box; enum mail_error error; const char *errstr, *storage_name; if (*ctx->dest_parent != '\0') { /* prefix destination mailbox name with given parent mailbox */ storage_name = ctx->dest_parent; ns = mail_namespace_find(user->namespaces, &storage_name); if (ns == NULL) { i_error("Can't find namespace for parent mailbox %s", ctx->dest_parent); return -1; } name = t_strdup_printf("%s%c%s", ctx->dest_parent, ns->sep, name); } storage_name = name; ns = mail_namespace_find(user->namespaces, &storage_name); if (ns == NULL) { i_error("Can't find namespace for mailbox %s", name); return -1; } box = mailbox_alloc(ns->list, storage_name, MAILBOX_FLAG_SAVEONLY | MAILBOX_FLAG_KEEP_RECENT); if (mailbox_create(box, NULL, FALSE) < 0) { errstr = mail_storage_get_last_error(mailbox_get_storage(box), &error); if (error != MAIL_ERROR_EXISTS) { i_error("Couldn't create mailbox %s: %s", name, errstr); mailbox_free(&box); return -1; } } if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) { i_error("Syncing mailbox %s failed: %s", name, mail_storage_get_last_error(mailbox_get_storage(box), NULL)); mailbox_free(&box); return -1; } *box_r = box; return 0; }
static int cmd_import_box_contents(struct doveadm_mail_iter *iter, struct mail *src_mail, struct mailbox *dest_box) { struct mail_storage *dest_storage = mailbox_get_storage(dest_box); struct mail_save_context *save_ctx; struct mailbox_transaction_context *dest_trans; const char *mailbox = mailbox_get_vname(dest_box); int ret = 0; dest_trans = mailbox_transaction_begin(dest_box, MAILBOX_TRANSACTION_FLAG_EXTERNAL); do { if (doveadm_debug) { i_debug("import: box=%s uid=%u", mailbox, src_mail->uid); } save_ctx = mailbox_save_alloc(dest_trans); if (mailbox_copy(&save_ctx, src_mail) < 0) { i_error("Copying box=%s uid=%u failed: %s", mailbox, src_mail->uid, mail_storage_get_last_error(dest_storage, NULL)); ret = -1; } } while (doveadm_mail_iter_next(iter, src_mail)); if (mailbox_transaction_commit(&dest_trans) < 0) { i_error("Committing copied mails to %s failed: %s", mailbox, mail_storage_get_last_error(dest_storage, NULL)); ret = -1; } return ret; }
int client_enable(struct client *client, enum mailbox_feature features) { struct mailbox_status status; int ret; if ((client->enabled_features & features) == features) return 0; client->enabled_features |= features; if (client->mailbox == NULL) return 0; ret = mailbox_enable(client->mailbox, features); if ((features & MAILBOX_FEATURE_CONDSTORE) != 0 && ret == 0) { /* CONDSTORE being enabled while mailbox is selected. Notify client of the latest HIGHESTMODSEQ. */ ret = mailbox_get_status(client->mailbox, STATUS_HIGHESTMODSEQ, &status); if (ret == 0) { client_send_line(client, t_strdup_printf( "* OK [HIGHESTMODSEQ %llu] Highest", (unsigned long long)status.highest_modseq)); } } if (ret < 0) { client_send_untagged_storage_error(client, mailbox_get_storage(client->mailbox)); } return ret; }
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; }
bool cmd_create(struct client_command_context *cmd) { enum mailbox_name_status status; struct mail_namespace *ns; const char *mailbox, *storage_name; struct mailbox *box; bool directory; size_t len; /* <mailbox> */ if (!client_read_string_args(cmd, 1, &mailbox)) return FALSE; ns = client_find_namespace(cmd, mailbox, &storage_name, NULL); if (ns == NULL) return TRUE; len = strlen(mailbox); if (len == 0 || mailbox[len-1] != ns->sep) directory = FALSE; else if (*storage_name == '\0') { client_send_tagline(cmd, "NO ["IMAP_RESP_CODE_ALREADYEXISTS "] Namespace already exists."); return TRUE; } else { /* name ends with hierarchy separator - client is just informing us that it wants to create children under this mailbox. */ directory = TRUE; storage_name = t_strndup(storage_name, strlen(storage_name)-1); mailbox = t_strndup(mailbox, len-1); } ns = client_find_namespace(cmd, mailbox, &storage_name, &status); if (ns == NULL) return TRUE; switch (status) { case MAILBOX_NAME_VALID: break; case MAILBOX_NAME_EXISTS_DIR: if (!directory) break; /* fall through */ case MAILBOX_NAME_EXISTS_MAILBOX: case MAILBOX_NAME_INVALID: case MAILBOX_NAME_NOINFERIORS: client_fail_mailbox_name_status(cmd, mailbox, NULL, status); return TRUE; } box = mailbox_alloc(ns->list, storage_name, 0); if (mailbox_create(box, NULL, directory) < 0) client_send_storage_error(cmd, mailbox_get_storage(box)); else client_send_tagline(cmd, "OK Create completed."); mailbox_free(&box); return TRUE; }
static int cmd_save_to_mailbox(struct save_cmd_context *ctx, struct mailbox *box, struct istream *input) { struct mail_storage *storage = mailbox_get_storage(box); struct mailbox_transaction_context *trans; struct mail_save_context *save_ctx; ssize_t ret; bool save_failed = FALSE; if (mailbox_open(box) < 0) { i_error("Failed to open mailbox %s: %s", mailbox_get_vname(box), mailbox_get_last_error(box, NULL)); doveadm_mail_failed_storage(&ctx->ctx, storage); return -1; } trans = mailbox_transaction_begin(box, MAILBOX_TRANSACTION_FLAG_EXTERNAL); save_ctx = mailbox_save_alloc(trans); if (mailbox_save_begin(&save_ctx, input) < 0) { i_error("Saving failed: %s", mailbox_get_last_error(box, NULL)); doveadm_mail_failed_storage(&ctx->ctx, storage); mailbox_transaction_rollback(&trans); return -1; } while ((ret = i_stream_read(input)) > 0 || ret == -2) { if (mailbox_save_continue(save_ctx) < 0) { save_failed = TRUE; ret = -1; break; } } i_assert(ret == -1); if (input->stream_errno != 0) { i_error("read(msg input) failed: %s", i_stream_get_error(input)); doveadm_mail_failed_error(&ctx->ctx, MAIL_ERROR_TEMP); } else if (save_failed) { i_error("Saving failed: %s", mailbox_get_last_error(box, NULL)); doveadm_mail_failed_storage(&ctx->ctx, storage); } else if (mailbox_save_finish(&save_ctx) < 0) { i_error("Saving failed: %s", mailbox_get_last_error(box, NULL)); doveadm_mail_failed_storage(&ctx->ctx, storage); } else if (mailbox_transaction_commit(&trans) < 0) { i_error("Save transaction commit failed: %s", mailbox_get_last_error(box, NULL)); doveadm_mail_failed_storage(&ctx->ctx, storage); } else { ret = 0; } if (save_ctx != NULL) mailbox_save_cancel(&save_ctx); if (trans != NULL) mailbox_transaction_rollback(&trans); i_assert(input->eof); return ret < 0 ? -1 : 0; }
int mail_set_attachment_keywords(struct mail *mail) { int ret; const struct mail_storage_settings *mail_set = mail_storage_get_settings(mailbox_get_storage(mail->box)); const char *const keyword_has_attachment[] = { MAIL_KEYWORD_HAS_ATTACHMENT, NULL, }; const char *const keyword_has_no_attachment[] = { MAIL_KEYWORD_HAS_NO_ATTACHMENT, NULL }; struct message_part_attachment_settings set = { .content_type_filter = mail_set->parsed_mail_attachment_content_type_filter, .exclude_inlined = mail_set->parsed_mail_attachment_exclude_inlined, }; struct mail_keywords *kw_has = NULL, *kw_has_not = NULL; /* walk all parts and see if there is an attachment */ struct message_part *parts; if (mail_get_parts(mail, &parts) < 0) { mail_set_critical(mail, "Failed to add attachment keywords: " "mail_get_parts() failed: %s", mail_storage_get_last_internal_error(mail->box->storage, NULL)); ret = -1; } else if (parts->data == NULL && mail_parse_parts(mail, &parts) < 0) { ret = -1; } else if (mailbox_keywords_create(mail->box, keyword_has_attachment, &kw_has) < 0 || mailbox_keywords_create(mail->box, keyword_has_no_attachment, &kw_has_not) < 0) { mail_set_critical(mail, "Failed to add attachment keywords: " "mailbox_keywords_create(%s) failed: %s", mailbox_get_vname(mail->box), mail_storage_get_last_internal_error(mail->box->storage, NULL)); ret = -1; } else { bool has_attachment = mail_message_has_attachment(parts, &set); /* make sure only one of the keywords gets set */ mail_update_keywords(mail, MODIFY_REMOVE, has_attachment ? kw_has_not : kw_has); mail_update_keywords(mail, MODIFY_ADD, has_attachment ? kw_has : kw_has_not); ret = has_attachment ? 1 : 0; } if (kw_has != NULL) mailbox_keywords_unref(&kw_has); if (kw_has_not != NULL) mailbox_keywords_unref(&kw_has_not); return ret; }
int imap_msgpart_url_open_mailbox(struct imap_msgpart_url *mpurl, struct mailbox **box_r, enum mail_error *error_code_r, const char **error_r) { struct mailbox_status box_status; enum mailbox_flags flags = MAILBOX_FLAG_READONLY; struct mail_namespace *ns; struct mailbox *box; if (mpurl->box != NULL) { *box_r = mpurl->box; *error_code_r = MAIL_ERROR_NONE; return 1; } /* find mailbox namespace */ ns = mail_namespace_find(mpurl->user->namespaces, mpurl->mailbox); /* open mailbox */ if (mpurl->selected_box != NULL && mailbox_equals(mpurl->selected_box, ns, mpurl->mailbox)) box = mpurl->selected_box; else box = mailbox_alloc(ns->list, mpurl->mailbox, flags); if (mailbox_open(box) < 0) { *error_r = mail_storage_get_last_error(mailbox_get_storage(box), error_code_r); if (box != mpurl->selected_box) mailbox_free(&box); return *error_code_r == MAIL_ERROR_TEMP ? -1 : 0; } /* verify UIDVALIDITY */ mailbox_get_open_status(box, STATUS_UIDVALIDITY, &box_status); if (mpurl->uidvalidity > 0 && box_status.uidvalidity != mpurl->uidvalidity) { *error_r = "Invalid UIDVALIDITY"; *error_code_r = MAIL_ERROR_EXPUNGED; if (box != mpurl->selected_box) mailbox_free(&box); return 0; } mpurl->box = box; *box_r = box; return 1; }
bool cmd_create(struct client_command_context *cmd) { struct mail_namespace *ns; const char *mailbox, *orig_mailbox; struct mailbox *box; bool directory; size_t len; /* <mailbox> */ if (!client_read_string_args(cmd, 1, &mailbox)) return FALSE; orig_mailbox = mailbox; ns = client_find_namespace(cmd, &mailbox); if (ns == NULL) return TRUE; len = strlen(orig_mailbox); if (len == 0 || orig_mailbox[len-1] != mail_namespace_get_sep(ns)) directory = FALSE; else { /* name ends with hierarchy separator - client is just informing us that it wants to create children under this mailbox. */ directory = TRUE; /* drop separator from mailbox. it's already dropped when WORKAROUND_TB_EXTRA_MAILBOX_SEP is enabled */ if (len == strlen(mailbox)) mailbox = t_strndup(mailbox, len-1); } box = mailbox_alloc(ns->list, mailbox, 0); if (mailbox_create(box, NULL, directory) < 0) client_send_storage_error(cmd, mailbox_get_storage(box)); else client_send_tagline(cmd, "OK Create completed."); mailbox_free(&box); return TRUE; }
bool cmd_close(struct client_command_context *cmd) { struct client *client = cmd->client; struct mailbox *mailbox = client->mailbox; struct mail_storage *storage; if (!client_verify_open_mailbox(cmd)) return TRUE; i_assert(client->mailbox_change_lock == NULL); client->mailbox = NULL; storage = mailbox_get_storage(mailbox); if (imap_expunge(mailbox, NULL) < 0) client_send_untagged_storage_error(client, storage); if (mailbox_sync(mailbox, 0) < 0) client_send_untagged_storage_error(client, storage); mailbox_free(&mailbox); client_update_mailbox_flags(client, NULL); client_send_tagline(cmd, "OK Close completed."); return TRUE; }
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) 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)); 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)); 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; } }
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 bool cmd_idle_continue(struct client_command_context *cmd) { struct client *client = cmd->client; struct cmd_idle_context *ctx = cmd->context; uoff_t orig_offset = client->output->offset; if (cmd->cancel) { idle_finish(ctx, FALSE, FALSE); return TRUE; } if (ctx->manual_cork) { /* we're coming from idle_callback instead of a normal I/O handler, so we'll have to do corking manually */ o_stream_cork(client->output); } if (ctx->sync_ctx != NULL) { if (imap_sync_more(ctx->sync_ctx) == 0) { /* unfinished */ if (ctx->manual_cork) { ctx->manual_cork = FALSE; o_stream_uncork(client->output); } cmd->state = CLIENT_COMMAND_STATE_WAIT_OUTPUT; return FALSE; } if (imap_sync_deinit(ctx->sync_ctx, ctx->cmd) < 0) { client_send_untagged_storage_error(client, mailbox_get_storage(client->mailbox)); mailbox_notify_changes_stop(client->mailbox); } ctx->sync_ctx = NULL; } if (client->output->offset != orig_offset && ctx->keepalive_to != NULL) idle_add_keepalive_timeout(ctx); if (ctx->sync_pending) { /* more changes occurred while we were sending changes to client */ idle_sync_now(client->mailbox, ctx); /* NOTE: this recurses back to this function, so we return here instead of doing everything twice. */ return FALSE; } cmd->state = CLIENT_COMMAND_STATE_WAIT_INPUT; if (ctx->manual_cork) { ctx->manual_cork = FALSE; o_stream_uncork(client->output); } if (client->output->closed) { idle_finish(ctx, FALSE, FALSE); return TRUE; } if (client->io == NULL) { /* input is pending */ client->io = io_add(i_stream_get_fd(client->input), IO_READ, idle_client_input, ctx); idle_client_input_more(ctx); } return FALSE; }
void client_send_box_error(struct client_command_context *cmd, struct mailbox *box) { client_send_storage_error(cmd, mailbox_get_storage(box)); }
bool cmd_copy(struct client_command_context *cmd) { struct client *client = cmd->client; struct mail_namespace *dest_ns; struct mail_storage *dest_storage; struct mailbox *destbox; struct mailbox_transaction_context *t; struct mail_search_args *search_args; const char *messageset, *mailbox, *storage_name, *src_uidset; enum mailbox_name_status status; 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; /* open the destination mailbox */ dest_ns = client_find_namespace(cmd, mailbox, &storage_name, &status); if (dest_ns == NULL) { mail_search_args_unref(&search_args); return TRUE; } switch (status) { case MAILBOX_NAME_EXISTS_MAILBOX: break; case MAILBOX_NAME_EXISTS_DIR: status = MAILBOX_NAME_VALID; /* fall through */ case MAILBOX_NAME_VALID: case MAILBOX_NAME_INVALID: case MAILBOX_NAME_NOINFERIORS: client_fail_mailbox_name_status(cmd, mailbox, "TRYCREATE", status); mail_search_args_unref(&search_args); return TRUE; } if (mailbox_equals(client->mailbox, dest_ns, storage_name)) destbox = client->mailbox; else { destbox = mailbox_alloc(dest_ns->list, storage_name, MAILBOX_FLAG_SAVEONLY | MAILBOX_FLAG_KEEP_RECENT); if (mailbox_open(destbox) < 0) { client_send_storage_error(cmd, mailbox_get_storage(destbox)); mailbox_free(&destbox); mail_search_args_unref(&search_args); return TRUE; } if (client->enabled_features != 0) mailbox_enable(destbox, client->enabled_features); } t = mailbox_transaction_begin(destbox, MAILBOX_TRANSACTION_FLAG_EXTERNAL | MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS); ret = fetch_and_copy(client, t, 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) ret = -1; else if (copy_count == 0) { str_append(msg, "OK No messages copied."); pool_unref(&changes.pool); } else if (seq_range_count(&changes.saved_uids) == 0) { /* not supported by backend (virtual) */ str_append(msg, "OK Copy completed."); pool_unref(&changes.pool); } else { i_assert(copy_count == seq_range_count(&changes.saved_uids)); 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); } dest_storage = mailbox_get_storage(destbox); if (destbox != client->mailbox) { sync_flags |= MAILBOX_SYNC_FLAG_FAST; imap_flags |= IMAP_SYNC_FLAG_SAFE; mailbox_free(&destbox); } 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; } }