From f7548d6d28c313cf80e6f3ef89aed16a19815df1 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 11:51:24 +0200 Subject: Adding upstream version 1:2.3.19.1+dfsg1. Signed-off-by: Daniel Baumann --- src/lib-storage/index/dbox-common/Makefile.am | 29 + src/lib-storage/index/dbox-common/Makefile.in | 843 +++++++++++++++++++++ .../index/dbox-common/dbox-attachment.c | 77 ++ .../index/dbox-common/dbox-attachment.h | 16 + src/lib-storage/index/dbox-common/dbox-file-fix.c | 519 +++++++++++++ src/lib-storage/index/dbox-common/dbox-file.c | 796 +++++++++++++++++++ src/lib-storage/index/dbox-common/dbox-file.h | 218 ++++++ src/lib-storage/index/dbox-common/dbox-mail.c | 318 ++++++++ src/lib-storage/index/dbox-common/dbox-mail.h | 34 + src/lib-storage/index/dbox-common/dbox-save.c | 226 ++++++ src/lib-storage/index/dbox-common/dbox-save.h | 41 + src/lib-storage/index/dbox-common/dbox-storage.c | 416 ++++++++++ src/lib-storage/index/dbox-common/dbox-storage.h | 85 +++ 13 files changed, 3618 insertions(+) create mode 100644 src/lib-storage/index/dbox-common/Makefile.am create mode 100644 src/lib-storage/index/dbox-common/Makefile.in create mode 100644 src/lib-storage/index/dbox-common/dbox-attachment.c create mode 100644 src/lib-storage/index/dbox-common/dbox-attachment.h create mode 100644 src/lib-storage/index/dbox-common/dbox-file-fix.c create mode 100644 src/lib-storage/index/dbox-common/dbox-file.c create mode 100644 src/lib-storage/index/dbox-common/dbox-file.h create mode 100644 src/lib-storage/index/dbox-common/dbox-mail.c create mode 100644 src/lib-storage/index/dbox-common/dbox-mail.h create mode 100644 src/lib-storage/index/dbox-common/dbox-save.c create mode 100644 src/lib-storage/index/dbox-common/dbox-save.h create mode 100644 src/lib-storage/index/dbox-common/dbox-storage.c create mode 100644 src/lib-storage/index/dbox-common/dbox-storage.h (limited to 'src/lib-storage/index/dbox-common') 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 + +#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 +#include +#include +#include + +#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: + + + + + + + Metadata block begins with DBOX_MAGIC_POST, followed by zero or more lines + in format . 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*( ) */ + 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]; + /* */ + 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 +#include +#include +#include + +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 + "--" */ + 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 -- cgit v1.2.3