diff options
Diffstat (limited to 'pigeonhole/src/lib-sieve/storage/file')
10 files changed, 4120 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/storage/file/Makefile.am b/pigeonhole/src/lib-sieve/storage/file/Makefile.am new file mode 100644 index 0000000..b401fa8 --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/file/Makefile.am @@ -0,0 +1,19 @@ +noinst_LTLIBRARIES = libsieve_storage_file.la + +AM_CPPFLAGS = \ + $(LIBDOVECOT_INCLUDE) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/src/lib-sieve \ + -I$(top_srcdir)/src/lib-sieve/util + +libsieve_storage_file_la_SOURCES = \ + sieve-file-script.c \ + sieve-file-script-sequence.c \ + sieve-file-storage-active.c \ + sieve-file-storage-save.c \ + sieve-file-storage-list.c \ + sieve-file-storage-quota.c \ + sieve-file-storage.c + +noinst_HEADERS = \ + sieve-file-storage.h diff --git a/pigeonhole/src/lib-sieve/storage/file/Makefile.in b/pigeonhole/src/lib-sieve/storage/file/Makefile.in new file mode 100644 index 0000000..8432831 --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/file/Makefile.in @@ -0,0 +1,714 @@ +# Makefile.in generated by automake 1.16.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2020 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-sieve/storage/file +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/dummy-config.h \ + $(top_builddir)/pigeonhole-config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libsieve_storage_file_la_LIBADD = +am_libsieve_storage_file_la_OBJECTS = sieve-file-script.lo \ + sieve-file-script-sequence.lo sieve-file-storage-active.lo \ + sieve-file-storage-save.lo sieve-file-storage-list.lo \ + sieve-file-storage-quota.lo sieve-file-storage.lo +libsieve_storage_file_la_OBJECTS = \ + $(am_libsieve_storage_file_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)/sieve-file-script-sequence.Plo \ + ./$(DEPDIR)/sieve-file-script.Plo \ + ./$(DEPDIR)/sieve-file-storage-active.Plo \ + ./$(DEPDIR)/sieve-file-storage-list.Plo \ + ./$(DEPDIR)/sieve-file-storage-quota.Plo \ + ./$(DEPDIR)/sieve-file-storage-save.Plo \ + ./$(DEPDIR)/sieve-file-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 = $(libsieve_storage_file_la_SOURCES) +DIST_SOURCES = $(libsieve_storage_file_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(noinst_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@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BINARY_CFLAGS = @BINARY_CFLAGS@ +BINARY_LDFLAGS = @BINARY_LDFLAGS@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@ +DLLTOOL = @DLLTOOL@ +DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@ +DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@ +DOVECOT_CFLAGS = @DOVECOT_CFLAGS@ +DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@ +DOVECOT_INSTALLED = @DOVECOT_INSTALLED@ +DOVECOT_LIBS = @DOVECOT_LIBS@ +DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@ +DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@ +DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@ +DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDAP_LIBS = @LDAP_LIBS@ +LDFLAGS = @LDFLAGS@ +LIBDOVECOT = @LIBDOVECOT@ +LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@ +LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@ +LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ +LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@ +LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@ +LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ +LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@ +LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ +LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@ +LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@ +LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@ +LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@ +LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@ +LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@ +LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@ +LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ +LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@ +LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@ +LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ +LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ +LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@ +LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@ +LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ +LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@ +LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@ +LIBDOVECOT_LUA = @LIBDOVECOT_LUA@ +LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@ +LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@ +LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@ +LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@ +LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@ +LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@ +LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ +LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@ +LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@ +LIBDOVECOT_SSL = @LIBDOVECOT_SSL@ +LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@ +LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ +LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ +LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@ +LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +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@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PIE_CFLAGS = @PIE_CFLAGS@ +PIE_LDFLAGS = @PIE_LDFLAGS@ +RANLIB = @RANLIB@ +RELRO_LDFLAGS = @RELRO_LDFLAGS@ +RUN_TEST = @RUN_TEST@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +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_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@ +docdir = @docdir@ +dovecot_docdir = @dovecot_docdir@ +dovecot_installed_moduledir = @dovecot_installed_moduledir@ +dovecot_moduledir = @dovecot_moduledir@ +dovecot_pkgincludedir = @dovecot_pkgincludedir@ +dovecot_pkglibdir = @dovecot_pkglibdir@ +dovecot_pkglibexecdir = @dovecot_pkglibexecdir@ +dovecot_statedir = @dovecot_statedir@ +dovecotdir = @dovecotdir@ +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@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sieve_docdir = @sieve_docdir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +noinst_LTLIBRARIES = libsieve_storage_file.la +AM_CPPFLAGS = \ + $(LIBDOVECOT_INCLUDE) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/src/lib-sieve \ + -I$(top_srcdir)/src/lib-sieve/util + +libsieve_storage_file_la_SOURCES = \ + sieve-file-script.c \ + sieve-file-script-sequence.c \ + sieve-file-storage-active.c \ + sieve-file-storage-save.c \ + sieve-file-storage-list.c \ + sieve-file-storage-quota.c \ + sieve-file-storage.c + +noinst_HEADERS = \ + sieve-file-storage.h + +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-sieve/storage/file/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/storage/file/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}; \ + } + +libsieve_storage_file.la: $(libsieve_storage_file_la_OBJECTS) $(libsieve_storage_file_la_DEPENDENCIES) $(EXTRA_libsieve_storage_file_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_storage_file_la_OBJECTS) $(libsieve_storage_file_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-file-script-sequence.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-file-script.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-file-storage-active.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-file-storage-list.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-file-storage-quota.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-file-storage-save.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-file-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 + +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: +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)/sieve-file-script-sequence.Plo + -rm -f ./$(DEPDIR)/sieve-file-script.Plo + -rm -f ./$(DEPDIR)/sieve-file-storage-active.Plo + -rm -f ./$(DEPDIR)/sieve-file-storage-list.Plo + -rm -f ./$(DEPDIR)/sieve-file-storage-quota.Plo + -rm -f ./$(DEPDIR)/sieve-file-storage-save.Plo + -rm -f ./$(DEPDIR)/sieve-file-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-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)/sieve-file-script-sequence.Plo + -rm -f ./$(DEPDIR)/sieve-file-script.Plo + -rm -f ./$(DEPDIR)/sieve-file-storage-active.Plo + -rm -f ./$(DEPDIR)/sieve-file-storage-list.Plo + -rm -f ./$(DEPDIR)/sieve-file-storage-quota.Plo + -rm -f ./$(DEPDIR)/sieve-file-storage-save.Plo + -rm -f ./$(DEPDIR)/sieve-file-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: + +.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-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 + +.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/pigeonhole/src/lib-sieve/storage/file/sieve-file-script-sequence.c b/pigeonhole/src/lib-sieve/storage/file/sieve-file-script-sequence.c new file mode 100644 index 0000000..a1ae75d --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/file/sieve-file-script-sequence.c @@ -0,0 +1,244 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "array.h" +#include "eacces-error.h" + +#include "sieve-common.h" +#include "sieve-script-private.h" + +#include "sieve-file-storage.h" + +#include <stdio.h> +#include <dirent.h> + +/* + * Script sequence + */ + +struct sieve_file_script_sequence { + struct sieve_script_sequence seq; + pool_t pool; + + ARRAY_TYPE(const_string) script_files; + unsigned int index; + + bool storage_is_file:1; +}; + +static int sieve_file_script_sequence_read_dir +(struct sieve_file_script_sequence *fseq, const char *path) +{ + struct sieve_storage *storage = fseq->seq.storage; + DIR *dirp; + int ret = 0; + + /* Open the directory */ + if ( (dirp = opendir(path)) == NULL ) { + switch ( errno ) { + case ENOENT: + sieve_storage_set_error(storage, + SIEVE_ERROR_NOT_FOUND, + "Script sequence location not found"); + break; + case EACCES: + sieve_storage_set_error(storage, + SIEVE_ERROR_NO_PERMISSION, + "Script sequence location not accessible"); + e_error(storage->event, + "Failed to open sieve sequence: %s", + eacces_error_get("stat", path)); + break; + default: + sieve_storage_set_critical(storage, + "Failed to open sieve sequence: " + "opendir(%s) failed: %m", path); + break; + } + return -1; + } + + /* Read and sort script files */ + for (;;) { + const char *const *files; + unsigned int count, i; + const char *file; + struct dirent *dp; + struct stat st; + + errno = 0; + if ( (dp=readdir(dirp)) == NULL ) + break; + + if ( !sieve_script_file_has_extension(dp->d_name) ) + continue; + + file = NULL; + T_BEGIN { + if ( path[strlen(path)-1] == '/' ) + file = t_strconcat(path, dp->d_name, NULL); + else + file = t_strconcat(path, "/", dp->d_name, NULL); + + if ( stat(file, &st) == 0 && S_ISREG(st.st_mode) ) + file = p_strdup(fseq->pool, dp->d_name); + else + file = NULL; + } T_END; + + if (file == NULL) + continue; + + /* Insert into sorted array */ + files = array_get(&fseq->script_files, &count); + for ( i = 0; i < count; i++ ) { + if ( strcmp(file, files[i]) < 0 ) + break; + } + + if ( i == count ) + array_append(&fseq->script_files, &file, 1); + else + array_insert(&fseq->script_files, i, &file, 1); + } + + if ( errno != 0 ) { + sieve_storage_set_critical(storage, + "Failed to read sequence directory: " + "readdir(%s) failed: %m", path); + ret = -1; + } + + /* Close the directory */ + if ( dirp != NULL && closedir(dirp) < 0 ) { + e_error(storage->event, + "Failed to close sequence directory: " + "closedir(%s) failed: %m", path); + } + return ret; +} + +struct sieve_script_sequence *sieve_file_storage_get_script_sequence +(struct sieve_storage *storage, enum sieve_error *error_r) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + struct sieve_file_script_sequence *fseq = NULL; + const char *name = storage->script_name; + const char *file; + pool_t pool; + struct stat st; + + /* Specified path can either be a regular file or a directory */ + if ( stat(fstorage->path, &st) != 0 ) { + switch ( errno ) { + case ENOENT: + sieve_storage_set_error(storage, + SIEVE_ERROR_NOT_FOUND, + "Script sequence location not found"); + break; + case EACCES: + sieve_storage_set_error(storage, + SIEVE_ERROR_NO_PERMISSION, + "Script sequence location not accessible"); + e_error(storage->event, + "Failed to open sieve sequence: %s", + eacces_error_get("stat", fstorage->path)); + break; + default: + sieve_storage_set_critical(storage, + "Failed to open sieve sequence: " + "stat(%s) failed: %m", fstorage->path); + break; + } + *error_r = storage->error_code; + return NULL; + } + + /* Create sequence object */ + pool = pool_alloconly_create("sieve_file_script_sequence", 1024); + fseq = p_new(pool, struct sieve_file_script_sequence, 1); + fseq->pool = pool; + sieve_script_sequence_init(&fseq->seq, storage); + + if ( S_ISDIR(st.st_mode) ) { + i_array_init(&fseq->script_files, 16); + + /* Path is directory */ + if (name == 0 || *name == '\0') { + /* Read all '.sieve' files in directory */ + if (sieve_file_script_sequence_read_dir + (fseq, fstorage->path) < 0) { + *error_r = storage->error_code; + sieve_file_script_sequence_destroy(&fseq->seq); + return NULL; + } + + } else { + /* Read specific script file */ + file = sieve_script_file_from_name(name); + file = p_strdup(pool, file); + array_append(&fseq->script_files, &file, 1); + } + + } else { + /* Path is a file + (apparently; we'll see about that once it is opened) */ + fseq->storage_is_file = TRUE; + } + + return &fseq->seq; +} + +struct sieve_script *sieve_file_script_sequence_next +(struct sieve_script_sequence *seq, enum sieve_error *error_r) +{ + struct sieve_file_script_sequence *fseq = + (struct sieve_file_script_sequence *)seq; + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)seq->storage; + struct sieve_file_script *fscript; + const char *const *files; + unsigned int count; + + if ( error_r != NULL ) + *error_r = SIEVE_ERROR_NONE; + + fscript = NULL; + if ( fseq->storage_is_file ) { + if ( fseq->index++ < 1 ) + fscript = sieve_file_script_open_from_name(fstorage, NULL); + + } else { + files = array_get(&fseq->script_files, &count); + + while ( fseq->index < count ) { + fscript = sieve_file_script_open_from_filename + (fstorage, files[fseq->index++], NULL); + if (fscript != NULL) + break; + if (seq->storage->error_code != SIEVE_ERROR_NOT_FOUND) + break; + sieve_storage_clear_error(seq->storage); + } + } + + if (fscript == NULL ) { + if ( error_r != NULL ) + *error_r = seq->storage->error_code; + return NULL; + } + return &fscript->script; +} + +void sieve_file_script_sequence_destroy(struct sieve_script_sequence *seq) +{ + struct sieve_file_script_sequence *fseq = + (struct sieve_file_script_sequence *)seq; + + if ( array_is_created(&fseq->script_files) ) + array_free(&fseq->script_files); + pool_unref(&fseq->pool); +} diff --git a/pigeonhole/src/lib-sieve/storage/file/sieve-file-script.c b/pigeonhole/src/lib-sieve/storage/file/sieve-file-script.c new file mode 100644 index 0000000..3c4ec60 --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/file/sieve-file-script.c @@ -0,0 +1,832 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "mempool.h" +#include "path-util.h" +#include "istream.h" +#include "time-util.h" +#include "eacces-error.h" + +#include "sieve-binary.h" +#include "sieve-script-private.h" + +#include "sieve-file-storage.h" + +#include <stdio.h> +#include <unistd.h> +#include <ctype.h> +#include <time.h> +#include <fcntl.h> + +/* + * Filename to name/name to filename + */ + +const char *sieve_script_file_get_scriptname(const char *filename) +{ + const char *ext; + + /* Extract the script name */ + ext = strrchr(filename, '.'); + if ( ext == NULL || ext == filename || + strcmp(ext, "."SIEVE_SCRIPT_FILEEXT) != 0 ) + return NULL; + + return t_strdup_until(filename, ext); +} + +bool sieve_script_file_has_extension(const char *filename) +{ + return ( sieve_script_file_get_scriptname(filename) != NULL ); +} + +const char *sieve_script_file_from_name(const char *name) +{ + return t_strconcat(name, "."SIEVE_SCRIPT_FILEEXT, NULL); +} + +/* + * Common error handling + */ + +static void sieve_file_script_handle_error +(struct sieve_file_script *fscript, const char *op, const char *path, + const char *name, enum sieve_error *error_r) +{ + struct sieve_script *script = &fscript->script; + const char *abspath, *error; + + switch ( errno ) { + case ENOENT: + if (t_abspath(path, &abspath, &error) < 0) { + sieve_script_set_error(script, + SIEVE_ERROR_TEMP_FAILURE, + "t_abspath(%s) failed: %s", + path, error); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + break; + } + e_debug(script->event, "File `%s' not found", abspath); + sieve_script_set_error(script, + SIEVE_ERROR_NOT_FOUND, + "Sieve script `%s' not found", name); + *error_r = SIEVE_ERROR_NOT_FOUND; + break; + case EACCES: + sieve_script_set_critical(script, + "Failed to %s sieve script: %s", + op, eacces_error_get(op, path)); + *error_r = SIEVE_ERROR_NO_PERMISSION; + break; + default: + sieve_script_set_critical(script, + "Failed to %s sieve script: %s(%s) failed: %m", + op, op, path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + break; + } +} + +/* + * + */ + +static struct sieve_file_script *sieve_file_script_alloc(void) +{ + struct sieve_file_script *fscript; + pool_t pool; + + pool = pool_alloconly_create("sieve_file_script", 2048); + fscript = p_new(pool, struct sieve_file_script, 1); + fscript->script = sieve_file_script; + fscript->script.pool = pool; + + return fscript; +} + +struct sieve_file_script *sieve_file_script_init_from_filename +(struct sieve_file_storage *fstorage, const char *filename, + const char *scriptname) +{ + struct sieve_storage *storage = &fstorage->storage; + struct sieve_file_script *fscript = NULL; + + /* Prevent initializing the active script link as a script when it + * resides in the sieve storage directory. + */ + if ( scriptname != NULL && fstorage->link_path != NULL && + *(fstorage->link_path) == '\0' ) { + if ( strcmp(filename, fstorage->active_fname) == 0 ) { + sieve_storage_set_error(storage, + SIEVE_ERROR_NOT_FOUND, + "Script `%s' does not exist.", scriptname); + return NULL; + } + } + + fscript = sieve_file_script_alloc(); + sieve_script_init + (&fscript->script, storage, &sieve_file_script, + sieve_file_storage_path_extend(fstorage, filename), scriptname); + fscript->filename = p_strdup(fscript->script.pool, filename); + return fscript; +} + +struct sieve_file_script *sieve_file_script_open_from_filename +(struct sieve_file_storage *fstorage, const char *filename, + const char *scriptname) +{ + struct sieve_file_script *fscript; + enum sieve_error error; + + fscript = sieve_file_script_init_from_filename + (fstorage, filename, scriptname); + if ( fscript == NULL ) + return NULL; + + if ( sieve_script_open(&fscript->script, &error) < 0 ) { + struct sieve_script *script = &fscript->script; + sieve_script_unref(&script); + return NULL; + } + + return fscript; +} + +struct sieve_file_script *sieve_file_script_init_from_name +(struct sieve_file_storage *fstorage, const char *name) +{ + struct sieve_storage *storage = &fstorage->storage; + struct sieve_file_script *fscript; + + if (name != NULL && S_ISDIR(fstorage->st.st_mode)) { + return sieve_file_script_init_from_filename + (fstorage, sieve_script_file_from_name(name), name); + } + + fscript = sieve_file_script_alloc(); + sieve_script_init + (&fscript->script, storage, &sieve_file_script, + fstorage->active_path, name); + return fscript; +} + +struct sieve_file_script *sieve_file_script_open_from_name +(struct sieve_file_storage *fstorage, const char *name) +{ + struct sieve_file_script *fscript; + enum sieve_error error; + + fscript = sieve_file_script_init_from_name(fstorage, name); + if ( fscript == NULL ) + return NULL; + + if ( sieve_script_open(&fscript->script, &error) < 0 ) { + struct sieve_script *script = &fscript->script; + sieve_script_unref(&script); + return NULL; + } + + return fscript; +} + +struct sieve_file_script *sieve_file_script_init_from_path +(struct sieve_file_storage *fstorage, const char *path, + const char *scriptname, enum sieve_error *error_r) +{ + struct sieve_instance *svinst = fstorage->storage.svinst; + struct sieve_file_storage *fsubstorage; + struct sieve_file_script *fscript; + struct sieve_storage *substorage; + enum sieve_error error; + + if ( error_r != NULL ) + *error_r = SIEVE_ERROR_NONE; + else + error_r = &error; + + fsubstorage = sieve_file_storage_init_from_path + (svinst, path, 0, error_r); + if (fsubstorage == NULL) + return NULL; + substorage = &fsubstorage->storage; + + fscript = sieve_file_script_alloc(); + sieve_script_init(&fscript->script, + substorage, &sieve_file_script, path, scriptname); + sieve_storage_unref(&substorage); + + return fscript; +} + +struct sieve_file_script *sieve_file_script_open_from_path +(struct sieve_file_storage *fstorage, const char *path, + const char *scriptname, enum sieve_error *error_r) +{ + struct sieve_storage *storage = &fstorage->storage; + struct sieve_file_script *fscript; + enum sieve_error error; + + if ( error_r != NULL ) + *error_r = SIEVE_ERROR_NONE; + else + error_r = &error; + + fscript = sieve_file_script_init_from_path + (fstorage, path, scriptname, error_r); + if (fscript == NULL) { + sieve_storage_set_error(storage, + *error_r, "Failed to open script"); + return NULL; + } + + if ( sieve_script_open(&fscript->script, error_r) < 0 ) { + struct sieve_script *script = &fscript->script; + const char *errormsg; + + errormsg = sieve_script_get_last_error(&fscript->script, error_r); + sieve_storage_set_error(storage, + *error_r, "%s", errormsg); + sieve_script_unref(&script); + return NULL; + } + + return fscript; +} + +/* + * Open + */ + +static int sieve_file_script_stat +(const char *path, struct stat *st, struct stat *lnk_st) +{ + if ( lstat(path, st) < 0 ) + return -1; + + *lnk_st = *st; + + if ( S_ISLNK(st->st_mode) && stat(path, st) < 0 ) + return -1; + + return 0; +} + +static const char * +path_split_filename(const char *path, const char **dirpath_r) +{ + const char *filename; + + filename = strrchr(path, '/'); + if ( filename == NULL ) { + *dirpath_r = ""; + filename = path; + } else { + *dirpath_r = t_strdup_until(path, filename); + filename++; + } + return filename; +} + +static int sieve_file_script_open +(struct sieve_script *script, enum sieve_error *error_r) +{ + struct sieve_file_script *fscript = + (struct sieve_file_script *)script; + struct sieve_storage *storage = script->storage; + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + pool_t pool = script->pool; + const char *filename, *name, *path; + const char *dirpath, *basename, *binpath, *binprefix; + struct stat st, lnk_st; + bool success = TRUE; + int ret = 0; + + filename = fscript->filename; + basename = NULL; + name = script->name; + st = fstorage->st; + lnk_st = fstorage->lnk_st; + + if (name == NULL) + name = storage->script_name; + + T_BEGIN { + if ( S_ISDIR(st.st_mode) ) { + /* Storage is a directory */ + path = fstorage->path; + + if ( (filename == NULL || *filename == '\0') && + name != NULL && *name != '\0' ) { + /* Name is used to find actual filename */ + filename = sieve_script_file_from_name(name); + basename = name; + } + if ( filename == NULL || *filename == '\0' ) { + sieve_script_set_critical(script, + "Sieve script file path '%s' is a directory.", path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + success = FALSE; + } else { + /* Extend storage path with filename */ + if (name == NULL) { + if ( basename == NULL && + (basename=sieve_script_file_get_scriptname(filename)) == NULL ) + basename = filename; + name = basename; + } else if (basename == NULL) { + basename = name; + } + dirpath = path; + + path = sieve_file_storage_path_extend(fstorage, filename); + ret = sieve_file_script_stat(path, &st, &lnk_st); + } + + } else { + /* Storage is a single file */ + path = fstorage->active_path; + + /* Extract filename from path */ + filename = path_split_filename(path, &dirpath); + + if ( (basename=sieve_script_file_get_scriptname(filename)) == NULL ) + basename = filename; + + if ( name == NULL ) + name = basename; + } + + if ( success ) { + if ( ret < 0 ) { + /* Make sure we have a script name for the error */ + if ( name == NULL ) { + i_assert( basename != NULL ); + name = basename; + } + sieve_file_script_handle_error + (fscript, "stat", path, name, error_r); + success = FALSE; + + } else if ( !S_ISREG(st.st_mode) ) { + sieve_script_set_critical(script, + "Sieve script file '%s' is not a regular file.", path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + success = FALSE; + } + } + + if ( success ) { + const char *bpath, *bfile, *bprefix; + + if ( storage->bin_dir != NULL ) { + bpath = storage->bin_dir; + bfile = sieve_binfile_from_name(name); + bprefix = name; + + } else { + bpath = dirpath; + bfile = sieve_binfile_from_name(basename); + bprefix = basename; + } + + if ( *bpath == '\0' ) { + binpath = bfile; + binprefix = bprefix; + } else if ( bpath[strlen(bpath)-1] == '/' ) { + binpath = t_strconcat(bpath, bfile, NULL); + binprefix = t_strconcat(bpath, bprefix, NULL); + } else { + binpath = t_strconcat(bpath, "/", bfile, NULL); + binprefix = t_strconcat(bpath, "/", bprefix, NULL); + } + + fscript->st = st; + fscript->lnk_st = lnk_st; + fscript->path = p_strdup(pool, path); + fscript->filename = p_strdup(pool, filename); + fscript->dirpath = p_strdup(pool, dirpath); + fscript->binpath = p_strdup(pool, binpath); + fscript->binprefix = p_strdup(pool, binprefix); + + fscript->script.location = fscript->path; + + if ( fscript->script.name == NULL ) + fscript->script.name = p_strdup(pool, basename); + } + } T_END; + + return ( success ? 0 : -1 ); +} + +static int sieve_file_script_get_stream +(struct sieve_script *script, struct istream **stream_r, + enum sieve_error *error_r) +{ + struct sieve_file_script *fscript = (struct sieve_file_script *)script; + struct stat st; + struct istream *result; + int fd; + + if ( (fd=open(fscript->path, O_RDONLY)) < 0 ) { + sieve_file_script_handle_error + (fscript, "open", fscript->path, fscript->script.name, error_r); + return -1; + } + + if ( fstat(fd, &st) != 0 ) { + sieve_script_set_critical(script, + "Failed to open sieve script: fstat(fd=%s) failed: %m", + fscript->path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + result = NULL; + } else { + /* Re-check the file type just to be sure */ + if ( !S_ISREG(st.st_mode) ) { + sieve_script_set_critical(script, + "Sieve script file `%s' is not a regular file", fscript->path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + result = NULL; + } else { + result = i_stream_create_fd_autoclose(&fd, SIEVE_FILE_READ_BLOCK_SIZE); + fscript->st = fscript->lnk_st = st; + } + } + + if ( result == NULL ) { + /* Something went wrong, close the fd */ + if ( fd >= 0 && close(fd) != 0 ) { + e_error(script->event, + "Failed to close sieve script: " + "close(fd=%s) failed: %m", fscript->path); + } + return -1; + } + + *stream_r = result; + return 0; +} + +/* + * Binary + */ + +static int sieve_file_script_binary_read_metadata +(struct sieve_script *script, struct sieve_binary_block *sblock, + sieve_size_t *offset ATTR_UNUSED) +{ + struct sieve_file_script *fscript = (struct sieve_file_script *)script; + struct sieve_instance *svinst = script->storage->svinst; + struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock); + const struct stat *sstat, *bstat; + + bstat = sieve_binary_stat(sbin); + if ( fscript->st.st_mtime > fscript->lnk_st.st_mtime || + (fscript->st.st_mtime == fscript->lnk_st.st_mtime && + ST_MTIME_NSEC(fscript->st) >= ST_MTIME_NSEC(fscript->lnk_st)) ) { + sstat = &fscript->st; + } else { + sstat = &fscript->lnk_st; + } + + if ( bstat->st_mtime < sstat->st_mtime || + (bstat->st_mtime == sstat->st_mtime && + ST_MTIME_NSEC(*bstat) <= ST_MTIME_NSEC(*sstat)) ) { + if ( svinst->debug ) { + e_debug(script->event, + "Sieve binary `%s' is not newer " + "than the Sieve script `%s' (%s.%lu <= %s.%lu)", + sieve_binary_path(sbin), sieve_script_location(script), + t_strflocaltime("%Y-%m-%d %H:%M:%S", bstat->st_mtime), + ST_MTIME_NSEC(*bstat), + t_strflocaltime("%Y-%m-%d %H:%M:%S", sstat->st_mtime), + ST_MTIME_NSEC(*sstat)); + } + return 0; + } + + return 1; +} + +static struct sieve_binary *sieve_file_script_binary_load +(struct sieve_script *script, enum sieve_error *error_r) +{ + struct sieve_file_script *fscript = (struct sieve_file_script *)script; + struct sieve_instance *svinst = script->storage->svinst; + + return sieve_binary_open(svinst, fscript->binpath, script, error_r); +} + +static int sieve_file_script_binary_save +(struct sieve_script *script, struct sieve_binary *sbin, bool update, + enum sieve_error *error_r) +{ + struct sieve_storage *storage = script->storage; + struct sieve_file_script *fscript = (struct sieve_file_script *)script; + + if ( storage->bin_dir != NULL && + sieve_storage_setup_bindir(storage, 0700) < 0 ) + return -1; + + return sieve_binary_save(sbin, fscript->binpath, update, + fscript->st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO), error_r); +} + +static const char *sieve_file_script_binary_get_prefix +(struct sieve_script *script) +{ + struct sieve_file_script *fscript = (struct sieve_file_script *)script; + + return fscript->binprefix; +} + +/* + * Management + */ + +static int sieve_file_storage_script_is_active(struct sieve_script *script) +{ + struct sieve_file_script *fscript = + (struct sieve_file_script *) script; + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)script->storage; + const char *afile; + int ret = 0; + + T_BEGIN { + ret = sieve_file_storage_active_script_get_file(fstorage, &afile); + + if ( ret > 0 ) { + /* Is the requested script active? */ + ret = ( strcmp(fscript->filename, afile) == 0 ? 1 : 0 ); + } + } T_END; + + return ret; +} + +static int sieve_file_storage_script_delete(struct sieve_script *script) +{ + struct sieve_file_script *fscript = + (struct sieve_file_script *)script; + int ret = 0; + + if ( sieve_file_storage_pre_modify(script->storage) < 0 ) + return -1; + + ret = unlink(fscript->path); + if ( ret < 0 ) { + if ( errno == ENOENT ) { + sieve_script_set_error(script, + SIEVE_ERROR_NOT_FOUND, + "Sieve script does not exist."); + } else { + sieve_script_set_critical(script, + "Performing unlink() failed on sieve file `%s': %m", + fscript->path); + } + } + return ret; +} + +static int _sieve_file_storage_script_activate +(struct sieve_file_script *fscript) +{ + struct sieve_script *script = &fscript->script; + struct sieve_storage *storage = script->storage; + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + struct stat st; + const char *link_path, *afile; + int activated = 0; + int ret; + + /* Find out whether there is an active script, but recreate + * the symlink either way. This way, any possible error in the symlink + * resolves automatically. This step is only necessary to provide a + * proper return value indicating whether the script was already active. + */ + ret = sieve_file_storage_active_script_get_file(fstorage, &afile); + + /* Is the requested script already active? */ + if ( ret <= 0 || strcmp(fscript->filename, afile) != 0 ) + activated = 1; + + i_assert( fstorage->link_path != NULL ); + + /* Check the scriptfile we are trying to activate */ + if ( lstat(fscript->path, &st) != 0 ) { + sieve_script_set_critical(script, + "Failed to activate Sieve script: lstat(%s) failed: %m.", + fscript->path); + return -1; + } + + /* Rescue a possible .dovecot.sieve regular file remaining from old + * installations. + */ + if ( !sieve_file_storage_active_rescue_regular(fstorage) ) { + /* Rescue failed, manual intervention is necessary */ + return -1; + } + + /* Just try to create the symlink first */ + link_path = t_strconcat + ( fstorage->link_path, fscript->filename, NULL ); + + ret = symlink(link_path, fstorage->active_path); + if ( ret < 0 ) { + if ( errno == EEXIST ) { + ret = sieve_file_storage_active_replace_link(fstorage, link_path); + if ( ret < 0 ) { + return ret; + } + } else { + /* Other error, critical */ + sieve_script_set_critical(script, + "Failed to activate Sieve script: " + "symlink(%s, %s) failed: %m", + link_path, fstorage->active_path); + return -1; + } + } + return activated; +} + +static int sieve_file_storage_script_activate +(struct sieve_script *script) +{ + struct sieve_file_script *fscript = + (struct sieve_file_script *)script; + int ret; + + if ( sieve_file_storage_pre_modify(script->storage) < 0 ) + return -1; + + T_BEGIN { + ret = _sieve_file_storage_script_activate(fscript); + } T_END; + + return ret; +} + +static int sieve_file_storage_script_rename +(struct sieve_script *script, const char *newname) +{ + struct sieve_file_script *fscript = + (struct sieve_file_script *)script; + struct sieve_storage *storage = script->storage; + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + const char *newpath, *newfile, *link_path; + int ret = 0; + + if ( sieve_file_storage_pre_modify(storage) < 0 ) + return -1; + + T_BEGIN { + newfile = sieve_script_file_from_name(newname); + newpath = t_strconcat( fstorage->path, "/", newfile, NULL ); + + /* The normal rename() system call overwrites the existing file without + * notice. Also, active scripts must not be disrupted by renaming a script. + * That is why we use a link(newpath) [activate newpath] unlink(oldpath) + */ + + /* Link to the new path */ + ret = link(fscript->path, newpath); + if ( ret >= 0 ) { + /* Is the requested script active? */ + if ( sieve_script_is_active(script) > 0 ) { + /* Active; make active link point to the new copy */ + i_assert( fstorage->link_path != NULL ); + link_path = t_strconcat + ( fstorage->link_path, newfile, NULL ); + + ret = sieve_file_storage_active_replace_link(fstorage, link_path); + } + + if ( ret >= 0 ) { + /* If all is good, remove the old link */ + if ( unlink(fscript->path) < 0 ) { + e_error(script->event, + "Failed to clean up after rename: " + "unlink(%s) failed: %m", fscript->path); + } + + if ( script->name != NULL && *script->name != '\0' ) + script->name = p_strdup(script->pool, newname); + fscript->path = p_strdup(script->pool, newpath); + fscript->filename = p_strdup(script->pool, newfile); + } else { + /* If something went wrong, remove the new link to restore previous + * state + */ + if ( unlink(newpath) < 0 ) { + e_error(script->event, + "Failed to clean up after failed rename: " + "unlink(%s) failed: %m", newpath); + } + } + } else { + /* Our efforts failed right away */ + switch ( errno ) { + case ENOENT: + sieve_script_set_error(script, SIEVE_ERROR_NOT_FOUND, + "Sieve script does not exist."); + break; + case EEXIST: + sieve_script_set_error(script, SIEVE_ERROR_EXISTS, + "A sieve script with that name already exists."); + break; + default: + sieve_script_set_critical(script, + "Failed to rename Sieve script: " + "link(%s, %s) failed: %m", fscript->path, newpath); + } + } + } T_END; + + return ret; +} + +/* + * Properties + */ + +static int sieve_file_script_get_size +(const struct sieve_script *script, uoff_t *size_r) +{ + struct sieve_file_script *fscript = (struct sieve_file_script *)script; + + *size_r = fscript->st.st_size; + return 1; +} + +const char *sieve_file_script_get_dirpath +(const struct sieve_script *script) +{ + struct sieve_file_script *fscript = (struct sieve_file_script *)script; + + if ( script->driver_name != sieve_file_script.driver_name ) + return NULL; + + return fscript->dirpath; +} + +const char *sieve_file_script_get_path +(const struct sieve_script *script) +{ + struct sieve_file_script *fscript = (struct sieve_file_script *)script; + + if ( script->driver_name != sieve_file_script.driver_name ) + return NULL; + + return fscript->path; +} + +/* + * Matching + */ + +static bool sieve_file_script_equals +(const struct sieve_script *script, const struct sieve_script *other) +{ + struct sieve_file_script *fscript = + (struct sieve_file_script *)script; + struct sieve_file_script *fother = + (struct sieve_file_script *)other; + + return ( CMP_DEV_T(fscript->st.st_dev, fother->st.st_dev) && + fscript->st.st_ino == fother->st.st_ino ); +} + +/* + * Driver definition + */ + +const struct sieve_script sieve_file_script = { + .driver_name = SIEVE_FILE_STORAGE_DRIVER_NAME, + .v = { + .open = sieve_file_script_open, + + .get_stream = sieve_file_script_get_stream, + + .binary_read_metadata = sieve_file_script_binary_read_metadata, + .binary_load = sieve_file_script_binary_load, + .binary_save = sieve_file_script_binary_save, + .binary_get_prefix = sieve_file_script_binary_get_prefix, + + .rename = sieve_file_storage_script_rename, + .delete = sieve_file_storage_script_delete, + .is_active = sieve_file_storage_script_is_active, + .activate = sieve_file_storage_script_activate, + + .get_size = sieve_file_script_get_size, + + .equals = sieve_file_script_equals + } +}; + diff --git a/pigeonhole/src/lib-sieve/storage/file/sieve-file-storage-active.c b/pigeonhole/src/lib-sieve/storage/file/sieve-file-storage-active.c new file mode 100644 index 0000000..28bc9eb --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/file/sieve-file-storage-active.c @@ -0,0 +1,403 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "path-util.h" +#include "ioloop.h" +#include "hostpid.h" +#include "file-copy.h" +#include "time-util.h" + +#include "sieve-file-storage.h" + +#include <unistd.h> + +/* + * Symlink manipulation + */ + +static int sieve_file_storage_active_read_link +(struct sieve_file_storage *fstorage, const char **link_r) +{ + struct sieve_storage *storage = &fstorage->storage; + const char *error = NULL; + int ret; + + ret = t_readlink(fstorage->active_path, link_r, &error); + + if ( ret < 0 ) { + *link_r = NULL; + + if ( errno == EINVAL ) { + /* Our symlink is no symlink. Report 'no active script'. + * Activating a script will automatically resolve this, so + * there is no need to panic on this one. + */ + if ( (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 && + (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 ) { + e_warning(storage->event, + "Active sieve script symlink %s is no symlink.", + fstorage->active_path); + } + return 0; + } + + if ( errno == ENOENT ) { + /* Symlink not found */ + return 0; + } + + /* We do need to panic otherwise */ + sieve_storage_set_critical(storage, + "Performing t_readlink() on active sieve symlink '%s' failed: %s", + fstorage->active_path, error); + return -1; + } + + /* ret is now assured to be valid, i.e. > 0 */ + return 1; +} + +static const char *sieve_file_storage_active_parse_link +(struct sieve_file_storage *fstorage, const char *link, + const char **scriptname_r) +{ + struct sieve_storage *storage = &fstorage->storage; + const char *fname, *scriptname, *scriptpath, *link_dir; + + /* Split off directory from link path */ + fname = strrchr(fstorage->active_path, '/'); + if (fname == NULL) + link_dir = ""; + else + link_dir = t_strdup_until(fstorage->active_path, fname+1); + + /* Split link into path and filename */ + fname = strrchr(link, '/'); + if ( fname != NULL ) { + scriptpath = t_strdup_until(link, fname+1); + fname++; + } else { + scriptpath = ""; + fname = link; + } + + /* Check the script name */ + scriptname = sieve_script_file_get_scriptname(fname); + + /* Warn if link is deemed to be invalid */ + if ( scriptname == NULL ) { + e_warning(storage->event, + "Active Sieve script symlink %s is broken: " + "Invalid scriptname (points to %s).", + fstorage->active_path, link); + return NULL; + } + + /* Check whether the path is any good */ + const char *error = NULL; + if ( t_normpath_to(scriptpath, link_dir, &scriptpath, &error) < 0 ) { + e_warning(storage->event, + "Failed to check active Sieve script symlink %s: " + "Failed to normalize path (points to %s): %s", + fstorage->active_path, scriptpath, error); + return NULL; + } + if ( strcmp(scriptpath, fstorage->path) != 0 ) { + e_warning(storage->event, + "Active sieve script symlink %s is broken: " + "Invalid/unknown path to storage (points to %s).", + fstorage->active_path, scriptpath); + return NULL; + } + + if ( scriptname_r != NULL ) + *scriptname_r = scriptname; + + return fname; +} + +int sieve_file_storage_active_replace_link +(struct sieve_file_storage *fstorage, const char *link_path) +{ + struct sieve_storage *storage = &fstorage->storage; + const char *active_path_new; + struct timeval *tv, tv_now; + int ret = 0; + + tv = &ioloop_timeval; + + for (;;) { + /* First the new symlink is created with a different filename */ + active_path_new = t_strdup_printf + ("%s-new.%s.P%sM%s.%s", + fstorage->active_path, + dec2str(tv->tv_sec), my_pid, + dec2str(tv->tv_usec), my_hostname); + + ret = symlink(link_path, active_path_new); + + if ( ret < 0 ) { + /* If link exists we try again later */ + if ( errno == EEXIST ) { + /* Wait and try again - very unlikely */ + sleep(2); + tv = &tv_now; + i_gettimeofday(&tv_now); + continue; + } + + /* Other error, critical */ + sieve_storage_set_critical(storage, + "Creating symlink() %s to %s failed: %m", + active_path_new, link_path); + return -1; + } + + /* Link created */ + break; + } + + /* Replace the existing link. This activates the new script */ + ret = rename(active_path_new, fstorage->active_path); + + if ( ret < 0 ) { + /* Failed; created symlink must be deleted */ + i_unlink(active_path_new); + sieve_storage_set_critical(storage, + "Performing rename() %s to %s failed: %m", + active_path_new, fstorage->active_path); + return -1; + } + + return 1; +} + +/* + * Active script properties + */ + +int sieve_file_storage_active_script_get_file +(struct sieve_file_storage *fstorage, const char **file_r) +{ + const char *link, *scriptfile; + int ret; + + *file_r = NULL; + + /* Read the active link */ + if ( (ret=sieve_file_storage_active_read_link(fstorage, &link)) <= 0 ) + return ret; + + /* Parse the link */ + scriptfile = sieve_file_storage_active_parse_link(fstorage, link, NULL); + + if (scriptfile == NULL) { + /* Obviously, someone has been playing with our symlink: + * ignore this situation and report 'no active script'. + * Activation should fix this situation. + */ + return 0; + } + + *file_r = scriptfile; + return 1; +} + +int sieve_file_storage_active_script_get_name +(struct sieve_storage *storage, const char **name_r) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + const char *link; + int ret; + + *name_r = NULL; + + /* Read the active link */ + if ( (ret=sieve_file_storage_active_read_link + (fstorage, &link)) <= 0 ) + return ret; + + if ( sieve_file_storage_active_parse_link + (fstorage, link, name_r) == NULL ) { + /* Obviously, someone has been playing with our symlink: + * ignore this situation and report 'no active script'. + * Activation should fix this situation. + */ + return 0; + } + + return 1; +} + +/* + * Active script + */ + +struct sieve_script *sieve_file_storage_active_script_open +(struct sieve_storage *storage) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + struct sieve_file_script *fscript; + const char *scriptfile, *link; + int ret; + + sieve_storage_clear_error(storage); + + /* Read the active link */ + if ( (ret=sieve_file_storage_active_read_link(fstorage, &link)) <= 0 ) { + if ( ret < 0 ) + return NULL; + + /* Try to open the active_path as a regular file */ + if ( S_ISDIR(fstorage->st.st_mode) ) { + fscript = sieve_file_script_open_from_path(fstorage, + fstorage->active_path, NULL, NULL); + } else { + fscript = sieve_file_script_open_from_name(fstorage, NULL); + } + if ( fscript == NULL ) { + if ( storage->error_code != SIEVE_ERROR_NOT_FOUND ) { + sieve_storage_set_critical(storage, + "Failed to open active path `%s' as regular file: %s", + fstorage->active_path, storage->error); + } + return NULL; + } + + return &fscript->script; + } + + /* Parse the link */ + scriptfile = sieve_file_storage_active_parse_link(fstorage, link, NULL); + if (scriptfile == NULL) { + /* Obviously someone has been playing with our symlink, + * ignore this situation and report 'no active script'. + * Activation should fix this situation. + */ + sieve_storage_set_error(storage, SIEVE_ERROR_NOT_FOUND, + "Active script is invalid"); + return NULL; + } + + fscript = sieve_file_script_open_from_path(fstorage, + fstorage->active_path, + sieve_script_file_get_scriptname(scriptfile), + NULL); + if ( fscript == NULL && storage->error_code == SIEVE_ERROR_NOT_FOUND ) { + e_warning(storage->event, + "Active sieve script symlink %s points to non-existent script " + "(points to %s).", fstorage->active_path, link); + } + return (fscript != NULL ? &fscript->script : NULL); +} + +int sieve_file_storage_active_script_get_last_change +(struct sieve_storage *storage, time_t *last_change_r) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + struct stat st; + + /* Try direct lstat first */ + if ( lstat(fstorage->active_path, &st) == 0 ) { + if ( !S_ISLNK(st.st_mode) ) { + *last_change_r = st.st_mtime; + return 0; + } + } + /* Check error */ + else if ( errno != ENOENT ) { + sieve_storage_set_critical(storage, + "lstat(%s) failed: %m", fstorage->active_path); + } + + /* Fall back to statting storage directory */ + return sieve_storage_get_last_change(storage, last_change_r); +} + +bool sieve_file_storage_active_rescue_regular +(struct sieve_file_storage *fstorage) +{ + struct sieve_storage *storage = &fstorage->storage; + struct stat st; + + /* Stat the file */ + if ( lstat(fstorage->active_path, &st) != 0 ) { + if ( errno != ENOENT ) { + sieve_storage_set_critical(storage, + "Failed to stat active sieve script symlink (%s): %m.", + fstorage->active_path); + return FALSE; + } + return TRUE; + } + + if ( S_ISLNK( st.st_mode ) ) { + e_debug(storage->event, + "Nothing to rescue %s.", fstorage->active_path); + return TRUE; /* Nothing to rescue */ + } + + /* Only regular files can be rescued */ + if ( S_ISREG( st.st_mode ) ) { + const char *dstpath; + bool result = TRUE; + + T_BEGIN { + + dstpath = t_strconcat( fstorage->path, "/", + sieve_script_file_from_name("dovecot.orig"), NULL ); + if ( file_copy(fstorage->active_path, dstpath, TRUE) < 1 ) { + sieve_storage_set_critical(storage, + "Active sieve script file '%s' is a regular file " + "and copying it to the script storage as '%s' failed. " + "This needs to be fixed manually.", + fstorage->active_path, dstpath); + result = FALSE; + } else { + e_info(storage->event, + "Moved active sieve script file '%s' " + "to script storage as '%s'.", + fstorage->active_path, dstpath); + } + } T_END; + + return result; + } + + sieve_storage_set_critical(storage, + "Active sieve script file '%s' is no symlink nor a regular file. " + "This needs to be fixed manually.", fstorage->active_path); + return FALSE; +} + +int sieve_file_storage_deactivate(struct sieve_storage *storage) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + int ret; + + if ( sieve_file_storage_pre_modify(storage) < 0 ) + return -1; + + if ( !sieve_file_storage_active_rescue_regular(fstorage) ) + return -1; + + /* Delete the symlink, so no script is active */ + ret = unlink(fstorage->active_path); + + if ( ret < 0 ) { + if ( errno != ENOENT ) { + sieve_storage_set_critical(storage, + "Failed to deactivate Sieve: " + "unlink(%s) failed: %m", fstorage->active_path); + return -1; + } else { + return 0; + } + } + return 1; +} diff --git a/pigeonhole/src/lib-sieve/storage/file/sieve-file-storage-list.c b/pigeonhole/src/lib-sieve/storage/file/sieve-file-storage-list.c new file mode 100644 index 0000000..75a34c9 --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/file/sieve-file-storage-list.c @@ -0,0 +1,140 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "eacces-error.h" + +#include "sieve-common.h" +#include "sieve-script-private.h" + +#include "sieve-file-storage.h" + +#include <stdio.h> +#include <dirent.h> + +struct sieve_file_list_context { + struct sieve_storage_list_context context; + pool_t pool; + + const char *active; + const char *dir; + DIR *dirp; +}; + +struct sieve_storage_list_context *sieve_file_storage_list_init +(struct sieve_storage *storage) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + struct sieve_file_list_context *flctx; + const char *active = NULL; + pool_t pool; + DIR *dirp; + + /* Open the directory */ + if ( (dirp = opendir(fstorage->path)) == NULL ) { + switch ( errno ) { + case ENOENT: + sieve_storage_set_error(storage, + SIEVE_ERROR_NOT_FOUND, + "Script storage not found"); + break; + case EACCES: + sieve_storage_set_error(storage, + SIEVE_ERROR_NO_PERMISSION, + "Script storage not accessible"); + e_error(storage->event, "Failed to list scripts: %s", + eacces_error_get("opendir", fstorage->path)); + break; + default: + sieve_storage_set_critical(storage, + "Failed to list scripts: " + "opendir(%s) failed: %m", fstorage->path); + break; + } + return NULL; + } + + T_BEGIN { + /* Get the name of the active script */ + if ( sieve_file_storage_active_script_get_file(fstorage, &active) < 0) { + flctx = NULL; + } else { + pool = pool_alloconly_create("sieve_file_list_context", 1024); + flctx = p_new(pool, struct sieve_file_list_context, 1); + flctx->pool = pool; + flctx->dirp = dirp; + flctx->active = ( active != NULL ? p_strdup(pool, active) : NULL ); + } + } T_END; + + if ( flctx == NULL ) { + if ( closedir(dirp) < 0) { + e_error(storage->event, + "closedir(%s) failed: %m", fstorage->path); + } + return NULL; + } + return &flctx->context; +} + +const char *sieve_file_storage_list_next +(struct sieve_storage_list_context *ctx, bool *active) +{ + struct sieve_file_list_context *flctx = + (struct sieve_file_list_context *)ctx; + const struct sieve_file_storage *fstorage = + (const struct sieve_file_storage *)ctx->storage; + struct dirent *dp; + const char *scriptname; + + *active = FALSE; + + for (;;) { + if ( (dp = readdir(flctx->dirp)) == NULL ) + return NULL; + + scriptname = sieve_script_file_get_scriptname(dp->d_name); + if (scriptname != NULL ) { + /* Don't list our active sieve script link if the link + * resides in the script dir (generally a bad idea). + */ + i_assert( fstorage->link_path != NULL ); + if ( *(fstorage->link_path) == '\0' && + strcmp(fstorage->active_fname, dp->d_name) == 0 ) + continue; + + break; + } + } + + if ( flctx->active != NULL && strcmp(dp->d_name, flctx->active) == 0 ) { + *active = TRUE; + flctx->active = NULL; + } + + return scriptname; +} + +int sieve_file_storage_list_deinit(struct sieve_storage_list_context *lctx) +{ + struct sieve_file_list_context *flctx = + (struct sieve_file_list_context *)lctx; + const struct sieve_file_storage *fstorage = + (const struct sieve_file_storage *)lctx->storage; + + if (closedir(flctx->dirp) < 0) { + e_error(lctx->storage->event, + "closedir(%s) failed: %m", fstorage->path); + } + + pool_unref(&flctx->pool); + + // FIXME: return error here if something went wrong during listing + return 0; +} + + + + diff --git a/pigeonhole/src/lib-sieve/storage/file/sieve-file-storage-quota.c b/pigeonhole/src/lib-sieve/storage/file/sieve-file-storage-quota.c new file mode 100644 index 0000000..65e075d --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/file/sieve-file-storage-quota.c @@ -0,0 +1,120 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" + +#include "sieve.h" +#include "sieve-script.h" + +#include "sieve-file-storage.h" + +#include <stdio.h> +#include <dirent.h> +#include <unistd.h> +#include <fcntl.h> + +int sieve_file_storage_quota_havespace +(struct sieve_storage *storage, const char *scriptname, size_t size, + enum sieve_storage_quota *quota_r, uint64_t *limit_r) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + struct dirent *dp; + DIR *dirp; + uint64_t script_count = 1; + uint64_t script_storage = size; + int result = 1; + + /* Open the directory */ + if ( (dirp = opendir(fstorage->path)) == NULL ) { + sieve_storage_set_critical(storage, + "quota: opendir(%s) failed: %m", fstorage->path); + return -1; + } + + /* Scan all files */ + for (;;) { + const char *name; + bool replaced = FALSE; + + /* Read next entry */ + errno = 0; + if ( (dp = readdir(dirp)) == NULL ) { + if ( errno != 0 ) { + sieve_storage_set_critical(storage, + "quota: readdir(%s) failed: %m", fstorage->path); + result = -1; + } + break; + } + + /* Parse filename */ + name = sieve_script_file_get_scriptname(dp->d_name); + + /* Ignore non-script files */ + if ( name == NULL ) + continue; + + /* Don't list our active sieve script link if the link + * resides in the script dir (generally a bad idea). + */ + i_assert( fstorage->link_path != NULL ); + if ( *(fstorage->link_path) == '\0' && + strcmp(fstorage->active_fname, dp->d_name) == 0 ) + continue; + + if ( strcmp(name, scriptname) == 0 ) + replaced = TRUE; + + /* Check count quota if necessary */ + if ( storage->max_scripts > 0 ) { + if ( !replaced ) { + script_count++; + + if ( script_count > storage->max_scripts ) { + *quota_r = SIEVE_STORAGE_QUOTA_MAXSCRIPTS; + *limit_r = storage->max_scripts; + result = 0; + break; + } + } + } + + /* Check storage quota if necessary */ + if ( storage->max_storage > 0 ) { + const char *path; + struct stat st; + + path = t_strconcat(fstorage->path, "/", dp->d_name, NULL); + + if ( stat(path, &st) < 0 ) { + e_warning(storage->event, + "quota: stat(%s) failed: %m", path); + continue; + } + + if ( !replaced ) { + script_storage += st.st_size; + + if ( script_storage > storage->max_storage ) { + *quota_r = SIEVE_STORAGE_QUOTA_MAXSTORAGE; + *limit_r = storage->max_storage; + result = 0; + break; + } + } + } + } + + /* Close directory */ + if ( closedir(dirp) < 0 ) { + sieve_storage_set_critical(storage, + "quota: closedir(%s) failed: %m", fstorage->path); + } + return result; +} + + + + diff --git a/pigeonhole/src/lib-sieve/storage/file/sieve-file-storage-save.c b/pigeonhole/src/lib-sieve/storage/file/sieve-file-storage-save.c new file mode 100644 index 0000000..bfbe380 --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/file/sieve-file-storage-save.c @@ -0,0 +1,544 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "hostpid.h" +#include "ioloop.h" +#include "array.h" +#include "buffer.h" +#include "istream.h" +#include "ostream.h" +#include "str.h" +#include "eacces-error.h" +#include "safe-mkstemp.h" + +#include "sieve-file-storage.h" + +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <utime.h> + +struct sieve_file_save_context { + struct sieve_storage_save_context context; + + pool_t pool; + + struct ostream *output; + int fd; + const char *tmp_path; + + time_t mtime; + + bool failed:1; + bool finished:1; +}; + +static const char *sieve_generate_tmp_filename(const char *scriptname) +{ + static struct timeval last_tv = { 0, 0 }; + struct timeval tv; + + /* use secs + usecs to guarantee uniqueness within this process. */ + if (ioloop_timeval.tv_sec > last_tv.tv_sec || + (ioloop_timeval.tv_sec == last_tv.tv_sec && + ioloop_timeval.tv_usec > last_tv.tv_usec)) { + tv = ioloop_timeval; + } else { + tv = last_tv; + if (++tv.tv_usec == 1000000) { + tv.tv_sec++; + tv.tv_usec = 0; + } + } + last_tv = tv; + + if ( scriptname == NULL ) { + return t_strdup_printf("%s.M%sP%s.%s.tmp", + dec2str(tv.tv_sec), dec2str(tv.tv_usec), + my_pid, my_hostname); + } + + scriptname = t_strdup_printf("%s_%s.M%sP%s.%s", + scriptname, dec2str(tv.tv_sec), dec2str(tv.tv_usec), + my_pid, my_hostname); + return sieve_script_file_from_name(scriptname); +} + +static int sieve_file_storage_create_tmp +(struct sieve_file_storage *fstorage, const char *scriptname, + const char **fpath_r) +{ + struct sieve_storage *storage = &fstorage->storage; + struct stat st; + unsigned int prefix_len; + const char *tmp_fname = NULL; + string_t *path; + int fd; + + path = t_str_new(256); + str_append(path, fstorage->path); + str_append(path, "/tmp/"); + prefix_len = str_len(path); + + for (;;) { + tmp_fname = sieve_generate_tmp_filename(scriptname); + str_truncate(path, prefix_len); + str_append(path, tmp_fname); + + /* stat() first to see if it exists. pretty much the only + possibility of that happening is if time had moved + backwards, but even then it's highly unlikely. */ + if (stat(str_c(path), &st) == 0) { + /* try another file name */ + } else if (errno != ENOENT) { + switch ( errno ) { + case EACCES: + sieve_storage_set_critical(storage, "save: %s", + eacces_error_get("stat", fstorage->path)); + break; + default: + sieve_storage_set_critical(storage, "save: " + "stat(%s) failed: %m", str_c(path)); + break; + } + return -1; + } else { + /* doesn't exist */ + mode_t old_mask = umask(0777 & ~(fstorage->file_create_mode)); + fd = open(str_c(path), + O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0777); + umask(old_mask); + + if (fd != -1 || errno != EEXIST) + break; + /* race condition between stat() and open(). + highly unlikely. */ + } + } + + *fpath_r = str_c(path); + if (fd == -1) { + if (ENOQUOTA(errno)) { + sieve_storage_set_error(storage, + SIEVE_ERROR_NO_QUOTA, + "Not enough disk quota"); + } else { + switch ( errno ) { + case EACCES: + sieve_storage_set_critical(storage, "save: %s", + eacces_error_get("open", fstorage->path)); + break; + default: + sieve_storage_set_critical(storage, "save: " + "open(%s) failed: %m", str_c(path)); + break; + } + } + } + + return fd; +} + +static int sieve_file_storage_script_move +(struct sieve_file_save_context *fsctx, const char *dst) +{ + struct sieve_storage_save_context *sctx = &fsctx->context; + struct sieve_storage *storage = sctx->storage; + int result = 0; + + T_BEGIN { + + /* Using rename() to ensure existing files are replaced + * without conflicts with other processes using the same + * file. The kernel wont fully delete the original until + * all processes have closed the file. + */ + if (rename(fsctx->tmp_path, dst) == 0) + result = 0; + else { + result = -1; + if ( ENOQUOTA(errno) ) { + sieve_storage_set_error(storage, + SIEVE_ERROR_NO_QUOTA, + "Not enough disk quota"); + } else if ( errno == EACCES ) { + sieve_storage_set_critical(storage, "save: " + "Failed to save Sieve script: " + "%s", eacces_error_get("rename", dst)); + } else { + sieve_storage_set_critical(storage, "save: " + "rename(%s, %s) failed: %m", fsctx->tmp_path, dst); + } + } + + /* Always destroy temp file */ + if (unlink(fsctx->tmp_path) < 0 && errno != ENOENT) { + e_warning(storage->event, "save: " + "unlink(%s) failed: %m", fsctx->tmp_path); + } + } T_END; + + return result; +} + +struct sieve_storage_save_context * +sieve_file_storage_save_alloc(struct sieve_storage *storage) +{ + struct sieve_file_save_context *fsctx; + pool_t pool; + + pool = pool_alloconly_create("sieve_file_save_context", 1024); + fsctx = p_new(pool, struct sieve_file_save_context, 1); + fsctx->context.pool = pool; + fsctx->context.storage = storage; + + return &fsctx->context; +} + +int sieve_file_storage_save_init(struct sieve_storage_save_context *sctx, + const char *scriptname, struct istream *input) +{ + struct sieve_storage *storage = sctx->storage; + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + struct sieve_file_save_context *fsctx = + (struct sieve_file_save_context *)sctx; + pool_t pool = sctx->pool; + const char *path; + int fd, ret = 0; + + if ( sieve_file_storage_pre_modify(storage) < 0 ) + return -1; + + if ( scriptname != NULL ) { + /* Prevent overwriting the active script link when it resides in the + * sieve storage directory. + */ + i_assert( fstorage->link_path != NULL ); + if ( *(fstorage->link_path) == '\0' ) { + const char *svext; + size_t namelen; + + svext = strrchr(fstorage->active_fname, '.'); + namelen = svext - fstorage->active_fname; + if ( svext != NULL && str_begins(svext+1, "sieve") && + strlen(scriptname) == namelen && + str_begins(fstorage->active_fname, scriptname) ) + { + sieve_storage_set_error(storage, + SIEVE_ERROR_BAD_PARAMS, + "Script name `%s' is reserved for internal use.", + scriptname); + return -1; + } + } + } + + T_BEGIN { + fd = sieve_file_storage_create_tmp(fstorage, scriptname, &path); + if (fd == -1) { + ret = -1; + } else { + fsctx->context.scriptname = p_strdup(pool, scriptname); + fsctx->context.input = input; + fsctx->fd = fd; + fsctx->output = o_stream_create_fd(fsctx->fd, 0); + fsctx->tmp_path = p_strdup(pool, path); + } + } T_END; + + return ret; +} + +int sieve_file_storage_save_continue +(struct sieve_storage_save_context *sctx) +{ + struct sieve_file_save_context *fsctx = + (struct sieve_file_save_context *)sctx; + + switch (o_stream_send_istream(fsctx->output, sctx->input)) { + case OSTREAM_SEND_ISTREAM_RESULT_FINISHED: + case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT: + return 0; + case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT: + i_unreached(); + case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT: + sieve_storage_set_critical(sctx->storage, + "save: read(%s) failed: %s", + i_stream_get_name(sctx->input), + i_stream_get_error(sctx->input)); + return -1; + case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT: + sieve_storage_set_critical(sctx->storage, + "save: write(%s) failed: %s", fsctx->tmp_path, + o_stream_get_error(fsctx->output)); + return -1; + } + return 0; +} + +int sieve_file_storage_save_finish +(struct sieve_storage_save_context *sctx) +{ + struct sieve_file_save_context *fsctx = + (struct sieve_file_save_context *)sctx; + struct sieve_storage *storage = sctx->storage; + int output_errno; + + if ( sctx->failed && fsctx->fd == -1 ) { + /* tmp file creation failed */ + return -1; + } + + T_BEGIN { + output_errno = fsctx->output->stream_errno; + o_stream_destroy(&fsctx->output); + + if ( fsync(fsctx->fd) < 0 ) { + sieve_storage_set_critical(storage, "save: " + "fsync(%s) failed: %m", fsctx->tmp_path); + sctx->failed = TRUE; + } + if ( close(fsctx->fd) < 0 ) { + sieve_storage_set_critical(storage, "save: " + "close(%s) failed: %m", fsctx->tmp_path); + sctx->failed = TRUE; + } + fsctx->fd = -1; + + if ( sctx->failed ) { + /* delete the tmp file */ + if (unlink(fsctx->tmp_path) < 0 && errno != ENOENT) { + e_warning(storage->event, "save: " + "unlink(%s) failed: %m", + fsctx->tmp_path); + } + + errno = output_errno; + if ( ENOQUOTA(errno) ) { + sieve_storage_set_error(storage, + SIEVE_ERROR_NO_QUOTA, + "Not enough disk quota"); + } else if ( errno != 0 ) { + sieve_storage_set_critical(storage, "save: " + "write(%s) failed: %m", fsctx->tmp_path); + } + fsctx->tmp_path = NULL; + } + } T_END; + + return ( sctx->failed ? -1 : 0 ); +} + +struct sieve_script *sieve_file_storage_save_get_tempscript +(struct sieve_storage_save_context *sctx) +{ + struct sieve_file_save_context *fsctx = + (struct sieve_file_save_context *)sctx; + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)sctx->storage; + struct sieve_file_script *tmpscript; + enum sieve_error error; + const char *scriptname; + + if (sctx->failed) + return NULL; + + if ( sctx->scriptobject != NULL ) + return sctx->scriptobject; + + scriptname = + ( sctx->scriptname == NULL ? "" : sctx->scriptname ); + tmpscript = sieve_file_script_open_from_path + (fstorage, fsctx->tmp_path, scriptname, &error); + + if ( tmpscript == NULL ) { + if ( error == SIEVE_ERROR_NOT_FOUND ) { + sieve_storage_set_critical(sctx->storage, "save: " + "Temporary script file `%s' got lost, " + "which should not happen (possibly deleted externally).", + fsctx->tmp_path); + } else { + sieve_storage_set_critical(sctx->storage, "save: " + "Failed to open temporary script file `%s'", + fsctx->tmp_path); + } + return NULL; + } + + return &tmpscript->script; +} + +static void sieve_file_storage_update_mtime +(struct sieve_storage *storage, const char *path, time_t mtime) +{ + struct utimbuf times = { .actime = mtime, .modtime = mtime }; + + if ( utime(path, ×) < 0 ) { + switch ( errno ) { + case ENOENT: + break; + case EACCES: + e_error(storage->event, "save: %s", + eacces_error_get("utime", path)); + break; + default: + e_error(storage->event, + "save: utime(%s) failed: %m", path); + } + } +} + +int sieve_file_storage_save_commit +(struct sieve_storage_save_context *sctx) +{ + struct sieve_file_save_context *fsctx = + (struct sieve_file_save_context *)sctx; + struct sieve_storage *storage = sctx->storage; + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)sctx->storage; + const char *dest_path; + bool failed = FALSE; + + i_assert(fsctx->output == NULL); + + T_BEGIN { + dest_path = t_strconcat(fstorage->path, "/", + sieve_script_file_from_name(sctx->scriptname), NULL); + + failed = ( sieve_file_storage_script_move(fsctx, dest_path) < 0 ); + if ( sctx->mtime != (time_t)-1 ) + sieve_file_storage_update_mtime(storage, dest_path, sctx->mtime); + } T_END; + + return ( failed ? -1 : 0 ); +} + +void sieve_file_storage_save_cancel(struct sieve_storage_save_context *sctx) +{ + struct sieve_file_save_context *fsctx = + (struct sieve_file_save_context *)sctx; + struct sieve_storage *storage = sctx->storage; + + if (fsctx->tmp_path != NULL && + unlink(fsctx->tmp_path) < 0 && errno != ENOENT) { + e_warning(storage->event, "save: unlink(%s) failed: %m", + fsctx->tmp_path); + } + + i_assert(fsctx->output == NULL); +} + +static int +sieve_file_storage_save_to(struct sieve_file_storage *fstorage, + string_t *temp_path, struct istream *input, + const char *target) +{ + struct sieve_storage *storage = &fstorage->storage; + struct ostream *output; + int fd; + + // FIXME: move this to base class + // FIXME: use io_stream_temp + + fd = safe_mkstemp_hostpid + (temp_path, fstorage->file_create_mode, (uid_t)-1, (gid_t)-1); + if ( fd < 0 ) { + if ( errno == EACCES ) { + sieve_storage_set_critical(storage, + "Failed to create temporary file: %s", + eacces_error_get_creating("open", str_c(temp_path))); + } else { + sieve_storage_set_critical(storage, + "Failed to create temporary file: open(%s) failed: %m", + str_c(temp_path)); + } + return -1; + } + + output = o_stream_create_fd(fd, 0); + switch ( o_stream_send_istream(output, input) ) { + case OSTREAM_SEND_ISTREAM_RESULT_FINISHED: + break; + case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT: + case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT: + i_unreached(); + case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT: + sieve_storage_set_critical(storage, + "read(%s) failed: %s", i_stream_get_name(input), + i_stream_get_error(input)); + o_stream_destroy(&output); + i_unlink(str_c(temp_path)); + return -1; + case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT: + sieve_storage_set_critical(storage, + "write(%s) failed: %s", str_c(temp_path), + o_stream_get_error(output)); + o_stream_destroy(&output); + i_unlink(str_c(temp_path)); + return -1; + } + o_stream_destroy(&output); + + if ( rename(str_c(temp_path), target) < 0 ) { + if ( ENOQUOTA(errno) ) { + sieve_storage_set_error(storage, + SIEVE_ERROR_NO_QUOTA, + "Not enough disk quota"); + } else if ( errno == EACCES ) { + sieve_storage_set_critical(storage, + "%s", eacces_error_get("rename", target)); + } else { + sieve_storage_set_critical(storage, + "rename(%s, %s) failed: %m", + str_c(temp_path), target); + } + i_unlink(str_c(temp_path)); + } + return 0; +} + +int sieve_file_storage_save_as +(struct sieve_storage *storage, struct istream *input, + const char *name) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + string_t *temp_path; + const char *dest_path; + + temp_path = t_str_new(256); + str_append(temp_path, fstorage->path); + str_append(temp_path, "/tmp/"); + str_append(temp_path, sieve_script_file_from_name(name)); + str_append_c(temp_path, '.'); + + dest_path = t_strconcat(fstorage->path, "/", + sieve_script_file_from_name(name), NULL); + + return sieve_file_storage_save_to + (fstorage, temp_path, input, dest_path); +} + +int sieve_file_storage_save_as_active +(struct sieve_storage *storage, struct istream *input, + time_t mtime) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + string_t *temp_path; + + temp_path = t_str_new(256); + str_append(temp_path, fstorage->active_path); + str_append_c(temp_path, '.'); + + if ( sieve_file_storage_save_to + (fstorage, temp_path, input, fstorage->active_path) < 0 ) + return -1; + + sieve_file_storage_update_mtime + (storage, fstorage->active_path, mtime); + return 0; +} + diff --git a/pigeonhole/src/lib-sieve/storage/file/sieve-file-storage.c b/pigeonhole/src/lib-sieve/storage/file/sieve-file-storage.c new file mode 100644 index 0000000..76b3ebf --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/file/sieve-file-storage.c @@ -0,0 +1,918 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "path-util.h" +#include "home-expand.h" +#include "ioloop.h" +#include "mkdir-parents.h" +#include "eacces-error.h" +#include "unlink-old-files.h" +#include "mail-storage-private.h" + +#include "sieve.h" +#include "sieve-common.h" +#include "sieve-settings.h" +#include "sieve-error-private.h" +#include "sieve-settings.h" + +#include "sieve-file-storage.h" + +#include <stdio.h> +#include <unistd.h> +#include <ctype.h> +#include <utime.h> +#include <sys/time.h> + + +#define MAX_DIR_CREATE_MODE 0770 + +/* + * Utility + */ + +const char *sieve_file_storage_path_extend +(struct sieve_file_storage *fstorage, const char *filename) +{ + const char *path = fstorage->path; + + if ( path[strlen(path)-1] == '/' ) + return t_strconcat(path, filename, NULL); + + return t_strconcat(path, "/", filename , NULL); +} + +/* + * + */ + +static int sieve_file_storage_stat +(struct sieve_file_storage *fstorage, const char *path, + enum sieve_error *error_r) +{ + struct sieve_storage *storage = &fstorage->storage; + struct stat st; + const char *abspath, *error; + + if ( lstat(path, &st) == 0 ) { + fstorage->lnk_st = st; + + if ( !S_ISLNK(st.st_mode) || stat(path, &st) == 0 ) { + fstorage->st = st; + return 0; + } + } + + switch ( errno ) { + case ENOENT: + if (t_abspath(path, &abspath, &error) < 0) { + sieve_storage_set_critical(storage, + "t_abspath(%s) failed: %s", path, error); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + break; + } + e_debug(storage->event, "Storage path `%s' not found", abspath); + sieve_storage_set_internal_error(storage); // should be overriden + *error_r = SIEVE_ERROR_NOT_FOUND; + break; + case EACCES: + sieve_storage_set_critical(storage, + "Failed to stat sieve storage path: %s", + eacces_error_get("stat", path)); + *error_r = SIEVE_ERROR_NO_PERMISSION; + break; + default: + sieve_storage_set_critical(storage, + "Failed to stat sieve storage path: " + "stat(%s) failed: %m", path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + break; + } + + return -1; +} + +static const char *sieve_storage_get_relative_link_path + (const char *active_path, const char *storage_dir) +{ + const char *link_path, *p; + size_t pathlen; + + /* Determine to what extent the sieve storage and active script + * paths match up. This enables the managed symlink to be short and the + * sieve storages can be moved around without trouble (if the active + * script path is common to the script storage). + */ + p = strrchr(active_path, '/'); + if ( p == NULL ) { + link_path = storage_dir; + } else { + pathlen = p - active_path; + + if ( strncmp( storage_dir, active_path, pathlen ) == 0 && + (storage_dir[pathlen] == '/' || storage_dir[pathlen] == '\0') ) + { + if ( storage_dir[pathlen] == '\0' ) + link_path = ""; + else + link_path = storage_dir + pathlen + 1; + } else + link_path = storage_dir; + } + + /* Add trailing '/' when link path is not empty + */ + pathlen = strlen(link_path); + if ( pathlen != 0 && link_path[pathlen-1] != '/') + return t_strconcat(link_path, "/", NULL); + + return t_strdup(link_path); +} + +static mode_t get_dir_mode(mode_t mode) +{ + /* Add the execute bit if either read or write bit is set */ + + if ((mode & 0600) != 0) mode |= 0100; + if ((mode & 0060) != 0) mode |= 0010; + if ((mode & 0006) != 0) mode |= 0001; + + return mode; +} + +static int mkdir_verify +(struct sieve_storage *storage, const char *dir, + mode_t mode, gid_t gid, const char *gid_origin) +{ + struct stat st; + + if ( stat(dir, &st) == 0 ) + return 0; + + if ( errno == EACCES ) { + e_error(storage->event, "mkdir_verify: %s", + eacces_error_get("stat", dir)); + return -1; + } else if ( errno != ENOENT ) { + e_error(storage->event, "mkdir_verify: " + "stat(%s) failed: %m", dir); + return -1; + } + + if ( mkdir_parents_chgrp(dir, mode, gid, gid_origin) == 0 ) { + e_debug(storage->event, "Created storage directory %s", dir); + return 0; + } + + switch ( errno ) { + case EEXIST: + return 0; + case ENOENT: + e_error(storage->event, + "Storage was deleted while it was being created"); + break; + case EACCES: + e_error(storage->event, "%s", + eacces_error_get_creating("mkdir_parents_chgrp", dir)); + break; + default: + e_error(storage->event, + "mkdir_parents_chgrp(%s) failed: %m", dir); + break; + } + + return -1; +} + +static int check_tmp(struct sieve_storage *storage, const char *path) +{ + struct stat st; + + /* If tmp/ directory exists, we need to clean it up once in a while */ + if ( stat(path, &st) < 0 ) { + if ( errno == ENOENT ) + return 0; + if ( errno == EACCES ) { + e_error(storage->event, "check_tmp: %s", + eacces_error_get("stat", path)); + return -1; + } + e_error(storage->event, "check_tmp: stat(%s) failed: %m", path); + return -1; + } + + if ( st.st_atime > st.st_ctime + SIEVE_FILE_STORAGE_TMP_DELETE_SECS ) { + /* The directory should be empty. we won't do anything + until ctime changes. */ + } else if ( st.st_atime < ioloop_time - SIEVE_FILE_STORAGE_TMP_SCAN_SECS ) { + /* Time to scan */ + (void)unlink_old_files(path, "", + ioloop_time - SIEVE_FILE_STORAGE_TMP_DELETE_SECS); + } + return 1; +} + +static struct sieve_storage *sieve_file_storage_alloc(void) +{ + struct sieve_file_storage *fstorage; + pool_t pool; + + pool = pool_alloconly_create("sieve_file_storage", 2048); + fstorage = p_new(pool, struct sieve_file_storage, 1); + fstorage->storage = sieve_file_storage; + fstorage->storage.pool = pool; + + return &fstorage->storage; +} + +static int sieve_file_storage_get_full_path +(struct sieve_file_storage *fstorage, const char **storage_path, + enum sieve_error *error_r) +{ + struct sieve_storage *storage = &fstorage->storage; + struct sieve_instance *svinst = storage->svinst; + const char *path = *storage_path; + + /* Get full storage path */ + + if ( path != NULL && + ((path[0] == '~' && (path[1] == '/' || path[1] == '\0')) || + (((svinst->flags & SIEVE_FLAG_HOME_RELATIVE) != 0 ) && path[0] != '/')) ) { + /* home-relative path. change to absolute. */ + const char *home = sieve_environment_get_homedir(svinst); + + if ( home != NULL ) { + if ( path[0] == '~' && (path[1] == '/' || path[1] == '\0') ) + path = home_expand_tilde(path, home); + else + path = t_strconcat(home, "/", path, NULL); + } else { + sieve_storage_set_critical(storage, + "Sieve storage path `%s' is relative to home directory, " + "but home directory is not available.", path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + } + *storage_path = path; + return 0; +} + +static int sieve_file_storage_get_full_active_path +(struct sieve_file_storage *fstorage, const char **active_path, + enum sieve_error *error_r) +{ + struct sieve_storage *storage = &fstorage->storage; + struct sieve_instance *svinst = storage->svinst; + const char *path = *active_path; + + if ( path != NULL && *path != '\0' && + ((path[0] == '~' && (path[1] == '/' || path[1] == '\0')) || + (((svinst->flags & SIEVE_FLAG_HOME_RELATIVE) != 0 ) && path[0] != '/')) + ) { + /* home-relative path. change to absolute. */ + const char *home = sieve_environment_get_homedir(svinst); + + if ( home != NULL ) { + if ( path[0] == '~' && (path[1] == '/' || path[1] == '\0') ) + path = home_expand_tilde(path, home); + else + path = t_strconcat(home, "/", path, NULL); + } else { + sieve_storage_set_critical(storage, + "Sieve storage active script path `%s' is relative to home directory, " + "but home directory is not available.", path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + } + *active_path = path; + return 0; +} + +static int sieve_file_storage_init_common +(struct sieve_file_storage *fstorage, const char *active_path, + const char *storage_path, bool exists, enum sieve_error *error_r) + ATTR_NULL(2, 3) +{ + struct sieve_storage *storage = &fstorage->storage; + const char *tmp_dir, *link_path, *active_fname, *storage_dir, *error; + bool have_link = FALSE; + int ret; + + i_assert( storage_path != NULL || active_path != NULL ); + + fstorage->prev_mtime = (time_t)-1; + + /* Get active script path */ + + if ( sieve_file_storage_get_full_active_path + (fstorage, &active_path, error_r) < 0 ) + return -1; + + /* Get the filename for the active script link */ + + active_fname = NULL; + if ( active_path != NULL && *active_path != '\0' ) { + const char *active_dir; + + active_fname = strrchr(active_path, '/'); + if ( active_fname == NULL ) { + active_fname = active_path; + active_dir = ""; + } else { + active_dir = t_strdup_until(active_path, active_fname); + active_fname++; + } + + if ( *active_fname == '\0' ) { + /* Link cannot be just a path ending in '/' */ + sieve_storage_set_critical(storage, + "Path to %sscript must include the filename (path=%s)", + ( storage_path != NULL ? "active link/" : "" ), + active_path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + + if (t_realpath(active_dir, &active_dir, &error) < 0) { + if (errno != ENOENT) { + e_error(storage->event, + "Failed to normalize active script directory " + "(path=%s): %s", active_dir, error); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + e_debug(storage->event, + "Failed to normalize active script directory " + "(path=%s): " + "Part of the path does not exist (yet)", + active_dir); + } else { + active_path = t_abspath_to(active_fname, active_dir); + } + + e_debug(storage->event, "Using %sSieve script path: %s", + (storage_path != NULL ? "active " : ""), active_path); + + fstorage->active_path = p_strdup(storage->pool, active_path); + fstorage->active_fname = p_strdup(storage->pool, active_fname); + } + + /* Determine storage path */ + + storage_dir = storage_path; + if ( storage_path != NULL && *storage_path != '\0' ) { + e_debug(storage->event, "Using script storage path: %s", + storage_path); + have_link = TRUE; + + } else { + if ((storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 ) { + sieve_storage_set_critical(storage, + "Storage path cannot be empty for write access"); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + + storage_path = active_path; + } + + i_assert(storage_path != NULL); + + /* Prepare for write access */ + + if ( (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 ) { + mode_t dir_create_mode, file_create_mode; + gid_t file_create_gid; + const char *file_create_gid_origin; + + /* Use safe permission defaults */ + file_create_mode = 0600; + dir_create_mode = 0700; + file_create_gid = (gid_t)-1; + file_create_gid_origin = "defaults"; + + /* Get actual permissions */ + if ( exists ) { + file_create_mode = (fstorage->st.st_mode & 0666) | 0600; + dir_create_mode = (fstorage->st.st_mode & 0777) | 0700; + file_create_gid_origin = storage_dir; + + if ( !S_ISDIR(fstorage->st.st_mode) ) { + /* We're getting permissions from a file. + Apply +x modes as necessary. */ + dir_create_mode = get_dir_mode(dir_create_mode); + } + + if (S_ISDIR(fstorage->st.st_mode) && + (fstorage->st.st_mode & S_ISGID) != 0) { + /* Directory's GID is used automatically for new files */ + file_create_gid = (gid_t)-1; + } else if ((fstorage->st.st_mode & 0070) >> 3 == + (fstorage->st.st_mode & 0007)) { + /* Group has same permissions as world, so don't bother changing it */ + file_create_gid = (gid_t)-1; + } else if (getegid() == fstorage->st.st_gid) { + /* Using our own gid, no need to change it */ + file_create_gid = (gid_t)-1; + } else { + file_create_gid = fstorage->st.st_gid; + } + } + + e_debug(storage->event, + "Using permissions from %s: mode=0%o gid=%ld", + file_create_gid_origin, (int)dir_create_mode, + file_create_gid == (gid_t)-1 ? + -1L : (long)file_create_gid); + + /* + * Ensure sieve local directory structure exists (full autocreate): + * This currently only consists of a ./tmp direcory + */ + + tmp_dir = t_strconcat(storage_path, "/tmp", NULL); + + /* Try to find and clean up tmp dir */ + if ( (ret=check_tmp(storage, tmp_dir)) < 0 ) { + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + + /* Auto-create if necessary */ + if ( ret == 0 && mkdir_verify(storage, tmp_dir, + dir_create_mode, file_create_gid, file_create_gid_origin) < 0 ) { + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + + fstorage->dir_create_mode = dir_create_mode; + fstorage->file_create_mode = file_create_mode; + fstorage->file_create_gid = file_create_gid; + } + + if ( !exists && sieve_file_storage_stat + (fstorage, storage_path, error_r) < 0 ) + return -1; + + if ( have_link ) { + if ( t_realpath(storage_path, &storage_path, &error) < 0 ) { + e_error(storage->event, + "Failed to normalize storage path (path=%s): %s", + storage_path, error); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + if ( active_path != NULL && *active_path != '\0' ) { + /* Get the path to be prefixed to the script name in the symlink + * pointing to the active script. + */ + link_path = sieve_storage_get_relative_link_path + (fstorage->active_path, storage_path); + + e_debug(storage->event, + "Relative path to sieve storage in active link: %s", + link_path); + + fstorage->link_path = p_strdup(storage->pool, link_path); + } + } + + fstorage->path = p_strdup(storage->pool, storage_path); + storage->location = fstorage->path; + + return 0; +} + +static int sieve_file_storage_init +(struct sieve_storage *storage, const char *const *options, + enum sieve_error *error_r) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + const char *storage_path = storage->location; + const char *active_path = ""; + bool exists = FALSE; + + if ( options != NULL ) { + while ( *options != NULL ) { + const char *option = *options; + + if ( strncasecmp(option, "active=", 7) == 0 && option[7] != '\0' ) { + active_path = option+7; + } else { + sieve_storage_set_critical(storage, + "Invalid option `%s'", option); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + + options++; + } + } + + /* Get full storage path */ + + if ( sieve_file_storage_get_full_path + (fstorage, &storage_path, error_r) < 0 ) + return -1; + + /* Stat storage directory */ + + if ( storage_path != NULL && *storage_path != '\0' ) { + if ( sieve_file_storage_stat(fstorage, storage_path, error_r) < 0 ) { + if ( *error_r != SIEVE_ERROR_NOT_FOUND ) + return -1; + if ( (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) == 0 ) { + /* For backwards compatibility, recognize when storage directory + does not exist while active script exists and is a regular + file. */ + if ( active_path == NULL || *active_path == '\0' ) + return -1; + if ( sieve_file_storage_get_full_active_path + (fstorage, &active_path, error_r) < 0 ) + return -1; + if ( sieve_file_storage_stat + (fstorage, active_path, error_r) < 0 ) + return -1; + if ( !S_ISREG(fstorage->lnk_st.st_mode) ) + return -1; + e_debug(storage->event, + "Sieve storage path `%s' not found, " + "but the active script `%s' is a regular file, " + "so this is used for backwards compatibility.", + storage_path, active_path); + storage_path = NULL; + } + } else { + exists = TRUE; + + if ( !S_ISDIR(fstorage->st.st_mode) ) { + if ( (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 ) { + sieve_storage_set_critical(storage, + "Sieve storage path `%s' is not a directory, " + "but it is to be opened for write access", storage_path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + if ( active_path != NULL && *active_path != '\0' ) { + e_warning(storage->event, + "Explicitly specified active script path `%s' is ignored; " + "storage path `%s' is not a directory", + active_path, storage_path); + } + active_path = storage_path; + storage_path = NULL; + } + } + } + + if ( active_path == NULL || *active_path == '\0' ) { + if ( storage->main_storage || + (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0) { + e_debug(storage->event, + "Active script path is unconfigured; " + "using default (path=%s)", + SIEVE_FILE_DEFAULT_PATH); + active_path = SIEVE_FILE_DEFAULT_PATH; + } + } + + return sieve_file_storage_init_common + (fstorage, active_path, storage_path, exists, error_r); +} + +static void sieve_file_storage_autodetect +(struct sieve_file_storage *fstorage, const char **storage_path_r) +{ + struct sieve_storage *storage = &fstorage->storage; + struct sieve_instance *svinst = storage->svinst; + const char *home = sieve_environment_get_homedir(svinst); + int mode = ( (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 ? + R_OK|W_OK|X_OK : R_OK|X_OK ); + + e_debug(storage->event, "Performing auto-detection"); + + /* We'll need to figure out the storage location ourself. + * + * It's $HOME/sieve or /sieve when (presumed to be) chrooted. + */ + if ( home != NULL && *home != '\0' ) { + /* Use default ~/sieve */ + e_debug(storage->event, "Use home (%s)", home); + *storage_path_r = t_strconcat(home, "/sieve", NULL); + } else { + e_debug(storage->event, "HOME is not set"); + + if (access("/sieve", mode) == 0) { + *storage_path_r = "/sieve"; + e_debug(storage->event, + "Directory `/sieve' exists, assuming chroot"); + } + } +} + +static int sieve_file_storage_do_init_legacy +(struct sieve_file_storage *fstorage, const char *active_path, + const char *storage_path, enum sieve_error *error_r) +{ + struct sieve_storage *storage = &fstorage->storage; + bool explicit = FALSE, exists = FALSE; + + if ( storage_path == NULL || *storage_path == '\0' ) { + /* Try autodectection */ + sieve_file_storage_autodetect(fstorage, &storage_path); + + if ( storage_path != NULL && *storage_path != '\0') { + /* Got something: stat it */ + if (sieve_file_storage_stat + (fstorage, storage_path, error_r) < 0 ) { + if (*error_r != SIEVE_ERROR_NOT_FOUND) { + /* Error */ + return -1; + } + } else if ( S_ISDIR(fstorage->st.st_mode) ) { + /* Success */ + exists = TRUE; + } + } + + if ( (storage_path == NULL || *storage_path == '\0') && + (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 ) { + sieve_storage_set_critical(storage, + "Could not find storage root directory for write access; " + "path was left unconfigured and autodetection failed"); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + + } else { + /* Get full storage path */ + if ( sieve_file_storage_get_full_path + (fstorage, &storage_path, error_r) < 0 ) + return -1; + + /* Stat storage directory */ + if ( sieve_file_storage_stat(fstorage, storage_path, error_r) < 0 ) { + if ( (*error_r != SIEVE_ERROR_NOT_FOUND) ) + return -1; + if ( (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) == 0 ) + storage_path = NULL; + } else { + exists = TRUE; + } + + /* Storage path must be a directory */ + if ( exists && !S_ISDIR(fstorage->st.st_mode) ) { + sieve_storage_set_critical(storage, + "Sieve storage path `%s' configured using sieve_dir " + "is not a directory", storage_path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + + explicit = TRUE; + } + + if ( (active_path == NULL || *active_path == '\0') ) { + if ( storage->main_storage || + (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0) { + e_debug(storage->event, + "Active script path is unconfigured; " + "using default (path=%s)", + SIEVE_FILE_DEFAULT_PATH); + active_path = SIEVE_FILE_DEFAULT_PATH; + } else { + return -1; + } + } + + if ( !explicit && !exists && + active_path != NULL && *active_path != '\0' && + (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) == 0 ) + storage_path = NULL; + + if ( sieve_file_storage_init_common + (fstorage, active_path, storage_path, exists, error_r) < 0 ) + return -1; + return 0; +} + +struct sieve_storage *sieve_file_storage_init_legacy +(struct sieve_instance *svinst, const char *active_path, + const char *storage_path, enum sieve_storage_flags flags, + enum sieve_error *error_r) +{ + struct sieve_storage *storage; + struct sieve_file_storage *fstorage; + + storage = sieve_storage_alloc(svinst, NULL, &sieve_file_storage, + "", flags, TRUE); + fstorage = (struct sieve_file_storage *)storage; + + T_BEGIN { + if ( sieve_file_storage_do_init_legacy + (fstorage, active_path, storage_path, error_r) < 0 ) { + sieve_storage_unref(&storage); + storage = NULL; + } + } T_END; + + return storage; +} + +struct sieve_file_storage *sieve_file_storage_init_from_path +(struct sieve_instance *svinst, const char *path, + enum sieve_storage_flags flags, enum sieve_error *error_r) +{ + struct sieve_storage *storage; + struct sieve_file_storage *fstorage; + + i_assert( path != NULL ); + + storage = sieve_storage_alloc(svinst, NULL, &sieve_file_storage, + "", flags, FALSE); + fstorage = (struct sieve_file_storage *)storage; + + T_BEGIN { + if ( sieve_file_storage_init_common + (fstorage, path, NULL, FALSE, error_r) < 0 ) { + sieve_storage_unref(&storage); + fstorage = NULL; + } + } T_END; + + return fstorage; +} + +static int sieve_file_storage_is_singular +(struct sieve_storage *storage) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + struct stat st; + + if ( fstorage->active_path == NULL ) + return 1; + + /* Stat the file */ + if ( lstat(fstorage->active_path, &st) != 0 ) { + if ( errno != ENOENT ) { + sieve_storage_set_critical(storage, + "Failed to stat active sieve script symlink (%s): %m.", + fstorage->active_path); + return -1; + } + return 0; + } + + if ( S_ISLNK( st.st_mode ) ) + return 0; + if ( !S_ISREG( st.st_mode ) ) { + sieve_storage_set_critical(storage, + "Active sieve script file '%s' is no symlink nor a regular file.", + fstorage->active_path); + return -1; + } + return 1; +} + +/* + * + */ + +static int sieve_file_storage_get_last_change +(struct sieve_storage *storage, time_t *last_change_r) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + struct stat st; + + if ( fstorage->prev_mtime == (time_t)-1 ) { + /* Get the storage mtime before we modify it ourself */ + if ( stat(fstorage->path, &st) < 0 ) { + if ( errno != ENOENT ) { + e_error(storage->event, + "stat(%s) failed: %m", + fstorage->path); + return -1; + } + st.st_mtime = 0; + } + + fstorage->prev_mtime = st.st_mtime; + } + + if ( last_change_r != NULL ) + *last_change_r = fstorage->prev_mtime; + return 0; +} + +int sieve_file_storage_pre_modify +(struct sieve_storage *storage) +{ + i_assert( (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 ); + + return sieve_storage_get_last_change(storage, NULL); +} + +static void sieve_file_storage_set_modified +(struct sieve_storage *storage, time_t mtime) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + struct utimbuf times; + time_t cur_mtime; + + if ( mtime != (time_t)-1 ) { + if ( sieve_storage_get_last_change(storage, &cur_mtime) >= 0 && + cur_mtime > mtime ) + return; + } else { + mtime = ioloop_time; + } + + times.actime = mtime; + times.modtime = mtime; + if ( utime(fstorage->path, ×) < 0 ) { + switch ( errno ) { + case ENOENT: + break; + case EACCES: + e_error(storage->event, "%s", + eacces_error_get("utime", fstorage->path)); + break; + default: + e_error(storage->event, + "utime(%s) failed: %m", fstorage->path); + } + } else { + fstorage->prev_mtime = mtime; + } +} + +/* + * Script access + */ + +static struct sieve_script *sieve_file_storage_get_script +(struct sieve_storage *storage, const char *name) +{ + struct sieve_file_storage *fstorage = + (struct sieve_file_storage *)storage; + struct sieve_file_script *fscript; + + T_BEGIN { + fscript = sieve_file_script_init_from_name(fstorage, name); + } T_END; + + return &fscript->script; +} + +/* + * Driver definition + */ + +const struct sieve_storage sieve_file_storage = { + .driver_name = SIEVE_FILE_STORAGE_DRIVER_NAME, + .version = 0, + .allows_synchronization = TRUE, + .v = { + .alloc = sieve_file_storage_alloc, + .init = sieve_file_storage_init, + + .get_last_change = sieve_file_storage_get_last_change, + .set_modified = sieve_file_storage_set_modified, + + .is_singular = sieve_file_storage_is_singular, + + .get_script = sieve_file_storage_get_script, + + .get_script_sequence = sieve_file_storage_get_script_sequence, + .script_sequence_next = sieve_file_script_sequence_next, + .script_sequence_destroy = sieve_file_script_sequence_destroy, + + .active_script_get_name = sieve_file_storage_active_script_get_name, + .active_script_open = sieve_file_storage_active_script_open, + .deactivate = sieve_file_storage_deactivate, + .active_script_get_last_change = + sieve_file_storage_active_script_get_last_change, + + .list_init = sieve_file_storage_list_init, + .list_next = sieve_file_storage_list_next, + .list_deinit = sieve_file_storage_list_deinit, + + .save_alloc = sieve_file_storage_save_alloc, + .save_init = sieve_file_storage_save_init, + .save_continue = sieve_file_storage_save_continue, + .save_finish = sieve_file_storage_save_finish, + .save_get_tempscript = sieve_file_storage_save_get_tempscript, + .save_cancel = sieve_file_storage_save_cancel, + .save_commit = sieve_file_storage_save_commit, + .save_as = sieve_file_storage_save_as, + .save_as_active = sieve_file_storage_save_as_active, + + .quota_havespace = sieve_file_storage_quota_havespace + } +}; diff --git a/pigeonhole/src/lib-sieve/storage/file/sieve-file-storage.h b/pigeonhole/src/lib-sieve/storage/file/sieve-file-storage.h new file mode 100644 index 0000000..ea7b92f --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/file/sieve-file-storage.h @@ -0,0 +1,186 @@ +#ifndef SIEVE_FILE_STORAGE_H +#define SIEVE_FILE_STORAGE_H + +#include "lib.h" +#include "mail-user.h" + +#include "sieve.h" +#include "sieve-script-private.h" +#include "sieve-storage-private.h" + +#include <sys/types.h> +#include <sys/stat.h> + +#define SIEVE_FILE_READ_BLOCK_SIZE (1024*8) + +#define SIEVE_FILE_DEFAULT_PATH "~/.dovecot."SIEVE_SCRIPT_FILEEXT + +/* How often to scan tmp/ directory for old files (based on dir's atime) */ +#define SIEVE_FILE_STORAGE_TMP_SCAN_SECS (8*60*60) +/* Delete files having ctime older than this from tmp/. 36h is standard. */ +#define SIEVE_FILE_STORAGE_TMP_DELETE_SECS (36*60*60) + +/* + * Storage class + */ + +struct sieve_file_storage { + struct sieve_storage storage; + + const char *path; + const char *active_path; + const char *active_fname; + const char *link_path; + + struct stat st; + struct stat lnk_st; + + mode_t dir_create_mode; + mode_t file_create_mode; + gid_t file_create_gid; + + time_t prev_mtime; +}; + +const char *sieve_file_storage_path_extend + (struct sieve_file_storage *fstorage, const char *filename); + +struct sieve_file_storage *sieve_file_storage_init_from_path +(struct sieve_instance *svinst, const char *path, + enum sieve_storage_flags flags, enum sieve_error *error_r) + ATTR_NULL(4); + +int sieve_file_storage_pre_modify + (struct sieve_storage *storage); + +/* Active script */ + +int sieve_file_storage_active_replace_link + (struct sieve_file_storage *fstorage, const char *link_path); +bool sieve_file_storage_active_rescue_regular + (struct sieve_file_storage *fstorage); + +int sieve_file_storage_active_script_get_name + (struct sieve_storage *storage, const char **name_r); +struct sieve_script *sieve_file_storage_active_script_open + (struct sieve_storage *storage); + +int sieve_file_storage_active_script_get_file + (struct sieve_file_storage *fstorage, const char **file_r); +int sieve_file_storage_active_script_is_no_link + (struct sieve_file_storage *fstorage); + +int sieve_file_storage_deactivate + (struct sieve_storage *storage); + +int sieve_file_storage_active_script_get_last_change + (struct sieve_storage *storage, time_t *last_change_r); + +/* Listing */ + +struct sieve_storage_list_context *sieve_file_storage_list_init + (struct sieve_storage *storage); +const char *sieve_file_storage_list_next + (struct sieve_storage_list_context *ctx, bool *active); +int sieve_file_storage_list_deinit + (struct sieve_storage_list_context *lctx); + +/* Saving */ + +struct sieve_storage_save_context * +sieve_file_storage_save_alloc(struct sieve_storage *storage); +int sieve_file_storage_save_init(struct sieve_storage_save_context *sctx, + const char *scriptname, struct istream *input); +int sieve_file_storage_save_continue + (struct sieve_storage_save_context *sctx); +int sieve_file_storage_save_finish + (struct sieve_storage_save_context *sctx); +struct sieve_script *sieve_file_storage_save_get_tempscript + (struct sieve_storage_save_context *sctx); +int sieve_file_storage_save_commit + (struct sieve_storage_save_context *sctx); +void sieve_file_storage_save_cancel + (struct sieve_storage_save_context *sctx); + +int sieve_file_storage_save_as + (struct sieve_storage *storage, struct istream *input, + const char *name); +int sieve_file_storage_save_as_active + (struct sieve_storage *storage, struct istream *input, + time_t mtime); + +/* Quota */ + +int sieve_file_storage_quota_havespace +(struct sieve_storage *storage, const char *scriptname, size_t size, + enum sieve_storage_quota *quota_r, uint64_t *limit_r); + +/* + * Sieve script filenames + */ + +const char *sieve_script_file_get_scriptname(const char *filename); +const char *sieve_script_file_from_name(const char *name); + +/* + * Script class + */ + +struct sieve_file_script { + struct sieve_script script; + + struct stat st; + struct stat lnk_st; + + const char *path; + const char *dirpath; + const char *filename; + const char *binpath; + const char *binprefix; + + time_t prev_mtime; +}; + +struct sieve_file_script *sieve_file_script_init_from_filename + (struct sieve_file_storage *fstorage, const char *filename, + const char *scriptname); +struct sieve_file_script *sieve_file_script_open_from_filename + (struct sieve_file_storage *fstorage, const char *filename, + const char *scriptname); +struct sieve_file_script *sieve_file_script_init_from_name + (struct sieve_file_storage *fstorage, const char *name); +struct sieve_file_script *sieve_file_script_open_from_name + (struct sieve_file_storage *fstorage, const char *name); + +struct sieve_file_script *sieve_file_script_init_from_path + (struct sieve_file_storage *fstorage, const char *path, + const char *scriptname, enum sieve_error *error_r) + ATTR_NULL(4); +struct sieve_file_script *sieve_file_script_open_from_path + (struct sieve_file_storage *fstorage, const char *path, + const char *scriptname, enum sieve_error *error_r) + ATTR_NULL(4); + +/* Return directory where script resides in. Returns NULL if this is not a file + * script. + */ +const char *sieve_file_script_get_dirpath + (const struct sieve_script *script); + +/* Return full path to file script. Returns NULL if this is not a file script. + */ +const char *sieve_file_script_get_path + (const struct sieve_script *script); + +/* + * Script sequence + */ + +struct sieve_script_sequence *sieve_file_storage_get_script_sequence + (struct sieve_storage *storage, enum sieve_error *error_r); + +struct sieve_script *sieve_file_script_sequence_next + (struct sieve_script_sequence *seq, enum sieve_error *error_r); +void sieve_file_script_sequence_destroy(struct sieve_script_sequence *seq); + +#endif |