summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2020-11-23 13:11:29 +0200
committerSergey Poznyakoff <gray@gnu.org>2020-11-23 13:20:58 +0200
commit9763fd4a43128bcd08de8e88cce4142473d38d06 (patch)
treec4d298e58eb19dd46cc2a11652013827ef9c264d
parentae91979e9664d99204beb116b9b5e4122e43938e (diff)
downloadmailutils-9763fd4a43128bcd08de8e88cce4142473d38d06.tar.gz
mailutils-9763fd4a43128bcd08de8e88cce4142473d38d06.tar.bz2
Implement mboxrb format (a replacement for the mbox used currently)
* configure.ac: New format: mboxrb * include/mailutils/filter.h (mu_fromrb_filter): New extern. * include/mailutils/registrar.h (mu_mboxrb_record): New extern. (mu_register_all_mbox_formats) (mu_register_local_mbox_formats): Add mu_mboxrb_record. * libmailutils/filter/filter.c (mu_filter_get_list): Add mu_fromrb_filter * libproto/Makefile.am [MU_COND_SUPPORT_MBOXRB]: Add mboxrb * bootstrap.conf: Remove special handling for testsuite/maildir (fixes 1d846d2d92). * testsuite/mbop.c: Implement the -r (--read-only) option. * include/mailutils/sys/Makefile.am: Add mboxrb.h * include/mailutils/sys/mboxrb.h: New file. * libmailutils/filter/Makefile.am (libfilter_la_SOURCES): Add fromrb.c * libmailutils/filter/fromrb.c: New file. * libmailutils/tests/Makefile.am: New file. * libmailutils/tests/fromrb.at: New file. * libmailutils/tests/testsuite.at: New file. * libproto/mboxrb/Makefile.am: New file. * libproto/mboxrb/mboxrb.c: New file. * libproto/mboxrb/message.c: New file. * libproto/mboxrb/tests/.gitignore: New file. * libproto/mboxrb/tests/Makefile.am: New file. * libproto/mboxrb/tests/atlocal.in: New file. * libproto/mboxrb/tests/attr.at: New file. * libproto/mboxrb/tests/autodetect.at: New file. * libproto/mboxrb/tests/body.at: New file. * libproto/mboxrb/tests/count.at: New file. * libproto/mboxrb/tests/env.at: New file. * libproto/mboxrb/tests/header.at: New file. * libproto/mboxrb/tests/testsuite.at: New file.
-rw-r--r--bootstrap.conf8
-rw-r--r--configure.ac1
-rw-r--r--include/mailutils/filter.h1
-rw-r--r--include/mailutils/registrar.h4
-rw-r--r--include/mailutils/sys/Makefile.am1
-rw-r--r--include/mailutils/sys/mboxrb.h98
-rw-r--r--libmailutils/filter/Makefile.am1
-rw-r--r--libmailutils/filter/filter.c2
-rw-r--r--libmailutils/filter/fromrb.c282
-rw-r--r--libmailutils/tests/Makefile.am1
-rw-r--r--libmailutils/tests/fromrb.at56
-rw-r--r--libmailutils/tests/testsuite.at1
-rw-r--r--libproto/Makefile.am3
-rw-r--r--libproto/mboxrb/Makefile.am10
-rw-r--r--libproto/mboxrb/mboxrb.c1739
-rw-r--r--libproto/mboxrb/message.c643
-rw-r--r--libproto/mboxrb/tests/.gitignore8
-rw-r--r--libproto/mboxrb/tests/Makefile.am60
-rw-r--r--libproto/mboxrb/tests/atlocal.in6
-rw-r--r--libproto/mboxrb/tests/attr.at59
-rw-r--r--libproto/mboxrb/tests/autodetect.at71
-rw-r--r--libproto/mboxrb/tests/body.at161
-rw-r--r--libproto/mboxrb/tests/count.at7
-rw-r--r--libproto/mboxrb/tests/env.at163
-rw-r--r--libproto/mboxrb/tests/header.at83
-rw-r--r--libproto/mboxrb/tests/testsuite.at26
-rw-r--r--testsuite/mbop.c20
27 files changed, 3505 insertions, 10 deletions
diff --git a/bootstrap.conf b/bootstrap.conf
index 348f71e37..a4d15b08e 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -96,14 +96,6 @@ if [ -z "$help" ]; then
eval set -- "`sed 's/#.*$//;/^$/d' .bootstrap | tr '\n' ' '` $*"
fi
- # Recreate missing maildir parts
- for mbox in testsuite/maildir/*
- do
- test -d $mbox/new || mkdir $mbox/new
- test -d $mbox/cur || mkdir $mbox/cur
- test -d $mbox/tmp || mkdir $mbox/tmp
- done
-
# Grab the latest radius.m4
test -f m4/radius.m4 ||
wget -P m4 http://git.savannah.gnu.org/cgit/radius.git/plain/scripts/radius.m4
diff --git a/configure.ac b/configure.ac
index ae80fd9c2..6c80ace4f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -989,6 +989,7 @@ MU_ENABLE_REMOTE_MAILBOX_FORMAT([pop])
MU_ENABLE_LOCAL_MAILBOX_FORMAT([mh])
MU_ENABLE_LOCAL_MAILBOX_FORMAT([maildir])
MU_ENABLE_LOCAL_MAILBOX_FORMAT([dotmail])
+MU_ENABLE_LOCAL_MAILBOX_FORMAT([mboxrb])
AC_SUBST(MU_SMTP_PROGRAMS_BUILD)
AC_SUBST(MU_SMTP_DEJATOOL)
diff --git a/include/mailutils/filter.h b/include/mailutils/filter.h
index e5fdca8e5..2ef398335 100644
--- a/include/mailutils/filter.h
+++ b/include/mailutils/filter.h
@@ -127,6 +127,7 @@ extern mu_filter_record_t mu_htmlent_filter;
extern mu_filter_record_t mu_xml_filter;
extern mu_filter_record_t mu_percent_filter;
extern mu_filter_record_t mu_dq_filter;
+extern mu_filter_record_t mu_fromrb_filter;
enum mu_iconv_fallback_mode
{
diff --git a/include/mailutils/registrar.h b/include/mailutils/registrar.h
index 5feac383d..3c252fcaf 100644
--- a/include/mailutils/registrar.h
+++ b/include/mailutils/registrar.h
@@ -114,6 +114,8 @@ extern mu_record_t mu_pops_record;
/* Local Mailbox Unix Mailbox, "mbox:" */
extern mu_record_t mu_mbox_record;
+extern mu_record_t mu_mboxrb_record;
+
/* Local MH, "mh:" */
extern mu_record_t mu_mh_record;
/* Maildir, "maildir:" */
@@ -142,6 +144,7 @@ extern mu_record_t mu_dotmail_record;
#define mu_register_all_mbox_formats() do {\
mu_registrar_record (mu_mbox_record);\
+ mu_registrar_record (mu_mboxrb_record);\
mu_registrar_record (mu_dotmail_record);\
mu_registrar_record (mu_pop_record);\
mu_registrar_record (mu_pops_record);\
@@ -154,6 +157,7 @@ extern mu_record_t mu_dotmail_record;
#define mu_register_local_mbox_formats() do {\
mu_registrar_record (mu_mbox_record);\
+ mu_registrar_record (mu_mboxrb_record);\
mu_registrar_record (mu_dotmail_record);\
mu_registrar_record (mu_mh_record);\
mu_registrar_record (mu_maildir_record);\
diff --git a/include/mailutils/sys/Makefile.am b/include/mailutils/sys/Makefile.am
index 435792aa0..d29849814 100644
--- a/include/mailutils/sys/Makefile.am
+++ b/include/mailutils/sys/Makefile.am
@@ -40,6 +40,7 @@ sysinclude_HEADERS = \
mailcap.h\
mailer.h\
mapfile_stream.h\
+ mboxrb.h\
memory_stream.h\
message_stream.h\
message.h\
diff --git a/include/mailutils/sys/mboxrb.h b/include/mailutils/sys/mboxrb.h
new file mode 100644
index 000000000..05e7eb0c4
--- /dev/null
+++ b/include/mailutils/sys/mboxrb.h
@@ -0,0 +1,98 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2019-2020 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_MBOX2_H
+#define _MAILUTILS_SYS_MBOX2_H
+
+# include <mailutils/types.h>
+
+enum mu_mboxrb_hdr
+ {
+ mu_mboxrb_hdr_status,
+ mu_mboxrb_hdr_x_imapbase,
+ mu_mboxrb_hdr_x_uid,
+ MU_MBOXRB_HDR_MAX
+ };
+
+struct mu_mboxrb_message
+{
+ /* Offsets in the mailbox */
+ mu_off_t message_start; /* Start of message */
+ size_t from_length; /* Length of the From_ line */
+ int env_sender_len;
+ int env_date_start;
+ mu_off_t body_start; /* Start of body */
+ mu_off_t message_end; /* Offset of the last byte of the message */
+ /* Additional info */
+ unsigned long uid; /* IMAP-style uid. */
+
+ unsigned body_lines_scanned:1; /* True if body_lines is initialized */
+ unsigned body_from_escaped:1; /* True if body is from-escaped (valid if
+ body_lines_scanned is true) */
+ unsigned uid_modified:1;/* UID|uidvalidity|uidnext has been modified */
+
+ int attr_flags; /* Packed "Status:" attribute flags */
+
+ /* The following two are set only if body_lines_scanned is true */
+ size_t body_size; /* Number of octets in message body
+ (after >From unescape) */
+ size_t body_lines; /* Number of lines in message body */
+ mu_message_t message; /* Pointer to the message object if any */
+ /* Backlink to the mailbox */
+ struct mu_mboxrb_mailbox *mbox; /* Mailbox */
+ size_t num; /* Number of this message in the mailbox (0-based) */
+};
+
+struct mu_mboxrb_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_mboxrb_mailbox
+{
+ char *name; /* Disk file name */
+ mu_mailbox_t mailbox; /* Associated mailbox */
+ int stream_flags; /* Flags used to create the mailbox stream */
+
+ mu_off_t size; /* Size of the mailbox. */
+ unsigned long uidvalidity; /* Uidvalidity value */
+ unsigned long uidnext; /* Expected next UID value */
+ unsigned uidvalidity_scanned:1; /* True if uidvalidity is initialized */
+ unsigned uidvalidity_changed:1; /* True if uidvalidity or uidnext has changed */
+
+ size_t x_imapbase_off; /* Offset of the X-IMAPbase header */
+ size_t x_imapbase_len; /* Length if the header without trailing \n */
+
+ struct mu_mboxrb_message **mesg; /* Array of messages */
+ size_t mesg_count; /* Number of messages in mesgv */
+ size_t mesg_max; /* Actual capacity of mesg */
+};
+
+int mu_mboxrb_mailbox_init (mu_mailbox_t mailbox);
+void mu_mboxrb_message_free (struct mu_mboxrb_message *dmsg);
+int mu_mboxrb_message_get (struct mu_mboxrb_message *dmsg, mu_message_t *mptr);
+int mu_mboxrb_message_attr_load (struct mu_mboxrb_message *dmsg);
+int mu_mboxrb_mailbox_uid_setup (struct mu_mboxrb_mailbox *dmp);
+int mu_mboxrb_message_reconstruct (mu_stream_t dest,
+ struct mu_mboxrb_message *dmsg,
+ struct mu_mboxrb_message_ref *ref,
+ char const *x_imapbase);
+
+#endif
diff --git a/libmailutils/filter/Makefile.am b/libmailutils/filter/Makefile.am
index 5a27dec15..353237b13 100644
--- a/libmailutils/filter/Makefile.am
+++ b/libmailutils/filter/Makefile.am
@@ -29,6 +29,7 @@ libfilter_la_SOURCES =\
filter.c\
fltchain.c\
fromflt.c\
+ fromrb.c\
header.c\
htmlent.c\
iconvflt.c\
diff --git a/libmailutils/filter/filter.c b/libmailutils/filter/filter.c
index e8d0b5ce7..381236b1e 100644
--- a/libmailutils/filter/filter.c
+++ b/libmailutils/filter/filter.c
@@ -87,6 +87,8 @@ mu_filter_get_list (mu_list_t *plist)
mu_list_append (filter_list, mu_xml_filter);
mu_list_append (filter_list, mu_percent_filter);
mu_list_append (filter_list, mu_dq_filter);
+ mu_list_append (filter_list, mu_fromrb_filter);
+
/* FIXME: add the default encodings? */
}
*plist = filter_list;
diff --git a/libmailutils/filter/fromrb.c b/libmailutils/filter/fromrb.c
new file mode 100644
index 000000000..40c1cae95
--- /dev/null
+++ b/libmailutils/filter/fromrb.c
@@ -0,0 +1,282 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <mailutils/errno.h>
+#include <mailutils/filter.h>
+#include <mailutils/stream.h>
+
+enum
+ {
+ S_INIT, /* Initial state */
+ S_BOL, /* Beginning of line */
+ S_ESC, /* Collecting > escapes */
+ S_FROM, /* Collecting "From " characters */
+ };
+
+struct transcoder
+{
+ int state; /* Transcoder state */
+ int count; /* Number of consecutive '>' seen. */
+ int len; /* Number of "From" charactes collected */
+};
+
+static char from_line[] = "From ";
+
+/* Move min(isize,osize) bytes from iptr to optr, removing initial '>'
+ from each sequence '>+From ' at the beginning of line */
+static enum mu_filter_result
+_fromrb_decoder (void *xd,
+ enum mu_filter_command cmd,
+ struct mu_filter_io *iobuf)
+{
+ const unsigned char *iptr;
+ size_t isize;
+ char *optr;
+ size_t osize;
+ struct transcoder *xcode = xd;
+ size_t i, j, k;
+ size_t len, reqlen;
+
+ switch (cmd)
+ {
+ case mu_filter_init:
+ xcode->state = S_BOL;
+ xcode->count = 0;
+ xcode->len = 0;
+ return mu_filter_ok;
+
+ case mu_filter_done:
+ return mu_filter_ok;
+
+ default:
+ break;
+ }
+
+ iptr = (const unsigned char *) iobuf->input;
+ isize = iobuf->isize;
+ optr = iobuf->output;
+ osize = iobuf->osize;
+
+ for (i = j = 0; i < isize && j < osize; i++)
+ {
+ unsigned char c = *iptr++;
+
+ switch (xcode->state)
+ {
+ case S_INIT:
+ optr[j++] = c;
+ if (c == '\n')
+ xcode->state = S_BOL;
+ break;
+
+ case S_BOL:
+ if (c == '>')
+ {
+ xcode->state = S_ESC;
+ xcode->count = 1;
+ }
+ else
+ {
+ optr[j++] = c;
+ xcode->state = S_INIT;
+ }
+ break;
+
+ case S_ESC:
+ if (c == '>')
+ {
+ xcode->count++;
+ }
+ else if (c == from_line[0])
+ {
+ xcode->state = S_FROM;
+ xcode->len = 1;
+ }
+ else
+ {
+ xcode->state = S_INIT;
+ goto emit;
+ }
+ break;
+
+ case S_FROM:
+ if (from_line[xcode->len] == 0)
+ {
+ xcode->count--;
+ }
+ else if (c == from_line[xcode->len])
+ {
+ xcode->len++;
+ continue;
+ }
+ else
+ {
+ //RESTORE
+ }
+ emit:
+ reqlen = xcode->len + xcode->count;
+ len = osize - j;
+ if (len < reqlen)
+ {
+ iobuf->osize = reqlen;
+ return mu_filter_moreoutput;
+ }
+ for (k = 0; k < xcode->count; k++, j++)
+ optr[j] = '>';
+ memcpy (optr + j, from_line, xcode->len);
+ j += xcode->len;
+
+ xcode->state = S_INIT;
+ xcode->count = xcode->len = 0;
+ i--;
+ iptr--;
+ }
+ }
+ iobuf->isize = i;
+ iobuf->osize = j;
+ return mu_filter_ok;
+}
+
+static enum mu_filter_result
+_fromrb_encoder (void *xd,
+ enum mu_filter_command cmd,
+ struct mu_filter_io *iobuf)
+{
+ const unsigned char *iptr;
+ size_t isize;
+ char *optr;
+ size_t osize;
+ struct transcoder *xcode = xd;
+ size_t i, j, k;
+ size_t len, reqlen;
+
+ switch (cmd)
+ {
+ case mu_filter_init:
+ xcode->state = S_BOL;
+ xcode->count = 0;
+ xcode->len = 0;
+ return mu_filter_ok;
+
+ case mu_filter_done:
+ return mu_filter_ok;
+
+ default:
+ break;
+ }
+
+ iptr = (const unsigned char *) iobuf->input;
+ isize = iobuf->isize;
+ optr = iobuf->output;
+ osize = iobuf->osize;
+
+ for (i = j = 0; i < isize && j < osize; i++)
+ {
+ unsigned char c = *iptr++;
+
+ switch (xcode->state)
+ {
+ case S_INIT:
+ optr[j++] = c;
+ if (c == '\n')
+ xcode->state = S_BOL;
+ break;
+
+ case S_BOL:
+ if (c == '>')
+ {
+ xcode->state = S_ESC;
+ xcode->count = 1;
+ }
+ else if (c == from_line[0])
+ {
+ xcode->state = S_FROM;
+ xcode->count = 0;
+ xcode->len = 1;
+ }
+ else
+ {
+ optr[j++] = c;
+ xcode->state = S_INIT;
+ }
+ break;
+
+ case S_ESC:
+ if (c == '>')
+ {
+ xcode->count++;
+ }
+ else if (c == from_line[0])
+ {
+ xcode->state = S_FROM;
+ xcode->len = 1;
+ }
+ else
+ {
+ xcode->state = S_INIT;
+ goto emit;
+ }
+ break;
+
+ case S_FROM:
+ if (from_line[xcode->len] == 0)
+ {
+ xcode->count++;
+ }
+ else if (c == from_line[xcode->len])
+ {
+ xcode->len++;
+ continue;
+ }
+ else
+ {
+ //RESTORE
+ }
+ emit:
+ reqlen = xcode->len + xcode->count;
+ len = osize - j;
+ if (len < reqlen)
+ {
+ iobuf->osize = reqlen;
+ return mu_filter_moreoutput;
+ }
+ for (k = 0; k < xcode->count; k++, j++)
+ optr[j] = '>';
+ memcpy (optr + j, from_line, xcode->len);
+ j += xcode->len;
+
+ xcode->state = S_INIT;
+ xcode->count = xcode->len = 0;
+ i--;
+ iptr--;
+ }
+ }
+ iobuf->isize = i;
+ iobuf->osize = j;
+ return mu_filter_ok;
+}
+
+
+static int
+_fromrb_alloc_state (void **pret, int mode,
+ int argc MU_ARG_UNUSED, const char **argv MU_ARG_UNUSED)
+{
+ *pret = malloc (sizeof (struct transcoder));
+ if (!*pret)
+ return ENOMEM;
+ return 0;
+}
+
+static struct _mu_filter_record _fromrb_filter = {
+ "FROMRB",
+ _fromrb_alloc_state,
+ _fromrb_encoder,
+ _fromrb_decoder
+};
+
+mu_filter_record_t mu_fromrb_filter = &_fromrb_filter;
+
+
diff --git a/libmailutils/tests/Makefile.am b/libmailutils/tests/Makefile.am
index 19aa995e6..6b5ea8bd5 100644
--- a/libmailutils/tests/Makefile.am
+++ b/libmailutils/tests/Makefile.am
@@ -103,6 +103,7 @@ TESTSUITE_AT += \
encode2047.at\
exp.at\
fromflt.at\
+ fromrb.at\
fsaf.at\
fsaftomod.at\
fsfolder00.at\
diff --git a/libmailutils/tests/fromrb.at b/libmailutils/tests/fromrb.at
new file mode 100644
index 000000000..022927bf2
--- /dev/null
+++ b/libmailutils/tests/fromrb.at
@@ -0,0 +1,56 @@
+AT_SETUP([FROMRB filter])
+AT_DATA([enc],
+[From me
+Fromme
+ From me
+Fram me
+From
+> From me
+>> From me
+> >From me
+>From me
+>>From me
+])
+AT_CHECK([fltst FROMRB encode read < enc],
+[0],
+[>From me
+Fromme
+ From me
+Fram me
+From
+> From me
+>> From me
+> >From me
+>>From me
+>>>From me
+])
+
+AT_DATA([dec],
+[From me
+>From me
+>>From me
+>>Fromme
+> From me
+>> From me
+> >From me
+>From
+>FroM X
+>FrOm X
+>FRom X
+])
+AT_CHECK([fltst FROMRB decode read < dec],
+[0],
+[From me
+From me
+>From me
+>>Fromme
+> From me
+>> From me
+> >From me
+>From
+>FroM X
+>FrOm X
+>FRom X
+])
+
+AT_CLEANUP
diff --git a/libmailutils/tests/testsuite.at b/libmailutils/tests/testsuite.at
index 6888f39e6..b78a61767 100644
--- a/libmailutils/tests/testsuite.at
+++ b/libmailutils/tests/testsuite.at
@@ -220,6 +220,7 @@ m4_include([encode2047.at])
AT_BANNER(Filters)
m4_include([fromflt.at])
+m4_include([fromrb.at])
m4_include([inline-comment.at])
m4_include([hdrflt.at])
m4_include([hdrcpy.at])
diff --git a/libproto/Makefile.am b/libproto/Makefile.am
index 6e3376ba6..a97641f01 100644
--- a/libproto/Makefile.am
+++ b/libproto/Makefile.am
@@ -36,3 +36,6 @@ if MU_COND_SUPPORT_DOTMAIL
SUBDIRS += dotmail
endif
+if MU_COND_SUPPORT_MBOXRB
+ SUBDIRS += mboxrb
+endif
diff --git a/libproto/mboxrb/Makefile.am b/libproto/mboxrb/Makefile.am
new file mode 100644
index 000000000..7bf659ed6
--- /dev/null
+++ b/libproto/mboxrb/Makefile.am
@@ -0,0 +1,10 @@
+AM_CPPFLAGS = $(MU_LIB_COMMON_INCLUDES)
+
+lib_LTLIBRARIES = libmu_mboxrb.la
+libmu_mboxrb_la_LDFLAGS=-version-info @VI_CURRENT@:@VI_REVISION@:@VI_AGE@
+libmu_mboxrb_la_LIBADD = $(MU_LIB_MAILUTILS)
+libmu_mboxrb_la_SOURCES = \
+ mboxrb.c\
+ message.c
+
+SUBDIRS = . tests
diff --git a/libproto/mboxrb/mboxrb.c b/libproto/mboxrb/mboxrb.c
new file mode 100644
index 000000000..e9babd517
--- /dev/null
+++ b/libproto/mboxrb/mboxrb.c
@@ -0,0 +1,1739 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2019-2020 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/mboxrb.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>
+#include <mailutils/cctype.h>
+#include <mailutils/sys/folder.h>
+#include <mailutils/sys/registrar.h>
+
+static void
+mboxrb_destroy (mu_mailbox_t mailbox)
+{
+ size_t i;
+ struct mu_mboxrb_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_mboxrb_message_free (dmp->mesg[i]);
+ }
+ free (dmp->mesg);
+ free (dmp->name);
+ free (dmp);
+ mailbox->data = NULL;
+ mu_monitor_unlock (mailbox->monitor);
+}
+
+static int
+mboxrb_mailbox_init_stream (struct mu_mboxrb_mailbox *dmp)
+{
+ int rc;
+ mu_mailbox_t mailbox = dmp->mailbox;
+
+ /*
+ * Initialize stream flags. If append mode is requested, convert it to
+ * read-write, so that mboxrb_flush_unlocked be able to update the
+ * X-IMAPbase header in the first message, if necessary.
+ */
+ dmp->stream_flags = mailbox->flags;
+ if (dmp->stream_flags & MU_STREAM_APPEND)
+ dmp->stream_flags = (dmp->stream_flags & ~MU_STREAM_APPEND) | MU_STREAM_RDWR;
+ rc = mu_mapfile_stream_create (&mailbox->stream, dmp->name, dmp->stream_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
+mboxrb_open (mu_mailbox_t mailbox, int flags)
+{
+ struct mu_mboxrb_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 = mboxrb_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;
+}
+
+enum
+ {
+ FLUSH_SYNC,
+ FLUSH_EXPUNGE, /* implies SYNC */
+ FLUSH_UIDVALIDITY
+ };
+
+static int mboxrb_flush (struct mu_mboxrb_mailbox *dmp, int flag);
+
+static int
+mboxrb_close (mu_mailbox_t mailbox)
+{
+ struct mu_mboxrb_mailbox *dmp = mailbox->data;
+ size_t i;
+
+ if (!dmp)
+ return EINVAL;
+
+ mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE1,
+ ("%s (%s)", __func__, dmp->name));
+
+ if (dmp->uidvalidity_changed && (dmp->stream_flags & MU_STREAM_WRITE))
+ mboxrb_flush (dmp, FLUSH_UIDVALIDITY);
+
+ mu_locker_unlock (mailbox->locker);
+ mu_monitor_wrlock (mailbox->monitor);
+ for (i = 0; i < dmp->mesg_count; i++)
+ {
+ mu_mboxrb_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 = 1;
+ mu_monitor_unlock (mailbox->monitor);
+ mu_stream_destroy (&mailbox->stream);
+ return 0;
+}
+
+static int
+mboxrb_remove (mu_mailbox_t mailbox)
+{
+ struct mu_mboxrb_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
+mboxrb_is_updated (mu_mailbox_t mailbox)
+{
+ struct mu_mboxrb_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
+mboxrb_cleanup (void *arg)
+{
+ mu_mailbox_t mailbox = arg;
+ mu_monitor_unlock (mailbox->monitor);
+ mu_locker_unlock (mailbox->locker);
+}
+#endif
+
+static int
+mboxrb_alloc_message (struct mu_mboxrb_mailbox *dmp,
+ struct mu_mboxrb_message **dmsg_ptr)
+{
+ struct mu_mboxrb_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
+mboxrb_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;
+}
+
+/* Notes on the UID subsystem
+
+ 1. The values of uidvalidity and uidnext are stored in the
+ X-IMAPbase header in the first message.
+ 2. Message UID is stored in the X-UID header in that message.
+ 3. To minimize unwanted modifications to the mailbox, the
+ UID subsystem is initialized only in the following cases:
+
+ 3a. Upon mailbox scanning, if the first message contains a
+ valid X-IMAPbase header. In this case, the
+ mboxrb_rescan_unlocked function initializes each
+ 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 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.
+
+