summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2019-02-26 10:46:15 +0200
committerSergey Poznyakoff <gray@gnu.org>2019-03-01 16:05:52 +0200
commit169099f43bfd1a45398139c27df5ebc4375f6a7d (patch)
treefc9795aff44d4b6ca693c3f20c7cc10868b910a0
parentde26c87ab7df32066f5408012d0d391fcb9c9535 (diff)
downloadmailutils-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.
-rw-r--r--NEWS4
-rw-r--r--configure.ac4
-rw-r--r--doc/texinfo/.gitignore5
-rw-r--r--include/mailutils/registrar.h4
-rw-r--r--include/mailutils/sys/dotmail.h86
-rw-r--r--libmu_scm/mailutils/ancilla.scm7
-rw-r--r--libproto/Makefile.am4
-rw-r--r--libproto/dotmail/Makefile.am27
-rw-r--r--libproto/dotmail/dotmail.c1306
-rw-r--r--libproto/dotmail/folder.c84
-rw-r--r--libproto/dotmail/message.c636
-rw-r--r--libproto/dotmail/tests/.gitignore10
-rw-r--r--libproto/dotmail/tests/Makefile.am85
-rw-r--r--libproto/dotmail/tests/append.at77
-rw-r--r--libproto/dotmail/tests/atlocal.in5
-rw-r--r--libproto/dotmail/tests/attr.at47
-rw-r--r--libproto/dotmail/tests/body.at51
-rw-r--r--libproto/dotmail/tests/count.at58
-rw-r--r--libproto/dotmail/tests/delete.at75
-rw-r--r--libproto/dotmail/tests/dm_mbox.c124
-rw-r--r--libproto/dotmail/tests/dm_mesg.c368
-rw-r--r--libproto/dotmail/tests/dm_qget.c61
-rw-r--r--libproto/dotmail/tests/env.at35
-rw-r--r--libproto/dotmail/tests/header.at88
-rw-r--r--libproto/dotmail/tests/qget.at59
-rw-r--r--libproto/dotmail/tests/recent.at84
-rw-r--r--libproto/dotmail/tests/testsuite.at45
-rw-r--r--libproto/dotmail/tests/uid.at62
-rw-r--r--libproto/dotmail/tests/uidvalidity.at37
-rw-r--r--libproto/dotmail/tests/unseen.at84
30 files changed, 3615 insertions, 7 deletions
diff --git a/NEWS b/NEWS
index ec96416fc..ca871224c 100644
--- a/NEWS
+++ b/NEWS
@@ -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