diff options
Diffstat (limited to 'libproto/dotmail/dotmail.c')
-rw-r--r-- | libproto/dotmail/dotmail.c | 679 |
1 files changed, 506 insertions, 173 deletions
diff --git a/libproto/dotmail/dotmail.c b/libproto/dotmail/dotmail.c index fa083bb98..18ce4a4ee 100644 --- a/libproto/dotmail/dotmail.c +++ b/libproto/dotmail/dotmail.c @@ -1,5 +1,5 @@ /* GNU Mailutils -- a suite of utilities for electronic mail - Copyright (C) 2019 Free Software Foundation, Inc. + Copyright (C) 2019-2024 Free Software Foundation, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -37,6 +37,7 @@ #include <mailutils/nls.h> #include <mailutils/header.h> #include <mailutils/attribute.h> +#include <mailutils/envelope.h> #include <mailutils/util.h> #include <mailutils/cctype.h> @@ -69,26 +70,17 @@ dotmail_mailbox_init_stream (struct mu_dotmail_mailbox *dmp) int rc; mu_mailbox_t mailbox = dmp->mailbox; - rc = mu_mapfile_stream_create (&mailbox->stream, dmp->name, mailbox->flags); + rc = mu_mailbox_stream_create (&mailbox->stream, dmp->name, mailbox->flags); if (rc) { mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, ("%s:%s (%s): %s", - __func__, "mu_mapfile_stream_create", dmp->name, + __func__, "mu_mailbox_file_open", dmp->name, mu_strerror (rc))); - - /* Fallback to regular file stream */ - rc = mu_file_stream_create (&mailbox->stream, dmp->name, mailbox->flags); - mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, - ("%s:%s (%s): %s", - __func__, "mu_file_stream_create", dmp->name, - mu_strerror (rc))); - - if (rc) - return rc; + return rc; } - mu_stream_set_buffer (mailbox->stream, mu_buffer_full, 0); + mu_stream_get_flags (mailbox->stream, &dmp->stream_flags); return 0; } @@ -107,21 +99,30 @@ dotmail_open (mu_mailbox_t mailbox, int flags) mailbox->flags = flags; rc = dotmail_mailbox_init_stream (dmp); - - if (mailbox->locker == NULL + if (rc == 0 + && mailbox->locker == NULL && (flags & (MU_STREAM_WRITE | MU_STREAM_APPEND | MU_STREAM_CREAT))) { - rc = mu_locker_create (&mailbox->locker, dmp->name, 0); + rc = mu_locker_create_ext (&mailbox->locker, dmp->name, NULL); if (rc) mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, ("%s:%s (%s): %s", - __func__, "mu_locker_create", dmp->name, + __func__, "mu_locker_create_ext", dmp->name, mu_strerror (rc))); } return rc; } +enum + { + FLUSH_SYNC, + FLUSH_EXPUNGE, /* implies SYNC */ + FLUSH_UIDVALIDITY + }; + +static int dotmail_flush (struct mu_dotmail_mailbox *dmp, int flag); + static int dotmail_close (mu_mailbox_t mailbox) { @@ -134,6 +135,9 @@ dotmail_close (mu_mailbox_t mailbox) mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE1, ("%s (%s)", __func__, dmp->name)); + if (dmp->uidvalidity_changed) + dotmail_flush (dmp, FLUSH_UIDVALIDITY); + mu_locker_unlock (mailbox->locker); mu_monitor_wrlock (mailbox->monitor); for (i = 0; i < dmp->mesg_count; i++) @@ -262,8 +266,10 @@ dotmail_dispatch (mu_mailbox_t mailbox, int evt, void *data) message's uid value from the X-UID header. The first message that lacks X-UID or with an X-UID that cannot be parsed, gets assigned new UID. The subsequent - messages are assigned new UIDs no matterwhether they - have X-UID headers. + messages are assigned new UIDs no matter whether they + have X-UID headers. In this case, the uidvalidity value + is reset to the current timestamp, to indicate that all + UIDs might have changed. 3b. When any of the following functions are called for the first time: dotmail_uidvalidity, dotmail_uidnext, @@ -288,7 +294,7 @@ dotmail_dispatch (mu_mailbox_t mailbox, int evt, void *data) static unsigned long dotmail_alloc_next_uid (struct mu_dotmail_mailbox *mbox) { - mbox->mesg[0]->uid_modified = 1; + mbox->uidvalidity_changed = 1; return mbox->uidnext++; } @@ -301,6 +307,39 @@ dotmail_message_alloc_uid (struct mu_dotmail_message *dmsg) dmsg->uid_modified = 1; } +/* Width of the decimal representation of the maximum value of the unsigned + * type t. 146/485 is the closest approximation of log10(2): + * + * log10(2) = .301030 + * 146/485 = .301031 +*/ +#define UINT_STRWIDTH(t) ((int)((sizeof(t) * 8 * 146 + 484) / 485)) + +/* + * The format for the X-IMAPbase header is: + * + * X-IMAPbase: <V> <N> + * + * where <V> and <N> are current values of the uidvalidity and uidnext + * parameters, correspondingly. + * + * The header is stored in the first message. To avoid rewriting entire + * mailbox when one of the parameters changes, the values of <V> and <N> + * are left-padded with spaces to the maximum width of their data types. + * + * Offset of the header in the mailbox and its length (without the + * trailing newline) are stored in x_imapbase_off and x_imapbase_len + * members of struct mu_dotmail_mailbox. + * + * The X_IMAPBASE_MAX macro returns maximum size of the buffer necessary + * for formatting the X-IMAPbase header. In fact, it is 2 bytes wider + * than necessary (due to the two '0' in the sample string below). + */ +#define X_IMAPBASE_MAX(d) \ + (sizeof (MU_HEADER_X_IMAPBASE ": 0 0") + \ + UINT_STRWIDTH ((d)->uidvalidity) + \ + UINT_STRWIDTH ((d)->uidnext)) + static int dotmail_rescan_unlocked (mu_mailbox_t mailbox, mu_off_t offset) { @@ -329,7 +368,7 @@ dotmail_rescan_unlocked (mu_mailbox_t mailbox, mu_off_t offset) int i, j; int force_init_uids = 0; - if (!(mailbox->flags & MU_STREAM_READ)) + if (!(dmp->stream_flags & MU_STREAM_READ)) return 0; rc = mu_streamref_create (&stream, mailbox->stream); @@ -481,6 +520,23 @@ dotmail_rescan_unlocked (mu_mailbox_t mailbox, mu_off_t offset) } else free (buf); + + if (i == mu_dotmail_hdr_x_imapbase) + { + mu_off_t off; + rc = mu_stream_seek (stream, 0, MU_SEEK_CUR, &off); + if (rc) + { + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("%s:%s (%s): %s", + __func__, "mu_stream_seek", dmp->name, + mu_strerror (rc))); + return rc; + } + dmp->x_imapbase_len = j + n; + dmp->x_imapbase_off = off - dmp->x_imapbase_len - 1; + } + state = dotmail_scan_header_newline; } else @@ -544,8 +600,16 @@ dotmail_rescan_unlocked (mu_mailbox_t mailbox, mu_off_t offset) if (!(!force_init_uids && dmsg->hdr[mu_dotmail_hdr_x_uid] && sscanf (dmsg->hdr[mu_dotmail_hdr_x_uid], - "%lu", &dmsg->uid) == 1)) - force_init_uids = 1; + "%lu", &dmsg->uid) == 1 + && dmsg->uid < dmp->uidnext + && (dmsg->num == 0 + || dmsg->uid > dmp->mesg[dmsg->num - 1]->uid))) + { + force_init_uids = 1; + dmp->uidvalidity = (unsigned long) time (NULL); + dmp->uidvalidity_changed = 1; + } + if (force_init_uids) dotmail_message_alloc_uid (dmsg); } @@ -593,7 +657,7 @@ dotmail_rescan (mu_mailbox_t mailbox, mu_off_t offset) if (!dmp) return EINVAL; - if (!(mailbox->flags & MU_STREAM_READ)) + if (!(dmp->stream_flags & MU_STREAM_READ)) return 0; mu_monitor_wrlock (mailbox->monitor); @@ -727,7 +791,8 @@ dotmail_message_unseen (mu_mailbox_t mailbox, size_t *pmsgno) } } - return MU_ERR_NOENT; + *pmsgno = 0; + return 0; } /* Initialize the mailbox UID subsystem. See the Notes above. */ @@ -744,7 +809,8 @@ mu_dotmail_mailbox_uid_setup (struct mu_dotmail_mailbox *dmp) dmp->uidvalidity = (unsigned long)time (NULL); dmp->uidnext = 1; dmp->uidvalidity_scanned = 1; - + dmp->uidvalidity_changed = 1; + for (i = 0; i < dmp->mesg_count; i++) dotmail_message_alloc_uid (dmp->mesg[i]); } @@ -752,7 +818,7 @@ mu_dotmail_mailbox_uid_setup (struct mu_dotmail_mailbox *dmp) } static int -dotmail_uidvalidity (mu_mailbox_t mailbox, unsigned long *puidvalidity) +dotmail_get_uidvalidity (mu_mailbox_t mailbox, unsigned long *puidvalidity) { struct mu_dotmail_mailbox *dmp = mailbox->data; int rc = mu_dotmail_mailbox_uid_setup (dmp); @@ -762,6 +828,16 @@ dotmail_uidvalidity (mu_mailbox_t mailbox, unsigned long *puidvalidity) } static int +dotmail_set_uidvalidity (mu_mailbox_t mailbox, unsigned long uidvalidity) +{ + struct mu_dotmail_mailbox *dmp = mailbox->data; + int rc = mu_dotmail_mailbox_uid_setup (dmp); + if (rc == 0) + dmp->uidvalidity = uidvalidity; + return rc; +} + +static int dotmail_uidnext (mu_mailbox_t mailbox, size_t *puidnext) { struct mu_dotmail_mailbox *dmp = mailbox->data; @@ -844,7 +920,8 @@ dotmail_quick_get_message (mu_mailbox_t mailbox, mu_message_qid_t qid, } static int -mailbox_append_message (mu_mailbox_t mailbox, mu_message_t msg) +mailbox_append_message (mu_mailbox_t mailbox, mu_message_t msg, + mu_envelope_t env, mu_attribute_t atr) { int rc; mu_off_t size; @@ -852,6 +929,8 @@ mailbox_append_message (mu_mailbox_t mailbox, mu_message_t msg) static char *exclude_headers[] = { MU_HEADER_X_IMAPBASE, MU_HEADER_X_UID, + MU_HEADER_STATUS, + NULL, NULL }; struct mu_dotmail_mailbox *dmp = mailbox->data; @@ -866,17 +945,72 @@ mailbox_append_message (mu_mailbox_t mailbox, mu_message_t msg) do { + char statbuf[MU_STATUS_BUF_SIZE]; + + if (atr) + { + rc = mu_attribute_to_string (atr, statbuf, MU_STATUS_BUF_SIZE, NULL); + if (rc) + break; + } + else + statbuf[0] = 0; + + if (env) + { + char const *s; + + if (mu_envelope_sget_sender (env, &s) == 0) + exclude_headers[3] = MU_HEADER_RETURN_PATH; + if (mu_envelope_sget_date (env, &s) == 0) + { + struct tm tm; + struct mu_timezone tz; + + if (mu_parse_date_dtl (s, NULL, NULL, &tm, &tz, NULL) == 0) + { + /* Format a "Received:" header with that date */ + mu_stream_printf (mailbox->stream, + "Received: from %s\n" + "\tby %s; ", + "localhost", "localhost"); + mu_c_streamftime (mailbox->stream, + MU_DATETIME_FORM_RFC822, &tm, &tz); + mu_stream_write (mailbox->stream, "\n", 1, NULL); + } + } + } + rc = mu_stream_header_copy (mailbox->stream, istr, exclude_headers); if (rc) break; + if (env) + { + char const *s; + + if (mu_envelope_sget_sender (env, &s) == 0) + { + /* Create a Return-Path header */ + mu_stream_printf (mailbox->stream, "%s: %s\n", + MU_HEADER_RETURN_PATH, s); + } + } + + /* Write status header */ + if (statbuf[0]) + mu_stream_printf (mailbox->stream, + "%s: %s\n", MU_HEADER_STATUS, statbuf); + /* Write UID-related data */ if (dmp->uidvalidity_scanned) { if (dmp->mesg_count == 0) - mu_stream_printf (mailbox->stream, "%s: %lu %lu\n", + mu_stream_printf (mailbox->stream, "%s: %*lu %*lu\n", MU_HEADER_X_IMAPBASE, + UINT_STRWIDTH (dmp->uidvalidity), dmp->uidvalidity, + UINT_STRWIDTH (dmp->uidnext), dmp->uidnext); mu_stream_printf (mailbox->stream, "%s: %lu\n", MU_HEADER_X_UID, @@ -923,7 +1057,8 @@ mailbox_append_message (mu_mailbox_t mailbox, mu_message_t msg) } static int -dotmail_append_message (mu_mailbox_t mailbox, mu_message_t msg) +dotmail_append_message (mu_mailbox_t mailbox, mu_message_t msg, + mu_envelope_t env, mu_attribute_t atr) { struct mu_dotmail_mailbox *dmp = mailbox->data; int rc; @@ -931,20 +1066,23 @@ dotmail_append_message (mu_mailbox_t mailbox, mu_message_t msg) rc = dotmail_refresh (mailbox); if (rc) return rc; - if (mailbox->locker - && (rc = mu_locker_lock (mailbox->locker)) != 0) + + mu_monitor_wrlock (mailbox->monitor); + if (mailbox->locker && (rc = mu_locker_lock (mailbox->locker)) != 0) { mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, ("%s(%s):%s: %s", __func__, dmp->name, "mu_locker_lock", mu_strerror (rc))); - return rc; } + else + { + rc = mailbox_append_message (mailbox, msg, env, atr); - rc = mailbox_append_message (mailbox, msg); - - if (mailbox->locker) - mu_locker_unlock (mailbox->locker); + if (mailbox->locker) + mu_locker_unlock (mailbox->locker); + } + mu_monitor_unlock (mailbox->monitor); return rc; } @@ -982,23 +1120,68 @@ dotmail_get_size (mu_mailbox_t mailbox, mu_off_t *psize) } static int +dotmail_stat (mu_mailbox_t mailbox, struct stat *st) +{ + int rc; + mu_transport_t trans[2]; + + rc = mu_stream_ioctl (mailbox->stream, MU_IOCTL_TRANSPORT, + MU_IOCTL_OP_GET, trans); + if (rc == 0) + { + if (fstat ((int) (intptr_t) trans[0], st)) + rc = errno; + } + return rc; +} + +static int +dotmail_set_priv (struct mu_dotmail_mailbox *dmp, struct stat *st) +{ + int rc; + mu_transport_t trans[2]; + + rc = mu_stream_ioctl (dmp->mailbox->stream, MU_IOCTL_TRANSPORT, + MU_IOCTL_OP_GET, trans); + if (rc == 0) + { + int fd = (intptr_t) trans[0]; + if (fchmod (fd, st->st_mode)) + { + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("%s:%s: chmod failed: %s", + __func__, dmp->name, strerror (errno))); + rc = errno; + } + else if (fchown (fd, st->st_uid, st->st_gid)) + { + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("%s:%s: chown failed: %s", + __func__, dmp->name, strerror (errno))); + rc = errno; + } + } + return rc; +} + +static int dotmail_get_atime (mu_mailbox_t mailbox, time_t *return_time) { struct mu_dotmail_mailbox *dmp = mailbox->data; struct stat st; - + int rc; + if (dmp == NULL) return EINVAL; - if (stat (dmp->name, &st)) - return errno; - *return_time = st.st_atime; - return 0; + if ((rc = dotmail_stat (mailbox, &st)) == 0) + *return_time = st.st_atime; + return rc; } struct mu_dotmail_flush_tracker { struct mu_dotmail_mailbox *dmp; - struct mu_dotmail_message_ref *ref; + size_t *ref; size_t mesg_count; }; @@ -1020,12 +1203,11 @@ tracker_free (struct mu_dotmail_flush_tracker *trk) free (trk->ref); } -static struct mu_dotmail_message_ref * +static struct mu_dotmail_message * tracker_next_ref (struct mu_dotmail_flush_tracker *trk, size_t orig_num) { - struct mu_dotmail_message_ref *ref = &trk->ref[trk->mesg_count++]; - ref->orig_num = orig_num; - return ref; + trk->ref[trk->mesg_count++] = orig_num; + return trk->dmp->mesg[orig_num]; } static void @@ -1040,28 +1222,29 @@ dotmail_tracker_sync (struct mu_dotmail_flush_tracker *trk) mu_dotmail_message_free (dmp->mesg[i]); dmp->size = 0; dmp->uidvalidity_scanned = 0; + dmp->uidvalidity_changed = 0; + dmp->x_imapbase_off = 0; + dmp->x_imapbase_len = 0; } else { + /* Mark */ + for (i = 0; i < trk->mesg_count; i++) + dmp->mesg[trk->ref[i]]->mark = 1; + /* Sweep */ + for (i = 0; i < dmp->mesg_count; i++) + if (!dmp->mesg[i]->mark) + mu_dotmail_message_free (dmp->mesg[i]); + /* Reorder */ for (i = 0; i < trk->mesg_count; i++) { - if (trk->ref[i].orig_num != i) - { - size_t j; - for (j = i; j < trk->ref[i].orig_num; j++) - mu_dotmail_message_free (dmp->mesg[j]); - dmp->mesg[i] = dmp->mesg[trk->ref[i].orig_num]; - } - dmp->mesg[i]->message_start = trk->ref[i].message_start; - dmp->mesg[i]->body_start = trk->ref[i].body_start; - dmp->mesg[i]->message_end = trk->ref[i].message_end; - if (trk->ref[i].rescan) - dmp->mesg[i]->body_lines_scanned = 0; + dmp->mesg[i] = dmp->mesg[trk->ref[i]]; + dmp->mesg[i]->mark = 0; } dmp->mesg_count = trk->mesg_count; - dmp->size = trk->ref[trk->mesg_count - 1].message_end + 2; + dmp->size = dmp->mesg[dmp->mesg_count - 1]->message_end + 2; } - /* FIXME: Check uidvalidity values */ + dmp->mesg_count = trk->mesg_count; } /* Write to the output stream DEST messages in the range [from,to). @@ -1075,39 +1258,36 @@ dotmail_mailbox_copy_unchanged (struct mu_dotmail_flush_tracker *trk, if (to > from) { size_t i; - mu_off_t off; + mu_off_t start, stop, off; int rc; struct mu_dotmail_mailbox *dmp = trk->dmp; + start = dmp->mesg[from]->message_start; + if (to == dmp->mesg_count) + stop = dmp->mesg[to-1]->message_end + 2; + else + stop = dmp->mesg[to]->message_start; + rc = mu_stream_seek (dest, 0, MU_SEEK_CUR, &off); if (rc) return rc; - off -= trk->dmp->mesg[from]->message_start; + off -= start; /* Fixup offsets */ for (i = from; i < to; i++) { - struct mu_dotmail_message *dmsg = trk->dmp->mesg[i]; - struct mu_dotmail_message_ref *ref = tracker_next_ref (trk, i); - ref->message_start = dmsg->message_start + off; - ref->body_start = dmsg->body_start + off; - ref->message_end = dmsg->message_end + off; - ref->rescan = 0; + struct mu_dotmail_message *ref = tracker_next_ref (trk, i); + ref->message_start += off; + ref->body_start += off; + ref->message_end += off; + // FIXME: Used to clear body_lines_scanned here. + // Not sure it is needed. } /* Copy data */ - if (to == dmp->mesg_count) - off = dmp->mesg[to-1]->message_end + 2; - else - off = dmp->mesg[to]->message_start; - - rc = mu_stream_seek (dmp->mailbox->stream, dmp->mesg[from]->message_start, - MU_SEEK_SET, NULL); + rc = mu_stream_seek (dmp->mailbox->stream, start, MU_SEEK_SET, NULL); if (rc) return rc; - return mu_stream_copy (dest, - dmp->mailbox->stream, - off - dmp->mesg[from]->message_start, - NULL); + return mu_stream_copy (dest, dmp->mailbox->stream, stop - start, NULL); } return 0; } @@ -1172,8 +1352,11 @@ dotmail_flush_temp (struct mu_dotmail_flush_tracker *trk, dmsg->hdr[mu_dotmail_hdr_x_imapbase] = NULL; if (save_imapbase == i) { - mu_asprintf (&dmsg->hdr[mu_dotmail_hdr_x_imapbase], "%lu %lu", - dmp->uidvalidity, dmp->uidnext); + mu_asprintf (&dmsg->hdr[mu_dotmail_hdr_x_imapbase], "%*lu %*lu", + UINT_STRWIDTH (dmp->uidvalidity), + dmp->uidvalidity, + UINT_STRWIDTH (dmp->uidnext), + dmp->uidnext); } rc = mu_dotmail_message_reconstruct (tempstr, dmsg, @@ -1193,12 +1376,60 @@ dotmail_flush_temp (struct mu_dotmail_flush_tracker *trk, return mu_stream_flush (tempstr); } +/* + * Copy the temporary mailbox stream TEMPSTR to the mailbox referred to by + * the tracker TRK. + */ +static inline int +dotmail_copyback (struct mu_dotmail_flush_tracker *trk, mu_stream_t tempstr) +{ + int rc; + mu_stream_t mbx_stream = trk->dmp->mailbox->stream; + mu_off_t size; + + rc = mu_stream_seek (tempstr, 0, MU_SEEK_SET, NULL); + if (rc) + { + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("%s: can't rewind temporary file: %s", + __func__, mu_strerror (rc))); + return rc; + } + + rc = mu_stream_seek (mbx_stream, 0, MU_SEEK_SET, NULL); + if (rc) + { + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("%s: can't rewind mailbox %s: %s", + __func__, trk->dmp->name, mu_strerror (rc))); + return rc; + } + + rc = mu_stream_copy (mbx_stream, tempstr, 0, &size); + if (rc) + { + mu_error (_("copying back to mailbox %s failed: %s"), + trk->dmp->name, mu_strerror (rc)); + return rc; + } + rc = mu_stream_truncate (mbx_stream, size); + if (rc) + { + mu_error (_("cannot truncate mailbox stream: %s"), + mu_stream_strerror (mbx_stream, rc)); + return rc; + } + + dotmail_tracker_sync (trk); + return 0; +} + /* Flush the mailbox described by the tracker TRK to the stream TEMPSTR. EXPUNGE is 1 if the MU_ATTRIBUTE_DELETED attribute is to be honored. Assumes that simultaneous access to the mailbox has been blocked. */ static int -dotmail_flush_unlocked (struct mu_dotmail_flush_tracker *trk, int expunge) +dotmail_flush_unlocked (struct mu_dotmail_flush_tracker *trk, int mode) { struct mu_dotmail_mailbox *dmp = trk->dmp; int rc; @@ -1213,14 +1444,75 @@ dotmail_flush_unlocked (struct mu_dotmail_flush_tracker *trk, int expunge) ("%s (%s)", __func__, dmp->name)); if (dmp->mesg_count == 0) return 0; - - if (dmp->mailbox->flags & MU_STREAM_APPEND) - return mu_stream_flush (dmp->mailbox->stream); - + if (mode == FLUSH_UIDVALIDITY && !dmp->uidvalidity_changed) + return 0; + rc = dotmail_refresh (dmp->mailbox); if (rc) return rc; + if (dmp->uidvalidity_changed) + { + size_t i; + char buf[X_IMAPBASE_MAX (dmp)]; + int n; + mu_stream_t stream = dmp->mailbox->stream; + + /* + * Format the X-IMAPbase header and check if it will fit in place + * of the existing one (if any). If so, write it at once and return. + */ + n = snprintf (buf, sizeof (buf), "%s: %*lu %*lu", + MU_HEADER_X_IMAPBASE, + UINT_STRWIDTH (dmp->uidvalidity), + dmp->uidvalidity, + UINT_STRWIDTH (dmp->uidnext), + dmp->uidnext); + + if (dmp->x_imapbase_len && dmp->x_imapbase_len >= n) + { + rc = mu_stream_seek (stream, dmp->x_imapbase_off, MU_SEEK_SET, NULL); + if (rc) + { + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("%s:%s (%s): %s", + __func__, "mu_stream_seek", dmp->name, + mu_strerror (rc))); + return rc; + } + rc = mu_stream_printf (stream, "%-*s", + (int) dmp->x_imapbase_len, + buf); + if (rc) + { + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("%s:%s (%s): %s", + __func__, "mu_stream_printf", dmp->name, + mu_strerror (rc))); + + } + return 0; + } + else + { + /* + * There is no X-IMAPbase header yet or it is not wide enough to + * accept the current value. Fall back to reformatting entire + * mailbox. Clear any other changes that might have been done + * to its messages. + */ + dirty = 0; + dmp->mesg[0]->uid_modified = 1; + + for (i = 1; i < dmp->mesg_count; i++) + { + struct mu_dotmail_message *dmsg = dmp->mesg[i]; + dmsg->uid_modified = 0; + dmsg->attr_flags &= ~(MU_ATTRIBUTE_MODIFIED|MU_ATTRIBUTE_DELETED); + } + } + } + for (dirty = 0; dirty < dmp->mesg_count; dirty++) { struct mu_dotmail_message *dmsg = dmp->mesg[dirty]; @@ -1233,95 +1525,136 @@ dotmail_flush_unlocked (struct mu_dotmail_flush_tracker *trk, int expunge) break; } - if (dirty + 1 == dmp->mesg_count) - return 0; - - p = strrchr (dmp->name, '/'); - if (p) - { - size_t l = p - dmp->name; - hints.tmpdir = malloc (l + 1); - if (!hints.tmpdir) - return ENOMEM; - memcpy (hints.tmpdir, dmp->name, l); - hints.tmpdir[l] = 0; - } - else - { - hints.tmpdir = mu_getcwd (); - if (!hints.tmpdir) - return ENOMEM; - } - rc = mu_tempfile (&hints, MU_TEMPFILE_TMPDIR, &tempfd, &tempname); - if (rc) - { - free (hints.tmpdir); - return rc; - } - rc = mu_fd_stream_create (&tempstr, tempname, tempfd, - MU_STREAM_RDWR|MU_STREAM_SEEK); - if (rc) - { - free (hints.tmpdir); - close (tempfd); - free (tempname); - return rc; - } - - rc = dotmail_flush_temp (trk, dirty, tempstr, expunge); - mu_stream_unref (tempstr); - if (rc == 0) - { - /* Rename mailbox to temporary copy */ - char *backup = mu_tempname (hints.tmpdir); - rc = rename (dmp->name, backup); - if (rc) + rc = 0; + if (dirty < dmp->mesg_count) + { + p = strrchr (dmp->name, '/'); + if (p) { - mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, - ("%s:%s: failed to rename to backup file %s: %s", - __func__, dmp->name, tempname, - mu_strerror (rc))); - unlink (backup); + size_t l = p - dmp->name; + hints.tmpdir = malloc (l + 1); + if (!hints.tmpdir) + return ENOMEM; + memcpy (hints.tmpdir, dmp->name, l); + hints.tmpdir[l] = 0; } else { - rc = rename (tempname, dmp->name); - if (rc == 0) - { - /* Success. Synchronize internal data with the counter. */ - dotmail_tracker_sync (trk); - mu_stream_destroy (&dmp->mailbox->stream); - rc = dotmail_mailbox_init_stream (dmp); - } - else + hints.tmpdir = mu_getcwd (); + if (!hints.tmpdir) + return ENOMEM; + } + rc = mu_tempfile (&hints, MU_TEMPFILE_TMPDIR, &tempfd, &tempname); + if (rc == 0) + { + rc = mu_fd_stream_create (&tempstr, tempname, tempfd, + MU_STREAM_RDWR|MU_STREAM_SEEK); + } + else if (rc == EACCES) + { + /* + * Mail spool directory is not writable for the user. Fall + * back to using temporary stream located elsewhere. When + * ready, it will be copied back to the mailbox. + * + * Reset the tempname to NULL to instruct the code below + * which approach to take. + */ + tempname = NULL; + + rc = mu_temp_file_stream_create (&tempstr, NULL, 0); + } + + if (rc) + { + free (hints.tmpdir); + close (tempfd); + free (tempname); + return rc; + } + + rc = dotmail_flush_temp (trk, dirty, tempstr, mode == FLUSH_EXPUNGE); + if (rc == 0) + { + if (tempname) { - int rc1; - mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, - ("%s: failed to rename temporary file %s %s: %s", - __func__, tempname, dmp->name, - mu_strerror (rc))); - rc1 = rename (backup, dmp->name); - if (rc1) + /* Mail spool is writable. Rename the temporary copy back + to mailbox */ + char *backup; + struct stat st; + + if ((rc = dotmail_stat (dmp->mailbox, &st)) != 0) + { + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("%s:%s: stat failed: %s", + __func__, dmp->name, strerror (errno))); + } + else { - mu_error (_("failed to restore %s from backup %s: %s"), - dmp->name, backup, mu_strerror (rc1)); - mu_error (_("backup left in %s"), backup); - free (backup); - backup = NULL; + mu_stream_flush (tempstr); + backup = mu_tempname (hints.tmpdir); + if (rename (dmp->name, backup)) + { + rc = errno; + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("%s:%s: failed to rename to backup file %s: %s", + __func__, dmp->name, tempname, + mu_strerror (rc))); + unlink (backup); + } + else + { + rc = rename (tempname, dmp->name); + if (rc == 0) + { + /* Success. Synchronize internal data with the + counter. */ + dotmail_tracker_sync (trk); + mu_stream_destroy (&dmp->mailbox->stream); + rc = dotmail_mailbox_init_stream (dmp); + if (rc == 0) + dotmail_set_priv (dmp, &st); + } + else + { + int rc1; + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("%s: failed to rename temporary file %s %s: %s", + __func__, tempname, dmp->name, + mu_strerror (rc))); + rc1 = rename (backup, dmp->name); + if (rc1) + { + mu_error (_("failed to restore %s from backup %s: %s"), + dmp->name, backup, mu_strerror (rc1)); + mu_error (_("backup left in %s"), backup); + free (backup); + backup = NULL; + } + } + } + + if (backup) + { + unlink (backup); + free (backup); + } + unlink (tempname); } } + else + { + /* Mail spool not writable. Copy the tempstr back to mailbox. */ + rc = dotmail_copyback (trk, tempstr); + } } - - if (backup) - { - unlink (backup); - free (backup); - } + free (tempname); + free (hints.tmpdir); + mu_stream_unref (tempstr); } - unlink (tempname); - free (tempname); - free (hints.tmpdir); - + + dmp->uidvalidity_changed = 0; + return rc; } @@ -1336,7 +1669,7 @@ dotmail_flush_unlocked (struct mu_dotmail_flush_tracker *trk, int expunge) the original mailbox remains unchanged. */ static int -dotmail_flush (struct mu_dotmail_mailbox *dmp, int expunge) +dotmail_flush (struct mu_dotmail_mailbox *dmp, int mode) { int rc; sigset_t signalset; @@ -1364,7 +1697,7 @@ dotmail_flush (struct mu_dotmail_mailbox *dmp, int expunge) rc = tracker_init (&trk, dmp); if (rc == 0) { - rc = dotmail_flush_unlocked (&trk, expunge); + rc = dotmail_flush_unlocked (&trk, mode); tracker_free (&trk); } @@ -1382,16 +1715,15 @@ static int dotmail_expunge (mu_mailbox_t mailbox) { struct mu_dotmail_mailbox *dmp = mailbox->data; - return dotmail_flush (dmp, 1); + return dotmail_flush (dmp, FLUSH_EXPUNGE); } static int dotmail_sync (mu_mailbox_t mailbox) { struct mu_dotmail_mailbox *dmp = mailbox->data; - return dotmail_flush (dmp, 0); + return dotmail_flush (dmp, FLUSH_SYNC); } - int mu_dotmail_mailbox_init (mu_mailbox_t mailbox) @@ -1439,7 +1771,8 @@ mu_dotmail_mailbox_init (mu_mailbox_t mailbox) mailbox->_expunge = dotmail_expunge; mailbox->_sync = dotmail_sync; - mailbox->_uidvalidity = dotmail_uidvalidity; + mailbox->_get_uidvalidity = dotmail_get_uidvalidity; + mailbox->_set_uidvalidity = dotmail_set_uidvalidity; mailbox->_uidnext = dotmail_uidnext; mailbox->_get_size = dotmail_get_size; mailbox->_get_atime = dotmail_get_atime; |