summaryrefslogtreecommitdiffstats
path: root/src/lib-storage/index/dbox-common
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:51:24 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:51:24 +0000
commitf7548d6d28c313cf80e6f3ef89aed16a19815df1 (patch)
treea3f6f2a3f247293bee59ecd28e8cd8ceb6ca064a /src/lib-storage/index/dbox-common
parentInitial commit. (diff)
downloaddovecot-upstream.tar.xz
dovecot-upstream.zip
Adding upstream version 1:2.3.19.1+dfsg1.upstream/1%2.3.19.1+dfsg1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib-storage/index/dbox-common')
-rw-r--r--src/lib-storage/index/dbox-common/Makefile.am29
-rw-r--r--src/lib-storage/index/dbox-common/Makefile.in843
-rw-r--r--src/lib-storage/index/dbox-common/dbox-attachment.c77
-rw-r--r--src/lib-storage/index/dbox-common/dbox-attachment.h16
-rw-r--r--src/lib-storage/index/dbox-common/dbox-file-fix.c519
-rw-r--r--src/lib-storage/index/dbox-common/dbox-file.c796
-rw-r--r--src/lib-storage/index/dbox-common/dbox-file.h218
-rw-r--r--src/lib-storage/index/dbox-common/dbox-mail.c318
-rw-r--r--src/lib-storage/index/dbox-common/dbox-mail.h34
-rw-r--r--src/lib-storage/index/dbox-common/dbox-save.c226
-rw-r--r--src/lib-storage/index/dbox-common/dbox-save.h41
-rw-r--r--src/lib-storage/index/dbox-common/dbox-storage.c416
-rw-r--r--src/lib-storage/index/dbox-common/dbox-storage.h85
13 files changed, 3618 insertions, 0 deletions
diff --git a/src/lib-storage/index/dbox-common/Makefile.am b/src/lib-storage/index/dbox-common/Makefile.am
new file mode 100644
index 0000000..ec3d511
--- /dev/null
+++ b/src/lib-storage/index/dbox-common/Makefile.am
@@ -0,0 +1,29 @@
+noinst_LTLIBRARIES = libstorage_dbox_common.la
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/src/lib \
+ -I$(top_srcdir)/src/lib-settings \
+ -I$(top_srcdir)/src/lib-fs \
+ -I$(top_srcdir)/src/lib-mail \
+ -I$(top_srcdir)/src/lib-imap \
+ -I$(top_srcdir)/src/lib-index \
+ -I$(top_srcdir)/src/lib-storage \
+ -I$(top_srcdir)/src/lib-storage/index
+
+libstorage_dbox_common_la_SOURCES = \
+ dbox-attachment.c \
+ dbox-file.c \
+ dbox-file-fix.c \
+ dbox-mail.c \
+ dbox-save.c \
+ dbox-storage.c
+
+headers = \
+ dbox-attachment.h \
+ dbox-file.h \
+ dbox-mail.h \
+ dbox-save.h \
+ dbox-storage.h
+
+pkginc_libdir=$(pkgincludedir)
+pkginc_lib_HEADERS = $(headers)
diff --git a/src/lib-storage/index/dbox-common/Makefile.in b/src/lib-storage/index/dbox-common/Makefile.in
new file mode 100644
index 0000000..60aaf66
--- /dev/null
+++ b/src/lib-storage/index/dbox-common/Makefile.in
@@ -0,0 +1,843 @@
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-storage/index/dbox-common
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ac_checktype2.m4 \
+ $(top_srcdir)/m4/ac_typeof.m4 $(top_srcdir)/m4/arc4random.m4 \
+ $(top_srcdir)/m4/blockdev.m4 $(top_srcdir)/m4/c99_vsnprintf.m4 \
+ $(top_srcdir)/m4/clock_gettime.m4 $(top_srcdir)/m4/crypt.m4 \
+ $(top_srcdir)/m4/crypt_xpg6.m4 $(top_srcdir)/m4/dbqlk.m4 \
+ $(top_srcdir)/m4/dirent_dtype.m4 $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/fd_passing.m4 $(top_srcdir)/m4/fdatasync.m4 \
+ $(top_srcdir)/m4/flexible_array_member.m4 \
+ $(top_srcdir)/m4/glibc.m4 $(top_srcdir)/m4/gmtime_max.m4 \
+ $(top_srcdir)/m4/gmtime_tm_gmtoff.m4 \
+ $(top_srcdir)/m4/ioloop.m4 $(top_srcdir)/m4/iovec.m4 \
+ $(top_srcdir)/m4/ipv6.m4 $(top_srcdir)/m4/libcap.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libwrap.m4 \
+ $(top_srcdir)/m4/linux_mremap.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/mmap_write.m4 \
+ $(top_srcdir)/m4/mntctl.m4 $(top_srcdir)/m4/modules.m4 \
+ $(top_srcdir)/m4/notify.m4 $(top_srcdir)/m4/nsl.m4 \
+ $(top_srcdir)/m4/off_t_max.m4 $(top_srcdir)/m4/pkg.m4 \
+ $(top_srcdir)/m4/pr_set_dumpable.m4 \
+ $(top_srcdir)/m4/q_quotactl.m4 $(top_srcdir)/m4/quota.m4 \
+ $(top_srcdir)/m4/random.m4 $(top_srcdir)/m4/rlimit.m4 \
+ $(top_srcdir)/m4/sendfile.m4 $(top_srcdir)/m4/size_t_signed.m4 \
+ $(top_srcdir)/m4/sockpeercred.m4 $(top_srcdir)/m4/sql.m4 \
+ $(top_srcdir)/m4/ssl.m4 $(top_srcdir)/m4/st_tim.m4 \
+ $(top_srcdir)/m4/static_array.m4 $(top_srcdir)/m4/test_with.m4 \
+ $(top_srcdir)/m4/time_t.m4 $(top_srcdir)/m4/typeof.m4 \
+ $(top_srcdir)/m4/typeof_dev_t.m4 \
+ $(top_srcdir)/m4/uoff_t_max.m4 $(top_srcdir)/m4/vararg.m4 \
+ $(top_srcdir)/m4/want_apparmor.m4 \
+ $(top_srcdir)/m4/want_bsdauth.m4 \
+ $(top_srcdir)/m4/want_bzlib.m4 \
+ $(top_srcdir)/m4/want_cassandra.m4 \
+ $(top_srcdir)/m4/want_cdb.m4 \
+ $(top_srcdir)/m4/want_checkpassword.m4 \
+ $(top_srcdir)/m4/want_clucene.m4 $(top_srcdir)/m4/want_db.m4 \
+ $(top_srcdir)/m4/want_gssapi.m4 $(top_srcdir)/m4/want_icu.m4 \
+ $(top_srcdir)/m4/want_ldap.m4 $(top_srcdir)/m4/want_lua.m4 \
+ $(top_srcdir)/m4/want_lz4.m4 $(top_srcdir)/m4/want_lzma.m4 \
+ $(top_srcdir)/m4/want_mysql.m4 $(top_srcdir)/m4/want_pam.m4 \
+ $(top_srcdir)/m4/want_passwd.m4 $(top_srcdir)/m4/want_pgsql.m4 \
+ $(top_srcdir)/m4/want_prefetch.m4 \
+ $(top_srcdir)/m4/want_shadow.m4 \
+ $(top_srcdir)/m4/want_sodium.m4 $(top_srcdir)/m4/want_solr.m4 \
+ $(top_srcdir)/m4/want_sqlite.m4 \
+ $(top_srcdir)/m4/want_stemmer.m4 \
+ $(top_srcdir)/m4/want_systemd.m4 \
+ $(top_srcdir)/m4/want_textcat.m4 \
+ $(top_srcdir)/m4/want_unwind.m4 $(top_srcdir)/m4/want_zlib.m4 \
+ $(top_srcdir)/m4/want_zstd.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libstorage_dbox_common_la_LIBADD =
+am_libstorage_dbox_common_la_OBJECTS = dbox-attachment.lo dbox-file.lo \
+ dbox-file-fix.lo dbox-mail.lo dbox-save.lo dbox-storage.lo
+libstorage_dbox_common_la_OBJECTS = \
+ $(am_libstorage_dbox_common_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/dbox-attachment.Plo \
+ ./$(DEPDIR)/dbox-file-fix.Plo ./$(DEPDIR)/dbox-file.Plo \
+ ./$(DEPDIR)/dbox-mail.Plo ./$(DEPDIR)/dbox-save.Plo \
+ ./$(DEPDIR)/dbox-storage.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libstorage_dbox_common_la_SOURCES)
+DIST_SOURCES = $(libstorage_dbox_common_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(pkginc_libdir)"
+HEADERS = $(pkginc_lib_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPARMOR_LIBS = @APPARMOR_LIBS@
+AR = @AR@
+AUTH_CFLAGS = @AUTH_CFLAGS@
+AUTH_LIBS = @AUTH_LIBS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+BISON = @BISON@
+CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@
+CASSANDRA_LIBS = @CASSANDRA_LIBS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CDB_LIBS = @CDB_LIBS@
+CFLAGS = @CFLAGS@
+CLUCENE_CFLAGS = @CLUCENE_CFLAGS@
+CLUCENE_LIBS = @CLUCENE_LIBS@
+COMPRESS_LIBS = @COMPRESS_LIBS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRYPT_LIBS = @CRYPT_LIBS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DICT_LIBS = @DICT_LIBS@
+DLLIB = @DLLIB@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FLEX = @FLEX@
+FUZZER_CPPFLAGS = @FUZZER_CPPFLAGS@
+FUZZER_LDFLAGS = @FUZZER_LDFLAGS@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KRB5CONFIG = @KRB5CONFIG@
+KRB5_CFLAGS = @KRB5_CFLAGS@
+KRB5_LIBS = @KRB5_LIBS@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LD_NO_WHOLE_ARCHIVE = @LD_NO_WHOLE_ARCHIVE@
+LD_WHOLE_ARCHIVE = @LD_WHOLE_ARCHIVE@
+LIBCAP = @LIBCAP@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@
+LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@
+LIBICONV = @LIBICONV@
+LIBICU_CFLAGS = @LIBICU_CFLAGS@
+LIBICU_LIBS = @LIBICU_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSODIUM_CFLAGS = @LIBSODIUM_CFLAGS@
+LIBSODIUM_LIBS = @LIBSODIUM_LIBS@
+LIBTIRPC_CFLAGS = @LIBTIRPC_CFLAGS@
+LIBTIRPC_LIBS = @LIBTIRPC_LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBWRAP_LIBS = @LIBWRAP_LIBS@
+LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LUA_CFLAGS = @LUA_CFLAGS@
+LUA_LIBS = @LUA_LIBS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MODULE_LIBS = @MODULE_LIBS@
+MODULE_SUFFIX = @MODULE_SUFFIX@
+MYSQL_CFLAGS = @MYSQL_CFLAGS@
+MYSQL_CONFIG = @MYSQL_CONFIG@
+MYSQL_LIBS = @MYSQL_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANDOC = @PANDOC@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PGSQL_CFLAGS = @PGSQL_CFLAGS@
+PGSQL_LIBS = @PGSQL_LIBS@
+PG_CONFIG = @PG_CONFIG@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+QUOTA_LIBS = @QUOTA_LIBS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RPCGEN = @RPCGEN@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SETTING_FILES = @SETTING_FILES@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SQLITE_CFLAGS = @SQLITE_CFLAGS@
+SQLITE_LIBS = @SQLITE_LIBS@
+SQL_CFLAGS = @SQL_CFLAGS@
+SQL_LIBS = @SQL_LIBS@
+SSL_CFLAGS = @SSL_CFLAGS@
+SSL_LIBS = @SSL_LIBS@
+STRIP = @STRIP@
+SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@
+SYSTEMD_LIBS = @SYSTEMD_LIBS@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+ZSTD_CFLAGS = @ZSTD_CFLAGS@
+ZSTD_LIBS = @ZSTD_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+dict_drivers = @dict_drivers@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+rundir = @rundir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sql_drivers = @sql_drivers@
+srcdir = @srcdir@
+ssldir = @ssldir@
+statedir = @statedir@
+sysconfdir = @sysconfdir@
+systemdservicetype = @systemdservicetype@
+systemdsystemunitdir = @systemdsystemunitdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libstorage_dbox_common.la
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/src/lib \
+ -I$(top_srcdir)/src/lib-settings \
+ -I$(top_srcdir)/src/lib-fs \
+ -I$(top_srcdir)/src/lib-mail \
+ -I$(top_srcdir)/src/lib-imap \
+ -I$(top_srcdir)/src/lib-index \
+ -I$(top_srcdir)/src/lib-storage \
+ -I$(top_srcdir)/src/lib-storage/index
+
+libstorage_dbox_common_la_SOURCES = \
+ dbox-attachment.c \
+ dbox-file.c \
+ dbox-file-fix.c \
+ dbox-mail.c \
+ dbox-save.c \
+ dbox-storage.c
+
+headers = \
+ dbox-attachment.h \
+ dbox-file.h \
+ dbox-mail.h \
+ dbox-save.h \
+ dbox-storage.h
+
+pkginc_libdir = $(pkgincludedir)
+pkginc_lib_HEADERS = $(headers)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-storage/index/dbox-common/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-storage/index/dbox-common/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libstorage_dbox_common.la: $(libstorage_dbox_common_la_OBJECTS) $(libstorage_dbox_common_la_DEPENDENCIES) $(EXTRA_libstorage_dbox_common_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libstorage_dbox_common_la_OBJECTS) $(libstorage_dbox_common_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbox-attachment.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbox-file-fix.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbox-file.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbox-mail.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbox-save.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbox-storage.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-pkginc_libHEADERS: $(pkginc_lib_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \
+ done
+
+uninstall-pkginc_libHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(pkginc_libdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/dbox-attachment.Plo
+ -rm -f ./$(DEPDIR)/dbox-file-fix.Plo
+ -rm -f ./$(DEPDIR)/dbox-file.Plo
+ -rm -f ./$(DEPDIR)/dbox-mail.Plo
+ -rm -f ./$(DEPDIR)/dbox-save.Plo
+ -rm -f ./$(DEPDIR)/dbox-storage.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-pkginc_libHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/dbox-attachment.Plo
+ -rm -f ./$(DEPDIR)/dbox-file-fix.Plo
+ -rm -f ./$(DEPDIR)/dbox-file.Plo
+ -rm -f ./$(DEPDIR)/dbox-mail.Plo
+ -rm -f ./$(DEPDIR)/dbox-save.Plo
+ -rm -f ./$(DEPDIR)/dbox-storage.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-pkginc_libHEADERS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-pkginc_libHEADERS install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-pkginc_libHEADERS
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/lib-storage/index/dbox-common/dbox-attachment.c b/src/lib-storage/index/dbox-common/dbox-attachment.c
new file mode 100644
index 0000000..cfc3f62
--- /dev/null
+++ b/src/lib-storage/index/dbox-common/dbox-attachment.c
@@ -0,0 +1,77 @@
+/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "istream.h"
+#include "str.h"
+#include "dbox-file.h"
+#include "dbox-save.h"
+#include "dbox-attachment.h"
+
+void dbox_attachment_save_write_metadata(struct mail_save_context *ctx,
+ string_t *str)
+{
+ const ARRAY_TYPE(mail_attachment_extref) *extrefs;
+
+ extrefs = index_attachment_save_get_extrefs(ctx);
+ if (extrefs == NULL || array_count(extrefs) == 0)
+ return;
+
+ str_append_c(str, DBOX_METADATA_EXT_REF);
+ index_attachment_append_extrefs(str, extrefs);
+ str_append_c(str, '\n');
+}
+
+static int
+dbox_attachment_file_get_stream_from(struct dbox_file *file,
+ const char *ext_refs,
+ struct istream **stream,
+ const char **error_r)
+{
+ const char *path_suffix;
+ uoff_t msg_size;
+
+ if (file->storage->attachment_dir == NULL) {
+ mail_storage_set_critical(&file->storage->storage,
+ "%s contains references to external attachments, "
+ "but mail_attachment_dir is unset", file->cur_path);
+ return -1;
+ }
+
+ msg_size = dbox_file_get_plaintext_size(file);
+ path_suffix = file->storage->v.get_attachment_path_suffix(file);
+ if (index_attachment_stream_get(file->storage->attachment_fs,
+ file->storage->attachment_dir,
+ path_suffix, stream, msg_size,
+ ext_refs, error_r) < 0)
+ return 0;
+ return 1;
+}
+
+int dbox_attachment_file_get_stream(struct dbox_file *file,
+ struct istream **stream)
+{
+ const char *ext_refs, *error;
+ int ret;
+
+ /* need to read metadata in case there are external references */
+ if ((ret = dbox_file_metadata_read(file)) <= 0)
+ return ret;
+
+ i_stream_seek(file->input, file->cur_offset + file->msg_header_size);
+
+ ext_refs = dbox_file_metadata_get(file, DBOX_METADATA_EXT_REF);
+ if (ext_refs == NULL)
+ return 1;
+
+ /* we have external references. */
+ T_BEGIN {
+ ret = dbox_attachment_file_get_stream_from(file, ext_refs,
+ stream, &error);
+ if (ret == 0) {
+ dbox_file_set_corrupted(file,
+ "Corrupted ext-refs metadata %s: %s",
+ ext_refs, error);
+ }
+ } T_END;
+ return ret;
+}
diff --git a/src/lib-storage/index/dbox-common/dbox-attachment.h b/src/lib-storage/index/dbox-common/dbox-attachment.h
new file mode 100644
index 0000000..a90ba54
--- /dev/null
+++ b/src/lib-storage/index/dbox-common/dbox-attachment.h
@@ -0,0 +1,16 @@
+#ifndef DBOX_ATTACHMENT_H
+#define DBOX_ATTACHMENT_H
+
+#include "index-attachment.h"
+
+struct dbox_file;
+
+void dbox_attachment_save_write_metadata(struct mail_save_context *ctx,
+ string_t *str);
+
+/* Build a single message body stream out of the current message and all of its
+ attachments. */
+int dbox_attachment_file_get_stream(struct dbox_file *file,
+ struct istream **stream);
+
+#endif
diff --git a/src/lib-storage/index/dbox-common/dbox-file-fix.c b/src/lib-storage/index/dbox-common/dbox-file-fix.c
new file mode 100644
index 0000000..1c44ca4
--- /dev/null
+++ b/src/lib-storage/index/dbox-common/dbox-file-fix.c
@@ -0,0 +1,519 @@
+/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "hex-dec.h"
+#include "istream.h"
+#include "ostream.h"
+#include "message-size.h"
+#include "dbox-storage.h"
+#include "dbox-file.h"
+
+#include <stdio.h>
+
+#define DBOX_MAIL_FILE_BROKEN_COPY_SUFFIX ".broken"
+
+static int
+dbox_file_match_pre_magic(struct istream *input,
+ uoff_t *pre_offset, size_t *need_bytes)
+{
+ const struct dbox_message_header *hdr;
+ const unsigned char *data;
+ size_t size;
+ uoff_t offset = input->v_offset;
+ bool have_lf = FALSE;
+
+ data = i_stream_get_data(input, &size);
+ if (data[0] == '\n') {
+ data++; size--; offset++;
+ have_lf = TRUE;
+ }
+ i_assert(data[0] == DBOX_MAGIC_PRE[0]);
+ if (size < sizeof(*hdr)) {
+ *need_bytes = sizeof(*hdr) + (have_lf ? 1 : 0);
+ return -1;
+ }
+ hdr = (const void *)data;
+ if (memcmp(hdr->magic_pre, DBOX_MAGIC_PRE, strlen(DBOX_MAGIC_PRE)) != 0)
+ return 0;
+ if (hdr->type != DBOX_MESSAGE_TYPE_NORMAL)
+ return 0;
+ if (hdr->space1 != ' ' || hdr->space2 != ' ')
+ return 0;
+ if (hex2dec(hdr->message_size_hex, sizeof(hdr->message_size_hex)) == 0 &&
+ memcmp(hdr->message_size_hex, "0000000000000000", sizeof(hdr->message_size_hex)) != 0)
+ return 0;
+
+ *pre_offset = offset;
+ return 1;
+}
+
+static bool memchr_nocontrol(const unsigned char *data, char chr,
+ unsigned int len, const unsigned char **pos_r)
+{
+ unsigned int i;
+
+ for (i = 0; i < len; i++) {
+ if (data[i] == chr) {
+ *pos_r = data+i;
+ return TRUE;
+ }
+ if (data[i] < ' ')
+ return FALSE;
+ }
+ *pos_r = NULL;
+ return TRUE;
+}
+
+static int
+dbox_file_match_post_magic(struct istream *input, bool input_full,
+ size_t *need_bytes)
+{
+ const unsigned char *data, *p;
+ size_t i, size;
+ bool allow_control;
+
+ data = i_stream_get_data(input, &size);
+ if (size < strlen(DBOX_MAGIC_POST)) {
+ *need_bytes = strlen(DBOX_MAGIC_POST);
+ return -1;
+ }
+ if (memcmp(data, DBOX_MAGIC_POST, strlen(DBOX_MAGIC_POST)) != 0)
+ return 0;
+
+ /* see if the metadata block looks valid */
+ for (i = strlen(DBOX_MAGIC_POST); i < size; ) {
+ switch (data[i]) {
+ case '\n':
+ return 1;
+ case DBOX_METADATA_GUID:
+ case DBOX_METADATA_POP3_UIDL:
+ case DBOX_METADATA_ORIG_MAILBOX:
+ case DBOX_METADATA_OLDV1_KEYWORDS:
+ /* these could contain anything */
+ allow_control = TRUE;
+ break;
+ case DBOX_METADATA_POP3_ORDER:
+ case DBOX_METADATA_RECEIVED_TIME:
+ case DBOX_METADATA_PHYSICAL_SIZE:
+ case DBOX_METADATA_VIRTUAL_SIZE:
+ case DBOX_METADATA_EXT_REF:
+ case DBOX_METADATA_OLDV1_EXPUNGED:
+ case DBOX_METADATA_OLDV1_FLAGS:
+ case DBOX_METADATA_OLDV1_SAVE_TIME:
+ case DBOX_METADATA_OLDV1_SPACE:
+ /* no control chars */
+ allow_control = FALSE;
+ break;
+ default:
+ if (data[i] < 'A' || data[i] > 'Z')
+ return 0;
+ /* unknown */
+ allow_control = TRUE;
+ break;
+ }
+ if (allow_control) {
+ p = memchr(data+i, '\n', size-i);
+ } else {
+ if (!memchr_nocontrol(data+i, '\n', size-i, &p))
+ return 0;
+ }
+ if (p == NULL) {
+ /* LF not found - try to find the end-of-metadata LF */
+ if (input_full) {
+ /* can't look any further - assume it's ok */
+ return 1;
+ }
+ *need_bytes = size+1;
+ return -1;
+ }
+ i = p - data+1;
+ }
+ *need_bytes = size+1;
+ return -1;
+}
+
+static int
+dbox_file_find_next_magic(struct dbox_file *file, uoff_t *offset_r, bool *pre_r)
+{
+ /* We're scanning message bodies here, trying to find the beginning of
+ the next message. Although our magic strings are very unlikely to
+ be found in regular emails, they are much more likely when emails
+ are stored compressed.. So try to be sure we find the correct
+ magic markers. */
+
+ struct istream *input = file->input;
+ uoff_t orig_offset, pre_offset, post_offset, prev_offset;
+ const unsigned char *data, *magic;
+ size_t size, need_bytes, prev_need_bytes;
+ int ret, match;
+
+ *pre_r = FALSE;
+
+ orig_offset = prev_offset = input->v_offset;
+ need_bytes = strlen(DBOX_MAGIC_POST); prev_need_bytes = 0;
+ while ((ret = i_stream_read_bytes(input, &data, &size, need_bytes)) > 0 ||
+ ret == -2) {
+ /* search for the beginning of a potential pre/post magic */
+ i_assert(size > 1);
+ i_assert(prev_offset != input->v_offset ||
+ need_bytes > prev_need_bytes);
+ prev_offset = input->v_offset;
+ prev_need_bytes = need_bytes;
+
+ magic = memchr(data, DBOX_MAGIC_PRE[0], size);
+ if (magic == NULL) {
+ i_stream_skip(input, size-1);
+ need_bytes = strlen(DBOX_MAGIC_POST);
+ continue;
+ }
+ if (magic == data && input->v_offset == orig_offset) {
+ /* beginning of the file */
+ } else if (magic != data && magic[-1] == '\n') {
+ /* PRE/POST block? leave \n */
+ i_stream_skip(input, magic-data-1);
+ } else {
+ i_stream_skip(input, magic-data+1);
+ need_bytes = strlen(DBOX_MAGIC_POST);
+ continue;
+ }
+
+ pre_offset = UOFF_T_MAX;
+ match = dbox_file_match_pre_magic(input, &pre_offset, &need_bytes);
+ if (match < 0) {
+ /* more data needed */
+ if (ret == -2) {
+ i_stream_skip(input, 2);
+ need_bytes = strlen(DBOX_MAGIC_POST);
+ }
+ continue;
+ }
+ if (match > 0)
+ *pre_r = TRUE;
+
+ match = dbox_file_match_post_magic(input, ret == -2, &need_bytes);
+ if (match < 0) {
+ /* more data needed */
+ if (ret == -2) {
+ i_stream_skip(input, 2);
+ need_bytes = strlen(DBOX_MAGIC_POST);
+ }
+ continue;
+ }
+ if (match > 0) {
+ post_offset = input->v_offset;
+ if (pre_offset == UOFF_T_MAX ||
+ post_offset < pre_offset) {
+ pre_offset = post_offset;
+ *pre_r = FALSE;
+ }
+ }
+
+ if (pre_offset != UOFF_T_MAX) {
+ *offset_r = pre_offset;
+ ret = 1;
+ break;
+ }
+ i_stream_skip(input, size-1);
+ }
+ if (ret <= 0) {
+ i_assert(ret == -1);
+ if (input->stream_errno != 0)
+ dbox_file_set_syscall_error(file, "read()");
+ else {
+ ret = 0;
+ *offset_r = input->v_offset;
+ }
+ }
+ i_stream_seek(input, orig_offset);
+ return ret <= 0 ? ret : 1;
+}
+
+static int
+stream_copy(struct dbox_file *file, struct ostream *output,
+ const char *out_path, uoff_t count)
+{
+ struct istream *input;
+ int ret = 0;
+
+ input = i_stream_create_limit(file->input, count);
+ o_stream_nsend_istream(output, input);
+
+ if (input->stream_errno != 0) {
+ mail_storage_set_critical(&file->storage->storage,
+ "read(%s) failed: %s", file->cur_path,
+ i_stream_get_error(input));
+ ret = -1;
+ } else if (o_stream_flush(output) < 0) {
+ mail_storage_set_critical(&file->storage->storage,
+ "write(%s) failed: %s", out_path,
+ o_stream_get_error(output));
+ ret = -1;
+ } else if (input->v_offset != count) {
+ mail_storage_set_critical(&file->storage->storage,
+ "o_stream_send_istream(%s) copied only %"
+ PRIuUOFF_T" of %"PRIuUOFF_T" bytes",
+ out_path, input->v_offset, count);
+ ret = -1;
+ }
+ i_stream_unref(&input);
+ return ret;
+}
+
+static void dbox_file_skip_broken_header(struct dbox_file *file)
+{
+ const size_t magic_len = strlen(DBOX_MAGIC_PRE);
+ const unsigned char *data;
+ size_t i, size;
+
+ /* if there's LF close to our position, assume that the header ends
+ there. */
+ data = i_stream_get_data(file->input, &size);
+ if (size > file->msg_header_size + 16)
+ size = file->msg_header_size + 16;
+ for (i = 0; i < size; i++) {
+ if (data[i] == '\n') {
+ i_stream_skip(file->input, i);
+ return;
+ }
+ }
+
+ /* skip at least the magic bytes if possible */
+ if (size > magic_len && memcmp(data, DBOX_MAGIC_PRE, magic_len) == 0)
+ i_stream_skip(file->input, magic_len);
+}
+
+static void
+dbox_file_copy_metadata(struct dbox_file *file, struct ostream *output,
+ bool *have_guid_r)
+{
+ const char *line;
+ uoff_t prev_offset = file->input->v_offset;
+
+ *have_guid_r = FALSE;
+ while ((line = i_stream_read_next_line(file->input)) != NULL) {
+ if (*line == DBOX_METADATA_OLDV1_SPACE || *line == '\0') {
+ /* end of metadata */
+ return;
+ }
+ if (*line < 32) {
+ /* broken - possibly a new pre-magic block */
+ i_stream_seek(file->input, prev_offset);
+ return;
+ }
+ if (*line == DBOX_METADATA_VIRTUAL_SIZE) {
+ /* it may be wrong - recreate it */
+ continue;
+ }
+ if (*line == DBOX_METADATA_GUID)
+ *have_guid_r = TRUE;
+ o_stream_nsend_str(output, line);
+ o_stream_nsend_str(output, "\n");
+ }
+}
+
+static int
+dbox_file_fix_write_stream(struct dbox_file *file, uoff_t start_offset,
+ const char *temp_path, struct ostream *output)
+{
+ struct dbox_message_header msg_hdr;
+ uoff_t offset, msg_size, hdr_offset, body_offset;
+ bool pre, write_header, have_guid;
+ struct message_size body;
+ bool has_nuls;
+ struct istream *body_input;
+ guid_128_t guid_128;
+ int ret;
+
+ i_stream_seek(file->input, 0);
+ if (start_offset > 0) {
+ /* copy the valid data */
+ if (stream_copy(file, output, temp_path, start_offset) < 0)
+ return -1;
+ } else {
+ /* the file header is broken. recreate it */
+ if (dbox_file_header_write(file, output) < 0) {
+ dbox_file_set_syscall_error(file, "write()");
+ return -1;
+ }
+ }
+
+ while ((ret = dbox_file_find_next_magic(file, &offset, &pre)) > 0) {
+ msg_size = offset - file->input->v_offset;
+ if (msg_size < 256 && pre) {
+ /* probably some garbage or some broken headers.
+ we most likely don't miss anything by skipping
+ over this data. */
+ i_stream_skip(file->input, msg_size);
+ hdr_offset = file->input->v_offset;
+ ret = dbox_file_read_mail_header(file, &msg_size);
+ if (ret <= 0) {
+ if (ret < 0)
+ return -1;
+ dbox_file_skip_broken_header(file);
+ body_offset = file->input->v_offset;
+ msg_size = UOFF_T_MAX;
+ } else {
+ i_stream_skip(file->input,
+ file->msg_header_size);
+ body_offset = file->input->v_offset;
+ i_stream_skip(file->input, msg_size);
+ }
+
+ ret = dbox_file_find_next_magic(file, &offset, &pre);
+ if (ret <= 0)
+ break;
+
+ if (!pre && msg_size == offset - body_offset) {
+ /* msg header ok, copy it */
+ i_stream_seek(file->input, hdr_offset);
+ if (stream_copy(file, output, temp_path,
+ file->msg_header_size) < 0)
+ return -1;
+ write_header = FALSE;
+ } else {
+ /* msg header is broken. write our own. */
+ i_stream_seek(file->input, body_offset);
+ if (msg_size != UOFF_T_MAX) {
+ /* previous magic find might have
+ skipped too much. seek back and
+ make sure */
+ ret = dbox_file_find_next_magic(file, &offset, &pre);
+ if (ret <= 0)
+ break;
+ }
+
+ write_header = TRUE;
+ msg_size = offset - body_offset;
+ }
+ } else {
+ /* treat this data as a separate message. */
+ write_header = TRUE;
+ body_offset = file->input->v_offset;
+ }
+ /* write msg header */
+ if (write_header) {
+ dbox_msg_header_fill(&msg_hdr, msg_size);
+ o_stream_nsend(output, &msg_hdr, sizeof(msg_hdr));
+ }
+ /* write msg body */
+ i_assert(file->input->v_offset == body_offset);
+ if (stream_copy(file, output, temp_path, msg_size) < 0)
+ return -1;
+ i_assert(file->input->v_offset == offset);
+
+ /* get message body size */
+ i_stream_seek(file->input, body_offset);
+ body_input = i_stream_create_limit(file->input, msg_size);
+ ret = message_get_body_size(body_input, &body, &has_nuls);
+ i_stream_unref(&body_input);
+ if (ret < 0) {
+ mail_storage_set_critical(&file->storage->storage,
+ "read(%s) failed: %s", file->cur_path,
+ i_stream_get_error(body_input));
+ return -1;
+ }
+
+ /* write msg metadata. */
+ i_assert(file->input->v_offset == offset);
+ ret = dbox_file_metadata_skip_header(file);
+ if (ret < 0)
+ return -1;
+ o_stream_nsend_str(output, DBOX_MAGIC_POST);
+ if (ret == 0)
+ have_guid = FALSE;
+ else
+ dbox_file_copy_metadata(file, output, &have_guid);
+ if (!have_guid) {
+ guid_128_generate(guid_128);
+ o_stream_nsend_str(output,
+ t_strdup_printf("%c%s\n", DBOX_METADATA_GUID,
+ guid_128_to_string(guid_128)));
+ }
+ o_stream_nsend_str(output,
+ t_strdup_printf("%c%llx\n", DBOX_METADATA_VIRTUAL_SIZE,
+ (unsigned long long)body.virtual_size));
+ o_stream_nsend_str(output, "\n");
+ if (output->stream_errno != 0)
+ break;
+ }
+ if (o_stream_flush(output) < 0) {
+ mail_storage_set_critical(&file->storage->storage,
+ "write(%s) failed: %s", temp_path, o_stream_get_error(output));
+ ret = -1;
+ }
+ return ret;
+}
+
+int dbox_file_fix(struct dbox_file *file, uoff_t start_offset)
+{
+ struct ostream *output;
+ const char *dir, *p, *temp_path, *broken_path;
+ bool deleted, have_messages;
+ int fd, ret;
+
+ i_assert(dbox_file_is_open(file));
+
+ p = strrchr(file->cur_path, '/');
+ i_assert(p != NULL);
+ dir = t_strdup_until(file->cur_path, p);
+
+ temp_path = t_strdup_printf("%s/%s", dir, dbox_generate_tmp_filename());
+ fd = file->storage->v.file_create_fd(file, temp_path, FALSE);
+ if (fd == -1)
+ return -1;
+
+ output = o_stream_create_fd_file(fd, 0, FALSE);
+ o_stream_cork(output);
+ ret = dbox_file_fix_write_stream(file, start_offset, temp_path, output);
+ if (ret < 0)
+ o_stream_abort(output);
+ have_messages = output->offset > file->file_header_size;
+ o_stream_unref(&output);
+ if (close(fd) < 0) {
+ mail_storage_set_critical(&file->storage->storage,
+ "close(%s) failed: %m", temp_path);
+ ret = -1;
+ }
+ if (ret < 0) {
+ if (unlink(temp_path) < 0) {
+ mail_storage_set_critical(&file->storage->storage,
+ "unlink(%s) failed: %m", temp_path);
+ }
+ return -1;
+ }
+ /* keep a copy of the original file in case someone wants to look
+ at it */
+ broken_path = t_strconcat(file->cur_path,
+ DBOX_MAIL_FILE_BROKEN_COPY_SUFFIX, NULL);
+ if (link(file->cur_path, broken_path) < 0) {
+ mail_storage_set_critical(&file->storage->storage,
+ "link(%s, %s) failed: %m",
+ file->cur_path, broken_path);
+ } else {
+ i_warning("dbox: Copy of the broken file saved to %s",
+ broken_path);
+ }
+ if (!have_messages) {
+ /* the resulting file has no messages. just delete the file. */
+ dbox_file_close(file);
+ i_unlink(temp_path);
+ i_unlink(file->cur_path);
+ return 0;
+ }
+ if (rename(temp_path, file->cur_path) < 0) {
+ mail_storage_set_critical(&file->storage->storage,
+ "rename(%s, %s) failed: %m",
+ temp_path, file->cur_path);
+ return -1;
+ }
+
+ /* file was successfully recreated - reopen it */
+ dbox_file_close(file);
+ if (dbox_file_open(file, &deleted) <= 0) {
+ mail_storage_set_critical(&file->storage->storage,
+ "dbox_file_fix(%s): reopening file failed",
+ file->cur_path);
+ return -1;
+ }
+ return 1;
+}
diff --git a/src/lib-storage/index/dbox-common/dbox-file.c b/src/lib-storage/index/dbox-common/dbox-file.c
new file mode 100644
index 0000000..16810b0
--- /dev/null
+++ b/src/lib-storage/index/dbox-common/dbox-file.c
@@ -0,0 +1,796 @@
+/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "array.h"
+#include "hex-dec.h"
+#include "hex-binary.h"
+#include "hostpid.h"
+#include "istream.h"
+#include "ostream.h"
+#include "file-lock.h"
+#include "file-dotlock.h"
+#include "mkdir-parents.h"
+#include "eacces-error.h"
+#include "str.h"
+#include "dbox-storage.h"
+#include "dbox-file.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+#define DBOX_READ_BLOCK_SIZE IO_BLOCK_SIZE
+
+#ifndef DBOX_FILE_LOCK_METHOD_FLOCK
+static const struct dotlock_settings dotlock_set = {
+ .stale_timeout = 60*10,
+ .use_excl_lock = TRUE
+};
+#endif
+
+const char *dbox_generate_tmp_filename(void)
+{
+ static unsigned int create_count = 0;
+
+ return t_strdup_printf(DBOX_TEMP_FILE_PREFIX"%"PRIdTIME_T".P%sQ%uM%u.%s",
+ ioloop_timeval.tv_sec, my_pid,
+ create_count++,
+ (unsigned int)ioloop_timeval.tv_usec,
+ my_hostname);
+}
+
+void dbox_file_set_syscall_error(struct dbox_file *file, const char *function)
+{
+ mail_storage_set_critical(&file->storage->storage,
+ "%s failed for file %s: %m",
+ function, file->cur_path);
+}
+
+void dbox_file_set_corrupted(struct dbox_file *file, const char *reason, ...)
+{
+ va_list args;
+
+ va_start(args, reason);
+ mail_storage_set_critical(&file->storage->storage,
+ "Corrupted dbox file %s (around offset=%"PRIuUOFF_T"): %s",
+ file->cur_path, file->input == NULL ? 0 : file->input->v_offset,
+ t_strdup_vprintf(reason, args));
+ va_end(args);
+
+ file->storage->v.set_file_corrupted(file);
+}
+
+void dbox_file_init(struct dbox_file *file)
+{
+ file->refcount = 1;
+ file->fd = -1;
+ file->cur_offset = UOFF_T_MAX;
+ file->cur_path = file->primary_path;
+}
+
+void dbox_file_free(struct dbox_file *file)
+{
+ i_assert(file->refcount == 0);
+
+ pool_unref(&file->metadata_pool);
+ dbox_file_close(file);
+ i_free(file->primary_path);
+ i_free(file->alt_path);
+ i_free(file);
+}
+
+void dbox_file_unref(struct dbox_file **_file)
+{
+ struct dbox_file *file = *_file;
+
+ *_file = NULL;
+
+ i_assert(file->refcount > 0);
+ if (--file->refcount == 0)
+ file->storage->v.file_unrefed(file);
+}
+
+static int dbox_file_parse_header(struct dbox_file *file, const char *line)
+{
+ const char *const *tmp, *value;
+ enum dbox_header_key key;
+
+ file->file_version = *line - '0';
+ if (!i_isdigit(line[0]) || line[1] != ' ' ||
+ (file->file_version != 1 && file->file_version != DBOX_VERSION)) {
+ dbox_file_set_corrupted(file, "Invalid dbox version");
+ return -1;
+ }
+ line += 2;
+
+ file->msg_header_size = 0;
+
+ for (tmp = t_strsplit(line, " "); *tmp != NULL; tmp++) {
+ uintmax_t time;
+ key = **tmp;
+ value = *tmp + 1;
+
+ switch (key) {
+ case DBOX_HEADER_OLDV1_APPEND_OFFSET:
+ break;
+ case DBOX_HEADER_MSG_HEADER_SIZE:
+ if (str_to_uint_hex(value, &file->msg_header_size) < 0) {
+ dbox_file_set_corrupted(file, "Invalid message header size");
+ return -1;
+ }
+ break;
+ case DBOX_HEADER_CREATE_STAMP:
+ if (str_to_uintmax_hex(value, &time) < 0) {
+ dbox_file_set_corrupted(file, "Invalid create time stamp");
+ return -1;
+ }
+ file->create_time = (time_t)time;
+ break;
+ }
+ }
+
+ if (file->msg_header_size == 0) {
+ dbox_file_set_corrupted(file, "Missing message header size");
+ return -1;
+ }
+ return 0;
+}
+
+static int dbox_file_read_header(struct dbox_file *file)
+{
+ const char *line;
+ unsigned int hdr_size;
+ int ret;
+
+ i_stream_seek(file->input, 0);
+ line = i_stream_read_next_line(file->input);
+ if (line == NULL) {
+ if (file->input->stream_errno == 0) {
+ dbox_file_set_corrupted(file,
+ "EOF while reading file header");
+ return 0;
+ }
+
+ dbox_file_set_syscall_error(file, "read()");
+ return -1;
+ }
+ hdr_size = file->input->v_offset;
+ T_BEGIN {
+ ret = dbox_file_parse_header(file, line) < 0 ? 0 : 1;
+ } T_END;
+ if (ret > 0)
+ file->file_header_size = hdr_size;
+ return ret;
+}
+
+static int dbox_file_open_fd(struct dbox_file *file, bool try_altpath)
+{
+ const char *path;
+ int flags = O_RDWR;
+ bool alt = FALSE;
+
+ /* try the primary path first */
+ path = file->primary_path;
+ while ((file->fd = open(path, flags)) == -1) {
+ if (errno == EACCES && flags == O_RDWR) {
+ flags = O_RDONLY;
+ continue;
+ }
+ if (errno != ENOENT) {
+ mail_storage_set_critical(&file->storage->storage,
+ "open(%s) failed: %m", path);
+ return -1;
+ }
+
+ if (file->alt_path == NULL || alt || !try_altpath) {
+ /* not found */
+ return 0;
+ }
+
+ /* try the alternative path */
+ path = file->alt_path;
+ alt = TRUE;
+ }
+ file->cur_path = path;
+ return 1;
+}
+
+static int dbox_file_open_full(struct dbox_file *file, bool try_altpath,
+ bool *notfound_r)
+{
+ int ret, fd;
+
+ *notfound_r = FALSE;
+ if (file->input != NULL)
+ return 1;
+
+ if (file->fd == -1) {
+ T_BEGIN {
+ ret = dbox_file_open_fd(file, try_altpath);
+ } T_END;
+ if (ret <= 0) {
+ if (ret < 0)
+ return -1;
+ *notfound_r = TRUE;
+ return 1;
+ }
+ }
+
+ /* we're manually checking at dbox_file_close() if we need to close the
+ fd or not. */
+ fd = file->fd;
+ file->input = i_stream_create_fd_autoclose(&fd, DBOX_READ_BLOCK_SIZE);
+ i_stream_set_name(file->input, file->cur_path);
+ i_stream_set_init_buffer_size(file->input, DBOX_READ_BLOCK_SIZE);
+ return dbox_file_read_header(file);
+}
+
+int dbox_file_open(struct dbox_file *file, bool *deleted_r)
+{
+ return dbox_file_open_full(file, TRUE, deleted_r);
+}
+
+int dbox_file_open_primary(struct dbox_file *file, bool *notfound_r)
+{
+ return dbox_file_open_full(file, FALSE, notfound_r);
+}
+
+int dbox_file_stat(struct dbox_file *file, struct stat *st_r)
+{
+ const char *path;
+ bool alt = FALSE;
+
+ if (dbox_file_is_open(file)) {
+ if (fstat(file->fd, st_r) < 0) {
+ mail_storage_set_critical(&file->storage->storage,
+ "fstat(%s) failed: %m", file->cur_path);
+ return -1;
+ }
+ return 0;
+ }
+
+ /* try the primary path first */
+ path = file->primary_path;
+ while (stat(path, st_r) < 0) {
+ if (errno != ENOENT) {
+ mail_storage_set_critical(&file->storage->storage,
+ "stat(%s) failed: %m", path);
+ return -1;
+ }
+
+ if (file->alt_path == NULL || alt) {
+ /* not found */
+ return -1;
+ }
+
+ /* try the alternative path */
+ path = file->alt_path;
+ alt = TRUE;
+ }
+ file->cur_path = path;
+ return 0;
+}
+
+int dbox_file_header_write(struct dbox_file *file, struct ostream *output)
+{
+ string_t *hdr;
+
+ hdr = t_str_new(128);
+ str_printfa(hdr, "%u %c%x %c%x\n", DBOX_VERSION,
+ DBOX_HEADER_MSG_HEADER_SIZE,
+ (unsigned int)sizeof(struct dbox_message_header),
+ DBOX_HEADER_CREATE_STAMP, (unsigned int)ioloop_time);
+
+ file->file_version = DBOX_VERSION;
+ file->file_header_size = str_len(hdr);
+ file->msg_header_size = sizeof(struct dbox_message_header);
+ return o_stream_send(output, str_data(hdr), str_len(hdr));
+}
+
+void dbox_file_close(struct dbox_file *file)
+{
+ dbox_file_unlock(file);
+ if (file->input != NULL) {
+ /* stream autocloses the fd when it gets destroyed. note that
+ the stream may outlive the struct dbox_file. */
+ i_stream_unref(&file->input);
+ file->fd = -1;
+ } else if (file->fd != -1) {
+ if (close(file->fd) < 0)
+ dbox_file_set_syscall_error(file, "close()");
+ file->fd = -1;
+ }
+ file->cur_offset = UOFF_T_MAX;
+}
+
+int dbox_file_try_lock(struct dbox_file *file)
+{
+ const char *error;
+ int ret;
+
+ i_assert(file->fd != -1);
+
+#ifdef DBOX_FILE_LOCK_METHOD_FLOCK
+ struct file_lock_settings lock_set = {
+ .lock_method = FILE_LOCK_METHOD_FLOCK,
+ };
+ ret = file_try_lock(file->fd, file->cur_path, F_WRLCK,
+ &lock_set, &file->lock, &error);
+ if (ret < 0) {
+ mail_storage_set_critical(&file->storage->storage,
+ "file_try_lock(%s) failed: %s", file->cur_path, error);
+ }
+#else
+ ret = file_dotlock_create(&dotlock_set, file->cur_path,
+ DOTLOCK_CREATE_FLAG_NONBLOCK, &file->lock);
+ if (ret < 0) {
+ mail_storage_set_critical(&file->storage->storage,
+ "file_dotlock_create(%s) failed: %m", file->cur_path);
+ }
+#endif
+ return ret;
+}
+
+void dbox_file_unlock(struct dbox_file *file)
+{
+ i_assert(!file->appending || file->lock == NULL);
+
+ if (file->lock != NULL) {
+#ifdef DBOX_FILE_LOCK_METHOD_FLOCK
+ file_unlock(&file->lock);
+#else
+ file_dotlock_delete(&file->lock);
+#endif
+ }
+ if (file->input != NULL)
+ i_stream_sync(file->input);
+}
+
+int dbox_file_read_mail_header(struct dbox_file *file, uoff_t *physical_size_r)
+{
+ struct dbox_message_header hdr;
+ const unsigned char *data;
+ size_t size;
+ int ret;
+
+ ret = i_stream_read_bytes(file->input, &data, &size,
+ file->msg_header_size);
+ if (ret <= 0) {
+ if (file->input->stream_errno == 0) {
+ /* EOF, broken offset or file truncated */
+ dbox_file_set_corrupted(file, "EOF reading msg header "
+ "(got %zu/%u bytes)",
+ size, file->msg_header_size);
+ return 0;
+ }
+ dbox_file_set_syscall_error(file, "read()");
+ return -1;
+ }
+ memcpy(&hdr, data, I_MIN(sizeof(hdr), file->msg_header_size));
+ if (memcmp(hdr.magic_pre, DBOX_MAGIC_PRE, sizeof(hdr.magic_pre)) != 0) {
+ /* probably broken offset */
+ dbox_file_set_corrupted(file, "msg header has bad magic value");
+ return 0;
+ }
+
+ if (data[file->msg_header_size-1] != '\n') {
+ dbox_file_set_corrupted(file, "msg header doesn't end with LF");
+ return 0;
+ }
+
+ *physical_size_r = hex2dec(hdr.message_size_hex,
+ sizeof(hdr.message_size_hex));
+ return 1;
+}
+
+int dbox_file_seek(struct dbox_file *file, uoff_t offset)
+{
+ uoff_t size;
+ int ret;
+
+ i_assert(file->input != NULL);
+
+ if (offset == 0)
+ offset = file->file_header_size;
+
+ if (offset != file->cur_offset) {
+ i_stream_seek(file->input, offset);
+ ret = dbox_file_read_mail_header(file, &size);
+ if (ret <= 0)
+ return ret;
+ file->cur_offset = offset;
+ file->cur_physical_size = size;
+ }
+ i_stream_seek(file->input, offset + file->msg_header_size);
+ return 1;
+}
+
+static int
+dbox_file_seek_next_at_metadata(struct dbox_file *file, uoff_t *offset)
+{
+ const char *line;
+ size_t buf_size;
+ int ret;
+
+ i_stream_seek(file->input, *offset);
+ if ((ret = dbox_file_metadata_skip_header(file)) <= 0)
+ return ret;
+
+ /* skip over the actual metadata */
+ buf_size = i_stream_get_max_buffer_size(file->input);
+ i_stream_set_max_buffer_size(file->input, SIZE_MAX);
+ while ((line = i_stream_read_next_line(file->input)) != NULL) {
+ if (*line == DBOX_METADATA_OLDV1_SPACE || *line == '\0') {
+ /* end of metadata */
+ break;
+ }
+ }
+ i_stream_set_max_buffer_size(file->input, buf_size);
+ *offset = file->input->v_offset;
+ return 1;
+}
+
+void dbox_file_seek_rewind(struct dbox_file *file)
+{
+ file->cur_offset = UOFF_T_MAX;
+}
+
+int dbox_file_seek_next(struct dbox_file *file, uoff_t *offset_r, bool *last_r)
+{
+ uoff_t offset;
+ int ret;
+
+ i_assert(file->input != NULL);
+
+ if (file->cur_offset == UOFF_T_MAX) {
+ /* first mail. we may not have read the file at all yet,
+ so set the offset afterwards. */
+ offset = 0;
+ } else {
+ offset = file->cur_offset + file->msg_header_size +
+ file->cur_physical_size;
+ if ((ret = dbox_file_seek_next_at_metadata(file, &offset)) <= 0) {
+ *offset_r = file->cur_offset;
+ return ret;
+ }
+ if (i_stream_read_eof(file->input)) {
+ *last_r = TRUE;
+ return 0;
+ }
+ }
+ *offset_r = offset;
+
+ *last_r = FALSE;
+
+ ret = dbox_file_seek(file, offset);
+ if (*offset_r == 0)
+ *offset_r = file->file_header_size;
+ return ret;
+}
+
+struct dbox_file_append_context *dbox_file_append_init(struct dbox_file *file)
+{
+ struct dbox_file_append_context *ctx;
+
+ i_assert(!file->appending);
+
+ file->appending = TRUE;
+
+ ctx = i_new(struct dbox_file_append_context, 1);
+ ctx->file = file;
+ if (file->fd != -1) {
+ ctx->output = o_stream_create_fd_file(file->fd, 0, FALSE);
+ o_stream_set_name(ctx->output, file->cur_path);
+ o_stream_set_finish_via_child(ctx->output, FALSE);
+ o_stream_cork(ctx->output);
+ }
+ return ctx;
+}
+
+int dbox_file_append_commit(struct dbox_file_append_context **_ctx)
+{
+ struct dbox_file_append_context *ctx = *_ctx;
+ int ret;
+
+ i_assert(ctx->file->appending);
+
+ *_ctx = NULL;
+
+ ret = dbox_file_append_flush(ctx);
+ if (ctx->last_checkpoint_offset != ctx->output->offset) {
+ o_stream_close(ctx->output);
+ if (ftruncate(ctx->file->fd, ctx->last_checkpoint_offset) < 0) {
+ dbox_file_set_syscall_error(ctx->file, "ftruncate()");
+ return -1;
+ }
+ }
+ o_stream_unref(&ctx->output);
+ ctx->file->appending = FALSE;
+ i_free(ctx);
+ return ret;
+}
+
+void dbox_file_append_rollback(struct dbox_file_append_context **_ctx)
+{
+ struct dbox_file_append_context *ctx = *_ctx;
+ struct dbox_file *file = ctx->file;
+ bool close_file = FALSE;
+
+ i_assert(ctx->file->appending);
+
+ *_ctx = NULL;
+ if (ctx->first_append_offset == 0) {
+ /* nothing changed */
+ } else if (ctx->first_append_offset == file->file_header_size) {
+ /* rolling back everything */
+ if (unlink(file->cur_path) < 0)
+ dbox_file_set_syscall_error(file, "unlink()");
+ close_file = TRUE;
+ } else {
+ /* truncating only some mails */
+ o_stream_close(ctx->output);
+ if (ftruncate(file->fd, ctx->first_append_offset) < 0)
+ dbox_file_set_syscall_error(file, "ftruncate()");
+ }
+ if (ctx->output != NULL) {
+ o_stream_abort(ctx->output);
+ o_stream_unref(&ctx->output);
+ }
+ i_free(ctx);
+
+ if (close_file)
+ dbox_file_close(file);
+ file->appending = FALSE;
+}
+
+int dbox_file_append_flush(struct dbox_file_append_context *ctx)
+{
+ struct mail_storage *storage = &ctx->file->storage->storage;
+
+ if (ctx->last_flush_offset == ctx->output->offset &&
+ ctx->last_checkpoint_offset == ctx->output->offset)
+ return 0;
+
+ if (o_stream_flush(ctx->output) < 0) {
+ dbox_file_set_syscall_error(ctx->file, "write()");
+ return -1;
+ }
+
+ if (ctx->last_checkpoint_offset != ctx->output->offset) {
+ if (ftruncate(ctx->file->fd, ctx->last_checkpoint_offset) < 0) {
+ dbox_file_set_syscall_error(ctx->file, "ftruncate()");
+ return -1;
+ }
+ if (o_stream_seek(ctx->output, ctx->last_checkpoint_offset) < 0) {
+ dbox_file_set_syscall_error(ctx->file, "lseek()");
+ return -1;
+ }
+ }
+
+ if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER) {
+ if (fdatasync(ctx->file->fd) < 0) {
+ dbox_file_set_syscall_error(ctx->file, "fdatasync()");
+ return -1;
+ }
+ }
+ ctx->last_flush_offset = ctx->output->offset;
+ return 0;
+}
+
+void dbox_file_append_checkpoint(struct dbox_file_append_context *ctx)
+{
+ ctx->last_checkpoint_offset = ctx->output->offset;
+}
+
+int dbox_file_get_append_stream(struct dbox_file_append_context *ctx,
+ struct ostream **output_r)
+{
+ struct dbox_file *file = ctx->file;
+ struct stat st;
+
+ if (ctx->output == NULL) {
+ /* file creation had failed */
+ return -1;
+ }
+ if (ctx->last_checkpoint_offset != ctx->output->offset) {
+ /* a message was aborted. don't try appending to this
+ file anymore. */
+ return -1;
+ }
+
+ if (file->file_version == 0) {
+ /* newly created file, write the file header */
+ if (dbox_file_header_write(file, ctx->output) < 0) {
+ dbox_file_set_syscall_error(file, "write()");
+ return -1;
+ }
+ *output_r = ctx->output;
+ return 1;
+ }
+
+ /* file has existing mails */
+ if (file->file_version != DBOX_VERSION ||
+ file->msg_header_size != sizeof(struct dbox_message_header)) {
+ /* created by an incompatible version, can't append */
+ return 0;
+ }
+
+ if (ctx->output->offset == 0) {
+ /* first append to existing file. seek to eof first. */
+ if (fstat(file->fd, &st) < 0) {
+ dbox_file_set_syscall_error(file, "fstat()");
+ return -1;
+ }
+ if (st.st_size < file->msg_header_size) {
+ dbox_file_set_corrupted(file,
+ "dbox file size too small");
+ return 0;
+ }
+ if (o_stream_seek(ctx->output, st.st_size) < 0) {
+ dbox_file_set_syscall_error(file, "lseek()");
+ return -1;
+ }
+ }
+ *output_r = ctx->output;
+ return 1;
+}
+
+int dbox_file_metadata_skip_header(struct dbox_file *file)
+{
+ struct dbox_metadata_header metadata_hdr;
+ const unsigned char *data;
+ size_t size;
+ int ret;
+
+ ret = i_stream_read_bytes(file->input, &data, &size,
+ sizeof(metadata_hdr));
+ if (ret <= 0) {
+ if (file->input->stream_errno == 0) {
+ /* EOF, broken offset */
+ dbox_file_set_corrupted(file,
+ "Unexpected EOF while reading metadata header");
+ return 0;
+ }
+ dbox_file_set_syscall_error(file, "read()");
+ return -1;
+ }
+ memcpy(&metadata_hdr, data, sizeof(metadata_hdr));
+ if (memcmp(metadata_hdr.magic_post, DBOX_MAGIC_POST,
+ sizeof(metadata_hdr.magic_post)) != 0) {
+ /* probably broken offset */
+ dbox_file_set_corrupted(file,
+ "metadata header has bad magic value");
+ return 0;
+ }
+ i_stream_skip(file->input, sizeof(metadata_hdr));
+ return 1;
+}
+
+static int
+dbox_file_metadata_read_at(struct dbox_file *file, uoff_t metadata_offset)
+{
+ const char *line;
+ size_t buf_size;
+ int ret;
+
+ if (file->metadata_pool != NULL)
+ p_clear(file->metadata_pool);
+ else {
+ file->metadata_pool =
+ pool_alloconly_create("dbox metadata", 1024);
+ }
+ p_array_init(&file->metadata, file->metadata_pool, 16);
+
+ i_stream_seek(file->input, metadata_offset);
+ if ((ret = dbox_file_metadata_skip_header(file)) <= 0)
+ return ret;
+
+ ret = 0;
+ buf_size = i_stream_get_max_buffer_size(file->input);
+ /* use unlimited line length for metadata */
+ i_stream_set_max_buffer_size(file->input, SIZE_MAX);
+ while ((line = i_stream_read_next_line(file->input)) != NULL) {
+ if (*line == DBOX_METADATA_OLDV1_SPACE || *line == '\0') {
+ /* end of metadata */
+ ret = 1;
+ break;
+ }
+ line = p_strdup(file->metadata_pool, line);
+ array_push_back(&file->metadata, &line);
+ }
+ i_stream_set_max_buffer_size(file->input, buf_size);
+ if (ret == 0)
+ dbox_file_set_corrupted(file, "missing end-of-metadata line");
+ return ret;
+}
+
+int dbox_file_metadata_read(struct dbox_file *file)
+{
+ uoff_t metadata_offset;
+ int ret;
+
+ i_assert(file->cur_offset != UOFF_T_MAX);
+
+ if (file->metadata_read_offset == file->cur_offset)
+ return 1;
+
+ metadata_offset = file->cur_offset + file->msg_header_size +
+ file->cur_physical_size;
+ ret = dbox_file_metadata_read_at(file, metadata_offset);
+ if (ret <= 0)
+ return ret;
+
+ file->metadata_read_offset = file->cur_offset;
+ return 1;
+}
+
+const char *dbox_file_metadata_get(struct dbox_file *file,
+ enum dbox_metadata_key key)
+{
+ const char *const *metadata;
+ unsigned int i, count;
+
+ metadata = array_get(&file->metadata, &count);
+ for (i = 0; i < count; i++) {
+ if (*metadata[i] == (char)key)
+ return metadata[i] + 1;
+ }
+ return NULL;
+}
+
+uoff_t dbox_file_get_plaintext_size(struct dbox_file *file)
+{
+ const char *value;
+ uintmax_t size;
+
+ i_assert(file->metadata_read_offset == file->cur_offset);
+
+ /* see if we have it in metadata */
+ value = dbox_file_metadata_get(file, DBOX_METADATA_PHYSICAL_SIZE);
+ if (value == NULL ||
+ str_to_uintmax_hex(value, &size) < 0 ||
+ size > UOFF_T_MAX) {
+ /* no. that means we can use the size in the header */
+ return file->cur_physical_size;
+ }
+
+ return (uoff_t)size;
+}
+
+void dbox_msg_header_fill(struct dbox_message_header *dbox_msg_hdr,
+ uoff_t message_size)
+{
+ memset(dbox_msg_hdr, ' ', sizeof(*dbox_msg_hdr));
+ memcpy(dbox_msg_hdr->magic_pre, DBOX_MAGIC_PRE,
+ sizeof(dbox_msg_hdr->magic_pre));
+ dbox_msg_hdr->type = DBOX_MESSAGE_TYPE_NORMAL;
+ dec2hex(dbox_msg_hdr->message_size_hex, message_size,
+ sizeof(dbox_msg_hdr->message_size_hex));
+ dbox_msg_hdr->save_lf = '\n';
+}
+
+int dbox_file_unlink(struct dbox_file *file)
+{
+ const char *path;
+ bool alt = FALSE;
+
+ path = file->primary_path;
+ while (unlink(path) < 0) {
+ if (errno != ENOENT) {
+ mail_storage_set_critical(&file->storage->storage,
+ "unlink(%s) failed: %m", path);
+ return -1;
+ }
+ if (file->alt_path == NULL || alt) {
+ /* not found */
+ return 0;
+ }
+
+ /* try the alternative path */
+ path = file->alt_path;
+ alt = TRUE;
+ }
+ return 1;
+}
diff --git a/src/lib-storage/index/dbox-common/dbox-file.h b/src/lib-storage/index/dbox-common/dbox-file.h
new file mode 100644
index 0000000..309c705
--- /dev/null
+++ b/src/lib-storage/index/dbox-common/dbox-file.h
@@ -0,0 +1,218 @@
+#ifndef DBOX_FILE_H
+#define DBOX_FILE_H
+
+/* The file begins with a header followed by zero or more messages:
+
+ <dbox message header>
+ <LF>
+ <message body>
+ <metadata>
+
+ Metadata block begins with DBOX_MAGIC_POST, followed by zero or more lines
+ in format <key character><value><LF>. The block ends with an empty line.
+ Unknown metadata should be ignored, but preserved when copying.
+
+ There should be no duplicates for the current metadata, but future
+ extensions may need them so they should be preserved.
+*/
+#define DBOX_VERSION 2
+#define DBOX_MAGIC_PRE "\001\002"
+#define DBOX_MAGIC_POST "\n\001\003\n"
+
+/* prefer flock(). fcntl() locking currently breaks if trying to access the
+ same file from multiple mail_storages within same process. that's why we
+ fallback to dotlocks. */
+#ifdef HAVE_FLOCK
+# define DBOX_FILE_LOCK_METHOD_FLOCK
+#endif
+
+struct dbox_file;
+struct stat;
+
+enum dbox_header_key {
+ /* Must be sizeof(struct dbox_message_header) when appending (hex) */
+ DBOX_HEADER_MSG_HEADER_SIZE = 'M',
+ /* Creation UNIX timestamp (hex) */
+ DBOX_HEADER_CREATE_STAMP = 'C',
+
+ /* metadata used by old Dovecot versions */
+ DBOX_HEADER_OLDV1_APPEND_OFFSET = 'A'
+};
+
+/* NOTE: all valid keys are uppercase characters. if this changes, change
+ dbox-file-fix.c:dbox_file_match_post_magic() to recognize them */
+enum dbox_metadata_key {
+ /* Globally unique identifier for the message. Preserved when
+ copying. */
+ DBOX_METADATA_GUID = 'G',
+ /* POP3 UIDL overriding the default format */
+ DBOX_METADATA_POP3_UIDL = 'P',
+ /* POP3 message ordering (for migrated mails) */
+ DBOX_METADATA_POP3_ORDER = 'O',
+ /* Received UNIX timestamp in hex */
+ DBOX_METADATA_RECEIVED_TIME = 'R',
+ /* Physical message size in hex. Necessary only if it differs from
+ the dbox_message_header.message_size_hex, for example because the
+ message is compressed. */
+ DBOX_METADATA_PHYSICAL_SIZE = 'Z',
+ /* Virtual message size in hex (line feeds counted as CRLF) */
+ DBOX_METADATA_VIRTUAL_SIZE = 'V',
+ /* Pointer to external message data. Format is:
+ 1*(<start offset> <byte count> <options> <ref>) */
+ DBOX_METADATA_EXT_REF = 'X',
+ /* Mailbox name where this message was originally saved to.
+ When rebuild finds a message whose mailbox is unknown, it's
+ placed to this mailbox. */
+ DBOX_METADATA_ORIG_MAILBOX = 'B',
+
+ /* metadata used by old Dovecot versions */
+ DBOX_METADATA_OLDV1_EXPUNGED = 'E',
+ DBOX_METADATA_OLDV1_FLAGS = 'F',
+ DBOX_METADATA_OLDV1_KEYWORDS = 'K',
+ DBOX_METADATA_OLDV1_SAVE_TIME = 'S',
+ DBOX_METADATA_OLDV1_SPACE = ' '
+};
+
+enum dbox_message_type {
+ /* Normal message */
+ DBOX_MESSAGE_TYPE_NORMAL = 'N'
+};
+
+struct dbox_message_header {
+ unsigned char magic_pre[2];
+ unsigned char type;
+ unsigned char space1;
+ unsigned char oldv1_uid_hex[8];
+ unsigned char space2;
+ unsigned char message_size_hex[16];
+ /* <space reserved for future extensions, LF is always last> */
+ unsigned char save_lf;
+};
+
+struct dbox_metadata_header {
+ unsigned char magic_post[sizeof(DBOX_MAGIC_POST)-1];
+};
+
+struct dbox_file {
+ struct dbox_storage *storage;
+ int refcount;
+
+ time_t create_time;
+ unsigned int file_version;
+ unsigned int file_header_size;
+ unsigned int msg_header_size;
+
+ const char *cur_path;
+ char *primary_path, *alt_path;
+ int fd;
+ struct istream *input;
+#ifdef DBOX_FILE_LOCK_METHOD_FLOCK
+ struct file_lock *lock;
+#else
+ struct dotlock *lock;
+#endif
+
+ uoff_t cur_offset;
+ uoff_t cur_physical_size;
+
+ /* Metadata for the currently seeked metadata block. */
+ pool_t metadata_pool;
+ ARRAY(const char *) metadata;
+ uoff_t metadata_read_offset;
+
+ bool appending:1;
+ bool corrupted:1;
+};
+
+struct dbox_file_append_context {
+ struct dbox_file *file;
+
+ uoff_t first_append_offset, last_checkpoint_offset, last_flush_offset;
+ struct ostream *output;
+};
+
+#define dbox_file_is_open(file) ((file)->fd != -1)
+#define dbox_file_is_in_alt(file) ((file)->cur_path == (file)->alt_path)
+
+void dbox_file_init(struct dbox_file *file);
+void dbox_file_unref(struct dbox_file **file);
+
+/* Open the file. Returns 1 if ok, 0 if file header is corrupted, -1 if error.
+ If file is deleted, deleted_r=TRUE and 1 is returned. */
+int dbox_file_open(struct dbox_file *file, bool *deleted_r);
+/* Try to open file only from primary path. */
+int dbox_file_open_primary(struct dbox_file *file, bool *notfound_r);
+/* Close the file handle from the file, but don't free it. */
+void dbox_file_close(struct dbox_file *file);
+
+/* fstat() or stat() the file. If file is already deleted, fails with
+ errno=ENOENT. */
+int dbox_file_stat(struct dbox_file *file, struct stat *st_r);
+
+/* Try to lock the dbox file. Returns 1 if ok, 0 if already locked by someone
+ else, -1 if error. */
+int dbox_file_try_lock(struct dbox_file *file);
+void dbox_file_unlock(struct dbox_file *file);
+
+/* Seek to given offset in file. Returns 1 if ok/expunged, 0 if file/offset is
+ corrupted, -1 if I/O error. */
+int dbox_file_seek(struct dbox_file *file, uoff_t offset);
+/* Start seeking at the beginning of the file. */
+void dbox_file_seek_rewind(struct dbox_file *file);
+/* Seek to next message after current one. If there are no more messages,
+ returns 0 and last_r is set to TRUE. Returns 1 if ok, 0 if file is
+ corrupted, -1 if I/O error. */
+int dbox_file_seek_next(struct dbox_file *file, uoff_t *offset_r, bool *last_r);
+
+/* Start appending to dbox file */
+struct dbox_file_append_context *dbox_file_append_init(struct dbox_file *file);
+/* Finish writing appended mails. */
+int dbox_file_append_commit(struct dbox_file_append_context **ctx);
+/* Truncate appended mails. */
+void dbox_file_append_rollback(struct dbox_file_append_context **ctx);
+/* Get output stream for appending a new message. Returns 1 if ok, 0 if file
+ can't be appended to (old file version or corruption) or -1 if error. */
+int dbox_file_get_append_stream(struct dbox_file_append_context *ctx,
+ struct ostream **output_r);
+/* Call after message has been fully saved. If this isn't done, the writes
+ since the last checkpoint are truncated. */
+void dbox_file_append_checkpoint(struct dbox_file_append_context *ctx);
+/* Flush output buffer. */
+int dbox_file_append_flush(struct dbox_file_append_context *ctx);
+
+/* Read current message's metadata. Returns 1 if ok, 0 if metadata is
+ corrupted, -1 if I/O error. */
+int dbox_file_metadata_read(struct dbox_file *file);
+/* Return wanted metadata value, or NULL if not found. */
+const char *dbox_file_metadata_get(struct dbox_file *file,
+ enum dbox_metadata_key key);
+
+/* Returns DBOX_METADATA_PHYSICAL_SIZE if set, otherwise physical size from
+ header. They differ only for e.g. compressed mails. */
+uoff_t dbox_file_get_plaintext_size(struct dbox_file *file);
+
+/* Fix a broken dbox file by rename()ing over it with a fixed file. Everything
+ before start_offset is assumed to be valid and is simply copied. The file
+ is reopened afterwards. Returns 1 if ok, 0 if the resulting file has no
+ mails and was deleted, -1 if I/O error. */
+int dbox_file_fix(struct dbox_file *file, uoff_t start_offset);
+/* Delete the given dbox file. Returns 1 if deleted, 0 if file wasn't found
+ or -1 if error. */
+int dbox_file_unlink(struct dbox_file *file);
+
+/* Fill dbox_message_header with given size. */
+void dbox_msg_header_fill(struct dbox_message_header *dbox_msg_hdr,
+ uoff_t message_size);
+
+void dbox_file_set_syscall_error(struct dbox_file *file, const char *function);
+void dbox_file_set_corrupted(struct dbox_file *file, const char *reason, ...)
+ ATTR_FORMAT(2, 3);
+
+/* private: */
+const char *dbox_generate_tmp_filename(void);
+void dbox_file_free(struct dbox_file *file);
+int dbox_file_header_write(struct dbox_file *file, struct ostream *output);
+int dbox_file_read_mail_header(struct dbox_file *file, uoff_t *physical_size_r);
+int dbox_file_metadata_skip_header(struct dbox_file *file);
+
+#endif
diff --git a/src/lib-storage/index/dbox-common/dbox-mail.c b/src/lib-storage/index/dbox-common/dbox-mail.c
new file mode 100644
index 0000000..49279f9
--- /dev/null
+++ b/src/lib-storage/index/dbox-common/dbox-mail.c
@@ -0,0 +1,318 @@
+/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "istream.h"
+#include "str.h"
+#include "index-storage.h"
+#include "index-mail.h"
+#include "index-pop3-uidl.h"
+#include "dbox-attachment.h"
+#include "dbox-storage.h"
+#include "dbox-file.h"
+#include "dbox-mail.h"
+
+
+struct mail *
+dbox_mail_alloc(struct mailbox_transaction_context *t,
+ enum mail_fetch_field wanted_fields,
+ struct mailbox_header_lookup_ctx *wanted_headers)
+{
+ struct dbox_mail *mail;
+ pool_t pool;
+
+ pool = pool_alloconly_create("mail", 2048);
+ mail = p_new(pool, struct dbox_mail, 1);
+
+ index_mail_init(&mail->imail, t, wanted_fields, wanted_headers, pool, NULL);
+ return &mail->imail.mail.mail;
+}
+
+void dbox_mail_close(struct mail *_mail)
+{
+ struct dbox_mail *mail = DBOX_MAIL(_mail);
+
+ index_mail_close(_mail);
+ /* close the dbox file only after index is closed, since it may still
+ try to read from it. */
+ if (mail->open_file != NULL)
+ dbox_file_unref(&mail->open_file);
+}
+
+int dbox_mail_metadata_read(struct dbox_mail *mail, struct dbox_file **file_r)
+{
+ struct dbox_storage *storage =
+ DBOX_STORAGE(mail->imail.mail.mail.box->storage);
+ uoff_t offset;
+
+ if (storage->v.mail_open(mail, &offset, file_r) < 0)
+ return -1;
+
+ if (dbox_file_seek(*file_r, offset) <= 0)
+ return -1;
+ if (dbox_file_metadata_read(*file_r) <= 0)
+ return -1;
+
+ if (mail->imail.data.stream != NULL) {
+ /* we just messed up mail's input stream by reading metadata */
+ i_stream_seek((*file_r)->input, offset);
+ i_stream_sync(mail->imail.data.stream);
+ }
+ return 0;
+}
+
+static int
+dbox_mail_metadata_get(struct dbox_mail *mail, enum dbox_metadata_key key,
+ const char **value_r)
+{
+ struct dbox_file *file;
+
+ if (dbox_mail_metadata_read(mail, &file) < 0)
+ return -1;
+
+ *value_r = dbox_file_metadata_get(file, key);
+ return 0;
+}
+
+int dbox_mail_get_physical_size(struct mail *_mail, uoff_t *size_r)
+{
+ struct dbox_mail *mail = DBOX_MAIL(_mail);
+ struct index_mail_data *data = &mail->imail.data;
+ struct dbox_file *file;
+
+ if (index_mail_get_physical_size(_mail, size_r) == 0)
+ return 0;
+
+ if (dbox_mail_metadata_read(mail, &file) < 0)
+ return -1;
+
+ data->physical_size = dbox_file_get_plaintext_size(file);
+ *size_r = data->physical_size;
+ return 0;
+}
+
+int dbox_mail_get_virtual_size(struct mail *_mail, uoff_t *size_r)
+{
+ struct dbox_mail *mail = DBOX_MAIL(_mail);
+ struct index_mail_data *data = &mail->imail.data;
+ const char *value;
+ uintmax_t size;
+
+ if (index_mail_get_cached_virtual_size(&mail->imail, size_r))
+ return 0;
+
+ if (dbox_mail_metadata_get(mail, DBOX_METADATA_VIRTUAL_SIZE,
+ &value) < 0)
+ return -1;
+ if (value == NULL)
+ return index_mail_get_virtual_size(_mail, size_r);
+
+ if (str_to_uintmax_hex(value, &size) < 0 || size > UOFF_T_MAX)
+ return -1;
+ data->virtual_size = (uoff_t)size;
+ *size_r = data->virtual_size;
+ return 0;
+}
+
+int dbox_mail_get_received_date(struct mail *_mail, time_t *date_r)
+{
+ struct dbox_mail *mail = DBOX_MAIL(_mail);
+ struct index_mail_data *data = &mail->imail.data;
+ const char *value;
+ uintmax_t time;
+
+ if (index_mail_get_received_date(_mail, date_r) == 0)
+ return 0;
+
+ if (dbox_mail_metadata_get(mail, DBOX_METADATA_RECEIVED_TIME,
+ &value) < 0)
+ return -1;
+
+ time = 0;
+ if (value != NULL && str_to_uintmax_hex(value, &time) < 0)
+ return -1;
+
+ data->received_date = (time_t)time;
+ *date_r = data->received_date;
+ return 0;
+}
+
+int dbox_mail_get_save_date(struct mail *_mail, time_t *date_r)
+{
+ struct dbox_storage *storage = DBOX_STORAGE(_mail->box->storage);
+ struct dbox_mail *mail = DBOX_MAIL(_mail);
+ struct index_mail_data *data = &mail->imail.data;
+ struct dbox_file *file;
+ struct stat st;
+ uoff_t offset;
+
+ if (index_mail_get_save_date(_mail, date_r) > 0)
+ return 1;
+
+ if (storage->v.mail_open(mail, &offset, &file) < 0)
+ return -1;
+
+ _mail->transaction->stats.fstat_lookup_count++;
+ if (dbox_file_stat(file, &st) < 0) {
+ if (errno == ENOENT)
+ mail_set_expunged(_mail);
+ return -1;
+ }
+ *date_r = data->save_date = st.st_ctime;
+ return 1;
+}
+
+static int
+dbox_get_cached_metadata(struct dbox_mail *mail, enum dbox_metadata_key key,
+ enum index_cache_field cache_field,
+ const char **value_r)
+{
+ struct index_mail *imail = &mail->imail;
+ struct index_mailbox_context *ibox =
+ INDEX_STORAGE_CONTEXT(imail->mail.mail.box);
+ const char *value;
+ string_t *str;
+ uint32_t order;
+
+ str = str_new(imail->mail.data_pool, 64);
+ if (mail_cache_lookup_field(imail->mail.mail.transaction->cache_view,
+ str, imail->mail.mail.seq,
+ ibox->cache_fields[cache_field].idx) > 0) {
+ if (cache_field == MAIL_CACHE_POP3_ORDER) {
+ i_assert(str_len(str) == sizeof(order));
+ memcpy(&order, str_data(str), sizeof(order));
+ str_truncate(str, 0);
+ if (order != 0)
+ str_printfa(str, "%u", order);
+ else {
+ /* order=0 means it doesn't exist. we don't
+ want to return "0" though, because then the
+ mails get ordered to beginning, while
+ nonexistent are supposed to be ordered at
+ the end. */
+ }
+ }
+ *value_r = str_c(str);
+ return 0;
+ }
+
+ if (dbox_mail_metadata_get(mail, key, &value) < 0)
+ return -1;
+
+ if (value == NULL)
+ value = "";
+ if (cache_field != MAIL_CACHE_POP3_ORDER) {
+ index_mail_cache_add_idx(imail, ibox->cache_fields[cache_field].idx,
+ value, strlen(value));
+ } else {
+ if (str_to_uint(value, &order) < 0)
+ order = 0;
+ index_mail_cache_add_idx(imail, ibox->cache_fields[cache_field].idx,
+ &order, sizeof(order));
+ }
+
+ /* don't return pointer to dbox metadata directly, since it may
+ change unexpectedly */
+ str_truncate(str, 0);
+ str_append(str, value);
+ *value_r = str_c(str);
+ return 0;
+}
+
+int dbox_mail_get_special(struct mail *_mail, enum mail_fetch_field field,
+ const char **value_r)
+{
+ struct dbox_mail *mail = DBOX_MAIL(_mail);
+ int ret;
+
+ /* keep the UIDL in cache file, otherwise POP3 would open all
+ mail files and read the metadata. same for GUIDs if they're
+ used. */
+ switch (field) {
+ case MAIL_FETCH_UIDL_BACKEND:
+ if (!index_pop3_uidl_can_exist(_mail)) {
+ *value_r = "";
+ return 0;
+ }
+ ret = dbox_get_cached_metadata(mail, DBOX_METADATA_POP3_UIDL,
+ MAIL_CACHE_POP3_UIDL, value_r);
+ if (ret == 0) {
+ index_pop3_uidl_update_exists(&mail->imail.mail.mail,
+ (*value_r)[0] != '\0');
+ }
+ return ret;
+ case MAIL_FETCH_POP3_ORDER:
+ if (!index_pop3_uidl_can_exist(_mail)) {
+ /* we're assuming that if there's a POP3 order, there's
+ also a UIDL */
+ *value_r = "";
+ return 0;
+ }
+ return dbox_get_cached_metadata(mail, DBOX_METADATA_POP3_ORDER,
+ MAIL_CACHE_POP3_ORDER, value_r);
+ case MAIL_FETCH_GUID:
+ return dbox_get_cached_metadata(mail, DBOX_METADATA_GUID,
+ MAIL_CACHE_GUID, value_r);
+ default:
+ break;
+ }
+
+ return index_mail_get_special(_mail, field, value_r);
+}
+
+static int
+get_mail_stream(struct dbox_mail *mail, uoff_t offset,
+ struct istream **stream_r)
+{
+ struct mail_private *pmail = &mail->imail.mail;
+ struct dbox_file *file = mail->open_file;
+ int ret;
+
+ if ((ret = dbox_file_seek(file, offset)) <= 0) {
+ *stream_r = NULL;
+ return ret;
+ }
+
+ *stream_r = i_stream_create_limit(file->input, file->cur_physical_size);
+ if (pmail->v.istream_opened != NULL) {
+ if (pmail->v.istream_opened(&pmail->mail, stream_r) < 0)
+ return -1;
+ }
+ if (file->storage->attachment_dir == NULL)
+ return 1;
+ else
+ return dbox_attachment_file_get_stream(file, stream_r);
+}
+
+int dbox_mail_get_stream(struct mail *_mail, bool get_body ATTR_UNUSED,
+ struct message_size *hdr_size,
+ struct message_size *body_size,
+ struct istream **stream_r)
+{
+ struct dbox_storage *storage = DBOX_STORAGE(_mail->box->storage);
+ struct dbox_mail *mail = DBOX_MAIL(_mail);
+ struct index_mail_data *data = &mail->imail.data;
+ struct istream *input;
+ uoff_t offset;
+ int ret;
+
+ if (data->stream == NULL) {
+ if (storage->v.mail_open(mail, &offset, &mail->open_file) < 0)
+ return -1;
+
+ ret = get_mail_stream(mail, offset, &input);
+ if (ret <= 0) {
+ if (ret < 0)
+ return -1;
+ dbox_file_set_corrupted(mail->open_file,
+ "uid=%u points to broken data at offset="
+ "%"PRIuUOFF_T, _mail->uid, offset);
+ i_stream_unref(&input);
+ return -1;
+ }
+ data->stream = input;
+ index_mail_set_read_buffer_size(_mail, input);
+ }
+
+ return index_mail_init_stream(&mail->imail, hdr_size, body_size,
+ stream_r);
+}
diff --git a/src/lib-storage/index/dbox-common/dbox-mail.h b/src/lib-storage/index/dbox-common/dbox-mail.h
new file mode 100644
index 0000000..c03652c
--- /dev/null
+++ b/src/lib-storage/index/dbox-common/dbox-mail.h
@@ -0,0 +1,34 @@
+#ifndef DBOX_MAIL_H
+#define DBOX_MAIL_H
+
+#include "index-mail.h"
+
+struct dbox_mail {
+ struct index_mail imail;
+
+ struct dbox_file *open_file;
+ uoff_t offset;
+};
+
+#define DBOX_MAIL(s) container_of(s, struct dbox_mail, imail.mail.mail)
+
+struct mail *
+dbox_mail_alloc(struct mailbox_transaction_context *t,
+ enum mail_fetch_field wanted_fields,
+ struct mailbox_header_lookup_ctx *wanted_headers);
+void dbox_mail_close(struct mail *mail);
+
+int dbox_mail_get_physical_size(struct mail *mail, uoff_t *size_r);
+int dbox_mail_get_virtual_size(struct mail *mail, uoff_t *size_r);
+int dbox_mail_get_received_date(struct mail *mail, time_t *date_r);
+int dbox_mail_get_save_date(struct mail *_mail, time_t *date_r);
+int dbox_mail_get_special(struct mail *mail, enum mail_fetch_field field,
+ const char **value_r);
+int dbox_mail_get_stream(struct mail *_mail, bool get_body ATTR_UNUSED,
+ struct message_size *hdr_size,
+ struct message_size *body_size,
+ struct istream **stream_r);
+
+int dbox_mail_metadata_read(struct dbox_mail *mail, struct dbox_file **file_r);
+
+#endif
diff --git a/src/lib-storage/index/dbox-common/dbox-save.c b/src/lib-storage/index/dbox-common/dbox-save.c
new file mode 100644
index 0000000..c5af8cc
--- /dev/null
+++ b/src/lib-storage/index/dbox-common/dbox-save.c
@@ -0,0 +1,226 @@
+/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "istream.h"
+#include "istream-crlf.h"
+#include "ostream.h"
+#include "str.h"
+#include "hex-binary.h"
+#include "index-mail.h"
+#include "index-storage.h"
+#include "dbox-attachment.h"
+#include "dbox-file.h"
+#include "dbox-save.h"
+
+void dbox_save_add_to_index(struct dbox_save_context *ctx)
+{
+ struct mail_save_data *mdata = &ctx->ctx.data;
+ enum mail_flags save_flags;
+
+ save_flags = mdata->flags & ENUM_NEGATE(MAIL_RECENT);
+ mail_index_append(ctx->trans, mdata->uid, &ctx->seq);
+ mail_index_update_flags(ctx->trans, ctx->seq, MODIFY_REPLACE,
+ save_flags);
+ if (mdata->keywords != NULL) {
+ mail_index_update_keywords(ctx->trans, ctx->seq,
+ MODIFY_REPLACE, mdata->keywords);
+ }
+ if (mdata->min_modseq != 0) {
+ mail_index_update_modseq(ctx->trans, ctx->seq,
+ mdata->min_modseq);
+ }
+}
+
+void dbox_save_begin(struct dbox_save_context *ctx, struct istream *input)
+{
+ struct mail_save_context *_ctx = &ctx->ctx;
+ struct mail_storage *_storage = _ctx->transaction->box->storage;
+ struct dbox_storage *storage = DBOX_STORAGE(_storage);
+ struct dbox_message_header dbox_msg_hdr;
+ struct istream *crlf_input;
+
+ dbox_save_add_to_index(ctx);
+
+ mail_set_seq_saving(_ctx->dest_mail, ctx->seq);
+
+ crlf_input = i_stream_create_lf(input);
+ ctx->input = index_mail_cache_parse_init(_ctx->dest_mail, crlf_input);
+ i_stream_unref(&crlf_input);
+
+ /* write a dummy header. it'll get rewritten when we're finished */
+ i_zero(&dbox_msg_hdr);
+ o_stream_cork(ctx->dbox_output);
+ if (o_stream_send(ctx->dbox_output, &dbox_msg_hdr,
+ sizeof(dbox_msg_hdr)) < 0) {
+ mail_set_critical(_ctx->dest_mail, "write(%s) failed: %s",
+ o_stream_get_name(ctx->dbox_output),
+ o_stream_get_error(ctx->dbox_output));
+ ctx->failed = TRUE;
+ }
+ _ctx->data.output = ctx->dbox_output;
+
+ if (_ctx->data.received_date == (time_t)-1)
+ _ctx->data.received_date = ioloop_time;
+ index_attachment_save_begin(_ctx, storage->attachment_fs, ctx->input);
+}
+
+int dbox_save_continue(struct mail_save_context *_ctx)
+{
+ struct dbox_save_context *ctx = DBOX_SAVECTX(_ctx);
+
+ if (ctx->failed)
+ return -1;
+
+ if (_ctx->data.attach != NULL)
+ return index_attachment_save_continue(_ctx);
+
+ if (index_storage_save_continue(_ctx, ctx->input,
+ _ctx->dest_mail) < 0) {
+ ctx->failed = TRUE;
+ return -1;
+ }
+ return 0;
+}
+
+void dbox_save_end(struct dbox_save_context *ctx)
+{
+ struct mail_save_data *mdata = &ctx->ctx.data;
+ struct ostream *dbox_output = ctx->dbox_output;
+ int ret;
+
+ i_assert(mdata->output != NULL);
+
+ if (mdata->attach != NULL && !ctx->failed) {
+ if (index_attachment_save_finish(&ctx->ctx) < 0)
+ ctx->failed = TRUE;
+ }
+ if (mdata->output != dbox_output) {
+ /* e.g. zlib plugin had changed this. make sure we
+ successfully write the trailer. */
+ ret = o_stream_finish(mdata->output);
+ } else {
+ /* no plugins - flush the output so far */
+ ret = o_stream_flush(mdata->output);
+ }
+ if (ret < 0) {
+ mail_set_critical(ctx->ctx.dest_mail,
+ "write(%s) failed: %s",
+ o_stream_get_name(mdata->output),
+ o_stream_get_error(mdata->output));
+ ctx->failed = TRUE;
+ }
+ if (mdata->output != dbox_output) {
+ o_stream_ref(dbox_output);
+ o_stream_destroy(&mdata->output);
+ mdata->output = dbox_output;
+ }
+ index_mail_cache_parse_deinit(ctx->ctx.dest_mail,
+ ctx->ctx.data.received_date,
+ !ctx->failed);
+ if (!ctx->failed)
+ index_mail_cache_pop3_data(ctx->ctx.dest_mail,
+ mdata->pop3_uidl,
+ mdata->pop3_order);
+}
+
+void dbox_save_write_metadata(struct mail_save_context *_ctx,
+ struct ostream *output, uoff_t output_msg_size,
+ const char *orig_mailbox_name,
+ guid_128_t guid_128)
+{
+ struct dbox_save_context *ctx = DBOX_SAVECTX(_ctx);
+ struct mail_save_data *mdata = &ctx->ctx.data;
+ struct dbox_metadata_header metadata_hdr;
+ const char *guid;
+ string_t *str;
+ uoff_t vsize;
+
+ i_zero(&metadata_hdr);
+ memcpy(metadata_hdr.magic_post, DBOX_MAGIC_POST,
+ sizeof(metadata_hdr.magic_post));
+ o_stream_nsend(output, &metadata_hdr, sizeof(metadata_hdr));
+
+ str = t_str_new(256);
+ if (output_msg_size != ctx->input->v_offset) {
+ /* a plugin changed the data written to disk, so the
+ "message size" dbox header doesn't contain the actual
+ "physical" message size. we need to save it as a
+ separate metadata header. */
+ str_printfa(str, "%c%llx\n", DBOX_METADATA_PHYSICAL_SIZE,
+ (unsigned long long)ctx->input->v_offset);
+ }
+ str_printfa(str, "%c%"PRIxTIME_T"\n", DBOX_METADATA_RECEIVED_TIME,
+ mdata->received_date);
+ if (mail_get_virtual_size(_ctx->dest_mail, &vsize) < 0)
+ i_unreached();
+ str_printfa(str, "%c%llx\n", DBOX_METADATA_VIRTUAL_SIZE,
+ (unsigned long long)vsize);
+ if (mdata->pop3_uidl != NULL) {
+ i_assert(strchr(mdata->pop3_uidl, '\n') == NULL);
+ str_printfa(str, "%c%s\n", DBOX_METADATA_POP3_UIDL,
+ mdata->pop3_uidl);
+ ctx->have_pop3_uidls = TRUE;
+ ctx->highest_pop3_uidl_seq =
+ I_MAX(ctx->highest_pop3_uidl_seq, ctx->seq);
+ }
+ if (mdata->pop3_order != 0) {
+ str_printfa(str, "%c%u\n", DBOX_METADATA_POP3_ORDER,
+ mdata->pop3_order);
+ ctx->have_pop3_orders = TRUE;
+ ctx->highest_pop3_uidl_seq =
+ I_MAX(ctx->highest_pop3_uidl_seq, ctx->seq);
+ }
+
+ guid = mdata->guid;
+ if (guid != NULL)
+ mail_generate_guid_128_hash(guid, guid_128);
+ else {
+ guid_128_generate(guid_128);
+ guid = guid_128_to_string(guid_128);
+ }
+ str_printfa(str, "%c%s\n", DBOX_METADATA_GUID, guid);
+
+ if (orig_mailbox_name != NULL &&
+ strchr(orig_mailbox_name, '\r') == NULL &&
+ strchr(orig_mailbox_name, '\n') == NULL) {
+ /* save the original mailbox name so if mailbox indexes get
+ corrupted we can place at least some (hopefully most) of
+ the messages to correct mailboxes. */
+ str_printfa(str, "%c%s\n", DBOX_METADATA_ORIG_MAILBOX,
+ orig_mailbox_name);
+ }
+
+ dbox_attachment_save_write_metadata(_ctx, str);
+
+ str_append_c(str, '\n');
+ o_stream_nsend(output, str_data(str), str_len(str));
+}
+
+void dbox_save_update_header_flags(struct dbox_save_context *ctx,
+ struct mail_index_view *sync_view,
+ uint32_t ext_id,
+ unsigned int flags_offset)
+{
+ const void *data;
+ size_t data_size;
+ uint8_t old_flags = 0, flags;
+
+ mail_index_get_header_ext(sync_view, ext_id, &data, &data_size);
+ if (flags_offset < data_size)
+ old_flags = *((const uint8_t *)data + flags_offset);
+ else {
+ /* grow old dbox header */
+ mail_index_ext_resize_hdr(ctx->trans, ext_id, flags_offset+1);
+ }
+
+ flags = old_flags;
+ if (ctx->have_pop3_uidls)
+ flags |= DBOX_INDEX_HEADER_FLAG_HAVE_POP3_UIDLS;
+ if (ctx->have_pop3_orders)
+ flags |= DBOX_INDEX_HEADER_FLAG_HAVE_POP3_ORDERS;
+ if (flags != old_flags) {
+ /* flags changed, update them */
+ mail_index_update_header_ext(ctx->trans, ext_id,
+ flags_offset, &flags, 1);
+ }
+}
diff --git a/src/lib-storage/index/dbox-common/dbox-save.h b/src/lib-storage/index/dbox-common/dbox-save.h
new file mode 100644
index 0000000..a17c923
--- /dev/null
+++ b/src/lib-storage/index/dbox-common/dbox-save.h
@@ -0,0 +1,41 @@
+#ifndef DBOX_SAVE_H
+#define DBOX_SAVE_H
+
+#include "dbox-storage.h"
+
+struct dbox_save_context {
+ struct mail_save_context ctx;
+ struct mail_index_transaction *trans;
+
+ /* updated for each appended mail: */
+ uint32_t seq;
+ struct istream *input;
+
+ struct ostream *dbox_output;
+
+ uint32_t highest_pop3_uidl_seq;
+ bool failed:1;
+ bool finished:1;
+ bool have_pop3_uidls:1;
+ bool have_pop3_orders:1;
+};
+
+#define DBOX_SAVECTX(s) container_of(s, struct dbox_save_context, ctx)
+
+void dbox_save_begin(struct dbox_save_context *ctx, struct istream *input);
+int dbox_save_continue(struct mail_save_context *_ctx);
+void dbox_save_end(struct dbox_save_context *ctx);
+
+void dbox_save_write_metadata(struct mail_save_context *ctx,
+ struct ostream *output, uoff_t output_msg_size,
+ const char *orig_mailbox_name,
+ guid_128_t guid_128_r) ATTR_NULL(4);
+
+void dbox_save_add_to_index(struct dbox_save_context *ctx);
+
+void dbox_save_update_header_flags(struct dbox_save_context *ctx,
+ struct mail_index_view *sync_view,
+ uint32_t ext_id,
+ unsigned int flags_offset);
+
+#endif
diff --git a/src/lib-storage/index/dbox-common/dbox-storage.c b/src/lib-storage/index/dbox-common/dbox-storage.c
new file mode 100644
index 0000000..5cac0d6
--- /dev/null
+++ b/src/lib-storage/index/dbox-common/dbox-storage.c
@@ -0,0 +1,416 @@
+/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "path-util.h"
+#include "ioloop.h"
+#include "fs-api.h"
+#include "mkdir-parents.h"
+#include "unlink-old-files.h"
+#include "mailbox-uidvalidity.h"
+#include "mailbox-list-private.h"
+#include "index-storage.h"
+#include "dbox-storage.h"
+
+#include <stdio.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <utime.h>
+
+void dbox_storage_get_list_settings(const struct mail_namespace *ns ATTR_UNUSED,
+ struct mailbox_list_settings *set)
+{
+ if (set->layout == NULL)
+ set->layout = MAILBOX_LIST_NAME_FS;
+ if (set->subscription_fname == NULL)
+ set->subscription_fname = DBOX_SUBSCRIPTION_FILE_NAME;
+ if (*set->maildir_name == '\0')
+ set->maildir_name = DBOX_MAILDIR_NAME;
+ if (*set->mailbox_dir_name == '\0')
+ set->mailbox_dir_name = DBOX_MAILBOX_DIR_NAME;
+}
+
+static bool
+dbox_alt_path_has_changed(const char *root_dir, const char *alt_path,
+ const char *alt_path2, const char *alt_symlink_path)
+{
+ const char *linkpath, *error;
+
+ if (t_readlink(alt_symlink_path, &linkpath, &error) < 0) {
+ if (errno == ENOENT)
+ return alt_path != NULL;
+ i_error("t_readlink(%s) failed: %s", alt_symlink_path, error);
+ return FALSE;
+ }
+
+ if (alt_path == NULL) {
+ i_warning("dbox %s: Original ALT=%s, "
+ "but currently no ALT path set", root_dir, linkpath);
+ return TRUE;
+ } else if (strcmp(linkpath, alt_path) != 0) {
+ if (strcmp(linkpath, alt_path2) == 0) {
+ /* FIXME: for backwards compatibility. old versions
+ created the symlink to mailboxes/ directory, which
+ was fine with sdbox, but didn't even exist with
+ mdbox. we'll silently replace the symlink. */
+ return TRUE;
+ }
+ i_warning("dbox %s: Original ALT=%s, "
+ "but currently ALT=%s", root_dir, linkpath, alt_path);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void dbox_verify_alt_path(struct mailbox_list *list)
+{
+ const char *root_dir, *alt_symlink_path, *alt_path, *alt_path2;
+
+ root_dir = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_DIR);
+ alt_symlink_path =
+ t_strconcat(root_dir, "/"DBOX_ALT_SYMLINK_NAME, NULL);
+ (void)mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_ALT_DIR,
+ &alt_path);
+ (void)mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX,
+ &alt_path2);
+ if (!dbox_alt_path_has_changed(root_dir, alt_path, alt_path2,
+ alt_symlink_path))
+ return;
+
+ /* unlink/create the current alt path symlink */
+ i_unlink_if_exists(alt_symlink_path);
+ if (alt_path != NULL) {
+ int ret = symlink(alt_path, alt_symlink_path);
+ if (ret < 0 && errno == ENOENT) {
+ /* root_dir doesn't exist yet - create it */
+ if (mailbox_list_mkdir_root(list, root_dir,
+ MAILBOX_LIST_PATH_TYPE_DIR) < 0)
+ return;
+ ret = symlink(alt_path, alt_symlink_path);
+ }
+ if (ret < 0 && errno != EEXIST) {
+ i_error("symlink(%s, %s) failed: %m",
+ alt_path, alt_symlink_path);
+ }
+ }
+}
+
+int dbox_storage_create(struct mail_storage *_storage,
+ struct mail_namespace *ns,
+ const char **error_r)
+{
+ struct dbox_storage *storage = DBOX_STORAGE(_storage);
+ const struct mail_storage_settings *set = _storage->set;
+ const char *error;
+
+ if (*set->mail_attachment_fs != '\0' &&
+ *set->mail_attachment_dir != '\0') {
+ const char *name, *args, *dir;
+
+ args = strpbrk(set->mail_attachment_fs, ": ");
+ if (args == NULL) {
+ name = set->mail_attachment_fs;
+ args = "";
+ } else {
+ name = t_strdup_until(set->mail_attachment_fs, args++);
+ }
+ if (strcmp(name, "sis-queue") == 0 &&
+ (_storage->class_flags & MAIL_STORAGE_CLASS_FLAG_FILE_PER_MSG) != 0) {
+ /* FIXME: the deduplication part doesn't work, because
+ sdbox renames the files.. */
+ *error_r = "mail_attachment_fs: "
+ "sis-queue not currently supported by sdbox";
+ return -1;
+ }
+ dir = mail_user_home_expand(_storage->user,
+ set->mail_attachment_dir);
+ storage->attachment_dir = p_strdup(_storage->pool, dir);
+
+ if (mailbox_list_init_fs(ns->list, _storage->event, name, args,
+ storage->attachment_dir,
+ &storage->attachment_fs, &error) < 0) {
+ *error_r = t_strdup_printf("mail_attachment_fs: %s",
+ error);
+ return -1;
+ }
+ }
+
+ if (!ns->list->set.alt_dir_nocheck)
+ dbox_verify_alt_path(ns->list);
+ return 0;
+}
+
+void dbox_storage_destroy(struct mail_storage *_storage)
+{
+ struct dbox_storage *storage = DBOX_STORAGE(_storage);
+
+ fs_deinit(&storage->attachment_fs);
+ index_storage_destroy(_storage);
+}
+
+uint32_t dbox_get_uidvalidity_next(struct mailbox_list *list)
+{
+ const char *path;
+
+ path = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_CONTROL);
+ path = t_strconcat(path, "/"DBOX_UIDVALIDITY_FILE_NAME, NULL);
+ return mailbox_uidvalidity_next(list, path);
+}
+
+void dbox_notify_changes(struct mailbox *box)
+{
+ const char *dir, *path;
+
+ if (box->notify_callback == NULL)
+ mailbox_watch_remove_all(box);
+ else {
+ if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_INDEX,
+ &dir) <= 0)
+ return;
+ path = t_strdup_printf("%s/"MAIL_INDEX_PREFIX".log", dir);
+ mailbox_watch_add(box, path);
+ }
+}
+
+static bool
+dbox_cleanup_temp_files(struct mailbox_list *list, const char *path,
+ time_t last_scan_time, time_t last_change_time)
+{
+ unsigned int interval = list->mail_set->mail_temp_scan_interval;
+
+ /* check once in a while if there are temp files to clean up */
+ if (interval == 0) {
+ /* disabled */
+ return FALSE;
+ } else if (last_scan_time >= ioloop_time - (time_t)interval) {
+ /* not the time to scan it yet */
+ return FALSE;
+ } else {
+ bool stated = FALSE;
+ if (last_change_time == (time_t)-1) {
+ /* Don't know the ctime yet - look it up. */
+ struct stat st;
+
+ if (stat(path, &st) < 0) {
+ if (errno == ENOENT)
+ i_error("stat(%s) failed: %m", path);
+ return FALSE;
+ }
+ last_change_time = st.st_ctime;
+ stated = TRUE;
+ }
+ if (last_scan_time > last_change_time + DBOX_TMP_DELETE_SECS) {
+ /* there haven't been any changes to this directory
+ since we last checked it. If we did an extra stat(),
+ we need to update the last_scan_time to avoid
+ stat()ing the next time. */
+ return stated;
+ }
+ const char *prefix =
+ mailbox_list_get_global_temp_prefix(list);
+ (void)unlink_old_files(path, prefix,
+ ioloop_time - DBOX_TMP_DELETE_SECS);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+int dbox_mailbox_check_existence(struct mailbox *box, time_t *path_ctime_r)
+{
+ const char *index_path, *box_path = mailbox_get_path(box);
+ struct stat st;
+ int ret = -1;
+
+ *path_ctime_r = (time_t)-1;
+
+ if (box->list->set.iter_from_index_dir) {
+ /* Just because the index directory exists, it doesn't mean
+ that the mailbox is selectable. Check that by seeing if
+ dovecot.index.log exists. If it doesn't, fallback to
+ checking for the dbox-Mails in the mail root directory.
+ So this also means that if a mailbox is \NoSelect, listing
+ it will always do a stat() for dbox-Mails in the mail root
+ directory. That's not ideal, but this makes the behavior
+ safer and \NoSelect mailboxes are somewhat rare. */
+ if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_INDEX,
+ &index_path) < 0)
+ return -1;
+ i_assert(index_path != NULL);
+ index_path = t_strconcat(index_path, "/", box->index_prefix,
+ ".log", NULL);
+ ret = stat(index_path, &st);
+ }
+ if (ret < 0) {
+ ret = stat(box_path, &st);
+ if (ret == 0)
+ *path_ctime_r = st.st_ctime;
+ }
+
+ if (ret == 0) {
+ return 0;
+ } else if (errno == ENOENT || errno == ENAMETOOLONG) {
+ mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
+ T_MAIL_ERR_MAILBOX_NOT_FOUND(box->vname));
+ return -1;
+ } else if (errno == EACCES) {
+ mailbox_set_critical(box, "%s",
+ mail_error_eacces_msg("stat", box_path));
+ return -1;
+ } else {
+ mailbox_set_critical(box, "stat(%s) failed: %m", box_path);
+ return -1;
+ }
+}
+
+int dbox_mailbox_open(struct mailbox *box, time_t path_ctime)
+{
+ const char *box_path = mailbox_get_path(box);
+
+ if (index_storage_mailbox_open(box, FALSE) < 0)
+ return -1;
+ mail_index_set_fsync_mode(box->index,
+ box->storage->set->parsed_fsync_mode,
+ MAIL_INDEX_FSYNC_MASK_APPENDS |
+ MAIL_INDEX_FSYNC_MASK_EXPUNGES);
+
+ const struct mail_index_header *hdr = mail_index_get_header(box->view);
+ if (dbox_cleanup_temp_files(box->list, box_path,
+ hdr->last_temp_file_scan, path_ctime)) {
+ /* temp files were scanned. update the last scan timestamp. */
+ index_mailbox_update_last_temp_file_scan(box);
+ }
+ return 0;
+}
+
+static int dir_is_empty(struct mail_storage *storage, const char *path)
+{
+ DIR *dir;
+ struct dirent *d;
+ int ret = 1;
+
+ dir = opendir(path);
+ if (dir == NULL) {
+ if (errno == ENOENT) {
+ /* race condition with DELETE/RENAME? */
+ return 1;
+ }
+ mail_storage_set_critical(storage, "opendir(%s) failed: %m",
+ path);
+ return -1;
+ }
+ while ((d = readdir(dir)) != NULL) {
+ if (*d->d_name == '.')
+ continue;
+
+ ret = 0;
+ break;
+ }
+ if (closedir(dir) < 0) {
+ mail_storage_set_critical(storage, "closedir(%s) failed: %m",
+ path);
+ ret = -1;
+ }
+ return ret;
+}
+
+int dbox_mailbox_create(struct mailbox *box,
+ const struct mailbox_update *update, bool directory)
+{
+ struct dbox_storage *storage = DBOX_STORAGE(box->storage);
+ const char *alt_path;
+ struct stat st;
+ int ret;
+
+ if ((ret = index_storage_mailbox_create(box, directory)) <= 0)
+ return ret;
+ if (mailbox_open(box) < 0)
+ return -1;
+ if (mail_index_get_header(box->view)->uid_validity != 0 &&
+ !box->storage->rebuilding_list_index) {
+ mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS,
+ "Mailbox already exists");
+ return -1;
+ }
+
+ /* if alt path already exists and contains files, rebuild storage so
+ that we don't start overwriting files. */
+ ret = mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX, &alt_path);
+ if (ret > 0 && stat(alt_path, &st) == 0) {
+ ret = dir_is_empty(box->storage, alt_path);
+ if (ret < 0)
+ return -1;
+ if (ret == 0) {
+ mailbox_set_critical(box,
+ "Existing files in alt path, "
+ "rebuilding storage to avoid losing messages");
+ storage->v.set_mailbox_corrupted(box);
+ return -1;
+ }
+ /* dir is empty, ignore it */
+ }
+ return dbox_mailbox_create_indexes(box, update);
+}
+
+int dbox_mailbox_create_indexes(struct mailbox *box,
+ const struct mailbox_update *update)
+{
+ struct dbox_storage *storage = DBOX_STORAGE(box->storage);
+ struct mail_index_sync_ctx *sync_ctx;
+ struct mail_index_view *view;
+ struct mail_index_transaction *trans;
+ int ret;
+
+ /* use syncing as a lock */
+ ret = mail_index_sync_begin(box->index, &sync_ctx, &view, &trans, 0);
+ if (ret <= 0) {
+ i_assert(ret != 0);
+ mailbox_set_index_error(box);
+ return -1;
+ }
+
+ if (mail_index_get_header(view)->uid_validity == 0) {
+ if (storage->v.mailbox_create_indexes(box, update, trans) < 0) {
+ mail_index_sync_rollback(&sync_ctx);
+ return -1;
+ }
+ }
+
+ return mail_index_sync_commit(&sync_ctx);
+}
+
+int dbox_verify_alt_storage(struct mailbox_list *list)
+{
+ const char *alt_path;
+ struct stat st;
+
+ if (!mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_ALT_DIR,
+ &alt_path))
+ return 0;
+
+ /* make sure alt storage is mounted. if it's not, abort the rebuild. */
+ if (stat(alt_path, &st) == 0)
+ return 0;
+ if (errno != ENOENT) {
+ i_error("stat(%s) failed: %m", alt_path);
+ return -1;
+ }
+
+ /* try to create the alt directory. if it fails, it means alt
+ storage isn't mounted. */
+ if (mailbox_list_mkdir_root(list, alt_path,
+ MAILBOX_LIST_PATH_TYPE_ALT_DIR) < 0)
+ return -1;
+ return 0;
+}
+
+bool dbox_header_have_flag(struct mailbox *box, uint32_t ext_id,
+ unsigned int flags_offset, uint8_t flag)
+{
+ const void *data;
+ size_t data_size;
+ uint8_t flags = 0;
+
+ mail_index_get_header_ext(box->view, ext_id, &data, &data_size);
+ if (flags_offset < data_size)
+ flags = *((const uint8_t *)data + flags_offset);
+ return (flags & flag) != 0;
+}
diff --git a/src/lib-storage/index/dbox-common/dbox-storage.h b/src/lib-storage/index/dbox-common/dbox-storage.h
new file mode 100644
index 0000000..af085df
--- /dev/null
+++ b/src/lib-storage/index/dbox-common/dbox-storage.h
@@ -0,0 +1,85 @@
+#ifndef DBOX_STORAGE_H
+#define DBOX_STORAGE_H
+
+#include "mail-storage-private.h"
+
+struct dbox_file;
+struct dbox_mail;
+struct dbox_storage;
+struct dbox_save_context;
+
+#define DBOX_SUBSCRIPTION_FILE_NAME "subscriptions"
+#define DBOX_UIDVALIDITY_FILE_NAME "dovecot-uidvalidity"
+#define DBOX_TEMP_FILE_PREFIX ".temp."
+#define DBOX_ALT_SYMLINK_NAME "dbox-alt-root"
+
+#define DBOX_MAILBOX_DIR_NAME "mailboxes"
+#define DBOX_TRASH_DIR_NAME "trash"
+#define DBOX_MAILDIR_NAME "dbox-Mails"
+
+/* Delete temp files having ctime older than this. */
+#define DBOX_TMP_DELETE_SECS (36*60*60)
+
+/* Flag specifies if the message should be in primary or alternative storage */
+#define DBOX_INDEX_FLAG_ALT MAIL_INDEX_MAIL_FLAG_BACKEND
+
+enum dbox_index_header_flags {
+ /* messages' metadata contain POP3 UIDLs */
+ DBOX_INDEX_HEADER_FLAG_HAVE_POP3_UIDLS = 0x01,
+ /* messages' metadata contain POP3 orders */
+ DBOX_INDEX_HEADER_FLAG_HAVE_POP3_ORDERS = 0x02
+};
+
+struct dbox_storage_vfuncs {
+ /* dbox file has zero references now. it should be either freed or
+ left open in case it's accessed again soon */
+ void (*file_unrefed)(struct dbox_file *file);
+ /* create a new file using the same permissions as file.
+ if parents=TRUE, create the directory if necessary */
+ int (*file_create_fd)(struct dbox_file *file, const char *path,
+ bool parents);
+ /* open the mail and return its file/offset */
+ int (*mail_open)(struct dbox_mail *mail, uoff_t *offset_r,
+ struct dbox_file **file_r);
+ /* create/update mailbox indexes */
+ int (*mailbox_create_indexes)(struct mailbox *box,
+ const struct mailbox_update *update,
+ struct mail_index_transaction *trans);
+ /* returns attachment path suffix. mdbox returns "", sdbox returns
+ "-<mailbox_guid>-<uid>" */
+ const char *(*get_attachment_path_suffix)(struct dbox_file *file);
+ /* mark the mailbox corrupted */
+ void (*set_mailbox_corrupted)(struct mailbox *box);
+ /* mark the file corrupted */
+ void (*set_file_corrupted)(struct dbox_file *file);
+};
+
+struct dbox_storage {
+ struct mail_storage storage;
+ struct dbox_storage_vfuncs v;
+
+ struct fs *attachment_fs;
+ const char *attachment_dir;
+};
+
+#define DBOX_STORAGE(s) container_of(s, struct dbox_storage, storage)
+
+void dbox_storage_get_list_settings(const struct mail_namespace *ns,
+ struct mailbox_list_settings *set);
+int dbox_storage_create(struct mail_storage *storage,
+ struct mail_namespace *ns,
+ const char **error_r);
+void dbox_storage_destroy(struct mail_storage *storage);
+uint32_t dbox_get_uidvalidity_next(struct mailbox_list *list);
+void dbox_notify_changes(struct mailbox *box);
+int dbox_mailbox_check_existence(struct mailbox *box, time_t *path_ctime_r);
+int dbox_mailbox_open(struct mailbox *box, time_t path_ctime);
+int dbox_mailbox_create(struct mailbox *box,
+ const struct mailbox_update *update, bool directory);
+int dbox_mailbox_create_indexes(struct mailbox *box,
+ const struct mailbox_update *update);
+int dbox_verify_alt_storage(struct mailbox_list *list);
+bool dbox_header_have_flag(struct mailbox *box, uint32_t ext_id,
+ unsigned int flags_offset, uint8_t flag);
+
+#endif