summaryrefslogtreecommitdiff
path: root/imap4d/copy.c
diff options
context:
space:
mode:
Diffstat (limited to 'imap4d/copy.c')
-rw-r--r--imap4d/copy.c170
1 files changed, 160 insertions, 10 deletions
diff --git a/imap4d/copy.c b/imap4d/copy.c
index b25e1ee30..e0ec3d3cc 100644
--- a/imap4d/copy.c
+++ b/imap4d/copy.c
@@ -35,6 +35,153 @@
copy messages in argv[2] to mailbox in argv[3]
*/
+static int
+copy_check_size (mu_mailbox_t mbox, size_t n, size_t *set, mu_off_t *size)
+{
+ int status;
+ size_t i;
+ mu_off_t total = 0;
+
+ for (i = 0; i < n; i++)
+ {
+ mu_message_t msg = NULL;
+ size_t msgno = set[i];
+ if (msgno)
+ {
+ status = mu_mailbox_get_message (mbox, msgno, &msg);
+ if (status)
+ {
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_get_message", NULL,
+ status);
+ return RESP_BAD;
+ }
+ else
+ {
+ size_t size;
+ status = mu_message_size (msg, &size);
+ if (status)
+ {
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_message_size", NULL,
+ status);
+ return RESP_BAD;
+ }
+ total += size;
+ }
+ }
+ }
+ *size = total;
+ return quota_check (total);
+}
+
+static int
+try_copy (mu_mailbox_t dst, mu_mailbox_t src, size_t n, size_t *set)
+{
+ int result;
+ size_t i;
+ mu_off_t total;
+
+ result = copy_check_size (src, n, set, &total);
+ if (result)
+ return result;
+
+ for (i = 0; i < n; i++)
+ {
+ mu_message_t msg = NULL;
+ size_t msgno = set[i];
+
+ if (msgno)
+ {
+ int status = mu_mailbox_get_message (src, msgno, &msg);
+ if (status)
+ {
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_get_message", NULL,
+ status);
+ return RESP_BAD;
+ }
+
+ status = mu_mailbox_append_message (dst, msg);
+ if (status)
+ {
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_append_message",
+ NULL,
+ status);
+ return RESP_BAD;
+ }
+ }
+ }
+ quota_update (total);
+ return RESP_OK;
+}
+
+static int
+safe_copy (mu_mailbox_t dst, mu_mailbox_t src, size_t n, size_t *set,
+ char **err_text)
+{
+ size_t nmesg;
+ int status;
+
+ status = mu_mailbox_messages_count (dst, &nmesg);
+ if (status)
+ {
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_messages_count",
+ NULL, status);
+ *err_text = "Operation failed";
+ return RESP_NO;
+ }
+
+ status = try_copy (dst, src, n, set);
+ if (status)
+ {
+ size_t maxmesg;
+
+ if (status == RESP_NO)
+ *err_text = "Mailbox quota exceeded";
+ else
+ *err_text = "Operation failed";
+
+ /* If the COPY command is unsuccessful for any reason, server
+ implementations MUST restore the destination mailbox to its state
+ before the COPY attempt. */
+
+ status = mu_mailbox_messages_count (dst, &maxmesg);
+ if (status)
+ {
+ mu_url_t url = NULL;
+
+ mu_mailbox_get_url (dst, &url);
+ mu_error (_("cannot count messages in mailbox %s: %s"),
+ mu_url_to_string (url), mu_strerror (status));
+ imap4d_bye (ERR_MAILBOX_CORRUPTED);
+ }
+
+ for (nmesg++; nmesg <= maxmesg; nmesg++)
+ {
+ mu_message_t msg;
+
+ if (mu_mailbox_get_message (dst, nmesg, &msg) == 0)
+ {
+ mu_attribute_t attr;
+ mu_message_get_attribute (msg, &attr);
+ mu_attribute_set_userflag (attr, MU_ATTRIBUTE_DELETED);
+ }
+ }
+
+ status = mu_mailbox_flush (dst, 1);
+ if (status)
+ {
+ mu_url_t url = NULL;
+
+ mu_mailbox_get_url (dst, &url);
+ mu_error (_("cannot flush mailbox %s: %s"),
+ mu_url_to_string (url), mu_strerror (status));
+ imap4d_bye (ERR_MAILBOX_CORRUPTED);
+ }
+ return RESP_NO;
+ }
+
+ return RESP_OK;
+}
+
int
imap4d_copy (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
@@ -71,6 +218,7 @@ imap4d_copy0 (imap4d_tokbuf_t tok, int isuid, char **err_text)
int arg = IMAP4_ARG_1 + !!isuid;
int ns;
+ *err_text = NULL;
if (imap4d_tokbuf_argc (tok) != arg + 2)
{
*err_text = "Invalid arguments";
@@ -89,11 +237,19 @@ imap4d_copy0 (imap4d_tokbuf_t tok, int isuid, char **err_text)
return RESP_OK;
}
+ if (isuid)
+ {
+ int i;
+ /* Fixup the message set. Perhaps util_msgset should do it itself? */
+ for (i = 0; i < n; i++)
+ set[i] = uid_to_msgno (set[i]);
+ }
+
mailbox_name = namespace_getfullpath (name, delim, &ns);
if (!mailbox_name)
{
- *err_text = "NO Copy failed.";
+ *err_text = "Copy failed.";
return RESP_NO;
}
@@ -106,14 +262,7 @@ imap4d_copy0 (imap4d_tokbuf_t tok, int isuid, char **err_text)
status = mu_mailbox_open (cmbox, MU_STREAM_RDWR | mailbox_mode[ns]);
if (status == 0)
{
- size_t i;
- for (i = 0; i < n; i++)
- {
- mu_message_t msg = NULL;
- size_t msgno = (isuid) ? uid_to_msgno (set[i]) : set[i];
- if (msgno && mu_mailbox_get_message (mbox, msgno, &msg) == 0)
- mu_mailbox_append_message (cmbox, msg);
- }
+ status = safe_copy (cmbox, mbox, n, set, err_text);
mu_mailbox_close (cmbox);
}
mu_mailbox_destroy (&cmbox);
@@ -132,6 +281,7 @@ imap4d_copy0 (imap4d_tokbuf_t tok, int isuid, char **err_text)
of the text of the tagged NO response. This gives a hint to the
client that it can attempt a CREATE command and retry the copy if
the CREATE is successful. */
- *err_text = "[TRYCREATE] failed";
+ if (!*err_text)
+ *err_text = "[TRYCREATE] failed";
return RESP_NO;
}

Return to:

Send suggestions and report system problems to the System administrator.