diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2019-02-26 10:46:15 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2019-03-01 16:05:52 +0200 |
commit | 169099f43bfd1a45398139c27df5ebc4375f6a7d (patch) | |
tree | fc9795aff44d4b6ca693c3f20c7cc10868b910a0 | |
parent | de26c87ab7df32066f5408012d0d391fcb9c9535 (diff) | |
download | mailutils-169099f43bfd1a45398139c27df5ebc4375f6a7d.tar.gz mailutils-169099f43bfd1a45398139c27df5ebc4375f6a7d.tar.bz2 |
Initial implementation of the dotfile format
* configure.ac: Enable new local format.
* include/mailutils/registrar.h (mu_dotmail_record): New extern.
(mu_register_all_mbox_formats)
(mu_register_local_mbox_formats): Add mu_dotmail_record
* include/mailutils/sys/dotmail.h: New file.
* libproto/dotmail/Makefile.am: New file.
* libproto/dotmail/dotmail.c: New file.
* libproto/dotmail/message.c: New file.
* libproto/dotmail/folder.c: New file.
* libproto/dotmail/tests/.gitignore: New file.
* libproto/dotmail/tests/Makefile.am: New file.
* libproto/dotmail/tests/atlocal.in: New file.
* libproto/dotmail/tests/count.at: New file.
* libproto/dotmail/tests/dm_mbox.c: New file.
* libproto/dotmail/tests/dm_mesg.c: New file.
* libproto/dotmail/tests/env.at: New file.
* libproto/dotmail/tests/header.at: New file.
* libproto/dotmail/tests/recent.at: New file.
* libproto/dotmail/tests/testsuite.at: New file.
* libproto/dotmail/tests/attr.at: New file.
* libproto/dotmail/tests/body.at: New file.
* libproto/dotmail/tests/uid.at: New file.
* libproto/dotmail/tests/dm_qget.c: New file.
* libproto/dotmail/tests/qget.at: New file.
* libproto/dotmail/tests/append.at: New file.
* libproto/dotmail/tests/delete.at: New file.
* libmu_scm/mailutils/ancilla.scm: Fix regular expression to
take into account changes introduced by 4bdc41ff90.
30 files changed, 3615 insertions, 7 deletions
@@ -1,9 +1,11 @@ -GNU mailutils NEWS -- history of user-visible changes. 2019-02-23 +GNU mailutils NEWS -- history of user-visible changes. 2019-03-01 Copyright (C) 2002-2019 Free Software Foundation, Inc. See the end of file for copying conditions. Please send mailutils bug reports to <bug-mailutils@gnu.org>. +Version 3.6.90 (git) + Version 3.6 - 2019-02-23 * Rewrite mailcap (RFC1524) support diff --git a/configure.ac b/configure.ac index 0c154ba3c..c52cfc6b7 100644 --- a/configure.ac +++ b/configure.ac @@ -16,7 +16,7 @@ dnl You should have received a copy of the GNU General Public License along dnl with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. AC_PREREQ(2.63) -AC_INIT([GNU Mailutils], [3.6], [bug-mailutils@gnu.org], [mailutils], +AC_INIT([GNU Mailutils], [3.6.90], [bug-mailutils@gnu.org], [mailutils], [http://mailutils.org]) AC_CONFIG_SRCDIR([libmailutils/mailbox/mailbox.c]) AC_CONFIG_AUX_DIR([build-aux]) @@ -1006,6 +1006,7 @@ MU_ENABLE_REMOTE_MAILBOX_FORMAT([pop]) MU_ENABLE_LOCAL_MAILBOX_FORMAT([nntp],[],[],["$mu_build_experimental" = "yes"]) MU_ENABLE_LOCAL_MAILBOX_FORMAT([mh]) MU_ENABLE_LOCAL_MAILBOX_FORMAT([maildir]) +MU_ENABLE_LOCAL_MAILBOX_FORMAT([dotmail]) AC_SUBST(MU_SMTP_PROGRAMS_BUILD) AC_SUBST(MU_SMTP_DEJATOOL) @@ -1519,6 +1520,7 @@ AC_CONFIG_FILES([ libmu_sieve/extensions/Makefile libproto/Makefile libproto/mbox/Makefile + libproto/mailer/Makefile maidag/Makefile mail/Makefile mail/testsuite/Makefile diff --git a/doc/texinfo/.gitignore b/doc/texinfo/.gitignore index 1a044fb19..1634846cd 100644 --- a/doc/texinfo/.gitignore +++ b/doc/texinfo/.gitignore @@ -30,4 +30,7 @@ stamp-vti texinfo.tex vers-muint.texi version.texi - +mailutils.kys +mailutils.vrs +manual.tar* +manual diff --git a/include/mailutils/registrar.h b/include/mailutils/registrar.h index 78c9dcabe..976e82190 100644 --- a/include/mailutils/registrar.h +++ b/include/mailutils/registrar.h @@ -131,9 +131,12 @@ extern mu_record_t mu_smtps_record; extern mu_record_t mu_sendmail_record; /* Program mailer, "prog://", "|" */ extern mu_record_t mu_prog_record; + +extern mu_record_t mu_dotmail_record; #define mu_register_all_mbox_formats() do {\ mu_registrar_record (mu_mbox_record);\ + mu_registrar_record (mu_dotmail_record);\ mu_registrar_record (mu_pop_record);\ mu_registrar_record (mu_pops_record);\ mu_registrar_record (mu_imap_record);\ @@ -145,6 +148,7 @@ extern mu_record_t mu_prog_record; #define mu_register_local_mbox_formats() do {\ mu_registrar_record (mu_mbox_record);\ + mu_registrar_record (mu_dotmail_record);\ mu_registrar_record (mu_mh_record);\ mu_registrar_record (mu_maildir_record);\ mu_registrar_set_default_record (MU_DEFAULT_RECORD);\ diff --git a/include/mailutils/sys/dotmail.h b/include/mailutils/sys/dotmail.h new file mode 100644 index 000000000..05795ab8a --- /dev/null +++ b/include/mailutils/sys/dotmail.h @@ -0,0 +1,86 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 2019 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 + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef _MAILUTILS_SYS_DOTMAIL_H +#define _MAILUTILS_SYS_DOTMAIL_H + +# include <mailutils/types.h> + +enum mu_dotmail_hdr + { + mu_dotmail_hdr_status, + mu_dotmail_hdr_x_imapbase, + mu_dotmail_hdr_x_uid, + MU_DOTMAIL_HDR_MAX + } status; + +struct mu_dotmail_message +{ + /* Offsets in the mailbox */ + mu_off_t message_start; /* Start of message */ + mu_off_t body_start; /* Start of body */ + mu_off_t message_end; /* End of message */ + /* Additional info */ + size_t body_lines; /* Number of lines in message body */ + unsigned long uid; /* IMAP-style uid. */ + char *hdr[MU_DOTMAIL_HDR_MAX]; /* Pre-scanned headers */ + unsigned headers_scanned:1; /* True if hdr is filled */ + unsigned attr_scanned:1; /* True if attr_flags is initialized */ + unsigned body_lines_scanned:1; /* True if body_lines is initialized */ + int attr_flags; /* Packed "Status:" attribute flags */ + mu_message_t message; /* Pointer to the message object if any */ + /* Backlink to the mailbox */ + struct mu_dotmail_mailbox *mbox; /* Mailbox */ + size_t num; /* Number of this message in the mailbox (0-based) */ +}; + +struct mu_dotmail_message_ref +{ + size_t orig_num; /* Original message index */ + mu_off_t message_start; /* Start of message */ + mu_off_t body_start; /* Start of body */ + mu_off_t message_end; /* End of message */ + int rescan; +}; + +struct mu_dotmail_mailbox +{ + char *name; /* Disk file name */ + mu_mailbox_t mailbox; /* Associated mailbox */ + + mu_off_t size; /* Size of the mailbox. */ + unsigned long uidvalidity; /* Uidvalidity value */ + unsigned uidnext; /* Expected next UID value */ + unsigned uidvalidity_scanned:1; /* True if uidvalidity is initialized */ + size_t scanned_uids_count; /* Number of messages for which UIDs have been + scanned */ + + struct mu_dotmail_message **mesg; /* Array of messages */ + size_t mesg_count; /* Number of messages in mesgv */ + size_t mesg_max; /* Actual capacity of mesg */ +}; + +int mu_dotmail_mailbox_init (mu_mailbox_t mailbox); +void mu_dotmail_message_free (struct mu_dotmail_message *dmsg); +int mu_dotmail_message_get (struct mu_dotmail_message *dmsg, mu_message_t *mptr); +int mu_dotmail_message_headers_prescan (struct mu_dotmail_message *dmsg); +int mu_dotmail_message_attr_load (struct mu_dotmail_message *dmsg); +int mu_dotmail_mailbox_scan_uids (mu_mailbox_t mailbox, size_t msgno); +int mu_dotmail_message_reconstruct (mu_stream_t dest, + struct mu_dotmail_message *dmsg, + struct mu_dotmail_message_ref *ref); + +#endif diff --git a/libmu_scm/mailutils/ancilla.scm b/libmu_scm/mailutils/ancilla.scm index ee4270d75..0466c613a 100644 --- a/libmu_scm/mailutils/ancilla.scm +++ b/libmu_scm/mailutils/ancilla.scm @@ -19,18 +19,17 @@ (use-modules ((mailutils mailutils)) ((ice-9 regex))) - (define-public (message-format dest msg) "Formats a message hiding all mutable information (email and date)." "The @samp{dest} argument has the same meaning as in @samp{format}." (let ((s (with-output-to-string (lambda () (display msg))))) (cond - ((string-match "^(#<message )\".+@.+\" \"(Sun|Mon|Tue|Wed|Thu|Fri|Sat) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) [ 0123][0-9] [[:digit:]]{2}:[[:digit:]]{2}\" ([[:digit:]]+) ([[:digit:]]+)>$" s) => + ((string-match "^(#<message )\".+@.+\" \"(((Sun|Mon|Tue|Wed|Thu|Fri|Sat) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) [ 0123][0-9] [[:digit:]]{2}:[[:digit:]]{2})|UNKNOWN)\" ([[:digit:]]+) ([[:digit:]]+)>$" s) => (lambda (m) (format dest "~a\"X@Y\" \"Dow Mon Day HH:MM\" ~a ~a>" (match:substring m 1) - (match:substring m 4) - (match:substring m 5))))))) + (match:substring m 6) + (match:substring m 7))))))) (define-public (string->message str) (mu-message-from-port (open-input-string str))) diff --git a/libproto/Makefile.am b/libproto/Makefile.am index db296eb32..e037ddde4 100644 --- a/libproto/Makefile.am +++ b/libproto/Makefile.am @@ -36,3 +36,7 @@ if MU_COND_SUPPORT_MAILDIR SUBDIRS += maildir endif +if MU_COND_SUPPORT_DOTMAIL + SUBDIRS += dotmail +endif + diff --git a/libproto/dotmail/Makefile.am b/libproto/dotmail/Makefile.am new file mode 100644 index 000000000..eeac8be28 --- /dev/null +++ b/libproto/dotmail/Makefile.am @@ -0,0 +1,27 @@ +## This file is part of GNU Mailutils. +## Copyright (C) 2019 Free Software Foundation, Inc. +## +## GNU Mailutils is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License as +## published by the Free Software Foundation; either version 3, or (at +## your option) any later version. +## +## GNU Mailutils is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. + +AM_CPPFLAGS = @MU_LIB_COMMON_INCLUDES@ + +lib_LTLIBRARIES = libmu_dotmail.la +libmu_dotmail_la_LDFLAGS=-version-info @VI_CURRENT@:@VI_REVISION@:@VI_AGE@ +libmu_dotmail_la_LIBADD = ${MU_LIB_MAILUTILS} +libmu_dotmail_la_SOURCES = \ + dotmail.c\ + message.c\ + folder.c + +SUBDIRS = . tests diff --git a/libproto/dotmail/dotmail.c b/libproto/dotmail/dotmail.c new file mode 100644 index 000000000..223dcf442 --- /dev/null +++ b/libproto/dotmail/dotmail.c @@ -0,0 +1,1306 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 2019 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 + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> +#include <stdlib.h> +#include <unistd.h> +#ifdef WITH_PTHREAD +# include <pthread.h> +#endif +#include <sys/stat.h> +#include <signal.h> +#include <mailutils/sys/dotmail.h> +#include <mailutils/sys/mailbox.h> +#include <mailutils/sys/message.h> +#include <mailutils/diag.h> +#include <mailutils/errno.h> +#include <mailutils/url.h> +#include <mailutils/property.h> +#include <mailutils/io.h> +#include <mailutils/observer.h> +#include <mailutils/filter.h> +#include <mailutils/stream.h> +#include <mailutils/locker.h> +#include <mailutils/nls.h> +#include <mailutils/header.h> +#include <mailutils/attribute.h> +#include <mailutils/util.h> + +static void +dotmail_destroy (mu_mailbox_t mailbox) +{ + size_t i; + struct mu_dotmail_mailbox *dmp = mailbox->data; + + if (!dmp) + return; + + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE1, + ("%s (%s)", __func__, dmp->name)); + mu_monitor_wrlock (mailbox->monitor); + for (i = 0; i < dmp->mesg_count; i++) + { + mu_dotmail_message_free (dmp->mesg[i]); + } + free (dmp->mesg); + free (dmp->name); + free (dmp); + mailbox->data = NULL; + mu_monitor_unlock (mailbox->monitor); +} + +static int +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); + if (rc) + { + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("%s:%s (%s): %s", + __func__, "mu_mapfile_stream_create", 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; + } + + mu_stream_set_buffer (mailbox->stream, mu_buffer_full, 0); + return 0; +} + +static int +dotmail_open (mu_mailbox_t mailbox, int flags) +{ + struct mu_dotmail_mailbox *dmp = mailbox->data; + int rc; + + if (!dmp) + return EINVAL; + + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE1, + ("%s(%s, 0x%x)", __func__, dmp->name, mailbox->flags)); + + mailbox->flags = flags; + + rc = dotmail_mailbox_init_stream (dmp); + + if (mailbox->locker == NULL + && (flags & (MU_STREAM_WRITE | MU_STREAM_APPEND | MU_STREAM_CREAT))) + { + rc = mu_locker_create (&mailbox->locker, dmp->name, 0); + if (rc) + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("%s:%s (%s): %s", + __func__, "mu_locker_create", dmp->name, + mu_strerror (rc))); + } + + return rc; +} + +static int +dotmail_close (mu_mailbox_t mailbox) +{ + struct mu_dotmail_mailbox *dmp = mailbox->data; + size_t i; + + if (!dmp) + return EINVAL; + + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE1, + ("%s (%s)", __func__, dmp->name)); + + mu_locker_unlock (mailbox->locker); + mu_monitor_wrlock (mailbox->monitor); + for (i = 0; i < dmp->mesg_count; i++) + { + mu_dotmail_message_free (dmp->mesg[i]); + } + free (dmp->mesg); + dmp->mesg = NULL; + dmp->mesg_count = dmp->mesg_max = 0; + dmp->size = 0; + dmp->uidvalidity = 0; + dmp->uidnext = 0; + mu_monitor_unlock (mailbox->monitor); + mu_stream_destroy (&mailbox->stream); + return 0; +} + +static int +dotmail_remove (mu_mailbox_t mailbox) +{ + struct mu_dotmail_mailbox *dmp = mailbox->data; + + if (!dmp) + return EINVAL; + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE1, + ("%s (%s)", __func__, dmp->name)); + if (unlink (dmp->name)) + return errno; + return 0; +} + +static int +dotmail_is_updated (mu_mailbox_t mailbox) +{ + struct mu_dotmail_mailbox *dmp = mailbox->data; + mu_off_t size = 0; + + if (!dmp) + return 0; + + if (mu_stream_size (mailbox->stream, &size) != 0) + return 1; + if (size < dmp->size) + { + mu_observable_notify (mailbox->observable, MU_EVT_MAILBOX_CORRUPT, + mailbox); + mu_diag_output (MU_DIAG_EMERG, _("mailbox corrupted, shrank in size")); + return 0; + } + return (dmp->size == size); +} + +#ifdef WITH_PTHREAD +void +dotmail_cleanup (void *arg) +{ + mu_mailbox_t mailbox = arg; + mu_monitor_unlock (mailbox->monitor); + mu_locker_unlock (mailbox->locker); +} +#endif + +static int +dotmail_alloc_message (struct mu_dotmail_mailbox *dmp, + struct mu_dotmail_message **dmsg_ptr) +{ + struct mu_dotmail_message *dmsg; + + if (dmp->mesg_count == dmp->mesg_max) + { + size_t n = dmp->mesg_max; + void *p; + + if (n == 0) + n = 64; + else + { + if ((size_t) -1 / 3 * 2 / sizeof (dmp->mesg[0]) <= n) + return ENOMEM; + n += (n + 1) / 2; + } + p = realloc (dmp->mesg, n * sizeof (dmp->mesg[0])); + if (!p) + return ENOMEM; + dmp->mesg = p; + dmp->mesg_max = n; + } + dmsg = calloc (1, sizeof (*dmsg)); + if (!dmsg) + return ENOMEM; + dmsg->mbox = dmp; + dmsg->num = dmp->mesg_count; + dmp->mesg[dmp->mesg_count++] = dmsg; + *dmsg_ptr = dmsg; + return 0; +} + +static int +dotmail_dispatch (mu_mailbox_t mailbox, int evt, void *data) +{ + if (!mailbox->observable) + return 0; + + mu_monitor_unlock (mailbox->monitor); + if (mu_observable_notify (mailbox->observable, evt, data)) + { + if (mailbox->locker) + mu_locker_unlock (mailbox->locker); + return EINTR; + } + mu_monitor_wrlock (mailbox->monitor); + return 0; +} + +static int +dotmail_rescan_unlocked (mu_mailbox_t mailbox, mu_off_t offset) +{ + struct mu_dotmail_mailbox *dmp = mailbox->data; + mu_stream_t stream; + char cur; + size_t n; + enum dotmail_scan_state + { + dotmail_scan_init, + dotmail_scan_header, + dotmail_scan_header_newline, + dotmail_scan_body, + dotmail_scan_body_newline, + dotmail_scan_dot + } state = dotmail_scan_init; + struct mu_dotmail_message *dmsg; + size_t lines = 0; + int rc; + + rc = mu_streamref_create (&stream, mailbox->stream); + if (rc) + { + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("%s:%s (%s): %s", + __func__, "mu_streamref_create", dmp->name, + mu_strerror (rc))); + return rc; + } + + rc = mu_stream_seek (stream, offset, 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; + } + + while ((rc = mu_stream_read (stream, &cur, 1, &n)) == 0) + { + if (n == 0) + { + if (state != dotmail_scan_init && state != dotmail_scan_dot) + { + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("%s (%s): message %lu ended prematurely", + __func__, dmp->name, + (unsigned long) dmp->mesg_count)); + --dmp->mesg_count; + // FIXME: status = MU_ERR_PARSE; + } + break; + } + + if (cur == '\n') + { + /* Ping them every 1000 lines. Should be tunable. */ + if (((lines + 1) % 1000) == 0) + dotmail_dispatch (mailbox, MU_EVT_MAILBOX_PROGRESS, NULL); + } + + switch (state) + { + case dotmail_scan_init: + /* Start new message */ + rc = dotmail_alloc_message (dmp, &dmsg); + if (rc) + { + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("%s:%s (%s): %s", + __func__, "dotmail_alloc_message", dmp->name, + mu_strerror (rc))); + return rc; + } + rc = mu_stream_seek (stream, 0, MU_SEEK_CUR, &dmsg->message_start); + 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; + } + --dmsg->message_start; + state = dotmail_scan_header; + break; + + case dotmail_scan_header: + if (cur == '\n') + { + state = dotmail_scan_header_newline; + } + break; + + case dotmail_scan_header_newline: + if (cur == '\n') + { + rc = mu_stream_seek (stream, 0, MU_SEEK_CUR, &dmsg->body_start); + 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; + } + state = dotmail_scan_body; + } + else + state = dotmail_scan_header; + break; + + case dotmail_scan_body: + if (cur == '\n') + { + dmsg->body_lines++; + state = dotmail_scan_body_newline; + } + break; + + case dotmail_scan_body_newline: + if (cur == '.') + state = dotmail_scan_dot; + else if (cur == '\n') + /* keep state */; + else + state = dotmail_scan_body; + break; + + case dotmail_scan_dot: + if (cur == '\n') + { + size_t count; + + dmsg->body_lines_scanned = 1; + + rc = mu_stream_seek (stream, 0, MU_SEEK_CUR, &dmsg->message_end); + 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; + } + dmsg->message_end -= 2; + + /* Every 100 mesgs update the lock, it should be every minute. */ + if (mailbox->locker && (dmp->mesg_count % 100) == 0) + mu_locker_touchlock (mailbox->locker); + + count = dmp->mesg_count; + dotmail_dispatch (mailbox, MU_EVT_MESSAGE_ADD, &count); + + state = dotmail_scan_init; + } + else + state = dotmail_scan_body; + break; + } + } + + if (rc) + { + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, + ("%s:%s (%s): %s", + __func__, "mu_stream_read", dmp->name, + mu_strerror (rc))); + } + mu_stream_unref (stream); + + return rc; +} + +/* Scan the mailbox starting from the given offset */ +static int +dotmail_rescan (mu_mailbox_t mailbox, mu_off_t offset) +{ + struct mu_dotmail_mailbox *dmp = mailbox->data; + int rc; + + if (!dmp) + return EINVAL; + + mu_monitor_wrlock (mailbox->monitor); +#ifdef WITH_PTHREAD + pthread_cleanup_push (dotmail_cleanup, (void *)mailbox); +#endif + + rc = mu_stream_size (mailbox->stream, &dmp->size); + if (rc != 0) + { + mu_monitor_unlock (mailbox->monitor); + return rc; + } + + if (mailbox->locker && (rc = mu_locker_lock (mailbox->locker))) + { + mu_monitor_unlock (mailbox->monitor); + return rc; + } + + rc = dotmail_rescan_unlocked (mailbox, offset); + + if (mailbox->locker) + mu_locker_unlock (mailbox->locker); + mu_monitor_unlock (mailbox->monitor); + +#ifdef WITH_PTHREAD + pthread_cleanup_pop (0); +#endif + + return rc; +} + +static int +dotmail_refresh (mu_mailbox_t mailbox) +{ + struct mu_dotmail_mailbox *dmp = mailbox->data; + + if (dotmail_is_updated (mailbox)) + return 0; + return dotmail_rescan (mailbox, + dmp->mesg_count == 0 + ? 0 + : dmp->mesg[dmp->mesg_count - 1]->message_end + 2); +} + +static int +dotmail_scan (mu_mailbox_t mailbox, size_t i, size_t *pcount) +{ + struct mu_dotmail_mailbox *dmp = mailbox->data; + + if (!dmp) + return EINVAL; + + mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE1, + ("%s (%s)", __func__, dmp->name)); + + if (i == 0 || (dmp->mesg_count && i > dmp->mesg_count)) + return EINVAL; + + if (!dotmail_is_updated (mailbox)) + { + int rc; + + while (i < dmp->mesg_count) + mu_dotmail_message_free (dmp->mesg[dmp->mesg_count--]); + + rc = dotmail_refresh (mailbox); + if (rc) + return rc; + } + else if (mailbox->observable) + { + for (; i <= dmp->mesg_count; i++) + { + size_t tmp = i; + if (mu_observable_notify (mailbox->observable, MU_EVT_MESSAGE_ADD, + &tmp) != 0) + break; + /* FIXME: Hardcoded value! Must be configurable */ + if (((i + 1) % 50) == 0) + mu_observable_notify (mailbox->observable, MU_EVT_MAILBOX_PROGRESS, + NULL); + } + } + if (pcount) + *pcount = dmp->mesg_count; + return 0; +} + +static int +dotmail_prescan_headers (mu_mailbox_t mailbox) +{ + struct mu_dotmail_mailbox *dmp = mailbox->data; + size_t i = 0; + + int rc = dotmail_refresh (mailbox); + if (rc) + return rc; + + for (; i < dmp->mesg_count; i++) + mu_dotmail_message_headers_prescan (dmp->mesg[i]); + + return 0; +} + +static int +dotmail_messages_recent (mu_mailbox_t mailbox, size_t *pcount) +{ + size_t i; + size_t count = 0; + int rc; + struct mu_dotmail_mailbox *dmp = mailbox->data; + + rc = dotmail_prescan_headers (mailbox); + if (rc) + return rc; + + for (i = 0; i < dmp->mesg_count; i++) + { + mu_dotmail_message_attr_load (dmp->mesg[i]); + if (MU_ATTRIBUTE_IS_UNSEEN (dmp->mesg[i]->attr_flags)) + ++count; + } + + *pcount = count; + + return 0; +} + +static int +dotmail_message_unseen (mu_mailbox_t mailbox, size_t *pmsgno) +{ + size_t i; + int rc; + struct mu_dotmail_mailbox *dmp = mailbox->data; + + rc = dotmail_prescan_headers (mailbox); + if (rc) + return rc; + + for (i = 0; i < dmp->mesg_count; i++) + { + mu_dotmail_message_attr_load (dmp->mesg[i]); + if (MU_ATTRIBUTE_IS_UNREAD (dmp->mesg[i]->attr_flags)) + { + *pmsgno = i + 1; + return 0; + } + } + + return MU_ERR_NOENT; +} + +static int +dotmail_uidvalidity (mu_mailbox_t mailbox, unsigned long *puidvalidity) +{ + struct mu_dotmail_mailbox *dmp = mailbox->data; + + if (!dmp->uidvalidity_scanned) + { + int rc = dotmail_refresh (mailbox); + if (rc) + return rc; + + if (dmp->mesg_count) + { + mu_dotmail_message_attr_load (dmp->mesg[0]); + if (dmp->mesg[0]->hdr[mu_dotmail_hdr_x_imapbase]) + { + if (sscanf (dmp->mesg[0]->hdr[mu_dotmail_hdr_x_imapbase], + "%lu %u", + &dmp->uidvalidity, &dmp->uidnext) != 2) + { + dmp->uidvalidity = (unsigned long)time (NULL); + dmp->uidnext = dmp->mesg_count + 1; + } + } + } + else + { + dmp->uidvalidity = (unsigned long)time (NULL); + dmp->uidnext = dmp->mesg_count + 1; + } + dmp->uidvalidity_scanned = 1; + } + + if (puidvalidity) + *puidvalidity = dmp->uidvalidity; + + return |