diff options
Diffstat (limited to 'pigeonhole/src')
505 files changed, 145087 insertions, 0 deletions
diff --git a/pigeonhole/src/Makefile.am b/pigeonhole/src/Makefile.am new file mode 100644 index 0000000..85c1e44 --- /dev/null +++ b/pigeonhole/src/Makefile.am @@ -0,0 +1,21 @@ + + +sieve_subdirs = \ + lib-sieve \ + plugins \ + lib-sieve-tool \ + sieve-tools \ + testsuite + +if BUILD_MANAGESIEVE +managesieve_subdirs = \ + lib-managesieve \ + managesieve \ + managesieve-login +else +managesieve_subdirs = +endif + +SUBDIRS = \ + $(sieve_subdirs) \ + $(managesieve_subdirs) diff --git a/pigeonhole/src/Makefile.in b/pigeonhole/src/Makefile.in new file mode 100644 index 0000000..4b09bcb --- /dev/null +++ b/pigeonhole/src/Makefile.in @@ -0,0 +1,709 @@ +# 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 +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 $(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 = +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 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +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 +DIST_SUBDIRS = lib-sieve plugins lib-sieve-tool sieve-tools testsuite \ + lib-managesieve managesieve managesieve-login +am__DIST_COMMON = $(srcdir)/Makefile.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +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@ +sieve_subdirs = \ + lib-sieve \ + plugins \ + lib-sieve-tool \ + sieve-tools \ + testsuite + +@BUILD_MANAGESIEVE_FALSE@managesieve_subdirs = +@BUILD_MANAGESIEVE_TRUE@managesieve_subdirs = \ +@BUILD_MANAGESIEVE_TRUE@ lib-managesieve \ +@BUILD_MANAGESIEVE_TRUE@ managesieve \ +@BUILD_MANAGESIEVE_TRUE@ managesieve-login + +SUBDIRS = \ + $(sieve_subdirs) \ + $(managesieve_subdirs) + +all: all-recursive + +.SUFFIXES: +$(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/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/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): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(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-recursive + +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-recursive + +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 + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +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-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-libtool cscopelist-am ctags \ + ctags-am distclean 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 \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean 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-managesieve/Makefile.am b/pigeonhole/src/lib-managesieve/Makefile.am new file mode 100644 index 0000000..b036680 --- /dev/null +++ b/pigeonhole/src/lib-managesieve/Makefile.am @@ -0,0 +1,15 @@ +noinst_LTLIBRARIES = libmanagesieve.la + +AM_CPPFLAGS = \ + $(LIBDOVECOT_INCLUDE) \ + -I$(top_srcdir) + +libmanagesieve_la_SOURCES = \ + managesieve-arg.c \ + managesieve-quote.c \ + managesieve-parser.c + +noinst_HEADERS = \ + managesieve-arg.h \ + managesieve-quote.h \ + managesieve-parser.h diff --git a/pigeonhole/src/lib-managesieve/Makefile.in b/pigeonhole/src/lib-managesieve/Makefile.in new file mode 100644 index 0000000..c52cc11 --- /dev/null +++ b/pigeonhole/src/lib-managesieve/Makefile.in @@ -0,0 +1,691 @@ +# 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-managesieve +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) +libmanagesieve_la_LIBADD = +am_libmanagesieve_la_OBJECTS = managesieve-arg.lo managesieve-quote.lo \ + managesieve-parser.lo +libmanagesieve_la_OBJECTS = $(am_libmanagesieve_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)/managesieve-arg.Plo \ + ./$(DEPDIR)/managesieve-parser.Plo \ + ./$(DEPDIR)/managesieve-quote.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 = $(libmanagesieve_la_SOURCES) +DIST_SOURCES = $(libmanagesieve_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 = libmanagesieve.la +AM_CPPFLAGS = \ + $(LIBDOVECOT_INCLUDE) \ + -I$(top_srcdir) + +libmanagesieve_la_SOURCES = \ + managesieve-arg.c \ + managesieve-quote.c \ + managesieve-parser.c + +noinst_HEADERS = \ + managesieve-arg.h \ + managesieve-quote.h \ + managesieve-parser.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-managesieve/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-managesieve/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}; \ + } + +libmanagesieve.la: $(libmanagesieve_la_OBJECTS) $(libmanagesieve_la_DEPENDENCIES) $(EXTRA_libmanagesieve_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libmanagesieve_la_OBJECTS) $(libmanagesieve_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/managesieve-arg.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/managesieve-parser.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/managesieve-quote.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)/managesieve-arg.Plo + -rm -f ./$(DEPDIR)/managesieve-parser.Plo + -rm -f ./$(DEPDIR)/managesieve-quote.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)/managesieve-arg.Plo + -rm -f ./$(DEPDIR)/managesieve-parser.Plo + -rm -f ./$(DEPDIR)/managesieve-quote.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-managesieve/managesieve-arg.c b/pigeonhole/src/lib-managesieve/managesieve-arg.c new file mode 100644 index 0000000..178bf45 --- /dev/null +++ b/pigeonhole/src/lib-managesieve/managesieve-arg.c @@ -0,0 +1,207 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "strescape.h" +#include "managesieve-arg.h" + +bool managesieve_arg_get_atom(const struct managesieve_arg *arg, + const char **str_r) +{ + if (arg->type != MANAGESIEVE_ARG_ATOM) + return FALSE; + + *str_r = arg->_data.str; + return TRUE; +} + +bool managesieve_arg_get_number(const struct managesieve_arg *arg, + uoff_t *number_r) +{ + const char *data; + uoff_t num = 0; + size_t i; + + if (arg->type != MANAGESIEVE_ARG_ATOM) + return FALSE; + + data = arg->_data.str; + for (i = 0; i < arg->str_len; i++) { + uoff_t newnum; + + if (data[i] < '0' || data[i] > '9') + return FALSE; + + newnum = num*10 + (data[i] -'0'); + if (newnum < num) + return FALSE; + + num = newnum; + } + + *number_r = num; + return TRUE; +} + +bool managesieve_arg_get_quoted(const struct managesieve_arg *arg, + const char **str_r) +{ + if (arg->type != MANAGESIEVE_ARG_STRING) + return FALSE; + + *str_r = arg->_data.str; + return TRUE; +} + +bool managesieve_arg_get_string(const struct managesieve_arg *arg, + const char **str_r) +{ + if (arg->type != MANAGESIEVE_ARG_STRING && + arg->type != MANAGESIEVE_ARG_LITERAL) + return FALSE; + + *str_r = arg->_data.str; + return TRUE; +} + +bool managesieve_arg_get_string_stream(const struct managesieve_arg *arg, + struct istream **stream_r) +{ + if (arg->type != MANAGESIEVE_ARG_STRING_STREAM) + return FALSE; + + *stream_r = arg->_data.str_stream; + return TRUE; +} + +bool managesieve_arg_get_list(const struct managesieve_arg *arg, + const struct managesieve_arg **list_r) +{ + unsigned int count; + + return managesieve_arg_get_list_full(arg, list_r, &count); +} + +bool managesieve_arg_get_list_full(const struct managesieve_arg *arg, + const struct managesieve_arg **list_r, + unsigned int *list_count_r) +{ + unsigned int count; + + if (arg->type != MANAGESIEVE_ARG_LIST) + return FALSE; + + *list_r = array_get(&arg->_data.list, &count); + + /* drop MANAGESIEVE_ARG_EOL from list size */ + i_assert(count > 0); + *list_count_r = count - 1; + return TRUE; +} + +const char *managesieve_arg_as_atom(const struct managesieve_arg *arg) +{ + const char *str; + + if (!managesieve_arg_get_atom(arg, &str)) + i_unreached(); + return str; +} + +const char *managesieve_arg_as_string(const struct managesieve_arg *arg) +{ + const char *str; + + if (!managesieve_arg_get_string(arg, &str)) + i_unreached(); + return str; +} + +struct istream * +managesieve_arg_as_string_stream(const struct managesieve_arg *arg) +{ + struct istream *stream; + + if (!managesieve_arg_get_string_stream(arg, &stream)) + i_unreached(); + return stream; +} + +const struct managesieve_arg * +managesieve_arg_as_list(const struct managesieve_arg *arg) +{ + const struct managesieve_arg *ret; + + if (!managesieve_arg_get_list(arg, &ret)) + i_unreached(); + return ret; +} + +bool managesieve_arg_atom_equals(const struct managesieve_arg *arg, + const char *str) +{ + const char *value; + + if (!managesieve_arg_get_atom(arg, &value)) + return FALSE; + return strcasecmp(value, str) == 0; +} + +void managesieve_write_arg(string_t *dest, const struct managesieve_arg *arg) +{ + const char *strval; + + switch (arg->type) { + case MANAGESIEVE_ARG_ATOM: + str_append(dest, managesieve_arg_as_atom(arg)); + break; + case MANAGESIEVE_ARG_STRING: + strval = managesieve_arg_as_string(arg); + str_append_c(dest, '"'); + str_append_escaped(dest, strval, strlen(strval)); + str_append_c(dest, '"'); + break; + case MANAGESIEVE_ARG_STRING_STREAM: + str_append(dest, "\"<too large>\""); + break; + case MANAGESIEVE_ARG_LITERAL: { + const char *strarg = managesieve_arg_as_string(arg); + str_printfa(dest, "{%zu}\r\n", + strlen(strarg)); + str_append(dest, strarg); + break; + } + case MANAGESIEVE_ARG_LIST: + str_append_c(dest, '('); + managesieve_write_args(dest, managesieve_arg_as_list(arg)); + str_append_c(dest, ')'); + break; + case MANAGESIEVE_ARG_NONE: + case MANAGESIEVE_ARG_EOL: + i_unreached(); + } +} + +void managesieve_write_args(string_t *dest, const struct managesieve_arg *args) +{ + bool first = TRUE; + + for (; !MANAGESIEVE_ARG_IS_EOL(args); args++) { + if (first) + first = FALSE; + else + str_append_c(dest, ' '); + managesieve_write_arg(dest, args); + } +} + +const char *managesieve_args_to_str(const struct managesieve_arg *args) +{ + string_t *str; + + str = t_str_new(128); + managesieve_write_args(str, args); + return str_c(str); +} + diff --git a/pigeonhole/src/lib-managesieve/managesieve-arg.h b/pigeonhole/src/lib-managesieve/managesieve-arg.h new file mode 100644 index 0000000..23f7c05 --- /dev/null +++ b/pigeonhole/src/lib-managesieve/managesieve-arg.h @@ -0,0 +1,117 @@ +#ifndef MANAGESIEVE_ARG_H +#define MANAGESIEVE_ARG_H + +#include "array.h" + +/* + * QUOTED-SPECIALS = <"> / "\" + */ +#define IS_QUOTED_SPECIAL(c) \ + ((c) == '"' || (c) == '\\') + +/* + * ATOM-SPECIALS = "(" / ")" / "{" / SP / CTL / QUOTED-SPECIALS + */ +#define IS_ATOM_SPECIAL(c) \ + ((c) == '(' || (c) == ')' || (c) == '{' || \ + (c) <= 32 || (c) == 0x7f || \ + IS_QUOTED_SPECIAL(c)) + +/* + * CHAR = %x01-7F + */ +#define IS_CHAR(c) \ + (((c) & 0x80) == 0) + +/* + * TEXT-CHAR = %x01-09 / %x0B-0C / %x0E-7F + * ;; any CHAR except CR and LF + */ +#define IS_TEXT_CHAR(c) \ + (IS_CHAR(c) && (c) != '\r' && (c) != '\n') + +/* + * SAFE-CHAR = %x01-09 / %x0B-0C / %x0E-21 / + * %x23-5B / %x5D-7F + * ;; any TEXT-CHAR except QUOTED-SPECIALS + */ +#define IS_SAFE_CHAR(c) \ + (IS_TEXT_CHAR(c) && !IS_QUOTED_SPECIAL(c)) + +enum managesieve_arg_type { + MANAGESIEVE_ARG_NONE = 0, + MANAGESIEVE_ARG_ATOM, + MANAGESIEVE_ARG_STRING, + MANAGESIEVE_ARG_STRING_STREAM, + + MANAGESIEVE_ARG_LIST, + + /* literals are returned as MANAGESIEVE_ARG_STRING by default */ + MANAGESIEVE_ARG_LITERAL, + + MANAGESIEVE_ARG_EOL /* end of argument list */ +}; + +ARRAY_DEFINE_TYPE(managesieve_arg_list, struct managesieve_arg); +struct managesieve_arg { + enum managesieve_arg_type type; + /* parent argument; always of type MANAGESIEVE_ARG_LIST */ + struct managesieve_arg *parent; + + /* Set when _data.str is set */ + size_t str_len; + + union { + const char *str; + struct istream *str_stream; + ARRAY_TYPE(managesieve_arg_list) list; + } _data; +}; + +#define MANAGESIEVE_ARG_IS_EOL(arg) \ + ((arg)->type == MANAGESIEVE_ARG_EOL) + +bool managesieve_arg_get_atom(const struct managesieve_arg *arg, + const char **str_r) ATTR_WARN_UNUSED_RESULT; +bool managesieve_arg_get_number(const struct managesieve_arg *arg, + uoff_t *number_r) ATTR_WARN_UNUSED_RESULT; +bool managesieve_arg_get_quoted(const struct managesieve_arg *arg, + const char **str_r) ATTR_WARN_UNUSED_RESULT; +bool managesieve_arg_get_string(const struct managesieve_arg *arg, + const char **str_r) ATTR_WARN_UNUSED_RESULT; + +bool managesieve_arg_get_string_stream(const struct managesieve_arg *arg, + struct istream **stream_r) + ATTR_WARN_UNUSED_RESULT; + +bool managesieve_arg_get_list(const struct managesieve_arg *arg, + const struct managesieve_arg **list_r) + ATTR_WARN_UNUSED_RESULT; +bool managesieve_arg_get_list_full(const struct managesieve_arg *arg, + const struct managesieve_arg **list_r, + unsigned int *list_count_r) + ATTR_WARN_UNUSED_RESULT; + +/* Similar to above, but assumes that arg is already of correct type. */ +const char *managesieve_arg_as_atom(const struct managesieve_arg *arg); +const char *managesieve_arg_as_string(const struct managesieve_arg *arg); +struct istream * +managesieve_arg_as_string_stream(const struct managesieve_arg *arg); +const struct managesieve_arg * +managesieve_arg_as_list(const struct managesieve_arg *arg); + +/* Returns TRUE if arg is atom and case-insensitively matches str */ +bool managesieve_arg_atom_equals(const struct managesieve_arg *arg, + const char *str); + +/* Write ManageSieve arg to the given string. Because + MANAGESIVE_ARG_LITERAL_SIZE* have no content, they're written as + "{size}\r\n<too large>". */ +void managesieve_write_arg(string_t *dest, const struct managesieve_arg *arg); +/* Same as managesieve_write_arg(), but write all the args until EOL. */ +void managesieve_write_args(string_t *dest, const struct managesieve_arg *args); +/* Like managesieve_write_args(), but return the string allocated from data + stack. */ +const char *managesieve_args_to_str(const struct managesieve_arg *args); + +#endif diff --git a/pigeonhole/src/lib-managesieve/managesieve-parser.c b/pigeonhole/src/lib-managesieve/managesieve-parser.c new file mode 100644 index 0000000..546e431 --- /dev/null +++ b/pigeonhole/src/lib-managesieve/managesieve-parser.c @@ -0,0 +1,757 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "unichar.h" +#include "istream-private.h" +#include "ostream.h" +#include "strescape.h" +#include "managesieve-parser.h" + +#define is_linebreak(c) \ + ((c) == '\r' || (c) == '\n') + +#define LIST_INIT_COUNT 7 + +enum arg_parse_type { + ARG_PARSE_NONE = 0, + ARG_PARSE_ATOM, + ARG_PARSE_STRING, + ARG_PARSE_LITERAL, + ARG_PARSE_LITERAL_DATA +}; + +struct managesieve_parser { + /* permanent */ + pool_t pool; + struct istream *input; + size_t max_line_size; + enum managesieve_parser_flags flags; + + /* reset by managesieve_parser_reset(): */ + size_t line_size; + ARRAY_TYPE(managesieve_arg_list) root_list; + ARRAY_TYPE(managesieve_arg_list) *cur_list; + struct managesieve_arg *list_arg; + + enum arg_parse_type cur_type; + size_t cur_pos; /* parser position in input buffer */ + + int str_first_escape; /* ARG_PARSE_STRING: index to first '\' */ + uoff_t literal_size; /* ARG_PARSE_LITERAL: string size */ + + struct istream *str_stream; + + const char *error; + + bool literal_skip_crlf:1; + bool literal_nonsync:1; + bool eol:1; + bool fatal_error:1; +}; + +static struct istream *quoted_string_istream_create + (struct managesieve_parser *parser); + +struct managesieve_parser * +managesieve_parser_create(struct istream *input, size_t max_line_size) +{ + struct managesieve_parser *parser; + + parser = i_new(struct managesieve_parser, 1); + parser->pool = pool_alloconly_create("MANAGESIEVE parser", 8192); + parser->input = input; + parser->max_line_size = max_line_size; + + p_array_init(&parser->root_list, parser->pool, LIST_INIT_COUNT); + parser->cur_list = &parser->root_list; + return parser; +} + +void managesieve_parser_destroy(struct managesieve_parser **parser) +{ + i_stream_unref(&(*parser)->str_stream); + + pool_unref(&(*parser)->pool); + i_free(*parser); + *parser = NULL; +} + +void managesieve_parser_reset(struct managesieve_parser *parser) +{ + p_clear(parser->pool); + + parser->line_size = 0; + + p_array_init(&parser->root_list, parser->pool, LIST_INIT_COUNT); + parser->cur_list = &parser->root_list; + parser->list_arg = NULL; + + parser->cur_type = ARG_PARSE_NONE; + parser->cur_pos = 0; + + parser->str_first_escape = 0; + parser->literal_size = 0; + + parser->error = NULL; + + parser->literal_skip_crlf = FALSE; + parser->eol = FALSE; + + i_stream_unref(&parser->str_stream); +} + +const char * +managesieve_parser_get_error(struct managesieve_parser *parser, bool *fatal) +{ + *fatal = parser->fatal_error; + return parser->error; +} + +/* Skip over everything parsed so far, plus the following whitespace */ +static bool +managesieve_parser_skip_to_next(struct managesieve_parser *parser, + const unsigned char **data, size_t *data_size) +{ + size_t i; + + for (i = parser->cur_pos; i < *data_size; i++) { + if ((*data)[i] != ' ') + break; + } + + parser->line_size += i; + i_stream_skip(parser->input, i); + parser->cur_pos = 0; + + *data += i; + *data_size -= i; + return *data_size > 0; +} + +static struct managesieve_arg * +managesieve_arg_create(struct managesieve_parser *parser) +{ + struct managesieve_arg *arg; + + arg = array_append_space(parser->cur_list); + arg->parent = parser->list_arg; + + return arg; +} + +static void +managesieve_parser_save_arg(struct managesieve_parser *parser, + const unsigned char *data, size_t size) +{ + struct managesieve_arg *arg; + char *str; + + arg = managesieve_arg_create(parser); + + switch (parser->cur_type) { + case ARG_PARSE_ATOM: + /* Simply save the string */ + arg->type = MANAGESIEVE_ARG_ATOM; + arg->_data.str = p_strndup(parser->pool, data, size); + arg->str_len = size; + break; + case ARG_PARSE_STRING: + /* Data is quoted and may contain escapes. */ + if ((parser->flags & + MANAGESIEVE_PARSE_FLAG_STRING_STREAM) != 0) { + arg->type = MANAGESIEVE_ARG_STRING_STREAM; + arg->_data.str_stream = parser->str_stream; + } else { + i_assert(size > 0); + + arg->type = MANAGESIEVE_ARG_STRING; + str = p_strndup(parser->pool, data+1, size-1); + + /* remove the escapes */ + if (parser->str_first_escape >= 0 && + (parser->flags & + MANAGESIEVE_PARSE_FLAG_NO_UNESCAPE) == 0) + (void)str_unescape(str); + + arg->_data.str = str; + arg->str_len = strlen(str); + } + break; + case ARG_PARSE_LITERAL_DATA: + if ((parser->flags & + MANAGESIEVE_PARSE_FLAG_STRING_STREAM) != 0) { + arg->type = MANAGESIEVE_ARG_STRING_STREAM; + arg->_data.str_stream = parser->str_stream; + } else if ((parser->flags & + MANAGESIEVE_PARSE_FLAG_LITERAL_TYPE) != 0) { + arg->type = MANAGESIEVE_ARG_LITERAL; + arg->_data.str = p_strndup(parser->pool, data, size); + arg->str_len = size; + } else { + arg->type = MANAGESIEVE_ARG_STRING; + arg->_data.str = p_strndup(parser->pool, data, size); + arg->str_len = size; + } + break; + default: + i_unreached(); + } + + parser->cur_type = ARG_PARSE_NONE; +} + +static bool is_valid_atom_char(struct managesieve_parser *parser, char chr) +{ + if (IS_ATOM_SPECIAL((unsigned char)chr)) { + parser->error = "Invalid characters in atom"; + return FALSE; + } else if ((((unsigned char)chr) & 0x80) != 0) { + parser->error = "8bit data in atom"; + return FALSE; + } + + return TRUE; +} + +static bool +managesieve_parser_read_atom(struct managesieve_parser *parser, + const unsigned char *data, size_t data_size) +{ + size_t i; + + /* Read until we've found space, CR or LF. */ + for (i = parser->cur_pos; i < data_size; i++) { + if (data[i] == ' ' || data[i] == ')' || + is_linebreak(data[i])) { + managesieve_parser_save_arg(parser, data, i); + break; + } else if (!is_valid_atom_char(parser, data[i])) + return FALSE; + } + + parser->cur_pos = i; + return (parser->cur_type == ARG_PARSE_NONE); +} + +static bool +managesieve_parser_read_string(struct managesieve_parser *parser, + const unsigned char *data, size_t data_size) +{ + size_t i; + + /* QUOTED-CHAR = SAFE-UTF8-CHAR / "\" QUOTED-SPECIALS + quoted = <"> *QUOTED-CHAR <"> + ;; limited to 1024 octets between the <">s + */ + + /* Read until we've found non-escaped ", CR or LF */ + for (i = parser->cur_pos; i < data_size; i++) { + if (data[i] == '"') { + if (!uni_utf8_data_is_valid(data+1, i-1)) { + parser->error = + "Invalid UTF-8 character in quoted-string."; + return FALSE; + } + + managesieve_parser_save_arg(parser, data, i); + i++; /* skip the trailing '"' too */ + break; + } + + if (data[i] == '\0') { + parser->error = "NULs not allowed in strings"; + return FALSE; + } + + if (data[i] == '\\') { + if (i+1 == data_size) { + /* Known data ends with '\' - leave it to next + time as well if it happens to be \" */ + break; + } + + /* Save the first escaped char */ + if (parser->str_first_escape < 0) + parser->str_first_escape = i; + + /* Skip the escaped char */ + i++; + + if (!IS_QUOTED_SPECIAL(data[i])) { + parser->error = + "Escaped quoted-string character is not a QUOTED-SPECIAL."; + return FALSE; + } + continue; + } + + if ((data[i] & 0x80) == 0 && !IS_SAFE_CHAR(data[i])) { + parser->error = "String contains invalid character."; + return FALSE; + } + } + + parser->cur_pos = i; + return (parser->cur_type == ARG_PARSE_NONE); +} + +static bool managesieve_parser_literal_end(struct managesieve_parser *parser) +{ + if ((parser->flags & MANAGESIEVE_PARSE_FLAG_STRING_STREAM) == 0) { + if (parser->line_size >= parser->max_line_size || + (parser->literal_size > + (parser->max_line_size - parser->line_size))) { + /* Too long string, abort. */ + parser->error = "Literal size too large"; + parser->fatal_error = TRUE; + return FALSE; + } + } + + parser->cur_type = ARG_PARSE_LITERAL_DATA; + parser->literal_skip_crlf = TRUE; + + parser->cur_pos = 0; + return TRUE; +} + +static bool +managesieve_parser_read_literal(struct managesieve_parser *parser, + const unsigned char *data, size_t data_size) +{ + size_t i; + + /* Expecting digits + "}" */ + for (i = parser->cur_pos; i < data_size; i++) { + if (data[i] == '}') { + parser->line_size += i+1; + i_stream_skip(parser->input, i+1); + + return managesieve_parser_literal_end(parser); + } + + if (parser->literal_nonsync) { + parser->error = "Expecting '}' after '+'"; + return FALSE; + } + + if (data[i] == '+') { + parser->literal_nonsync = TRUE; + continue; + } + + if (data[i] < '0' || data[i] > '9') { + parser->error = "Invalid literal size"; + return FALSE; + } + + if (parser->literal_size >= ((uoff_t)-1 / 10)) { + if (parser->literal_size > ((uoff_t)-1 / 10) || + (uoff_t)(data[i] - '0') > ((uoff_t)-1 % 10)) { + parser->error = "Literal size too large"; + return FALSE; + } + } + parser->literal_size = parser->literal_size * 10 + + (data[i] - '0'); + } + + parser->cur_pos = i; + return FALSE; +} + +static bool +managesieve_parser_read_literal_data(struct managesieve_parser *parser, + const unsigned char *data, + size_t data_size) +{ + if (parser->literal_skip_crlf) { + /* Skip \r\n or \n, anything else gives an error */ + if (data_size == 0) + return FALSE; + + if (*data == '\r') { + parser->line_size++; + data++; data_size--; + i_stream_skip(parser->input, 1); + + if (data_size == 0) + return FALSE; + } + + if (*data != '\n') { + parser->error = "Missing LF after literal size"; + return FALSE; + } + + parser->line_size++; + data++; data_size--; + i_stream_skip(parser->input, 1); + + parser->literal_skip_crlf = FALSE; + + i_assert(parser->cur_pos == 0); + } + + if ((parser->flags & MANAGESIEVE_PARSE_FLAG_STRING_STREAM) == 0) { + /* Now we just wait until we've read enough data */ + if (data_size < parser->literal_size) { + return FALSE; + } else { + if (!uni_utf8_data_is_valid( + data, (size_t)parser->literal_size)) { + parser->error = + "Invalid UTF-8 character in literal string."; + return FALSE; + } + + managesieve_parser_save_arg( + parser, data, (size_t)parser->literal_size); + parser->cur_pos = (size_t)parser->literal_size; + return TRUE; + } + } else { + /* We don't read the data; we just create a stream for the + literal */ + parser->eol = TRUE; + parser->str_stream = i_stream_create_limit( + parser->input, parser->literal_size); + managesieve_parser_save_arg(parser, NULL, 0); + return TRUE; + } +} + +/* Returns TRUE if argument was fully processed. Also returns TRUE if an + argument inside a list was processed. */ +static bool managesieve_parser_read_arg(struct managesieve_parser *parser) +{ + const unsigned char *data; + size_t data_size; + + data = i_stream_get_data(parser->input, &data_size); + if (data_size == 0) + return FALSE; + + while (parser->cur_type == ARG_PARSE_NONE) { + /* We haven't started parsing yet */ + if (!managesieve_parser_skip_to_next(parser, &data, &data_size)) + return FALSE; + i_assert(parser->cur_pos == 0); + + switch (data[0]) { + case '\r': + case '\n': + /* Unexpected end of line */ + parser->eol = TRUE; + return FALSE; + case '"': + parser->cur_type = ARG_PARSE_STRING; + parser->str_first_escape = -1; + break; + case '{': + parser->cur_type = ARG_PARSE_LITERAL; + parser->literal_size = 0; + parser->literal_nonsync = FALSE; + break; + default: + if (!is_valid_atom_char(parser, data[0])) + return FALSE; + parser->cur_type = ARG_PARSE_ATOM; + break; + } + parser->cur_pos++; + } + + i_assert(data_size > 0); + + switch (parser->cur_type) { + case ARG_PARSE_ATOM: + if (!managesieve_parser_read_atom(parser, data, data_size)) + return FALSE; + break; + case ARG_PARSE_STRING: + if ((parser->flags & + MANAGESIEVE_PARSE_FLAG_STRING_STREAM) != 0) { + parser->eol = TRUE; + parser->line_size += parser->cur_pos; + i_stream_skip(parser->input, parser->cur_pos); + parser->cur_pos = 0; + parser->str_stream = + quoted_string_istream_create(parser); + managesieve_parser_save_arg(parser, NULL, 0); + + } else if (!managesieve_parser_read_string(parser, + data, data_size)) { + return FALSE; + } + break; + case ARG_PARSE_LITERAL: + if (!managesieve_parser_read_literal(parser, data, data_size)) + return FALSE; + + /* Pass through to parsing data. since input->skip was + modified, we need to get the data start position again. */ + data = i_stream_get_data(parser->input, &data_size); + + /* fall through */ + case ARG_PARSE_LITERAL_DATA: + if (!managesieve_parser_read_literal_data(parser, + data, data_size)) + return FALSE; + break; + default: + i_unreached(); + } + + i_assert(parser->cur_type == ARG_PARSE_NONE); + return TRUE; +} + +/* ARG_PARSE_NONE checks that last argument isn't only partially parsed. */ +#define IS_UNFINISHED(parser) \ + ((parser)->cur_type != ARG_PARSE_NONE || \ + (parser)->cur_list != &parser->root_list) + +static int +finish_line(struct managesieve_parser *parser, unsigned int count, + const struct managesieve_arg **args_r) +{ + struct managesieve_arg *arg; + int ret = array_count(&parser->root_list); + + parser->line_size += parser->cur_pos; + i_stream_skip(parser->input, parser->cur_pos); + parser->cur_pos = 0; + + /* Fill the missing parameters with NILs */ + while (count > array_count(&parser->root_list)) { + arg = array_append_space(&parser->root_list); + arg->type = MANAGESIEVE_ARG_NONE; + } + arg = array_append_space(&parser->root_list); + arg->type = MANAGESIEVE_ARG_EOL; + + *args_r = array_get(&parser->root_list, &count); + return ret; +} + +int managesieve_parser_read_args(struct managesieve_parser *parser, + unsigned int count, + enum managesieve_parser_flags flags, + const struct managesieve_arg **args_r) +{ + parser->flags = flags; + + while (!parser->eol && (count == 0 || IS_UNFINISHED(parser) || + array_count(&parser->root_list) < count)) { + if (!managesieve_parser_read_arg(parser)) + break; + + if (parser->line_size > parser->max_line_size) { + parser->error = "MANAGESIEVE command line too large"; + break; + } + } + + if (parser->error != NULL) { + /* Error, abort */ + parser->line_size += parser->cur_pos; + i_stream_skip(parser->input, parser->cur_pos); + parser->cur_pos = 0; + *args_r = NULL; + return -1; + } else if ((!IS_UNFINISHED(parser) && count > 0 && + array_count(&parser->root_list) >= count) || parser->eol) { + /* All arguments read / end of line. */ + return finish_line(parser, count, args_r); + } else { + /* Need more data */ + *args_r = NULL; + return -2; + } +} + +int managesieve_parser_finish_line(struct managesieve_parser *parser, + unsigned int count, + enum managesieve_parser_flags flags, + const struct managesieve_arg **args_r) +{ + const unsigned char *data; + size_t data_size; + int ret; + + ret = managesieve_parser_read_args(parser, count, flags, args_r); + if (ret == -2) { + /* We should have noticed end of everything except atom */ + if (parser->cur_type == ARG_PARSE_ATOM) { + data = i_stream_get_data(parser->input, &data_size); + managesieve_parser_save_arg(parser, data, data_size); + } + } + return finish_line(parser, count, args_r); +} + +const char *managesieve_parser_read_word(struct managesieve_parser *parser) +{ + const unsigned char *data; + size_t i, data_size; + + data = i_stream_get_data(parser->input, &data_size); + + for (i = 0; i < data_size; i++) { + if (data[i] == ' ' || data[i] == '\r' || data[i] == '\n') + break; + } + + if (i < data_size) { + data_size = i + (data[i] == ' ' ? 1 : 0); + parser->line_size += data_size; + i_stream_skip(parser->input, data_size); + return p_strndup(parser->pool, data, i); + } else { + return NULL; + } +} + +/* + * Quoted string stream + */ + +struct quoted_string_istream { + struct istream_private istream; + + /* The end '"' was found */ + bool str_end:1; +}; + +static int +quoted_string_istream_read_parent(struct quoted_string_istream *qsstream, + unsigned int min_bytes) +{ + struct istream_private *stream = &qsstream->istream; + size_t size, avail; + ssize_t ret; + + size = i_stream_get_data_size(stream->parent); + while (size < min_bytes) { + ret = i_stream_read_memarea(stream->parent); + if (ret <= 0) { + if (ret == -2) { + /* Tiny parent buffer size - shouldn't happen */ + return -2; + } + stream->istream.stream_errno = + stream->parent->stream_errno; + stream->istream.eof = stream->parent->eof; + if (ret == -1 && stream->istream.stream_errno == 0) { + io_stream_set_error(&stream->iostream, + "Quoted string ends without closing quotes"); + stream->istream.stream_errno = EPIPE; + } + return ret; + } + size = i_stream_get_data_size(stream->parent); + } + + if (!i_stream_try_alloc(stream, size, &avail)) + return -2; + return 1; +} + +static ssize_t quoted_string_istream_read(struct istream_private *stream) +{ + struct quoted_string_istream *qsstream = + (struct quoted_string_istream *)stream; + const unsigned char *data; + unsigned int extra; + size_t i, dest, size; + ssize_t ret; + + if (qsstream->str_end) { + stream->istream.eof = TRUE; + return -1; + } + + ret = quoted_string_istream_read_parent(qsstream, 1); + if (ret <= 0) + return ret; + + /* @UNSAFE */ + dest = stream->pos; + extra = 0; + + data = i_stream_get_data(stream->parent, &size); + for (i = 0; i < size && dest < stream->buffer_size; ) { + if (data[i] == '"') { + i++; + qsstream->str_end = TRUE; + if (dest == stream->pos) { + i_stream_skip(stream->parent, i); + stream->istream.eof = TRUE; + return -1; + } + break; + } else if (data[i] == '\\') { + if (i+1 == size) { + /* Not enough input for \x */ + extra = 1; + break; + } + i++; + + if (!IS_QUOTED_SPECIAL(data[i])) { + /* Invalid string */ + io_stream_set_error(&stream->iostream, + "Escaped quoted-string character is not a QUOTED-SPECIAL"); + stream->istream.stream_errno = EINVAL; + return -1; + } + stream->w_buffer[dest++] = data[i]; + i++; + } else { + if (data[i] == '\r' || data[i] == '\n') { + io_stream_set_error(&stream->iostream, + "Quoted string contains an invalid character"); + stream->istream.stream_errno = EINVAL; + return -1; + } + + stream->w_buffer[dest++] = data[i]; + i++; + } + i_assert(dest <= stream->buffer_size); + } + i_stream_skip(stream->parent, i); + + ret = dest - stream->pos; + if (ret == 0) { + /* Not enough input */ + i_assert(i == 0); + i_assert(extra > 0); + ret = quoted_string_istream_read_parent(qsstream, extra+1); + if (ret <= 0) + return ret; + return quoted_string_istream_read(stream); + } + i_assert(ret > 0); + stream->pos = dest; + return ret; +} + +static struct +istream *quoted_string_istream_create(struct managesieve_parser *parser) +{ + struct quoted_string_istream *qsstream; + + qsstream = i_new(struct quoted_string_istream, 1); + qsstream->istream.max_buffer_size = + parser->input->real_stream->max_buffer_size; + qsstream->istream.read = quoted_string_istream_read; + + qsstream->istream.istream.readable_fd = FALSE; + qsstream->istream.istream.blocking = parser->input->blocking; + qsstream->istream.istream.seekable = FALSE; + return i_stream_create(&qsstream->istream, parser->input, + i_stream_get_fd(parser->input), 0); +} diff --git a/pigeonhole/src/lib-managesieve/managesieve-parser.h b/pigeonhole/src/lib-managesieve/managesieve-parser.h new file mode 100644 index 0000000..93e67ae --- /dev/null +++ b/pigeonhole/src/lib-managesieve/managesieve-parser.h @@ -0,0 +1,67 @@ +#ifndef MANAGESIEVE_PARSER_H +#define MANAGESIEVE_PARSER_H + +#include "managesieve-arg.h" + +enum managesieve_parser_flags { + /* Set this flag if you want to read a string argument as as stream. + Useful when you need to deal with large strings. The string must be + the last read argument. */ + MANAGESIEVE_PARSE_FLAG_STRING_STREAM = 0x01, + /* Don't remove '\' chars from string arguments */ + MANAGESIEVE_PARSE_FLAG_NO_UNESCAPE = 0x02, + /* Return literals as MANAGESIEVE_ARG_LITERAL instead of + MANAGESIEVE_ARG_STRING */ + MANAGESIEVE_PARSE_FLAG_LITERAL_TYPE = 0x04 +}; + +struct managesieve_parser; + +/* Create new MANAGESIEVE argument parser. + + max_line_size can be used to approximately limit the maximum amount of memory + that gets allocated when parsing a line. Input buffer size limits the maximum + size of each parsed token. + + Usually the largest lines are large only because they have a one huge message + set token, so you'll probably want to keep input buffer size the same as + max_line_size. That means the maximum memory usage is around + 2 * max_line_size. */ +struct managesieve_parser * +managesieve_parser_create(struct istream *input, size_t max_line_size); +void managesieve_parser_destroy(struct managesieve_parser **parser); + +/* Reset the parser to initial state. */ +void managesieve_parser_reset(struct managesieve_parser *parser); + +/* Return the last error in parser. fatal is set to TRUE if there's no way to + continue parsing, currently only if too large non-sync literal size was + given. */ +const char * +managesieve_parser_get_error(struct managesieve_parser *parser, bool *fatal); + +/* Read a number of arguments. This function doesn't call i_stream_read(), you + need to do that. Returns number of arguments read (may be less than count + in case of EOL), -2 if more data is needed or -1 if error occurred. + + count-sized array of arguments are stored into args when return value is 0 or + larger. If all arguments weren't read, they're set to NIL. count can be set + to 0 to read all arguments in the line. Last element in args is always of + type MANAGESIEVE_ARG_EOL. */ +int managesieve_parser_read_args(struct managesieve_parser *parser, + unsigned int count, + enum managesieve_parser_flags flags, + const struct managesieve_arg **args_r); + +/* Just like managesieve_parser_read_args(), but assume \n at end of data in + input stream. */ +int managesieve_parser_finish_line(struct managesieve_parser *parser, + unsigned int count, + enum managesieve_parser_flags flags, + const struct managesieve_arg **args_r); + +/* Read one word - used for reading command name. Returns NULL if more data is + needed. */ +const char *managesieve_parser_read_word(struct managesieve_parser *parser); + +#endif diff --git a/pigeonhole/src/lib-managesieve/managesieve-quote.c b/pigeonhole/src/lib-managesieve/managesieve-quote.c new file mode 100644 index 0000000..a86818c --- /dev/null +++ b/pigeonhole/src/lib-managesieve/managesieve-quote.c @@ -0,0 +1,121 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "unichar.h" +#include "managesieve-parser.h" +#include "managesieve-quote.h" + +/* Turn the value string into a valid MANAGESIEVE string or literal, no matter + * what. QUOTED-SPECIALS are escaped, but any invalid (UTF-8) character + * is simply removed. Linebreak characters are not considered invalid, but + * they do force the generation of a string literal. + */ +void managesieve_quote_append(string_t *str, const unsigned char *value, + size_t value_len, bool compress_lwsp) +{ + size_t i, extra = 0, escape = 0; + string_t *tmp; + bool + last_lwsp = TRUE, + literal = FALSE, + modify = FALSE; + + if (value == NULL) { + str_append(str, "\"\""); + return; + } + + if (value_len == (size_t)-1) + value_len = strlen((const char *) value); + + for (i = 0; i < value_len; i++) { + switch (value[i]) { + case ' ': + case '\t': + if (last_lwsp && compress_lwsp) { + modify = TRUE; + extra++; + } + last_lwsp = TRUE; + break; + case '"': + case '\\': + escape++; + last_lwsp = FALSE; + break; + case 13: + case 10: + literal = TRUE; + last_lwsp = TRUE; + break; + default: + last_lwsp = FALSE; + } + } + + if (!literal) { + /* no linebreak chars, return as (escaped) "string" */ + str_append_c(str, '"'); + } else { + /* return as literal */ + str_printfa(str, "{%zu}\r\n", value_len - extra); + } + + tmp = t_str_new(value_len+escape+4); + if (!modify && (literal || escape == 0)) + str_append_data(tmp, value, value_len); + else { + last_lwsp = TRUE; + for (i = 0; i < value_len; i++) { + switch (value[i]) { + case '"': + case '\\': + last_lwsp = FALSE; + if (!literal) + str_append_c(tmp, '\\'); + str_append_c(tmp, value[i]); + break; + case ' ': + case '\t': + if (!last_lwsp || !compress_lwsp) + str_append_c(tmp, ' '); + last_lwsp = TRUE; + break; + case 13: + case 10: + last_lwsp = TRUE; + str_append_c(tmp, value[i]); + break; + default: + last_lwsp = FALSE; + str_append_c(tmp, value[i]); + break; + } + } + } + + if ( uni_utf8_get_valid_data(str_data(tmp), str_len(tmp), str) ) + str_append_str(str, tmp); + + if (!literal) + str_append_c(str, '"'); +} + +char *managesieve_quote(pool_t pool, const unsigned char *value, size_t value_len) +{ + string_t *str; + char *ret; + + if (value == NULL) + return "\"\""; + + T_BEGIN { + str = t_str_new(value_len + MAX_INT_STRLEN + 5); + managesieve_quote_append(str, value, value_len, TRUE); + ret = p_strndup(pool, str_data(str), str_len(str)); + } T_END; + + return ret; +} diff --git a/pigeonhole/src/lib-managesieve/managesieve-quote.h b/pigeonhole/src/lib-managesieve/managesieve-quote.h new file mode 100644 index 0000000..cae5491 --- /dev/null +++ b/pigeonhole/src/lib-managesieve/managesieve-quote.h @@ -0,0 +1,17 @@ +#ifndef IMAP_QUOTE_H +#define IMAP_QUOTE_H + +/* Return value suitable for sending to client, either as quoted-string or + literal. Note that this also converts TABs into spaces, multiple spaces + into single space and NULs to #128. */ +char *managesieve_quote(pool_t pool, const unsigned char *value, size_t value_len); + +/* Append to existing string. */ +void managesieve_quote_append(string_t *str, const unsigned char *value, + size_t value_len, bool compress_lwsp); + +#define managesieve_quote_append_string(str, value, compress_lwsp) \ + managesieve_quote_append(str, (const unsigned char *)(value), \ + (size_t)-1, compress_lwsp) + +#endif diff --git a/pigeonhole/src/lib-sieve-tool/Makefile.am b/pigeonhole/src/lib-sieve-tool/Makefile.am new file mode 100644 index 0000000..3386824 --- /dev/null +++ b/pigeonhole/src/lib-sieve-tool/Makefile.am @@ -0,0 +1,13 @@ +noinst_LTLIBRARIES = libsieve-tool.la + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib-sieve \ + -I$(top_srcdir)/src/lib-sieve/util \ + $(LIBDOVECOT_INCLUDE) \ + $(LIBDOVECOT_SERVICE_INCLUDE) + +libsieve_tool_la_SOURCES = \ + sieve-tool.c + +noinst_HEADERS = \ + sieve-tool.h diff --git a/pigeonhole/src/lib-sieve-tool/Makefile.in b/pigeonhole/src/lib-sieve-tool/Makefile.in new file mode 100644 index 0000000..c56deeb --- /dev/null +++ b/pigeonhole/src/lib-sieve-tool/Makefile.in @@ -0,0 +1,680 @@ +# 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-tool +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_tool_la_LIBADD = +am_libsieve_tool_la_OBJECTS = sieve-tool.lo +libsieve_tool_la_OBJECTS = $(am_libsieve_tool_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-tool.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_tool_la_SOURCES) +DIST_SOURCES = $(libsieve_tool_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-tool.la +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib-sieve \ + -I$(top_srcdir)/src/lib-sieve/util \ + $(LIBDOVECOT_INCLUDE) \ + $(LIBDOVECOT_SERVICE_INCLUDE) + +libsieve_tool_la_SOURCES = \ + sieve-tool.c + +noinst_HEADERS = \ + sieve-tool.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-tool/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve-tool/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-tool.la: $(libsieve_tool_la_OBJECTS) $(libsieve_tool_la_DEPENDENCIES) $(EXTRA_libsieve_tool_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_tool_la_OBJECTS) $(libsieve_tool_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-tool.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-tool.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-tool.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-tool/sieve-tool.c b/pigeonhole/src/lib-sieve-tool/sieve-tool.c new file mode 100644 index 0000000..435f973 --- /dev/null +++ b/pigeonhole/src/lib-sieve-tool/sieve-tool.c @@ -0,0 +1,672 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "lib-signals.h" +#include "array.h" +#include "ioloop.h" +#include "ostream.h" +#include "hostpid.h" +#include "dict.h" +#include "mail-namespace.h" +#include "mail-storage.h" +#include "mail-user.h" +#include "message-address.h" +#include "smtp-params.h" +#include "master-service.h" +#include "master-service-settings.h" +#include "mail-storage-service.h" + +#include "sieve.h" +#include "sieve-plugins.h" +#include "sieve-extensions.h" + +#include "mail-raw.h" + +#include "sieve-tool.h" + +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <pwd.h> +#include <sysexits.h> + +/* + * Global state + */ + +struct sieve_tool { + char *name; + + bool no_config; + + char *username; + char *homedir; + + char *sieve_extensions; + ARRAY_TYPE(const_string) sieve_plugins; + + sieve_tool_setting_callback_t setting_callback; + void *setting_callback_context; + + struct sieve_instance *svinst; + + struct mail_storage_service_ctx *storage_service; + struct mail_storage_service_user *service_user; + struct mail_user *mail_user_dovecot; + struct mail_user *mail_user; + + struct mail_user *mail_raw_user; + struct mail_raw *mail_raw; + + bool debug:1; +}; + +struct sieve_tool *sieve_tool; + +/* + * Settings management + */ + +static const char * +sieve_tool_sieve_get_setting(void *context, const char *identifier) +{ + struct sieve_tool *tool = (struct sieve_tool *) context; + + if (tool->setting_callback != NULL) { + return tool->setting_callback(tool->setting_callback_context, + identifier); + } + + if (tool->mail_user_dovecot == NULL) + return NULL; + + return mail_user_plugin_getenv(tool->mail_user_dovecot, identifier); +} + +static const char *sieve_tool_sieve_get_homedir(void *context) +{ + struct sieve_tool *tool = (struct sieve_tool *) context; + + return sieve_tool_get_homedir(tool); +} + +const struct sieve_callbacks sieve_tool_callbacks = { + sieve_tool_sieve_get_homedir, + sieve_tool_sieve_get_setting +}; + +/* + * Initialization + */ + +static void +sieve_tool_get_user_data(const char **username_r, const char **homedir_r) +{ + uid_t process_euid = geteuid(); + struct passwd *pw; + const char *user = NULL, *home = NULL; + + user = getenv("USER"); + home = getenv("HOME"); + + if (user == NULL || *user == '\0' || home == NULL || *home == '\0') { + if ((pw = getpwuid(process_euid)) != NULL) { + user = pw->pw_name; + home = pw->pw_dir; + } + } + + if (username_r != NULL) { + if (user == NULL || *user == '\0') { + i_fatal("couldn't lookup our username (uid=%s)", + dec2str(process_euid)); + } + + *username_r = t_strdup(user); + } + + if (homedir_r != NULL) + *homedir_r = t_strdup(home); +} + +struct sieve_tool * +sieve_tool_init(const char *name, int *argc, char **argv[], + const char *getopt_str, bool no_config) +{ + struct sieve_tool *tool; + enum master_service_flags service_flags = + MASTER_SERVICE_FLAG_STANDALONE | + MASTER_SERVICE_FLAG_DONT_SEND_STATS | + MASTER_SERVICE_FLAG_NO_INIT_DATASTACK_FRAME | + MASTER_SERVICE_FLAG_DISABLE_SSL_SET; + + if (no_config) + service_flags |= MASTER_SERVICE_FLAG_NO_CONFIG_SETTINGS; + + master_service = master_service_init(name, service_flags, + argc, argv, getopt_str); + + tool = i_new(struct sieve_tool, 1); + tool->name = i_strdup(name); + tool->no_config = no_config; + + i_array_init(&tool->sieve_plugins, 16); + + return tool; +} + +int sieve_tool_getopt(struct sieve_tool *tool) +{ + int c; + + while ((c = master_getopt(master_service)) > 0) { + switch (c) { + case 'x': + /* extensions */ + if (tool->sieve_extensions != NULL) { + i_fatal_status( + EX_USAGE, + "duplicate -x option specified, " + "but only one allowed."); + } + tool->sieve_extensions = i_strdup(optarg); + break; + case 'u': + if (tool->username == NULL) + tool->username = i_strdup(optarg); + break; + case 'P': + /* Plugin */ + { + const char *plugin; + + plugin = t_strdup(optarg); + array_append(&tool->sieve_plugins, &plugin, 1); + } + break; + case 'D': + tool->debug = TRUE; + break; + default: + return c; + } + } + + return c; +} + +static void sieve_tool_load_plugins(struct sieve_tool *tool) +{ + unsigned int i, count; + const char * const *plugins; + + plugins = array_get(&tool->sieve_plugins, &count); + for (i = 0; i < count; i++) { + const char *path, *file = strrchr(plugins[i], '/'); + + if (file != NULL) { + path = t_strdup_until(plugins[i], file); + file = file+1; + } else { + path = NULL; + file = plugins[i]; + } + + sieve_plugins_load(tool->svinst, path, file); + } +} + +struct sieve_instance * +sieve_tool_init_finish(struct sieve_tool *tool, bool init_mailstore, + bool preserve_root) +{ + enum mail_storage_service_flags storage_service_flags = + MAIL_STORAGE_SERVICE_FLAG_NO_CHDIR | + MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT | + MAIL_STORAGE_SERVICE_FLAG_USE_SYSEXITS; + struct mail_storage_service_input service_input; + struct sieve_environment svenv; + const char *username = tool->username; + const char *homedir = tool->homedir; + const char *errstr; + + master_service_init_finish(master_service); + + if (username == NULL) { + sieve_tool_get_user_data(&username, &homedir); + + username = tool->username = i_strdup(username); + + if (tool->homedir != NULL) + i_free(tool->homedir); + tool->homedir = i_strdup(homedir); + + if (preserve_root) { + storage_service_flags |= + MAIL_STORAGE_SERVICE_FLAG_NO_RESTRICT_ACCESS; + } + } else { + storage_service_flags |= + MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP; + } + + if (!init_mailstore) + storage_service_flags |= + MAIL_STORAGE_SERVICE_FLAG_NO_NAMESPACES; + + i_zero(&service_input); + service_input.module = "mail"; + service_input.service = tool->name; + service_input.username = username; + + tool->storage_service = mail_storage_service_init( + master_service, NULL, storage_service_flags); + if (mail_storage_service_lookup_next( + tool->storage_service, &service_input, &tool->service_user, + &tool->mail_user_dovecot, &errstr) <= 0) + i_fatal("%s", errstr); + + if (master_service_set(master_service, + "mail_full_filesystem_access=yes") < 0) + i_unreached(); + + memset((void *)&svenv, 0, sizeof(svenv)); + svenv.username = username; + (void)mail_user_get_home(tool->mail_user_dovecot, &svenv.home_dir); + svenv.hostname = my_hostdomain(); + svenv.base_dir = tool->mail_user_dovecot->set->base_dir; + svenv.temp_dir = tool->mail_user_dovecot->set->mail_temp_dir; + svenv.location = SIEVE_ENV_LOCATION_MS; + svenv.delivery_phase = SIEVE_DELIVERY_PHASE_POST; + + /* Initialize Sieve Engine */ + if ((tool->svinst = sieve_init(&svenv, &sieve_tool_callbacks, + tool, tool->debug)) == NULL) + i_fatal("failed to initialize sieve implementation"); + + /* Load Sieve plugins */ + if (array_count(&tool->sieve_plugins) > 0) + sieve_tool_load_plugins(tool); + + /* Set active Sieve extensions */ + if (tool->sieve_extensions != NULL) { + sieve_set_extensions(tool->svinst, tool->sieve_extensions); + } else if (tool->no_config) { + sieve_set_extensions(tool->svinst, NULL); + } + + return tool->svinst; +} + +void sieve_tool_deinit(struct sieve_tool **_tool) +{ + struct sieve_tool *tool = *_tool; + + *_tool = NULL; + + /* Deinitialize Sieve engine */ + sieve_deinit(&tool->svinst); + + /* Free options */ + + if (tool->username != NULL) + i_free(tool->username); + if (tool->homedir != NULL) + i_free(tool->homedir); + + if (tool->sieve_extensions != NULL) + i_free(tool->sieve_extensions); + array_free(&tool->sieve_plugins); + + /* Free raw mail */ + + if (tool->mail_raw != NULL) + mail_raw_close(&tool->mail_raw); + + if (tool->mail_raw_user != NULL) + mail_user_unref(&tool->mail_raw_user); + + /* Free mail service */ + + if (tool->mail_user != NULL) + mail_user_unref(&tool->mail_user); + if (tool->mail_user_dovecot != NULL) + mail_user_unref(&tool->mail_user_dovecot); + + mail_storage_service_user_unref(&tool->service_user); + mail_storage_service_deinit(&tool->storage_service); + + /* Free sieve tool object */ + + i_free(tool->name); + i_free(tool); + + /* Deinitialize service */ + master_service_deinit(&master_service); +} + +/* + * Mail environment + */ + +void sieve_tool_init_mail_user(struct sieve_tool *tool, + const char *mail_location) +{ + struct mail_user *mail_user_dovecot = tool->mail_user_dovecot; + const char *username = tool->username; + struct mail_namespace *ns = NULL; + const char *home = NULL, *errstr = NULL; + + tool->mail_user = mail_user_alloc(NULL, username, + mail_user_dovecot->set_info, + mail_user_dovecot->unexpanded_set); + + if ((home = sieve_tool_get_homedir(sieve_tool)) != NULL) + mail_user_set_home(tool->mail_user, home); + + if (mail_user_init(tool->mail_user, &errstr) < 0) + i_fatal("Test user initialization failed: %s", errstr); + + if (mail_namespaces_init_location(tool->mail_user, mail_location, + &errstr) < 0) + i_fatal("Test storage creation failed: %s", errstr); + + ns = tool->mail_user->namespaces; + ns->flags |= NAMESPACE_FLAG_NOQUOTA | NAMESPACE_FLAG_NOACL; +} + +static void sieve_tool_init_mail_raw_user(struct sieve_tool *tool) +{ + if (tool->mail_raw_user == NULL) { + tool->mail_raw_user = mail_raw_user_create( + master_service, tool->mail_user_dovecot); + } +} + +struct mail * +sieve_tool_open_file_as_mail(struct sieve_tool *tool, const char *path) +{ + sieve_tool_init_mail_raw_user(tool); + + if (tool->mail_raw != NULL) + mail_raw_close(&tool->mail_raw); + + tool->mail_raw = mail_raw_open_file(tool->mail_raw_user, path); + + return tool->mail_raw->mail; +} + +struct mail * +sieve_tool_open_data_as_mail(struct sieve_tool *tool, string_t *mail_data) +{ + sieve_tool_init_mail_raw_user(tool); + + if (tool->mail_raw != NULL) + mail_raw_close(&tool->mail_raw); + + tool->mail_raw = mail_raw_open_data(tool->mail_raw_user, mail_data); + + return tool->mail_raw->mail; +} + +/* + * Configuration + */ + +void sieve_tool_set_homedir(struct sieve_tool *tool, const char *homedir) +{ + if (tool->homedir != NULL) { + if (strcmp(homedir, tool->homedir) == 0) + return; + + i_free(tool->homedir); + } + + tool->homedir = i_strdup(homedir); + + if (tool->mail_user_dovecot != NULL) + mail_user_set_home(tool->mail_user_dovecot, tool->homedir); + if (tool->mail_user != NULL) + mail_user_set_home(tool->mail_user, tool->homedir); +} + +void sieve_tool_set_setting_callback(struct sieve_tool *tool, + sieve_tool_setting_callback_t callback, + void *context) +{ + tool->setting_callback = callback; + tool->setting_callback_context = context; +} + +/* + * Accessors + */ + +const char *sieve_tool_get_username(struct sieve_tool *tool) +{ + const char *username; + + if (tool->username == NULL) { + sieve_tool_get_user_data(&username, NULL); + return username; + } + + return tool->username; +} + +const char *sieve_tool_get_homedir(struct sieve_tool *tool) +{ + const char *homedir = NULL; + + if (tool->homedir != NULL) + return tool->homedir; + + if (tool->mail_user_dovecot != NULL && + mail_user_get_home(tool->mail_user_dovecot, &homedir) > 0) + return tool->homedir; + + sieve_tool_get_user_data(NULL, &homedir); + return homedir; +} + +struct mail_user *sieve_tool_get_mail_user(struct sieve_tool *tool) +{ + return (tool->mail_user == NULL ? + tool->mail_user_dovecot : tool->mail_user); +} + +struct mail_user *sieve_tool_get_mail_raw_user(struct sieve_tool *tool) +{ + sieve_tool_init_mail_raw_user(tool); + return tool->mail_raw_user; +} + +/* + * Commonly needed functionality + */ + +static const struct smtp_address * +sieve_tool_get_address(struct mail *mail, const char *header) +{ + struct message_address *addr; + struct smtp_address *smtp_addr; + const char *str; + + if (mail_get_first_header(mail, header, &str) <= 0) + return NULL; + addr = message_address_parse(pool_datastack_create(), + (const unsigned char *)str, + strlen(str), 1, 0); + if (addr == NULL || addr->mailbox == NULL || + addr->domain == NULL || *addr->mailbox == '\0' || + *addr->domain == '\0') + return NULL; + if (smtp_address_create_from_msg_temp(addr, &smtp_addr) < 0) + return NULL; + return smtp_addr; +} + +void sieve_tool_get_envelope_data(struct sieve_message_data *msgdata, + struct mail *mail, + const struct smtp_address *sender, + const struct smtp_address *rcpt_orig, + const struct smtp_address *rcpt_final) +{ + struct smtp_params_rcpt *rcpt_params; + + /* Get sender address */ + if (sender == NULL) + sender = sieve_tool_get_address(mail, "Return-path"); + if (sender == NULL) + sender = sieve_tool_get_address(mail, "Sender"); + if (sender == NULL) + sender = sieve_tool_get_address(mail, "From"); + if (sender == NULL) + sender = smtp_address_create_temp("sender", "example.com"); + + /* Get recipient address */ + if (rcpt_final == NULL) + rcpt_final = sieve_tool_get_address(mail, "Envelope-To"); + if (rcpt_final == NULL) + rcpt_final = sieve_tool_get_address(mail, "To"); + if (rcpt_final == NULL) { + rcpt_final = smtp_address_create_temp("recipient", + "example.com"); + } + if (rcpt_orig == NULL) + rcpt_orig = rcpt_final; + + msgdata->envelope.mail_from = sender; + msgdata->envelope.rcpt_to = rcpt_final; + + rcpt_params = t_new(struct smtp_params_rcpt, 1); + rcpt_params->orcpt.addr = rcpt_orig; + + msgdata->envelope.rcpt_params = rcpt_params; +} + +/* + * File I/O + */ + +struct ostream *sieve_tool_open_output_stream(const char *filename) +{ + struct ostream *outstream; + int fd; + + if (strcmp(filename, "-") == 0) + outstream = o_stream_create_fd(1, 0); + else { + if ((fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, + 0600)) < 0) + i_fatal("failed to open file for writing: %m"); + + outstream = o_stream_create_fd_autoclose(&fd, 0); + } + + return outstream; +} + +/* + * Sieve script handling + */ + +struct sieve_binary * +sieve_tool_script_compile(struct sieve_instance *svinst, + const char *filename, const char *name) +{ + struct sieve_error_handler *ehandler; + struct sieve_binary *sbin; + + ehandler = sieve_stderr_ehandler_create(svinst, 0); + sieve_error_handler_accept_infolog(ehandler, TRUE); + sieve_error_handler_accept_debuglog(ehandler, svinst->debug); + + if ((sbin = sieve_compile(svinst, filename, name, ehandler, + 0, NULL)) == NULL) + i_fatal("failed to compile sieve script '%s'", filename); + + sieve_error_handler_unref(&ehandler); + return sbin; +} + +struct sieve_binary * +sieve_tool_script_open(struct sieve_instance *svinst, const char *filename) +{ + struct sieve_error_handler *ehandler; + struct sieve_binary *sbin; + + ehandler = sieve_stderr_ehandler_create(svinst, 0); + sieve_error_handler_accept_infolog(ehandler, TRUE); + sieve_error_handler_accept_debuglog(ehandler, svinst->debug); + + if ((sbin = sieve_open(svinst, filename, NULL, ehandler, + 0, NULL)) == NULL) { + sieve_error_handler_unref(&ehandler); + i_fatal("failed to compile sieve script"); + } + + sieve_error_handler_unref(&ehandler); + + sieve_save(sbin, FALSE, NULL); + return sbin; +} + +void sieve_tool_dump_binary_to(struct sieve_binary *sbin, + const char *filename, bool hexdump) +{ + struct ostream *dumpstream; + + if (filename == NULL) + return; + + dumpstream = sieve_tool_open_output_stream(filename); + if (dumpstream != NULL) { + if (hexdump) + (void)sieve_hexdump(sbin, dumpstream); + else + (void)sieve_dump(sbin, dumpstream, FALSE); + if (o_stream_finish(dumpstream) < 0) { + i_fatal("write(%s) failed: %s", filename, + o_stream_get_error(dumpstream)); + } + o_stream_destroy(&dumpstream); + } else { + i_fatal("Failed to create stream for sieve code dump."); + } +} + +/* + * Commandline option parsing + */ + +void sieve_tool_parse_trace_option(struct sieve_trace_config *tr_config, + const char *tr_option) +{ + if (str_begins(tr_option, "level=")) { + const char *lvl = &tr_option[6]; + + if (strcmp(lvl, "none") == 0) { + tr_config->level = SIEVE_TRLVL_NONE; + } else if (strcmp(lvl, "actions") == 0) { + tr_config->level = SIEVE_TRLVL_ACTIONS; + } else if (strcmp(lvl, "commands") == 0) { + tr_config->level = SIEVE_TRLVL_COMMANDS; + } else if (strcmp(lvl, "tests") == 0) { + tr_config->level = SIEVE_TRLVL_TESTS; + } else if (strcmp(lvl, "matching") == 0) { + tr_config->level = SIEVE_TRLVL_MATCHING; + } else { + i_fatal_status(EX_USAGE, + "Unknown -tlevel= trace level: %s", lvl); + } + } else if (strcmp(tr_option, "debug") == 0) { + tr_config->flags |= SIEVE_TRFLG_DEBUG; + } else if (strcmp(tr_option, "addresses") == 0) { + tr_config->flags |= SIEVE_TRFLG_ADDRESSES; + } else { + i_fatal_status(EX_USAGE, "Unknown -t trace option value: %s", + tr_option); + } +} diff --git a/pigeonhole/src/lib-sieve-tool/sieve-tool.h b/pigeonhole/src/lib-sieve-tool/sieve-tool.h new file mode 100644 index 0000000..02f4198 --- /dev/null +++ b/pigeonhole/src/lib-sieve-tool/sieve-tool.h @@ -0,0 +1,100 @@ +#ifndef SIEVE_TOOL_H +#define SIEVE_TOOL_H + +#include "sieve-common.h" + +/* + * Types + */ + +typedef const char * +(*sieve_tool_setting_callback_t)(void *context, const char *identifier); + +/* + * Global variables + */ + +extern struct sieve_tool *sieve_tool; + +/* + * Initialization + */ + +struct sieve_tool * +sieve_tool_init(const char *name, int *argc, char **argv[], + const char *getopt_str, bool no_config); + +int sieve_tool_getopt(struct sieve_tool *tool); + +struct sieve_instance * +sieve_tool_init_finish(struct sieve_tool *tool, bool init_mailstore, + bool preserve_root); + +void sieve_tool_deinit(struct sieve_tool **_tool); + +/* + * Mail environment + */ + +void sieve_tool_init_mail_user(struct sieve_tool *tool, + const char *mail_location); + +struct mail * +sieve_tool_open_file_as_mail(struct sieve_tool *tool, const char *path); +struct mail * +sieve_tool_open_data_as_mail(struct sieve_tool *tool, string_t *mail_data); + +/* + * Accessors + */ + +const char *sieve_tool_get_username(struct sieve_tool *tool); +const char *sieve_tool_get_homedir(struct sieve_tool *tool); +struct mail_user *sieve_tool_get_mail_user(struct sieve_tool *tool); +struct mail_user *sieve_tool_get_mail_raw_user(struct sieve_tool *tool); + +/* + * Configuration + */ + +void sieve_tool_set_homedir(struct sieve_tool *tool, const char *homedir); +void sieve_tool_set_setting_callback(struct sieve_tool *tool, + sieve_tool_setting_callback_t callback, + void *context); + +/* + * Commonly needed functionality + */ + +void sieve_tool_get_envelope_data(struct sieve_message_data *msgdata, + struct mail *mail, + const struct smtp_address *sender, + const struct smtp_address *rcpt_orig, + const struct smtp_address *rcpt_final); + +/* + * File I/O + */ + +struct ostream *sieve_tool_open_output_stream(const char *filename); + +/* + * Sieve script handling + */ + +struct sieve_binary * +sieve_tool_script_compile(struct sieve_instance *svinst, + const char *filename, const char *name); +struct sieve_binary * +sieve_tool_script_open(struct sieve_instance *svinst, const char *filename); +void sieve_tool_dump_binary_to(struct sieve_binary *sbin, + const char *filename, bool hexdump); + +/* + * Command line option parsing + */ + +void sieve_tool_parse_trace_option(struct sieve_trace_config *tr_config, + const char *tr_option); + +#endif diff --git a/pigeonhole/src/lib-sieve/Makefile.am b/pigeonhole/src/lib-sieve/Makefile.am new file mode 100644 index 0000000..2e80871 --- /dev/null +++ b/pigeonhole/src/lib-sieve/Makefile.am @@ -0,0 +1,188 @@ +SUBDIRS = util storage plugins + +dovecot_pkglib_LTLIBRARIES = libdovecot-sieve.la + +AM_CPPFLAGS = \ + $(LIBDOVECOT_INCLUDE) \ + $(LIBDOVECOT_SERVICE_INCLUDE) \ + -I$(top_srcdir)/src/lib-sieve/util \ + -DMODULEDIR=\""$(dovecot_moduledir)"\" + +tests = \ + tst-truefalse.c \ + tst-not.c \ + tst-anyof.c \ + tst-allof.c \ + tst-address.c \ + tst-header.c \ + tst-exists.c \ + tst-size.c + +commands = \ + cmd-require.c \ + cmd-stop.c \ + cmd-if.c \ + cmd-keep.c \ + cmd-redirect.c \ + cmd-discard.c + +extensions = \ + ext-fileinto.c \ + ext-reject.c \ + ext-envelope.c \ + ext-encoded-character.c + +match_types = \ + mcht-is.c \ + mcht-contains.c \ + mcht-matches.c + +comparators = \ + cmp-i-octet.c \ + cmp-i-ascii-casemap.c + +if BUILD_UNFINISHED +unfinished_storages = +unfinished_plugins = +endif + +strgdir = $(top_builddir)/src/lib-sieve/storage +storages = \ + $(strgdir)/data/libsieve_storage_data.la \ + $(strgdir)/file/libsieve_storage_file.la \ + $(strgdir)/dict/libsieve_storage_dict.la \ + $(strgdir)/ldap/libsieve_storage_ldap.la \ + $(unfinished_storages) + +extdir = $(top_builddir)/src/lib-sieve/plugins +plugins = \ + $(extdir)/vacation/libsieve_ext_vacation.la \ + $(extdir)/subaddress/libsieve_ext_subaddress.la \ + $(extdir)/comparator-i-ascii-numeric/libsieve_ext_comparator-i-ascii-numeric.la \ + $(extdir)/relational/libsieve_ext_relational.la \ + $(extdir)/regex/libsieve_ext_regex.la \ + $(extdir)/copy/libsieve_ext_copy.la \ + $(extdir)/imap4flags/libsieve_ext_imap4flags.la \ + $(extdir)/include/libsieve_ext_include.la \ + $(extdir)/body/libsieve_ext_body.la \ + $(extdir)/variables/libsieve_ext_variables.la \ + $(extdir)/enotify/libsieve_ext_enotify.la \ + $(extdir)/notify/libsieve_ext_notify.la \ + $(extdir)/environment/libsieve_ext_environment.la \ + $(extdir)/mailbox/libsieve_ext_mailbox.la \ + $(extdir)/date/libsieve_ext_date.la \ + $(extdir)/spamvirustest/libsieve_ext_spamvirustest.la \ + $(extdir)/ihave/libsieve_ext_ihave.la \ + $(extdir)/editheader/libsieve_ext_editheader.la \ + $(extdir)/duplicate/libsieve_ext_duplicate.la \ + $(extdir)/index/libsieve_ext_index.la \ + $(extdir)/metadata/libsieve_ext_metadata.la \ + $(extdir)/mime/libsieve_ext_mime.la \ + $(extdir)/special-use/libsieve_ext_special_use.la \ + $(extdir)/vnd.dovecot/debug/libsieve_ext_debug.la \ + $(extdir)/vnd.dovecot/environment/libsieve_ext_vnd_environment.la \ + $(extdir)/vnd.dovecot/report/libsieve_ext_vnd_report.la \ + $(unfinished_plugins) + +libdovecot_sieve_la_DEPENDENCIES = \ + $(storages) \ + $(plugins) \ + $(top_builddir)/src/lib-sieve/util/libsieve_util.la \ + $(LIBDOVECOT_STORAGE_DEPS) \ + $(LIBDOVECOT_DEPS) +libdovecot_sieve_la_LIBADD = \ + $(storages) \ + $(plugins) \ + $(top_builddir)/src/lib-sieve/util/libsieve_util.la \ + $(LIBDOVECOT_STORAGE) \ + $(LIBDOVECOT) + +libdovecot_sieve_la_SOURCES = \ + sieve-settings.c \ + sieve-message.c \ + sieve-smtp.c \ + sieve-lexer.c \ + sieve-script.c \ + sieve-storage.c \ + sieve-storage-sync.c \ + sieve-ast.c \ + sieve-binary.c \ + sieve-binary-file.c \ + sieve-binary-code.c \ + sieve-binary-debug.c \ + sieve-parser.c \ + sieve-address.c \ + sieve-validator.c \ + sieve-generator.c \ + sieve-execute.c \ + sieve-interpreter.c \ + sieve-runtime-trace.c \ + sieve-code-dumper.c \ + sieve-binary-dumper.c \ + sieve-result.c \ + sieve-error.c \ + sieve-objects.c \ + sieve-stringlist.c \ + sieve-comparators.c \ + sieve-match-types.c \ + sieve-address-parts.c \ + sieve-address-source.c \ + sieve-match.c \ + sieve-commands.c \ + sieve-code.c \ + sieve-actions.c \ + sieve-extensions.c \ + sieve-plugins.c \ + $(comparators) \ + $(match_types) \ + $(tests) \ + $(commands) \ + $(extensions) \ + sieve.c + +headers = \ + sieve-config.h \ + sieve-types.h \ + sieve-common.h \ + sieve-limits.h \ + sieve-settings.h \ + sieve-message.h \ + sieve-smtp.h \ + sieve-lexer.h \ + sieve-script.h \ + sieve-script-private.h \ + sieve-storage.h \ + sieve-storage-private.h \ + sieve-ast.h \ + sieve-binary.h \ + sieve-binary-private.h \ + sieve-parser.h \ + sieve-address.h \ + sieve-validator.h \ + sieve-generator.h \ + sieve-execute.h \ + sieve-interpreter.h \ + sieve-runtime-trace.h \ + sieve-runtime.h \ + sieve-code-dumper.h \ + sieve-binary-dumper.h \ + sieve-dump.h \ + sieve-result.h \ + sieve-error.h \ + sieve-error-private.h \ + sieve-objects.h \ + sieve-stringlist.h \ + sieve-match.h \ + sieve-comparators.h \ + sieve-match-types.h \ + sieve-address-parts.h \ + sieve-address-source.h \ + sieve-commands.h \ + sieve-code.h \ + sieve-actions.h \ + sieve-extensions.h \ + sieve-plugins.h \ + sieve.h + +pkginc_libdir=$(dovecot_pkgincludedir)/sieve +pkginc_lib_HEADERS = $(headers) diff --git a/pigeonhole/src/lib-sieve/Makefile.in b/pigeonhole/src/lib-sieve/Makefile.in new file mode 100644 index 0000000..104d4df --- /dev/null +++ b/pigeonhole/src/lib-sieve/Makefile.in @@ -0,0 +1,1311 @@ +# 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 +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 $(pkginc_lib_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 = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(dovecot_pkglibdir)" \ + "$(DESTDIR)$(pkginc_libdir)" +LTLIBRARIES = $(dovecot_pkglib_LTLIBRARIES) +am__DEPENDENCIES_1 = +am__DEPENDENCIES_2 = $(strgdir)/data/libsieve_storage_data.la \ + $(strgdir)/file/libsieve_storage_file.la \ + $(strgdir)/dict/libsieve_storage_dict.la \ + $(strgdir)/ldap/libsieve_storage_ldap.la $(am__DEPENDENCIES_1) +am__DEPENDENCIES_3 = $(extdir)/vacation/libsieve_ext_vacation.la \ + $(extdir)/subaddress/libsieve_ext_subaddress.la \ + $(extdir)/comparator-i-ascii-numeric/libsieve_ext_comparator-i-ascii-numeric.la \ + $(extdir)/relational/libsieve_ext_relational.la \ + $(extdir)/regex/libsieve_ext_regex.la \ + $(extdir)/copy/libsieve_ext_copy.la \ + $(extdir)/imap4flags/libsieve_ext_imap4flags.la \ + $(extdir)/include/libsieve_ext_include.la \ + $(extdir)/body/libsieve_ext_body.la \ + $(extdir)/variables/libsieve_ext_variables.la \ + $(extdir)/enotify/libsieve_ext_enotify.la \ + $(extdir)/notify/libsieve_ext_notify.la \ + $(extdir)/environment/libsieve_ext_environment.la \ + $(extdir)/mailbox/libsieve_ext_mailbox.la \ + $(extdir)/date/libsieve_ext_date.la \ + $(extdir)/spamvirustest/libsieve_ext_spamvirustest.la \ + $(extdir)/ihave/libsieve_ext_ihave.la \ + $(extdir)/editheader/libsieve_ext_editheader.la \ + $(extdir)/duplicate/libsieve_ext_duplicate.la \ + $(extdir)/index/libsieve_ext_index.la \ + $(extdir)/metadata/libsieve_ext_metadata.la \ + $(extdir)/mime/libsieve_ext_mime.la \ + $(extdir)/special-use/libsieve_ext_special_use.la \ + $(extdir)/vnd.dovecot/debug/libsieve_ext_debug.la \ + $(extdir)/vnd.dovecot/environment/libsieve_ext_vnd_environment.la \ + $(extdir)/vnd.dovecot/report/libsieve_ext_vnd_report.la \ + $(am__DEPENDENCIES_1) +am__objects_1 = cmp-i-octet.lo cmp-i-ascii-casemap.lo +am__objects_2 = mcht-is.lo mcht-contains.lo mcht-matches.lo +am__objects_3 = tst-truefalse.lo tst-not.lo tst-anyof.lo tst-allof.lo \ + tst-address.lo tst-header.lo tst-exists.lo tst-size.lo +am__objects_4 = cmd-require.lo cmd-stop.lo cmd-if.lo cmd-keep.lo \ + cmd-redirect.lo cmd-discard.lo +am__objects_5 = ext-fileinto.lo ext-reject.lo ext-envelope.lo \ + ext-encoded-character.lo +am_libdovecot_sieve_la_OBJECTS = sieve-settings.lo sieve-message.lo \ + sieve-smtp.lo sieve-lexer.lo sieve-script.lo sieve-storage.lo \ + sieve-storage-sync.lo sieve-ast.lo sieve-binary.lo \ + sieve-binary-file.lo sieve-binary-code.lo \ + sieve-binary-debug.lo sieve-parser.lo sieve-address.lo \ + sieve-validator.lo sieve-generator.lo sieve-execute.lo \ + sieve-interpreter.lo sieve-runtime-trace.lo \ + sieve-code-dumper.lo sieve-binary-dumper.lo sieve-result.lo \ + sieve-error.lo sieve-objects.lo sieve-stringlist.lo \ + sieve-comparators.lo sieve-match-types.lo \ + sieve-address-parts.lo sieve-address-source.lo sieve-match.lo \ + sieve-commands.lo sieve-code.lo sieve-actions.lo \ + sieve-extensions.lo sieve-plugins.lo $(am__objects_1) \ + $(am__objects_2) $(am__objects_3) $(am__objects_4) \ + $(am__objects_5) sieve.lo +libdovecot_sieve_la_OBJECTS = $(am_libdovecot_sieve_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)/cmd-discard.Plo \ + ./$(DEPDIR)/cmd-if.Plo ./$(DEPDIR)/cmd-keep.Plo \ + ./$(DEPDIR)/cmd-redirect.Plo ./$(DEPDIR)/cmd-require.Plo \ + ./$(DEPDIR)/cmd-stop.Plo ./$(DEPDIR)/cmp-i-ascii-casemap.Plo \ + ./$(DEPDIR)/cmp-i-octet.Plo \ + ./$(DEPDIR)/ext-encoded-character.Plo \ + ./$(DEPDIR)/ext-envelope.Plo ./$(DEPDIR)/ext-fileinto.Plo \ + ./$(DEPDIR)/ext-reject.Plo ./$(DEPDIR)/mcht-contains.Plo \ + ./$(DEPDIR)/mcht-is.Plo ./$(DEPDIR)/mcht-matches.Plo \ + ./$(DEPDIR)/sieve-actions.Plo \ + ./$(DEPDIR)/sieve-address-parts.Plo \ + ./$(DEPDIR)/sieve-address-source.Plo \ + ./$(DEPDIR)/sieve-address.Plo ./$(DEPDIR)/sieve-ast.Plo \ + ./$(DEPDIR)/sieve-binary-code.Plo \ + ./$(DEPDIR)/sieve-binary-debug.Plo \ + ./$(DEPDIR)/sieve-binary-dumper.Plo \ + ./$(DEPDIR)/sieve-binary-file.Plo ./$(DEPDIR)/sieve-binary.Plo \ + ./$(DEPDIR)/sieve-code-dumper.Plo ./$(DEPDIR)/sieve-code.Plo \ + ./$(DEPDIR)/sieve-commands.Plo \ + ./$(DEPDIR)/sieve-comparators.Plo ./$(DEPDIR)/sieve-error.Plo \ + ./$(DEPDIR)/sieve-execute.Plo ./$(DEPDIR)/sieve-extensions.Plo \ + ./$(DEPDIR)/sieve-generator.Plo \ + ./$(DEPDIR)/sieve-interpreter.Plo ./$(DEPDIR)/sieve-lexer.Plo \ + ./$(DEPDIR)/sieve-match-types.Plo ./$(DEPDIR)/sieve-match.Plo \ + ./$(DEPDIR)/sieve-message.Plo ./$(DEPDIR)/sieve-objects.Plo \ + ./$(DEPDIR)/sieve-parser.Plo ./$(DEPDIR)/sieve-plugins.Plo \ + ./$(DEPDIR)/sieve-result.Plo \ + ./$(DEPDIR)/sieve-runtime-trace.Plo \ + ./$(DEPDIR)/sieve-script.Plo ./$(DEPDIR)/sieve-settings.Plo \ + ./$(DEPDIR)/sieve-smtp.Plo ./$(DEPDIR)/sieve-storage-sync.Plo \ + ./$(DEPDIR)/sieve-storage.Plo ./$(DEPDIR)/sieve-stringlist.Plo \ + ./$(DEPDIR)/sieve-validator.Plo ./$(DEPDIR)/sieve.Plo \ + ./$(DEPDIR)/tst-address.Plo ./$(DEPDIR)/tst-allof.Plo \ + ./$(DEPDIR)/tst-anyof.Plo ./$(DEPDIR)/tst-exists.Plo \ + ./$(DEPDIR)/tst-header.Plo ./$(DEPDIR)/tst-not.Plo \ + ./$(DEPDIR)/tst-size.Plo ./$(DEPDIR)/tst-truefalse.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 = $(libdovecot_sieve_la_SOURCES) +DIST_SOURCES = $(libdovecot_sieve_la_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(pkginc_lib_HEADERS) +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +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 +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +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@ +SUBDIRS = util storage plugins +dovecot_pkglib_LTLIBRARIES = libdovecot-sieve.la +AM_CPPFLAGS = \ + $(LIBDOVECOT_INCLUDE) \ + $(LIBDOVECOT_SERVICE_INCLUDE) \ + -I$(top_srcdir)/src/lib-sieve/util \ + -DMODULEDIR=\""$(dovecot_moduledir)"\" + +tests = \ + tst-truefalse.c \ + tst-not.c \ + tst-anyof.c \ + tst-allof.c \ + tst-address.c \ + tst-header.c \ + tst-exists.c \ + tst-size.c + +commands = \ + cmd-require.c \ + cmd-stop.c \ + cmd-if.c \ + cmd-keep.c \ + cmd-redirect.c \ + cmd-discard.c + +extensions = \ + ext-fileinto.c \ + ext-reject.c \ + ext-envelope.c \ + ext-encoded-character.c + +match_types = \ + mcht-is.c \ + mcht-contains.c \ + mcht-matches.c + +comparators = \ + cmp-i-octet.c \ + cmp-i-ascii-casemap.c + +@BUILD_UNFINISHED_TRUE@unfinished_storages = +@BUILD_UNFINISHED_TRUE@unfinished_plugins = +strgdir = $(top_builddir)/src/lib-sieve/storage +storages = \ + $(strgdir)/data/libsieve_storage_data.la \ + $(strgdir)/file/libsieve_storage_file.la \ + $(strgdir)/dict/libsieve_storage_dict.la \ + $(strgdir)/ldap/libsieve_storage_ldap.la \ + $(unfinished_storages) + +extdir = $(top_builddir)/src/lib-sieve/plugins +plugins = \ + $(extdir)/vacation/libsieve_ext_vacation.la \ + $(extdir)/subaddress/libsieve_ext_subaddress.la \ + $(extdir)/comparator-i-ascii-numeric/libsieve_ext_comparator-i-ascii-numeric.la \ + $(extdir)/relational/libsieve_ext_relational.la \ + $(extdir)/regex/libsieve_ext_regex.la \ + $(extdir)/copy/libsieve_ext_copy.la \ + $(extdir)/imap4flags/libsieve_ext_imap4flags.la \ + $(extdir)/include/libsieve_ext_include.la \ + $(extdir)/body/libsieve_ext_body.la \ + $(extdir)/variables/libsieve_ext_variables.la \ + $(extdir)/enotify/libsieve_ext_enotify.la \ + $(extdir)/notify/libsieve_ext_notify.la \ + $(extdir)/environment/libsieve_ext_environment.la \ + $(extdir)/mailbox/libsieve_ext_mailbox.la \ + $(extdir)/date/libsieve_ext_date.la \ + $(extdir)/spamvirustest/libsieve_ext_spamvirustest.la \ + $(extdir)/ihave/libsieve_ext_ihave.la \ + $(extdir)/editheader/libsieve_ext_editheader.la \ + $(extdir)/duplicate/libsieve_ext_duplicate.la \ + $(extdir)/index/libsieve_ext_index.la \ + $(extdir)/metadata/libsieve_ext_metadata.la \ + $(extdir)/mime/libsieve_ext_mime.la \ + $(extdir)/special-use/libsieve_ext_special_use.la \ + $(extdir)/vnd.dovecot/debug/libsieve_ext_debug.la \ + $(extdir)/vnd.dovecot/environment/libsieve_ext_vnd_environment.la \ + $(extdir)/vnd.dovecot/report/libsieve_ext_vnd_report.la \ + $(unfinished_plugins) + +libdovecot_sieve_la_DEPENDENCIES = \ + $(storages) \ + $(plugins) \ + $(top_builddir)/src/lib-sieve/util/libsieve_util.la \ + $(LIBDOVECOT_STORAGE_DEPS) \ + $(LIBDOVECOT_DEPS) + +libdovecot_sieve_la_LIBADD = \ + $(storages) \ + $(plugins) \ + $(top_builddir)/src/lib-sieve/util/libsieve_util.la \ + $(LIBDOVECOT_STORAGE) \ + $(LIBDOVECOT) + +libdovecot_sieve_la_SOURCES = \ + sieve-settings.c \ + sieve-message.c \ + sieve-smtp.c \ + sieve-lexer.c \ + sieve-script.c \ + sieve-storage.c \ + sieve-storage-sync.c \ + sieve-ast.c \ + sieve-binary.c \ + sieve-binary-file.c \ + sieve-binary-code.c \ + sieve-binary-debug.c \ + sieve-parser.c \ + sieve-address.c \ + sieve-validator.c \ + sieve-generator.c \ + sieve-execute.c \ + sieve-interpreter.c \ + sieve-runtime-trace.c \ + sieve-code-dumper.c \ + sieve-binary-dumper.c \ + sieve-result.c \ + sieve-error.c \ + sieve-objects.c \ + sieve-stringlist.c \ + sieve-comparators.c \ + sieve-match-types.c \ + sieve-address-parts.c \ + sieve-address-source.c \ + sieve-match.c \ + sieve-commands.c \ + sieve-code.c \ + sieve-actions.c \ + sieve-extensions.c \ + sieve-plugins.c \ + $(comparators) \ + $(match_types) \ + $(tests) \ + $(commands) \ + $(extensions) \ + sieve.c + +headers = \ + sieve-config.h \ + sieve-types.h \ + sieve-common.h \ + sieve-limits.h \ + sieve-settings.h \ + sieve-message.h \ + sieve-smtp.h \ + sieve-lexer.h \ + sieve-script.h \ + sieve-script-private.h \ + sieve-storage.h \ + sieve-storage-private.h \ + sieve-ast.h \ + sieve-binary.h \ + sieve-binary-private.h \ + sieve-parser.h \ + sieve-address.h \ + sieve-validator.h \ + sieve-generator.h \ + sieve-execute.h \ + sieve-interpreter.h \ + sieve-runtime-trace.h \ + sieve-runtime.h \ + sieve-code-dumper.h \ + sieve-binary-dumper.h \ + sieve-dump.h \ + sieve-result.h \ + sieve-error.h \ + sieve-error-private.h \ + sieve-objects.h \ + sieve-stringlist.h \ + sieve-match.h \ + sieve-comparators.h \ + sieve-match-types.h \ + sieve-address-parts.h \ + sieve-address-source.h \ + sieve-commands.h \ + sieve-code.h \ + sieve-actions.h \ + sieve-extensions.h \ + sieve-plugins.h \ + sieve.h + +pkginc_libdir = $(dovecot_pkgincludedir)/sieve +pkginc_lib_HEADERS = $(headers) +all: all-recursive + +.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/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/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): + +install-dovecot_pkglibLTLIBRARIES: $(dovecot_pkglib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(dovecot_pkglib_LTLIBRARIES)'; test -n "$(dovecot_pkglibdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(dovecot_pkglibdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(dovecot_pkglibdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(dovecot_pkglibdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(dovecot_pkglibdir)"; \ + } + +uninstall-dovecot_pkglibLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(dovecot_pkglib_LTLIBRARIES)'; test -n "$(dovecot_pkglibdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(dovecot_pkglibdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(dovecot_pkglibdir)/$$f"; \ + done + +clean-dovecot_pkglibLTLIBRARIES: + -test -z "$(dovecot_pkglib_LTLIBRARIES)" || rm -f $(dovecot_pkglib_LTLIBRARIES) + @list='$(dovecot_pkglib_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}; \ + } + +libdovecot-sieve.la: $(libdovecot_sieve_la_OBJECTS) $(libdovecot_sieve_la_DEPENDENCIES) $(EXTRA_libdovecot_sieve_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) -rpath $(dovecot_pkglibdir) $(libdovecot_sieve_la_OBJECTS) $(libdovecot_sieve_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-discard.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-if.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-keep.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-redirect.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-require.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-stop.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmp-i-ascii-casemap.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmp-i-octet.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-encoded-character.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-envelope.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-fileinto.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-reject.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mcht-contains.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mcht-is.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mcht-matches.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-actions.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-address-parts.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-address-source.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-address.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-ast.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-binary-code.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-binary-debug.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-binary-dumper.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-binary-file.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-binary.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-code-dumper.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-code.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-commands.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-comparators.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-error.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-execute.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-extensions.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-generator.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-interpreter.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-lexer.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-match-types.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-match.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-message.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-objects.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-parser.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-plugins.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-result.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-runtime-trace.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-script.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-settings.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-smtp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-storage-sync.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-storage.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-stringlist.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-validator.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-address.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-allof.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-anyof.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-exists.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-header.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-not.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-size.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-truefalse.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) + @$(NORMAL_INSTALL) + @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ + done + +uninstall-pkginc_libHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(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-recursive + +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-recursive + +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 + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(dovecot_pkglibdir)" "$(DESTDIR)$(pkginc_libdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +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-recursive + +clean-am: clean-dovecot_pkglibLTLIBRARIES clean-generic clean-libtool \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/cmd-discard.Plo + -rm -f ./$(DEPDIR)/cmd-if.Plo + -rm -f ./$(DEPDIR)/cmd-keep.Plo + -rm -f ./$(DEPDIR)/cmd-redirect.Plo + -rm -f ./$(DEPDIR)/cmd-require.Plo + -rm -f ./$(DEPDIR)/cmd-stop.Plo + -rm -f ./$(DEPDIR)/cmp-i-ascii-casemap.Plo + -rm -f ./$(DEPDIR)/cmp-i-octet.Plo + -rm -f ./$(DEPDIR)/ext-encoded-character.Plo + -rm -f ./$(DEPDIR)/ext-envelope.Plo + -rm -f ./$(DEPDIR)/ext-fileinto.Plo + -rm -f ./$(DEPDIR)/ext-reject.Plo + -rm -f ./$(DEPDIR)/mcht-contains.Plo + -rm -f ./$(DEPDIR)/mcht-is.Plo + -rm -f ./$(DEPDIR)/mcht-matches.Plo + -rm -f ./$(DEPDIR)/sieve-actions.Plo + -rm -f ./$(DEPDIR)/sieve-address-parts.Plo + -rm -f ./$(DEPDIR)/sieve-address-source.Plo + -rm -f ./$(DEPDIR)/sieve-address.Plo + -rm -f ./$(DEPDIR)/sieve-ast.Plo + -rm -f ./$(DEPDIR)/sieve-binary-code.Plo + -rm -f ./$(DEPDIR)/sieve-binary-debug.Plo + -rm -f ./$(DEPDIR)/sieve-binary-dumper.Plo + -rm -f ./$(DEPDIR)/sieve-binary-file.Plo + -rm -f ./$(DEPDIR)/sieve-binary.Plo + -rm -f ./$(DEPDIR)/sieve-code-dumper.Plo + -rm -f ./$(DEPDIR)/sieve-code.Plo + -rm -f ./$(DEPDIR)/sieve-commands.Plo + -rm -f ./$(DEPDIR)/sieve-comparators.Plo + -rm -f ./$(DEPDIR)/sieve-error.Plo + -rm -f ./$(DEPDIR)/sieve-execute.Plo + -rm -f ./$(DEPDIR)/sieve-extensions.Plo + -rm -f ./$(DEPDIR)/sieve-generator.Plo + -rm -f ./$(DEPDIR)/sieve-interpreter.Plo + -rm -f ./$(DEPDIR)/sieve-lexer.Plo + -rm -f ./$(DEPDIR)/sieve-match-types.Plo + -rm -f ./$(DEPDIR)/sieve-match.Plo + -rm -f ./$(DEPDIR)/sieve-message.Plo + -rm -f ./$(DEPDIR)/sieve-objects.Plo + -rm -f ./$(DEPDIR)/sieve-parser.Plo + -rm -f ./$(DEPDIR)/sieve-plugins.Plo + -rm -f ./$(DEPDIR)/sieve-result.Plo + -rm -f ./$(DEPDIR)/sieve-runtime-trace.Plo + -rm -f ./$(DEPDIR)/sieve-script.Plo + -rm -f ./$(DEPDIR)/sieve-settings.Plo + -rm -f ./$(DEPDIR)/sieve-smtp.Plo + -rm -f ./$(DEPDIR)/sieve-storage-sync.Plo + -rm -f ./$(DEPDIR)/sieve-storage.Plo + -rm -f ./$(DEPDIR)/sieve-stringlist.Plo + -rm -f ./$(DEPDIR)/sieve-validator.Plo + -rm -f ./$(DEPDIR)/sieve.Plo + -rm -f ./$(DEPDIR)/tst-address.Plo + -rm -f ./$(DEPDIR)/tst-allof.Plo + -rm -f ./$(DEPDIR)/tst-anyof.Plo + -rm -f ./$(DEPDIR)/tst-exists.Plo + -rm -f ./$(DEPDIR)/tst-header.Plo + -rm -f ./$(DEPDIR)/tst-not.Plo + -rm -f ./$(DEPDIR)/tst-size.Plo + -rm -f ./$(DEPDIR)/tst-truefalse.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-dovecot_pkglibLTLIBRARIES \ + install-pkginc_libHEADERS + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f ./$(DEPDIR)/cmd-discard.Plo + -rm -f ./$(DEPDIR)/cmd-if.Plo + -rm -f ./$(DEPDIR)/cmd-keep.Plo + -rm -f ./$(DEPDIR)/cmd-redirect.Plo + -rm -f ./$(DEPDIR)/cmd-require.Plo + -rm -f ./$(DEPDIR)/cmd-stop.Plo + -rm -f ./$(DEPDIR)/cmp-i-ascii-casemap.Plo + -rm -f ./$(DEPDIR)/cmp-i-octet.Plo + -rm -f ./$(DEPDIR)/ext-encoded-character.Plo + -rm -f ./$(DEPDIR)/ext-envelope.Plo + -rm -f ./$(DEPDIR)/ext-fileinto.Plo + -rm -f ./$(DEPDIR)/ext-reject.Plo + -rm -f ./$(DEPDIR)/mcht-contains.Plo + -rm -f ./$(DEPDIR)/mcht-is.Plo + -rm -f ./$(DEPDIR)/mcht-matches.Plo + -rm -f ./$(DEPDIR)/sieve-actions.Plo + -rm -f ./$(DEPDIR)/sieve-address-parts.Plo + -rm -f ./$(DEPDIR)/sieve-address-source.Plo + -rm -f ./$(DEPDIR)/sieve-address.Plo + -rm -f ./$(DEPDIR)/sieve-ast.Plo + -rm -f ./$(DEPDIR)/sieve-binary-code.Plo + -rm -f ./$(DEPDIR)/sieve-binary-debug.Plo + -rm -f ./$(DEPDIR)/sieve-binary-dumper.Plo + -rm -f ./$(DEPDIR)/sieve-binary-file.Plo + -rm -f ./$(DEPDIR)/sieve-binary.Plo + -rm -f ./$(DEPDIR)/sieve-code-dumper.Plo + -rm -f ./$(DEPDIR)/sieve-code.Plo + -rm -f ./$(DEPDIR)/sieve-commands.Plo + -rm -f ./$(DEPDIR)/sieve-comparators.Plo + -rm -f ./$(DEPDIR)/sieve-error.Plo + -rm -f ./$(DEPDIR)/sieve-execute.Plo + -rm -f ./$(DEPDIR)/sieve-extensions.Plo + -rm -f ./$(DEPDIR)/sieve-generator.Plo + -rm -f ./$(DEPDIR)/sieve-interpreter.Plo + -rm -f ./$(DEPDIR)/sieve-lexer.Plo + -rm -f ./$(DEPDIR)/sieve-match-types.Plo + -rm -f ./$(DEPDIR)/sieve-match.Plo + -rm -f ./$(DEPDIR)/sieve-message.Plo + -rm -f ./$(DEPDIR)/sieve-objects.Plo + -rm -f ./$(DEPDIR)/sieve-parser.Plo + -rm -f ./$(DEPDIR)/sieve-plugins.Plo + -rm -f ./$(DEPDIR)/sieve-result.Plo + -rm -f ./$(DEPDIR)/sieve-runtime-trace.Plo + -rm -f ./$(DEPDIR)/sieve-script.Plo + -rm -f ./$(DEPDIR)/sieve-settings.Plo + -rm -f ./$(DEPDIR)/sieve-smtp.Plo + -rm -f ./$(DEPDIR)/sieve-storage-sync.Plo + -rm -f ./$(DEPDIR)/sieve-storage.Plo + -rm -f ./$(DEPDIR)/sieve-stringlist.Plo + -rm -f ./$(DEPDIR)/sieve-validator.Plo + -rm -f ./$(DEPDIR)/sieve.Plo + -rm -f ./$(DEPDIR)/tst-address.Plo + -rm -f ./$(DEPDIR)/tst-allof.Plo + -rm -f ./$(DEPDIR)/tst-anyof.Plo + -rm -f ./$(DEPDIR)/tst-exists.Plo + -rm -f ./$(DEPDIR)/tst-header.Plo + -rm -f ./$(DEPDIR)/tst-not.Plo + -rm -f ./$(DEPDIR)/tst-size.Plo + -rm -f ./$(DEPDIR)/tst-truefalse.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-dovecot_pkglibLTLIBRARIES \ + uninstall-pkginc_libHEADERS + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-am clean \ + clean-dovecot_pkglibLTLIBRARIES clean-generic clean-libtool \ + 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-dovecot_pkglibLTLIBRARIES \ + install-dvi install-dvi-am install-exec install-exec-am \ + install-html install-html-am install-info install-info-am \ + install-man install-pdf install-pdf-am \ + install-pkginc_libHEADERS install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-dovecot_pkglibLTLIBRARIES \ + uninstall-pkginc_libHEADERS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/pigeonhole/src/lib-sieve/cmd-discard.c b/pigeonhole/src/lib-sieve/cmd-discard.c new file mode 100644 index 0000000..de460ae --- /dev/null +++ b/pigeonhole/src/lib-sieve/cmd-discard.c @@ -0,0 +1,173 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str-sanitize.h" + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-code.h" +#include "sieve-dump.h" +#include "sieve-actions.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-result.h" + +/* + * Discard command + * + * Syntax + * discard + */ + +static bool +cmd_discard_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx ATTR_UNUSED); + +const struct sieve_command_def cmd_discard = { + .identifier = "discard", + .type = SCT_COMMAND, + .positional_args = 0, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .generate = cmd_discard_generate +}; + +/* + * Discard operation + */ + +static bool +cmd_discard_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +cmd_discard_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def cmd_discard_operation = { + .mnemonic = "DISCARD", + .code = SIEVE_OPERATION_DISCARD, + .dump = cmd_discard_operation_dump, + .execute = cmd_discard_operation_execute +}; + +/* + * Discard actions + */ + +static bool +act_discard_equals(const struct sieve_script_env *senv, + const struct sieve_action *act1, + const struct sieve_action *act2); +static int +act_discard_check_duplicate(const struct sieve_runtime_env *renv, + const struct sieve_action *act, + const struct sieve_action *act_other); +static void +act_discard_print(const struct sieve_action *action, + const struct sieve_result_print_env *rpenv, bool *keep); +static int +act_discard_execute(const struct sieve_action_exec_env *aenv, void *tr_context, + bool *keep); + +const struct sieve_action_def act_discard = { + .name = "discard", + .equals = act_discard_equals, + .check_duplicate = act_discard_check_duplicate, + .print = act_discard_print, + .execute = act_discard_execute, +}; + +/* + * Code generation + */ + +static bool +cmd_discard_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd ATTR_UNUSED) +{ + sieve_operation_emit(cgenv->sblock, NULL, &cmd_discard_operation); + + return TRUE; +} + +/* + * Code dump + */ + +static bool +cmd_discard_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + sieve_code_dumpf(denv, "DISCARD"); + sieve_code_descend(denv); + + return (sieve_action_opr_optional_dump(denv, address, NULL) == 0); +} + +/* + * Interpretation + */ + +static int +cmd_discard_operation_execute(const struct sieve_runtime_env *renv ATTR_UNUSED, + sieve_size_t *address ATTR_UNUSED) +{ + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, + "discard action; cancel implicit keep"); + + if (sieve_result_add_action(renv, NULL, "discard", &act_discard, + NULL, NULL, 0, FALSE) < 0) + return SIEVE_EXEC_FAILURE; + return SIEVE_EXEC_OK; +} + +/* + * Action implementation + */ + +static bool +act_discard_equals(const struct sieve_script_env *senv ATTR_UNUSED, + const struct sieve_action *act1 ATTR_UNUSED, + const struct sieve_action *act2 ATTR_UNUSED) +{ + return TRUE; +} + +static int +act_discard_check_duplicate(const struct sieve_runtime_env *renv ATTR_UNUSED, + const struct sieve_action *act ATTR_UNUSED, + const struct sieve_action *act_other ATTR_UNUSED) +{ + return 1; +} + +static void +act_discard_print(const struct sieve_action *action ATTR_UNUSED, + const struct sieve_result_print_env *rpenv, bool *keep) +{ + sieve_result_action_printf(rpenv, "discard"); + + *keep = FALSE; +} + +static int +act_discard_execute(const struct sieve_action_exec_env *aenv, + void *tr_context ATTR_UNUSED, bool *keep) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + + eenv->exec_status->significant_action_executed = TRUE; + + struct event_passthrough *e = sieve_action_create_finish_event(aenv); + + sieve_result_event_log(aenv, e->event(), + "Marked message to be discarded if not explicitly delivered " + "(discard action)"); + *keep = FALSE; + + return SIEVE_EXEC_OK; +} + diff --git a/pigeonhole/src/lib-sieve/cmd-if.c b/pigeonhole/src/lib-sieve/cmd-if.c new file mode 100644 index 0000000..2ae186e --- /dev/null +++ b/pigeonhole/src/lib-sieve/cmd-if.c @@ -0,0 +1,277 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-code.h" +#include "sieve-binary.h" + +/* + * Commands + */ + +static bool cmd_if_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); +static bool cmd_elsif_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); +static bool cmd_if_validate_const + (struct sieve_validator *valdtr, struct sieve_command *cmd, + int *const_current, int const_next); +static bool cmd_if_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd); +static bool cmd_else_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd); + +/* If command + * + * Syntax: + * if <test1: test> <block1: block> + */ + +const struct sieve_command_def cmd_if = { + .identifier = "if", + .type = SCT_COMMAND, + .positional_args = 0, + .subtests = 1, + .block_allowed = TRUE, + .block_required = TRUE, + .validate = cmd_if_validate, + .validate_const = cmd_if_validate_const, + .generate = cmd_if_generate +}; + +/* ElsIf command + * + * Santax: + * elsif <test2: test> <block2: block> + */ + +const struct sieve_command_def cmd_elsif = { + .identifier = "elsif", + .type = SCT_COMMAND, + .positional_args = 0, + .subtests = 1, + .block_allowed = TRUE, + .block_required = TRUE, + .validate = cmd_elsif_validate, + .validate_const = cmd_if_validate_const, + .generate = cmd_if_generate +}; + +/* Else command + * + * Syntax: + * else <block> + */ + +const struct sieve_command_def cmd_else = { + .identifier = "else", + .type = SCT_COMMAND, + .positional_args = 0, + .subtests = 0, + .block_allowed = TRUE, + .block_required = TRUE, + .validate = cmd_elsif_validate, + .validate_const = cmd_if_validate_const, + .generate = cmd_else_generate +}; + +/* + * Context management + */ + +struct cmd_if_context_data { + struct cmd_if_context_data *previous; + struct cmd_if_context_data *next; + + int const_condition; + + bool jump_generated; + sieve_size_t exit_jump; +}; + +static void cmd_if_initialize_context_data +(struct sieve_command *cmd, struct cmd_if_context_data *previous) +{ + struct cmd_if_context_data *cmd_data; + + /* Assign context */ + cmd_data = p_new(sieve_command_pool(cmd), struct cmd_if_context_data, 1); + cmd_data->exit_jump = 0; + cmd_data->jump_generated = FALSE; + + /* Update linked list of contexts */ + cmd_data->previous = previous; + cmd_data->next = NULL; + if ( previous != NULL ) + previous->next = cmd_data; + + /* Check const status */ + cmd_data->const_condition = -1; + while ( previous != NULL ) { + if ( previous->const_condition > 0 ) { + cmd_data->const_condition = 0; + break; + } + previous = previous->previous; + } + + /* Assign to command context */ + cmd->data = cmd_data; +} + +/* + * Validation + */ + +static bool cmd_if_validate +(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *cmd) +{ + /* Start if-command structure */ + cmd_if_initialize_context_data(cmd, NULL); + + return TRUE; +} + +static bool cmd_elsif_validate +(struct sieve_validator *valdtr, struct sieve_command *cmd) +{ + struct sieve_command *prev; + + i_assert(cmd != NULL); + prev = sieve_command_prev(cmd); + + /* Check valid command placement */ + if ( prev == NULL || + ( !sieve_command_is(prev, cmd_if) && !sieve_command_is(prev, cmd_elsif) ) ) + { + sieve_command_validate_error(valdtr, cmd, + "the %s command must follow an if or elseif command", + sieve_command_identifier(cmd)); + return FALSE; + } + + /* Previous command in this block is 'if' or 'elsif', so we can safely refer + * to its context data + */ + cmd_if_initialize_context_data(cmd, prev->data); + + return TRUE; +} + +static bool cmd_if_validate_const +(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *cmd, + int *const_current, int const_next) +{ + struct cmd_if_context_data *cmd_data = + (struct cmd_if_context_data *) cmd->data; + + if ( cmd_data != NULL ) { + if ( cmd_data->const_condition == 0 ) { + *const_current = cmd_data->const_condition; + return FALSE; + } + + cmd_data->const_condition = const_next; + } + + *const_current = const_next; + + return ( const_next < 0 ); +} + +/* + * Code generation + */ + +/* The if command does not generate specific IF-ELSIF-ELSE opcodes, but only uses + * JMP instructions. This is why the implementation of the if command does not + * include an opcode implementation. + */ + +static void cmd_if_resolve_exit_jumps +(struct sieve_binary_block *sblock, struct cmd_if_context_data *cmd_data) +{ + struct cmd_if_context_data *if_ctx = cmd_data->previous; + + /* Iterate backwards through all if-command contexts and resolve the + * exit jumps to the current code position. + */ + while ( if_ctx != NULL ) { + if ( if_ctx->jump_generated ) + sieve_binary_resolve_offset(sblock, if_ctx->exit_jump); + if_ctx = if_ctx->previous; + } +} + +static bool cmd_if_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + struct sieve_binary_block *sblock = cgenv->sblock; + struct cmd_if_context_data *cmd_data = + (struct cmd_if_context_data *) cmd->data; + struct sieve_ast_node *test; + struct sieve_jumplist jmplist; + + /* Generate test condition */ + if ( cmd_data->const_condition < 0 ) { + /* Prepare jumplist */ + sieve_jumplist_init_temp(&jmplist, sblock); + + test = sieve_ast_test_first(cmd->ast_node); + if ( !sieve_generate_test(cgenv, test, &jmplist, FALSE) ) + return FALSE; + } + + /* Case true { */ + if ( cmd_data->const_condition != 0 ) { + if ( !sieve_generate_block(cgenv, cmd->ast_node) ) + return FALSE; + } + + /* Are we the final command in this if-elsif-else structure? */ + if ( cmd_data->next == NULL || cmd_data->const_condition == 1 ) { + /* Yes, Resolve previous exit jumps to this point */ + cmd_if_resolve_exit_jumps(sblock, cmd_data); + + } else if ( cmd_data->const_condition < 0 ) { + /* No, generate jump to end of if-elsif-else structure (resolved later) + * This of course is not necessary if the {} block contains a command + * like stop at top level that unconditionally exits the block already + * anyway. + */ + if ( !sieve_command_block_exits_unconditionally(cmd) ) { + sieve_operation_emit(sblock, NULL, &sieve_jmp_operation); + cmd_data->exit_jump = sieve_binary_emit_offset(sblock, 0); + cmd_data->jump_generated = TRUE; + } + } + + if ( cmd_data->const_condition < 0 ) { + /* Case false ... (subsequent elsif/else commands might generate more) */ + sieve_jumplist_resolve(&jmplist); + } + + return TRUE; +} + +static bool cmd_else_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + struct cmd_if_context_data *cmd_data = + (struct cmd_if_context_data *) cmd->data; + + /* Else { */ + if ( cmd_data->const_condition != 0 ) { + if ( !sieve_generate_block(cgenv, cmd->ast_node) ) + return FALSE; + + /* } End: resolve all exit blocks */ + cmd_if_resolve_exit_jumps(cgenv->sblock, cmd_data); + } + + return TRUE; +} + diff --git a/pigeonhole/src/lib-sieve/cmd-keep.c b/pigeonhole/src/lib-sieve/cmd-keep.c new file mode 100644 index 0000000..b619a80 --- /dev/null +++ b/pigeonhole/src/lib-sieve/cmd-keep.c @@ -0,0 +1,113 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-code.h" +#include "sieve-dump.h" +#include "sieve-message.h" +#include "sieve-actions.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-result.h" + +/* + * Keep command + * + * Syntax: + * keep + */ + +static bool cmd_keep_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd); + +const struct sieve_command_def cmd_keep = { + .identifier = "keep", + .type = SCT_COMMAND, + .positional_args = 0, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .generate = cmd_keep_generate +}; + +/* + * Keep operation + */ + +static bool cmd_keep_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int cmd_keep_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def cmd_keep_operation = { + .mnemonic = "KEEP", + .code = SIEVE_OPERATION_KEEP, + .dump = cmd_keep_operation_dump, + .execute = cmd_keep_operation_execute +}; + +/* + * Code generation + */ + +static bool cmd_keep_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + /* Emit opcode */ + sieve_operation_emit(cgenv->sblock, NULL, &cmd_keep_operation); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, cmd, NULL); +} + +/* + * Code dump + */ + +static bool cmd_keep_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + sieve_code_dumpf(denv, "KEEP"); + sieve_code_descend(denv); + + return ( sieve_action_opr_optional_dump(denv, address, NULL) == 0 ); +} + +/* + * Interpretation + */ + +static int cmd_keep_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + struct sieve_side_effects_list *slist = NULL; + int ret = 0; + + /* + * Read data + */ + + /* Optional operands (side effects only) */ + if ( sieve_action_opr_optional_read(renv, address, NULL, &ret, &slist) != 0 ) + return ret; + + /* + * Perform operation + */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, + "keep action; store message in default mailbox"); + + /* Add keep action to result. + */ + if ( sieve_result_add_keep(renv, slist) < 0 ) + return SIEVE_EXEC_FAILURE; + + return SIEVE_EXEC_OK; +} + + diff --git a/pigeonhole/src/lib-sieve/cmd-redirect.c b/pigeonhole/src/lib-sieve/cmd-redirect.c new file mode 100644 index 0000000..6a3b0a4 --- /dev/null +++ b/pigeonhole/src/lib-sieve/cmd-redirect.c @@ -0,0 +1,677 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "ioloop.h" +#include "str-sanitize.h" +#include "strfuncs.h" +#include "istream.h" +#include "istream-header-filter.h" +#include "ostream.h" +#include "mail-storage.h" + +#include "rfc2822.h" + +#include "sieve-common.h" +#include "sieve-limits.h" +#include "sieve-address.h" +#include "sieve-commands.h" +#include "sieve-code.h" +#include "sieve-message.h" +#include "sieve-actions.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-code-dumper.h" +#include "sieve-result.h" +#include "sieve-smtp.h" +#include "sieve-message.h" + +#include <stdio.h> + +/* + * Redirect command + * + * Syntax + * redirect <address: string> + */ + +static bool +cmd_redirect_validate(struct sieve_validator *validator, + struct sieve_command *cmd); +static bool +cmd_redirect_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd); + +const struct sieve_command_def cmd_redirect = { + .identifier = "redirect", + .type = SCT_COMMAND, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = cmd_redirect_validate, + .generate = cmd_redirect_generate +}; + +/* + * Redirect operation + */ + +static bool +cmd_redirect_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +cmd_redirect_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def cmd_redirect_operation = { + .mnemonic = "REDIRECT", + .code = SIEVE_OPERATION_REDIRECT, + .dump = cmd_redirect_operation_dump, + .execute = cmd_redirect_operation_execute +}; + +/* + * Redirect action + */ + +static bool +act_redirect_equals(const struct sieve_script_env *senv, + const struct sieve_action *act1, + const struct sieve_action *act2); +static int +act_redirect_check_duplicate(const struct sieve_runtime_env *renv, + const struct sieve_action *act, + const struct sieve_action *act_other); +static void +act_redirect_print(const struct sieve_action *action, + const struct sieve_result_print_env *rpenv, bool *keep); + +static int +act_redirect_start(const struct sieve_action_exec_env *aenv, void **tr_context); +static int +act_redirect_execute(const struct sieve_action_exec_env *aenv, void *tr_context, + bool *keep); +static int +act_redirect_commit(const struct sieve_action_exec_env *aenv, void *tr_context); + +const struct sieve_action_def act_redirect = { + .name = "redirect", + .flags = SIEVE_ACTFLAG_TRIES_DELIVER, + .equals = act_redirect_equals, + .check_duplicate = act_redirect_check_duplicate, + .print = act_redirect_print, + .start = act_redirect_start, + .execute = act_redirect_execute, + .commit = act_redirect_commit, +}; + +/* + * Validation + */ + +static bool +cmd_redirect_validate(struct sieve_validator *validator, + struct sieve_command *cmd) +{ + struct sieve_instance *svinst = sieve_validator_svinst(validator); + struct sieve_ast_argument *arg = cmd->first_positional; + + /* Check and activate address argument */ + + if (!sieve_validate_positional_argument(validator, cmd, arg, "address", + 1, SAAT_STRING)) + return FALSE; + + if (!sieve_validator_argument_activate(validator, cmd, arg, FALSE)) + return FALSE; + + /* We can only assess the validity of the outgoing address when it is + * a string literal. For runtime-generated strings this needs to be + * done at runtime. + */ + if (sieve_argument_is_string_literal(arg)) { + string_t *raw_address = sieve_ast_argument_str(arg); + const char *error; + bool result; + + T_BEGIN { + /* Parse the address */ + result = sieve_address_validate_str(raw_address, &error); + if (!result) { + sieve_argument_validate_error( + validator, arg, + "specified redirect address '%s' is invalid: %s", + str_sanitize(str_c(raw_address),128), + error); + } + } T_END; + + return result; + } + + if (svinst->max_redirects == 0) { + sieve_command_validate_error(validator, cmd, + "local policy prohibits the use of a redirect action"); + return FALSE; + } + return TRUE; +} + +/* + * Code generation + */ + +static bool +cmd_redirect_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd) +{ + sieve_operation_emit(cgenv->sblock, NULL, &cmd_redirect_operation); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, cmd, NULL); +} + +/* + * Code dump + */ + +static bool +cmd_redirect_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + sieve_code_dumpf(denv, "REDIRECT"); + sieve_code_descend(denv); + + if (sieve_action_opr_optional_dump(denv, address, NULL) != 0) + return FALSE; + + return sieve_opr_string_dump(denv, address, "address"); +} + +/* + * Code execution + */ + +static int +cmd_redirect_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + struct sieve_instance *svinst = eenv->svinst; + struct sieve_side_effects_list *slist = NULL; + string_t *redirect; + const struct smtp_address *to_address; + const char *error; + int ret; + + /* + * Read data + */ + + /* Optional operands (side effects only) */ + if (sieve_action_opr_optional_read(renv, address, NULL, + &ret, &slist) != 0) + return ret; + + /* Read the address */ + if ((ret = sieve_opr_string_read(renv, address, "address", + &redirect)) <= 0) + return ret; + + /* + * Perform operation + */ + + /* Parse the address */ + to_address = sieve_address_parse_str(redirect, &error); + if (to_address == NULL) { + sieve_runtime_error(renv, NULL, + "specified redirect address '%s' is invalid: %s", + str_sanitize(str_c(redirect),128), error); + return SIEVE_EXEC_FAILURE; + } + + if (svinst->max_redirects == 0) { + sieve_runtime_error(renv, NULL, + "local policy prohibits the use of a redirect action"); + return SIEVE_EXEC_FAILURE; + } + + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_ACTIONS)) { + sieve_runtime_trace(renv, 0, "redirect action"); + sieve_runtime_trace_descend(renv); + sieve_runtime_trace(renv, 0, "forward message to address %s", + smtp_address_encode_path(to_address)); + } + + /* Add redirect action to the result */ + + return sieve_act_redirect_add_to_result(renv, "redirect", slist, + to_address); +} + +/* + * Action implementation + */ + +struct act_redirect_transaction { + const char *msg_id, *new_msg_id; + const char *dupeid; + + bool skip_redirect:1; +}; + +static bool +act_redirect_equals(const struct sieve_script_env *senv ATTR_UNUSED, + const struct sieve_action *act1, + const struct sieve_action *act2) +{ + struct act_redirect_context *rd_ctx1 = + (struct act_redirect_context *)act1->context; + struct act_redirect_context *rd_ctx2 = + (struct act_redirect_context *)act2->context; + + /* Address is already normalized */ + return (smtp_address_equals(rd_ctx1->to_address, rd_ctx2->to_address)); +} + +static int +act_redirect_check_duplicate(const struct sieve_runtime_env *renv, + const struct sieve_action *act, + const struct sieve_action *act_other) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + + return (act_redirect_equals(eenv->scriptenv, act, act_other) ? 1 : 0); +} + +static void +act_redirect_print(const struct sieve_action *action, + const struct sieve_result_print_env *rpenv, bool *keep) +{ + struct act_redirect_context *ctx = + (struct act_redirect_context *)action->context; + + sieve_result_action_printf(rpenv, "redirect message to: %s", + smtp_address_encode_path(ctx->to_address)); + *keep = FALSE; +} + +static int +act_redirect_send(const struct sieve_action_exec_env *aenv, struct mail *mail, + struct act_redirect_context *ctx, const char *new_msg_id) + ATTR_NULL(4) +{ + static const char *hide_headers[] = { "Return-Path" }; + const struct sieve_execute_env *eenv = aenv->exec_env; + struct sieve_instance *svinst = eenv->svinst; + struct sieve_message_context *msgctx = aenv->msgctx; + const struct sieve_script_env *senv = eenv->scriptenv; + struct sieve_address_source env_from = svinst->redirect_from; + struct istream *input; + struct ostream *output; + const struct smtp_address *sender; + const char *error; + struct sieve_smtp_context *sctx; + int ret; + + /* Just to be sure */ + if (!sieve_smtp_available(senv)) { + sieve_result_global_warning(aenv, "no means to send mail"); + return SIEVE_EXEC_FAILURE; + } + + if (mail_get_stream(mail, NULL, NULL, &input) < 0) { + return sieve_result_mail_error(aenv, mail, + "failed to read input message"); + } + + /* Determine which sender to use + + From RFC 5228, Section 4.2: + + The envelope sender address on the outgoing message is chosen by the + sieve implementation. It MAY be copied from the message being + processed. However, if the message being processed has an empty + envelope sender address the outgoing message MUST also have an empty + envelope sender address. This last requirement is imposed to prevent + loops in the case where a message is redirected to an invalid address + when then returns a delivery status notification that also ends up + being redirected to the same invalid address. + */ + if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0) { + /* Envelope available */ + sender = sieve_message_get_sender(msgctx); + if (sender != NULL && + sieve_address_source_get_address(&env_from, svinst, senv, + msgctx, eenv->flags, + &sender) < 0) + sender = NULL; + } else { + /* No envelope available */ + ret = sieve_address_source_get_address(&env_from, svinst, senv, + msgctx, eenv->flags, + &sender); + if (ret < 0) + sender = NULL; + else if (ret == 0) + sender = svinst->user_email; + } + + /* Open SMTP transport */ + sctx = sieve_smtp_start_single(senv, ctx->to_address, sender, &output); + + /* Remove unwanted headers */ + input = i_stream_create_header_filter( + input, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR, + hide_headers, N_ELEMENTS(hide_headers), + *null_header_filter_callback, (void *)NULL); + + T_BEGIN { + string_t *hdr = t_str_new(256); + const struct smtp_address *user_email; + + /* Prepend sieve headers (should not affect signatures) */ + rfc2822_header_append(hdr, "X-Sieve", SIEVE_IMPLEMENTATION, + FALSE, NULL); + if (svinst->user_email == NULL && + (eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0) + user_email = sieve_message_get_final_recipient(msgctx); + else + user_email = sieve_get_user_email(svinst); + if (user_email != NULL) { + rfc2822_header_append(hdr, "X-Sieve-Redirected-From", + smtp_address_encode(user_email), + FALSE, NULL); + } + + /* Add new Message-ID if message doesn't have one */ + if (new_msg_id != NULL) + rfc2822_header_write(hdr, "Message-ID", new_msg_id); + + o_stream_nsend(output, str_data(hdr), str_len(hdr)); + } T_END; + + o_stream_nsend_istream(output, input); + + if (input->stream_errno != 0) { + sieve_result_critical(aenv, "failed to read input message", + "read(%s) failed: %s", + i_stream_get_name(input), + i_stream_get_error(input)); + i_stream_unref(&input); + sieve_smtp_abort(sctx); + return SIEVE_EXEC_TEMP_FAILURE; + } + i_stream_unref(&input); + + /* Close SMTP transport */ + if ((ret = sieve_smtp_finish(sctx, &error)) <= 0) { + if (ret < 0) { + sieve_result_global_error( + aenv, "failed to redirect message to <%s>: %s " + "(temporary failure)", + smtp_address_encode(ctx->to_address), + str_sanitize(error, 512)); + return SIEVE_EXEC_TEMP_FAILURE; + } + + sieve_result_global_log_error( + aenv, "failed to redirect message to <%s>: %s " + "(permanent failure)", + smtp_address_encode(ctx->to_address), + str_sanitize(error, 512)); + return SIEVE_EXEC_FAILURE; + } + + return SIEVE_EXEC_OK; +} + +static int +act_redirect_get_duplicate_id(struct act_redirect_context *ctx, + const struct sieve_action_exec_env *aenv, + const char *msg_id, const char **dupeid_r) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + struct sieve_message_context *msgctx = aenv->msgctx; + const struct sieve_message_data *msgdata = eenv->msgdata; + struct mail *mail = msgdata->mail; + const struct smtp_address *recipient; + const char *resent_id = NULL, *list_id = NULL; + + /* Read identifying headers */ + if (mail_get_first_header(mail, "resent-message-id", &resent_id) < 0) { + return sieve_result_mail_error( + aenv, mail, + "failed to read header field `resent-message-id'"); + } + if (resent_id == NULL && + mail_get_first_header(mail, "resent-from", &resent_id) < 0) { + return sieve_result_mail_error( + aenv, mail, + "failed to read header field `resent-from'"); + } + if (mail_get_first_header(mail, "list-id", &list_id) < 0) { + return sieve_result_mail_error( + aenv, mail, + "failed to read header field `list-id'"); + } + + if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0) + recipient = sieve_message_get_orig_recipient(msgctx); + else + recipient = sieve_get_user_email(eenv->svinst); + + pool_t pool = sieve_result_pool(aenv->result); + + /* Base the duplicate ID on: + - the message id + - the recipient running this Sieve script + - redirect target address + - if this message is resent: the message-id or from-address of + the original message + - if the message came through a mailing list: the mailinglist ID + */ + *dupeid_r = p_strdup_printf( + pool, "%s-%s-%s-%s-%s", msg_id, + (recipient != NULL ? smtp_address_encode(recipient) : ""), + smtp_address_encode(ctx->to_address), + (resent_id != NULL ? resent_id : ""), + (list_id != NULL ? list_id : "")); + return SIEVE_EXEC_OK; +} + +static int +act_redirect_check_loop_header(const struct sieve_action_exec_env *aenv, + struct mail *mail, bool *loop_detected_r) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + struct sieve_message_context *msgctx = aenv->msgctx; + const char *const *headers; + const char *recipient, *user_email; + const struct smtp_address *addr; + int ret; + + *loop_detected_r = FALSE; + + ret = mail_get_headers(mail, "x-sieve-redirected-from", &headers); + if (ret < 0) { + return sieve_result_mail_error( + aenv, mail, "failed to read header field " + "`x-sieve-redirected-from'"); + } + + if (ret == 0) + return SIEVE_EXEC_OK; + + recipient = user_email = NULL; + if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0) { + addr = sieve_message_get_final_recipient(msgctx); + if (addr != NULL) + recipient = smtp_address_encode(addr); + } + addr = sieve_get_user_email(eenv->svinst); + if (addr != NULL) + user_email = smtp_address_encode(addr); + + while (*headers != NULL) { + const char *header = t_str_trim(*headers, " \t\r\n"); + if (recipient != NULL && strcmp(header, recipient) == 0) { + *loop_detected_r = TRUE; + break; + } + if (user_email != NULL && strcmp(header, user_email) == 0) { + *loop_detected_r = TRUE; + break; + } + headers++; + } + + return SIEVE_EXEC_OK; +} + +static int +act_redirect_start(const struct sieve_action_exec_env *aenv, void **tr_context) +{ + struct act_redirect_transaction *trans; + pool_t pool = sieve_result_pool(aenv->result); + + /* Create transaction context */ + trans = p_new(pool, struct act_redirect_transaction, 1); + *tr_context = trans; + + return SIEVE_EXEC_OK; +} + +static int +act_redirect_execute(const struct sieve_action_exec_env *aenv, + void *tr_context, bool *keep) +{ + const struct sieve_action *action = aenv->action; + const struct sieve_execute_env *eenv = aenv->exec_env; + struct sieve_instance *svinst = eenv->svinst; + struct act_redirect_context *ctx = + (struct act_redirect_context *)action->context; + struct act_redirect_transaction *trans = tr_context; + struct sieve_message_context *msgctx = aenv->msgctx; + struct mail *mail = (action->mail != NULL ? + action->mail : sieve_message_get_mail(msgctx)); + const struct sieve_message_data *msgdata = eenv->msgdata; + bool duplicate, loop_detected = FALSE; + int ret; + + /* + * Prevent mail loops + */ + + /* Create Message-ID for the message if it has none */ + trans->msg_id = msgdata->id; + if (trans->msg_id == NULL) { + pool_t pool = sieve_result_pool(aenv->result); + const char *msg_id; + if (mail_get_message_id_no_validation(msgdata->mail, &msg_id) > 0) + trans->msg_id = p_strdup(pool, msg_id); + else { + msg_id = sieve_message_get_new_id(svinst); + trans->msg_id = trans->new_msg_id = p_strdup(pool, msg_id); + } + } + + /* Create ID for duplicate database lookup */ + ret = act_redirect_get_duplicate_id(ctx, aenv, trans->msg_id, + &trans->dupeid); + if (ret != SIEVE_EXEC_OK) + return ret; + i_assert(trans->dupeid != NULL); + + /* Check whether we've seen this message before */ + ret = sieve_action_duplicate_check(aenv, trans->dupeid, + strlen(trans->dupeid), + &duplicate); + if (ret < SIEVE_EXEC_OK) { + sieve_result_critical( + aenv, "failed to check for duplicate forward", + "failed to check for duplicate forward to <%s>%s", + smtp_address_encode(ctx->to_address), + (ret == SIEVE_EXEC_TEMP_FAILURE ? + " (temporaty failure)" : "")); + return ret; + } + if (duplicate) { + sieve_result_global_log( + aenv, "discarded duplicate forward to <%s>", + smtp_address_encode(ctx->to_address)); + trans->skip_redirect = TRUE; + return SIEVE_EXEC_OK; + } + + /* Check whether we've seen this message before based on added headers + */ + ret = act_redirect_check_loop_header(aenv, mail, &loop_detected); + if (ret != SIEVE_EXEC_OK) + return ret; + if (loop_detected) { + sieve_result_global_log( + aenv, "not forwarding message to <%s>: " + "the `x-sieve-redirected-from' header indicates a mail loop", + smtp_address_encode(ctx->to_address)); + trans->skip_redirect = TRUE; + return SIEVE_EXEC_OK; + } + + /* Cancel implicit keep */ + *keep = FALSE; + + return SIEVE_EXEC_OK; +} + +static int +act_redirect_commit(const struct sieve_action_exec_env *aenv, void *tr_context) +{ + const struct sieve_action *action = aenv->action; + const struct sieve_execute_env *eenv = aenv->exec_env; + struct sieve_instance *svinst = eenv->svinst; + struct act_redirect_context *ctx = + (struct act_redirect_context *)action->context; + struct sieve_message_context *msgctx = aenv->msgctx; + struct mail *mail = (action->mail != NULL ? + action->mail : sieve_message_get_mail(msgctx)); + struct act_redirect_transaction *trans = tr_context; + int ret; + + if (trans->skip_redirect) + return SIEVE_EXEC_OK; + + /* + * Try to forward the message + */ + + ret = act_redirect_send(aenv, mail, ctx, trans->new_msg_id); + if (ret == SIEVE_EXEC_OK) { + /* Mark this message id as forwarded to the specified + destination */ + sieve_action_duplicate_mark( + aenv, trans->dupeid, strlen(trans->dupeid), + ioloop_time + svinst->redirect_duplicate_period); + + eenv->exec_status->significant_action_executed = TRUE; + + struct event_passthrough *e = + sieve_action_create_finish_event(aenv)-> + add_str("redirect_target", + smtp_address_encode(ctx->to_address)); + + sieve_result_event_log(aenv, e->event(), + "forwarded to <%s>", + smtp_address_encode(ctx->to_address)); + + /* Indicate that message was successfully forwarded */ + eenv->exec_status->message_forwarded = TRUE; + + return SIEVE_EXEC_OK; + } + + return ret; +} diff --git a/pigeonhole/src/lib-sieve/cmd-require.c b/pigeonhole/src/lib-sieve/cmd-require.c new file mode 100644 index 0000000..93a2a26 --- /dev/null +++ b/pigeonhole/src/lib-sieve/cmd-require.c @@ -0,0 +1,86 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-extensions.h" +#include "sieve-validator.h" +#include "sieve-generator.h" + +/* + * Require command + * + * Syntax + * Syntax: require <capabilities: string-list> + */ + +static bool cmd_require_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); + +const struct sieve_command_def cmd_require = { + .identifier = "require", + .type = SCT_COMMAND, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = cmd_require_validate +}; + +/* + * Validation + */ + +static bool cmd_require_validate +(struct sieve_validator *valdtr, struct sieve_command *cmd) +{ + bool result = TRUE; + struct sieve_ast_argument *arg; + struct sieve_command *prev = sieve_command_prev(cmd); + + /* Check valid command placement */ + if ( !sieve_command_is_toplevel(cmd) || + ( !sieve_command_is_first(cmd) && prev != NULL && + !sieve_command_is(prev, cmd_require) ) ) + { + sieve_command_validate_error(valdtr, cmd, + "require commands can only be placed at top level " + "at the beginning of the file"); + return FALSE; + } + + /* Check argument and load specified extension(s) */ + + arg = cmd->first_positional; + if ( sieve_ast_argument_type(arg) == SAAT_STRING ) { + /* Single string */ + const struct sieve_extension *ext = sieve_validator_extension_load_by_name + (valdtr, cmd, arg, sieve_ast_argument_strc(arg)); + + if ( ext == NULL ) result = FALSE; + + } else if ( sieve_ast_argument_type(arg) == SAAT_STRING_LIST ) { + /* String list */ + struct sieve_ast_argument *stritem = sieve_ast_strlist_first(arg); + + while ( stritem != NULL ) { + const struct sieve_extension *ext = sieve_validator_extension_load_by_name + (valdtr, cmd, stritem, sieve_ast_strlist_strc(stritem)); + + if ( ext == NULL ) result = FALSE; + + stritem = sieve_ast_strlist_next(stritem); + } + } else { + /* Something else */ + sieve_argument_validate_error(valdtr, arg, + "the require command accepts a single string or string list argument, " + "but %s was found", + sieve_ast_argument_name(arg)); + return FALSE; + } + + return result; +} diff --git a/pigeonhole/src/lib-sieve/cmd-stop.c b/pigeonhole/src/lib-sieve/cmd-stop.c new file mode 100644 index 0000000..23acadb --- /dev/null +++ b/pigeonhole/src/lib-sieve/cmd-stop.c @@ -0,0 +1,86 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-code.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" + +/* + * Stop command + * + * Syntax + * stop + */ + +static bool cmd_stop_generate + (const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx ATTR_UNUSED); +static bool cmd_stop_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); + +const struct sieve_command_def cmd_stop = { + .identifier = "stop", + .type = SCT_COMMAND, + .positional_args = 0, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = cmd_stop_validate, + .generate = cmd_stop_generate +}; + +/* + * Stop operation + */ + +static int opc_stop_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def cmd_stop_operation = { + .mnemonic = "STOP", + .code = SIEVE_OPERATION_STOP, + .execute = opc_stop_execute +}; + +/* + * Command validation + */ + +static bool cmd_stop_validate +(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *cmd) +{ + sieve_command_exit_block_unconditionally(cmd); + + return TRUE; +} + +/* + * Code generation + */ + +static bool cmd_stop_generate +(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd ATTR_UNUSED) +{ + sieve_operation_emit(cgenv->sblock, NULL, &cmd_stop_operation); + + return TRUE; +} + +/* + * Code execution + */ + +static int opc_stop_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address ATTR_UNUSED) +{ + sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, + "stop command; end all script execution"); + + sieve_interpreter_interrupt(renv->interp); + + return SIEVE_EXEC_OK; +} + diff --git a/pigeonhole/src/lib-sieve/cmp-i-ascii-casemap.c b/pigeonhole/src/lib-sieve/cmp-i-ascii-casemap.c new file mode 100644 index 0000000..4f0dab4 --- /dev/null +++ b/pigeonhole/src/lib-sieve/cmp-i-ascii-casemap.c @@ -0,0 +1,99 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Comparator 'i;ascii-casemap': + * + */ + +#include "lib.h" + +#include "sieve-common.h" +#include "sieve-comparators.h" + +#include <string.h> +#include <stdio.h> +#include <ctype.h> + +/* + * Forward declarations + */ + +static int cmp_i_ascii_casemap_compare + (const struct sieve_comparator *cmp, + const char *val1, size_t val1_size, const char *val2, size_t val2_size); +static bool cmp_i_ascii_casemap_char_match + (const struct sieve_comparator *cmp, const char **val1, const char *val1_end, + const char **val2, const char *val2_end); + +/* + * Comparator object + */ + +const struct sieve_comparator_def i_ascii_casemap_comparator = { + SIEVE_OBJECT("i;ascii-casemap", + &comparator_operand, SIEVE_COMPARATOR_I_ASCII_CASEMAP), + .flags = + SIEVE_COMPARATOR_FLAG_ORDERING | + SIEVE_COMPARATOR_FLAG_EQUALITY | + SIEVE_COMPARATOR_FLAG_SUBSTRING_MATCH | + SIEVE_COMPARATOR_FLAG_PREFIX_MATCH, + .compare = cmp_i_ascii_casemap_compare, + .char_match = cmp_i_ascii_casemap_char_match, + .char_skip = sieve_comparator_octet_skip +}; + +/* + * Comparator implementation + */ + +static int cmp_i_ascii_casemap_compare( + const struct sieve_comparator *cmp ATTR_UNUSED, + const char *val1, size_t val1_size, const char *val2, size_t val2_size) +{ + int result; + + if ( val1_size == val2_size ) { + return strncasecmp(val1, val2, val1_size); + } + + if ( val1_size > val2_size ) { + result = strncasecmp(val1, val2, val2_size); + + if ( result == 0 ) return 1; + + return result; + } + + result = strncasecmp(val1, val2, val1_size); + + if ( result == 0 ) return -1; + + return result; +} + +static bool cmp_i_ascii_casemap_char_match + (const struct sieve_comparator *cmp ATTR_UNUSED, + const char **val, const char *val_end, + const char **key, const char *key_end) +{ + const char *val_begin = *val; + const char *key_begin = *key; + + while ( i_tolower(**val) == i_tolower(**key) && + *val < val_end && *key < key_end ) { + (*val)++; + (*key)++; + } + + if ( *key < key_end ) { + /* Reset */ + *val = val_begin; + *key = key_begin; + + return FALSE; + } + + return TRUE; +} + + diff --git a/pigeonhole/src/lib-sieve/cmp-i-octet.c b/pigeonhole/src/lib-sieve/cmp-i-octet.c new file mode 100644 index 0000000..caa46fa --- /dev/null +++ b/pigeonhole/src/lib-sieve/cmp-i-octet.c @@ -0,0 +1,97 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Comparator 'i;octet': + * + */ + +#include "lib.h" + +#include "sieve-common.h" +#include "sieve-comparators.h" + +#include <string.h> +#include <stdio.h> + +/* + * Forward declarations + */ + +static int cmp_i_octet_compare + (const struct sieve_comparator *cmp, + const char *val1, size_t val1_size, const char *val2, size_t val2_size); +static bool cmp_i_octet_char_match + (const struct sieve_comparator *cmp, const char **val1, const char *val1_end, + const char **val2, const char *val2_end); + +/* + * Comparator object + */ + +const struct sieve_comparator_def i_octet_comparator = { + SIEVE_OBJECT("i;octet", + &comparator_operand, SIEVE_COMPARATOR_I_OCTET), + .flags = + SIEVE_COMPARATOR_FLAG_ORDERING | + SIEVE_COMPARATOR_FLAG_EQUALITY | + SIEVE_COMPARATOR_FLAG_SUBSTRING_MATCH | + SIEVE_COMPARATOR_FLAG_PREFIX_MATCH, + .compare = cmp_i_octet_compare, + .char_match = cmp_i_octet_char_match, + .char_skip = sieve_comparator_octet_skip +}; + +/* + * Comparator implementation + */ + +static int cmp_i_octet_compare( + const struct sieve_comparator *cmp ATTR_UNUSED, + const char *val1, size_t val1_size, const char *val2, size_t val2_size) +{ + int result; + + if ( val1_size == val2_size ) { + return memcmp((void *) val1, (void *) val2, val1_size); + } + + if ( val1_size > val2_size ) { + result = memcmp((void *) val1, (void *) val2, val2_size); + + if ( result == 0 ) return 1; + + return result; + } + + result = memcmp((void *) val1, (void *) val2, val1_size); + + if ( result == 0 ) return -1; + + return result; +} + +static bool cmp_i_octet_char_match + (const struct sieve_comparator *cmp ATTR_UNUSED, + const char **val, const char *val_end, + const char **key, const char *key_end) +{ + const char *val_begin = *val; + const char *key_begin = *key; + + while ( **val == **key && *val < val_end && *key < key_end ) { + (*val)++; + (*key)++; + } + + if ( *key < key_end ) { + /* Reset */ + *val = val_begin; + *key = key_begin; + + return FALSE; + } + + return TRUE; +} + + diff --git a/pigeonhole/src/lib-sieve/ext-encoded-character.c b/pigeonhole/src/lib-sieve/ext-encoded-character.c new file mode 100644 index 0000000..d9e2b18 --- /dev/null +++ b/pigeonhole/src/lib-sieve/ext-encoded-character.c @@ -0,0 +1,271 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension encoded-character + * --------------------------- + * + * Authors: Stephan Bosch + * Specification: RFC 5228 + * Implementation: full + * Status: testing + * + */ + +#include "lib.h" +#include "unichar.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-validator.h" + +#include <ctype.h> + +/* + * Extension + */ + +static bool ext_encoded_character_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); + +const struct sieve_extension_def encoded_character_extension = { + .name = "encoded-character", + .validator_load = ext_encoded_character_validator_load, +}; + +/* + * Encoded string argument + */ + +bool arg_encoded_string_validate + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *context); + +const struct sieve_argument_def encoded_string_argument = { + .identifier = "@encoded-string", + .validate = arg_encoded_string_validate +}; + +/* Parsing */ + +static bool _skip_whitespace + (const char **in, const char *inend) +{ + while ( *in < inend ) { + if ( **in == '\r' ) { + (*in)++; + if ( **in != '\n' ) + return FALSE; + continue; + } + + /* (Loose LF is non-standard) */ + if ( **in != ' ' && **in != '\n' && **in != '\t' ) + break; + + (*in)++; + } + + return TRUE; +} + +static bool _parse_hexint +(const char **in, const char *inend, int max_digits, unsigned int *result) +{ + int digit = 0; + *result = 0; + + while ( *in < inend && (max_digits == 0 || digit < max_digits) ) { + + if ( (**in) >= '0' && (**in) <= '9' ) + *result = ((*result) << 4) + (**in) - ((unsigned int) '0'); + else if ( (**in) >= 'a' && (**in) <= 'f' ) + *result = ((*result) << 4) + (**in) - ((unsigned int) 'a') + 0x0a; + else if ( (**in) >= 'A' && (**in) <= 'F' ) + *result = ((*result) << 4) + (**in) - ((unsigned int) 'A') + 0x0a; + else + return ( digit > 0 ); + + (*in)++; + digit++; + } + + if ( digit == max_digits ) { + /* Hex digit _MUST_ end here */ + if ( (**in >= '0' && **in <= '9') || (**in >= 'a' && **in <= 'f') || + (**in >= 'A' && **in <= 'F') ) + return FALSE; + + return TRUE; + } + + return ( digit > 0 ); +} + +static bool _decode_hex +(const char **in, const char *inend, string_t *result) +{ + int values = 0; + + while ( *in < inend ) { + unsigned int hexpair; + + if ( !_skip_whitespace(in, inend) ) return FALSE; + + if ( !_parse_hexint(in, inend, 2, &hexpair) ) break; + + str_append_c(result, (unsigned char) hexpair); + values++; + } + + return ( values > 0 ); +} + +static bool _decode_unicode +(const char **in, const char *inend, string_t *result, + unsigned int *error_hex) +{ + int values = 0; + bool valid = TRUE; + + while ( *in < inend ) { + unsigned int unicode_hex; + + if ( !_skip_whitespace(in, inend) ) return FALSE; + + if ( !_parse_hexint(in, inend, 0, &unicode_hex) ) break; + + if ( uni_is_valid_ucs4((unichar_t) unicode_hex) ) + uni_ucs4_to_utf8_c((unichar_t) unicode_hex, result); + else { + if ( valid ) *error_hex = unicode_hex; + valid = FALSE; + } + values++; + } + + return ( values > 0 ); +} + +bool arg_encoded_string_validate +(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + bool result = TRUE; + enum { ST_NONE, ST_OPEN, ST_TYPE, ST_CLOSE } + state = ST_NONE; + string_t *str = sieve_ast_argument_str(*arg); + string_t *tmpstr, *newstr = NULL; + const char *p, *mark, *strstart, *substart = NULL; + const char *strval = (const char *) str_data(str); + const char *strend = strval + str_len(str); + unsigned int error_hex = 0; + + T_BEGIN { + tmpstr = t_str_new(32); + + p = strval; + strstart = p; + while ( result && p < strend ) { + switch ( state ) { + /* Normal string */ + case ST_NONE: + if ( *p == '$' ) { + substart = p; + state = ST_OPEN; + } + p++; + break; + /* Parsed '$' */ + case ST_OPEN: + if ( *p == '{' ) { + state = ST_TYPE; + p++; + } else + state = ST_NONE; + break; + /* Parsed '${' */ + case ST_TYPE: + mark = p; + /* Scan for 'hex' or 'unicode' */ + while ( p < strend && i_isalpha(*p) ) p++; + + if ( *p != ':' ) { + state = ST_NONE; + break; + } + + state = ST_CLOSE; + + str_truncate(tmpstr, 0); + if ( strncasecmp(mark, "hex", p - mark) == 0 ) { + /* Hexadecimal */ + p++; + if ( !_decode_hex(&p, strend, tmpstr) ) + state = ST_NONE; + } else if ( strncasecmp(mark, "unicode", p - mark) == 0 ) { + /* Unicode */ + p++; + if ( !_decode_unicode(&p, strend, tmpstr, &error_hex) ) + state = ST_NONE; + } else { + /* Invalid encoding */ + p++; + state = ST_NONE; + } + break; + case ST_CLOSE: + if ( *p == '}' ) { + /* We now know that the substitution is valid */ + + if ( error_hex != 0 ) { + sieve_argument_validate_error(valdtr, *arg, + "invalid unicode character 0x%08x in encoded character substitution", + error_hex); + result = FALSE; + break; + } + + if ( newstr == NULL ) { + newstr = str_new(sieve_ast_pool((*arg)->ast), str_len(str)*2); + } + + str_append_data(newstr, strstart, substart-strstart); + str_append_str(newstr, tmpstr); + + strstart = p + 1; + substart = strstart; + + p++; + } + state = ST_NONE; + } + } + } T_END; + + if ( !result ) return FALSE; + + if ( newstr != NULL ) { + if ( strstart != strend ) + str_append_data(newstr, strstart, strend-strstart); + + sieve_ast_argument_string_set(*arg, newstr); + } + + /* Pass the processed string to a (possible) next layer of processing */ + return sieve_validator_argument_activate_super + (valdtr, cmd, *arg, TRUE); +} + +/* + * Extension implementation + */ + +static bool ext_encoded_character_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + /* Override the constant string argument with our own */ + sieve_validator_argument_override + (valdtr, SAT_CONST_STRING, ext, &encoded_string_argument); + + return TRUE; +} diff --git a/pigeonhole/src/lib-sieve/ext-envelope.c b/pigeonhole/src/lib-sieve/ext-envelope.c new file mode 100644 index 0000000..e889cff --- /dev/null +++ b/pigeonhole/src/lib-sieve/ext-envelope.c @@ -0,0 +1,732 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension envelope + * ------------------ + * + * Authors: Stephan Bosch + * Specification: RFC 5228 + * Implementation: full + * Status: testing + * + */ + +#include "lib.h" +#include "str-sanitize.h" +#include "array.h" + +#include "sieve-common.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-address.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-address-parts.h" +#include "sieve-message.h" + +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-match.h" + +/* + * Forward declarations + */ + +static const struct sieve_command_def envelope_test; +const struct sieve_operation_def envelope_operation; +const struct sieve_extension_def envelope_extension; + +/* + * Extension + */ + +static bool +ext_envelope_validator_load(const struct sieve_extension *ext, + struct sieve_validator *valdtr); +static bool +ext_envelope_interpreter_load(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv, + sieve_size_t *address); + +static bool +ext_envelope_validator_validate(const struct sieve_extension *ext, + struct sieve_validator *valdtr, void *context, + struct sieve_ast_argument *require_arg, + bool required); +static int +ext_envelope_interpreter_run(const struct sieve_extension *this_ext, + const struct sieve_runtime_env *renv, + void *context, bool deferred); + +const struct sieve_extension_def envelope_extension = { + .name = "envelope", + .interpreter_load = ext_envelope_interpreter_load, + .validator_load = ext_envelope_validator_load, + SIEVE_EXT_DEFINE_OPERATION(envelope_operation) +}; +const struct sieve_validator_extension +envelope_validator_extension = { + .ext = &envelope_extension, + .validate = ext_envelope_validator_validate +}; +const struct sieve_interpreter_extension +envelope_interpreter_extension = { + .ext_def = &envelope_extension, + .run = ext_envelope_interpreter_run +}; + +static bool +ext_envelope_validator_load(const struct sieve_extension *ext, + struct sieve_validator *valdtr) +{ + /* Register new test */ + sieve_validator_register_command(valdtr, ext, &envelope_test); + + sieve_validator_extension_register(valdtr, ext, + &envelope_validator_extension, NULL); + return TRUE; +} + +static bool +ext_envelope_interpreter_load(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv, + sieve_size_t *address ATTR_UNUSED) +{ + sieve_interpreter_extension_register(renv->interp, ext, + &envelope_interpreter_extension, + NULL); + return TRUE; +} + +static bool +ext_envelope_validator_validate(const struct sieve_extension *ext, + struct sieve_validator *valdtr, + void *context ATTR_UNUSED, + struct sieve_ast_argument *require_arg, + bool required) +{ + if (required) { + enum sieve_compile_flags flags = + sieve_validator_compile_flags(valdtr); + + if ((flags & SIEVE_COMPILE_FLAG_NO_ENVELOPE) != 0) { + sieve_argument_validate_error( + valdtr, require_arg, + "the %s extension cannot be used in this context " + "(needs access to message envelope)", + sieve_extension_name(ext)); + return FALSE; + } + } + return TRUE; +} + +static int +ext_envelope_interpreter_run(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv, + void *context ATTR_UNUSED, bool deferred) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + + if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) != 0) { + if (!deferred) { + sieve_runtime_error( + renv, NULL, + "the %s extension cannot be used in this context " + "(needs access to message envelope)", + sieve_extension_name(ext)); + } + return SIEVE_EXEC_FAILURE; + } + return SIEVE_EXEC_OK; +} + +/* + * Envelope test + * + * Syntax + * envelope [COMPARATOR] [ADDRESS-PART] [MATCH-TYPE] + * <envelope-part: string-list> <key-list: string-list> + */ + +static bool +tst_envelope_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool +tst_envelope_validate(struct sieve_validator *valdtr, + struct sieve_command *tst); +static bool +tst_envelope_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +static const struct sieve_command_def envelope_test = { + .identifier = "envelope", + .type = SCT_TEST, + .positional_args= 2, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = tst_envelope_registered, + .validate = tst_envelope_validate, + .generate = tst_envelope_generate +}; + +/* + * Envelope operation + */ + +static bool +ext_envelope_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +ext_envelope_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def envelope_operation = { + .mnemonic = "ENVELOPE", + .ext_def = &envelope_extension, + .dump = ext_envelope_operation_dump, + .execute = ext_envelope_operation_execute +}; + +/* + * Envelope parts + * + * FIXME: not available to extensions + */ + +struct sieve_envelope_part { + const char *identifier; + + const struct smtp_address *const *(*get_addresses) + (const struct sieve_runtime_env *renv); + const char * const *(*get_values) + (const struct sieve_runtime_env *renv); +}; + +static const struct smtp_address *const * +_from_part_get_addresses(const struct sieve_runtime_env *renv); +static const char *const * +_from_part_get_values(const struct sieve_runtime_env *renv); +static const struct smtp_address *const * +_to_part_get_addresses(const struct sieve_runtime_env *renv); +static const char *const * +_to_part_get_values(const struct sieve_runtime_env *renv); +static const char *const * +_auth_part_get_values(const struct sieve_runtime_env *renv); + +static const struct sieve_envelope_part _from_part = { + "from", + _from_part_get_addresses, + _from_part_get_values, +}; + +static const struct sieve_envelope_part _to_part = { + "to", + _to_part_get_addresses, + _to_part_get_values, +}; + +static const struct sieve_envelope_part _auth_part = { + "auth", + NULL, + _auth_part_get_values, +}; + +static const struct sieve_envelope_part *_envelope_parts[] = { + /* Required */ + &_from_part, &_to_part, + + /* Non-standard */ + &_auth_part +}; + +static unsigned int _envelope_part_count = N_ELEMENTS(_envelope_parts); + +static const struct sieve_envelope_part * +_envelope_part_find(const char *identifier) +{ + unsigned int i; + + for (i = 0; i < _envelope_part_count; i++) { + if (strcasecmp(_envelope_parts[i]->identifier, + identifier) == 0) + return _envelope_parts[i]; + } + + return NULL; +} + +/* Envelope parts implementation */ + +static const struct smtp_address *const * +_from_part_get_addresses(const struct sieve_runtime_env *renv) +{ + ARRAY(const struct smtp_address *) envelope_values; + const struct smtp_address *address = + sieve_message_get_sender(renv->msgctx); + + t_array_init(&envelope_values, 2); + + if (address == NULL) + address = smtp_address_create_temp(NULL, NULL); + array_append(&envelope_values, &address, 1); + + (void)array_append_space(&envelope_values); + return array_idx(&envelope_values, 0); +} + +static const char *const * +_from_part_get_values(const struct sieve_runtime_env *renv) +{ + ARRAY(const char *)envelope_values; + const struct smtp_address *address = + sieve_message_get_sender(renv->msgctx); + const char *value; + + t_array_init(&envelope_values, 2); + + value = ""; + if (!smtp_address_isnull(address)) + value = smtp_address_encode(address); + array_append(&envelope_values, &value, 1); + + (void)array_append_space(&envelope_values); + + return array_idx(&envelope_values, 0); +} + +static const struct smtp_address *const * +_to_part_get_addresses(const struct sieve_runtime_env *renv) +{ + ARRAY(const struct smtp_address *) envelope_values; + const struct smtp_address *address = + sieve_message_get_orig_recipient(renv->msgctx); + + if (address != NULL && address->localpart != NULL) { + t_array_init(&envelope_values, 2); + + array_append(&envelope_values, &address, 1); + + (void)array_append_space(&envelope_values); + return array_idx(&envelope_values, 0); + } + return NULL; +} + +static const char *const * +_to_part_get_values(const struct sieve_runtime_env *renv) +{ + ARRAY(const char *) envelope_values; + const struct smtp_address *address = + sieve_message_get_orig_recipient(renv->msgctx); + + t_array_init(&envelope_values, 2); + + if (address != NULL && address->localpart != NULL) { + const char *value = smtp_address_encode(address); + array_append(&envelope_values, &value, 1); + } + + (void)array_append_space(&envelope_values); + + return array_idx(&envelope_values, 0); +} + +static const char *const * +_auth_part_get_values(const struct sieve_runtime_env *renv) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + ARRAY(const char *) envelope_values; + + t_array_init(&envelope_values, 2); + + if (eenv->msgdata->auth_user != NULL) + array_append(&envelope_values, &eenv->msgdata->auth_user, 1); + + (void)array_append_space(&envelope_values); + + return array_idx(&envelope_values, 0); +} + +/* + * Envelope address list + */ + +/* Forward declarations */ + +static int +sieve_envelope_address_list_next_string_item(struct sieve_stringlist *_strlist, + string_t **str_r); +static int +sieve_envelope_address_list_next_item(struct sieve_address_list *_addrlist, + struct smtp_address *addr_r, + string_t **unparsed_r); +static void +sieve_envelope_address_list_reset(struct sieve_stringlist *_strlist); + +/* Stringlist object */ + +struct sieve_envelope_address_list { + struct sieve_address_list addrlist; + + struct sieve_stringlist *env_parts; + + const struct smtp_address *const *cur_addresses; + const char * const *cur_values; + + int value_index; +}; + +static struct sieve_address_list * +sieve_envelope_address_list_create(const struct sieve_runtime_env *renv, + struct sieve_stringlist *env_parts) +{ + struct sieve_envelope_address_list *addrlist; + + addrlist = t_new(struct sieve_envelope_address_list, 1); + addrlist->addrlist.strlist.runenv = renv; + addrlist->addrlist.strlist.exec_status = SIEVE_EXEC_OK; + addrlist->addrlist.strlist.next_item = + sieve_envelope_address_list_next_string_item; + addrlist->addrlist.strlist.reset = sieve_envelope_address_list_reset; + addrlist->addrlist.next_item = sieve_envelope_address_list_next_item; + addrlist->env_parts = env_parts; + + return &addrlist->addrlist; +} + +static int +sieve_envelope_address_list_next_item(struct sieve_address_list *_addrlist, + struct smtp_address *addr_r, + string_t **unparsed_r) +{ + struct sieve_envelope_address_list *addrlist = + (struct sieve_envelope_address_list *)_addrlist; + const struct sieve_runtime_env *renv = _addrlist->strlist.runenv; + + if (addr_r != NULL) + smtp_address_init(addr_r, NULL, NULL); + if (unparsed_r != NULL) *unparsed_r = NULL; + + while (addrlist->cur_addresses == NULL && + addrlist->cur_values == NULL) { + const struct sieve_envelope_part *epart; + string_t *envp_item = NULL; + int ret; + + /* Read next header value from source list */ + if ((ret = sieve_stringlist_next_item(addrlist->env_parts, + &envp_item)) <= 0) + return ret; + + if (_addrlist->strlist.trace) { + sieve_runtime_trace( + _addrlist->strlist.runenv, 0, + "getting `%s' part from message envelope", + str_sanitize(str_c(envp_item), 80)); + } + + if ((epart=_envelope_part_find(str_c(envp_item))) != NULL) { + addrlist->value_index = 0; + + if (epart->get_addresses != NULL) { + /* Field contains addresses */ + addrlist->cur_addresses = + epart->get_addresses(renv); + + /* Drop empty list */ + if (addrlist->cur_addresses != NULL && + addrlist->cur_addresses[0] == NULL) + addrlist->cur_addresses = NULL; + } + + if (addrlist->cur_addresses == NULL && + epart->get_values != NULL) { + /* Field contains something else */ + addrlist->cur_values = epart->get_values(renv); + + /* Drop empty list */ + if (addrlist->cur_values != NULL && + addrlist->cur_values[0] == NULL) + addrlist->cur_values = NULL; + } + } + } + + /* Return next item */ + if (addrlist->cur_addresses != NULL) { + const struct smtp_address *addr = + addrlist->cur_addresses[addrlist->value_index]; + + if (addr->localpart == NULL) { + /* Null path <> */ + if (unparsed_r != NULL) + *unparsed_r = t_str_new_const("", 0); + } else { + if (addr_r != NULL) + *addr_r = *addr; + } + + /* Advance to next value */ + addrlist->value_index++; + if (addrlist->cur_addresses[addrlist->value_index] == NULL) { + addrlist->cur_addresses = NULL; + addrlist->value_index = 0; + } + } else { + if (unparsed_r != NULL) { + const char *value = + addrlist->cur_values[addrlist->value_index]; + + *unparsed_r = t_str_new_const(value, strlen(value)); + } + + /* Advance to next value */ + addrlist->value_index++; + if (addrlist->cur_values[addrlist->value_index] == NULL) { + addrlist->cur_values = NULL; + addrlist->value_index = 0; + } + } + + return 1; +} + +static int +sieve_envelope_address_list_next_string_item(struct sieve_stringlist *_strlist, + string_t **str_r) +{ + struct sieve_address_list *addrlist = + (struct sieve_address_list *)_strlist; + struct smtp_address addr; + int ret; + + if ((ret=sieve_envelope_address_list_next_item(addrlist, &addr, + str_r)) <= 0) + return ret; + + if (addr.localpart != NULL) { + const char *addr_str = smtp_address_encode(&addr); + if (str_r != NULL) + *str_r = t_str_new_const(addr_str, strlen(addr_str)); + } + return 1; +} + +static void +sieve_envelope_address_list_reset(struct sieve_stringlist *_strlist) +{ + struct sieve_envelope_address_list *addrlist = + (struct sieve_envelope_address_list *)_strlist; + + sieve_stringlist_reset(addrlist->env_parts); + addrlist->cur_addresses = NULL; + addrlist->cur_values = NULL; + addrlist->value_index = 0; +} + +/* + * Command Registration + */ + +static bool +tst_envelope_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_command_registration *cmd_reg) +{ + /* The order of these is not significant */ + sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR); + sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE); + sieve_address_parts_link_tags(valdtr, cmd_reg, SIEVE_AM_OPT_ADDRESS_PART); + return TRUE; +} + +/* + * Validation + */ + +static int +_envelope_part_is_supported(void *context, struct sieve_ast_argument *arg) +{ + const struct sieve_envelope_part **not_address = + (const struct sieve_envelope_part **) context; + + if (sieve_argument_is_string_literal(arg)) { + const struct sieve_envelope_part *epart; + + if ((epart=_envelope_part_find( + sieve_ast_strlist_strc(arg))) != NULL) { + if (epart->get_addresses == NULL) { + if (*not_address == NULL) + *not_address = epart; + } + return 1; + } + return 0; + } + return 1; /* Can't check at compile time */ +} + +static bool +tst_envelope_validate(struct sieve_validator *valdtr, struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + struct sieve_ast_argument *epart; + struct sieve_comparator cmp_default = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + struct sieve_match_type mcht_default = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + const struct sieve_envelope_part *not_address = NULL; + + if (!sieve_validate_positional_argument(valdtr, tst, arg, + "envelope part", 1, + SAAT_STRING_LIST)) { + return FALSE; + } + + if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE)) + return FALSE; + + /* Check whether supplied envelope parts are supported + * FIXME: verify dynamic envelope parts at runtime + */ + epart = arg; + if (sieve_ast_stringlist_map(&epart, (void *) ¬_address, + _envelope_part_is_supported) <= 0) { + i_assert(epart != NULL); + sieve_argument_validate_error( + valdtr, epart, + "specified envelope part '%s' is not supported by the envelope test", + str_sanitize(sieve_ast_strlist_strc(epart), 64)); + return FALSE; + } + + if (not_address != NULL) { + struct sieve_ast_argument *addrp_arg = + sieve_command_find_argument(tst, &address_part_tag); + + if (addrp_arg != NULL) { + sieve_argument_validate_error( + valdtr, addrp_arg, + "address part ':%s' specified while non-address envelope part '%s' " + "is tested with the envelope test", + sieve_ast_argument_tag(addrp_arg), + not_address->identifier); + return FALSE; + } + } + + arg = sieve_ast_argument_next(arg); + + if (!sieve_validate_positional_argument(valdtr, tst, arg, "key list", 2, + SAAT_STRING_LIST)) + return FALSE; + + if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE)) + return FALSE; + + /* Validate the key argument to a specified match type */ + return sieve_match_type_validate(valdtr, tst, arg, + &mcht_default, &cmp_default); +} + +/* + * Code generation + */ + +static bool +tst_envelope_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd) +{ + (void)sieve_operation_emit(cgenv->sblock, cmd->ext, + &envelope_operation); + + /* Generate arguments */ + if (!sieve_generate_arguments(cgenv, cmd, NULL)) + return FALSE; + return TRUE; +} + +/* + * Code dump + */ + +static bool +ext_envelope_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + sieve_code_dumpf(denv, "ENVELOPE"); + sieve_code_descend(denv); + + /* Handle any optional arguments */ + if (sieve_addrmatch_opr_optional_dump(denv, address, NULL) != 0) + return FALSE; + + return (sieve_opr_stringlist_dump(denv, address, "envelope part") && + sieve_opr_stringlist_dump(denv, address, "key list")); +} + +/* + * Interpretation + */ + +static int +ext_envelope_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + struct sieve_comparator cmp = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + struct sieve_match_type mcht = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + struct sieve_address_part addrp = + SIEVE_ADDRESS_PART_DEFAULT(all_address_part); + struct sieve_stringlist *env_part_list, *value_list, *key_list; + struct sieve_address_list *addr_list; + int match, ret; + + /* + * Read operands + */ + + /* Read optional operands */ + if (sieve_addrmatch_opr_optional_read(renv, address, NULL, &ret, + &addrp, &mcht, &cmp) < 0) + return ret; + + /* Read envelope-part */ + if ((ret = sieve_opr_stringlist_read(renv, address, "envelope-part", + &env_part_list)) <= 0) + return ret; + + /* Read key-list */ + if ((ret = sieve_opr_stringlist_read(renv, address, "key-list", + &key_list)) <= 0) + return ret; + + /* + * Perform test + */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "envelope test"); + + /* Create value stringlist */ + addr_list = sieve_envelope_address_list_create(renv, env_part_list); + value_list = sieve_address_part_stringlist_create(renv, &addrp, + addr_list); + + /* Perform match */ + if ((match = sieve_match(renv, &mcht, &cmp, + value_list, key_list, &ret)) < 0) + return ret; + + /* Set test result for subsequent conditional jump */ + sieve_interpreter_set_test_result(renv->interp, match > 0); + return SIEVE_EXEC_OK; +} + diff --git a/pigeonhole/src/lib-sieve/ext-fileinto.c b/pigeonhole/src/lib-sieve/ext-fileinto.c new file mode 100644 index 0000000..fb65758 --- /dev/null +++ b/pigeonhole/src/lib-sieve/ext-fileinto.c @@ -0,0 +1,225 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension fileinto + * ------------------ + * + * Authors: Stephan Bosch + * Specification: RFC 5228 + * Implementation: full + * Status: testing + * + */ + +#include "lib.h" +#include "str-sanitize.h" +#include "unichar.h" + +#include "sieve-common.h" +#include "sieve-extensions.h" +#include "sieve-binary.h" +#include "sieve-commands.h" +#include "sieve-code.h" +#include "sieve-message.h" +#include "sieve-actions.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-result.h" + +/* + * Forward declarations + */ + +static const struct sieve_command_def fileinto_command; +const struct sieve_operation_def fileinto_operation; +const struct sieve_extension_def fileinto_extension; + +/* + * Extension + */ + +static bool +ext_fileinto_validator_load(const struct sieve_extension *ext, + struct sieve_validator *valdtr); + +const struct sieve_extension_def fileinto_extension = { + .name = "fileinto", + .validator_load = ext_fileinto_validator_load, + SIEVE_EXT_DEFINE_OPERATION(fileinto_operation), +}; + +static bool +ext_fileinto_validator_load(const struct sieve_extension *ext, + struct sieve_validator *valdtr) +{ + /* Register new command */ + sieve_validator_register_command(valdtr, ext, &fileinto_command); + + return TRUE; +} + +/* + * Fileinto command + * + * Syntax: + * fileinto <folder: string> + */ + +static bool +cmd_fileinto_validate(struct sieve_validator *valdtr, + struct sieve_command *cmd); +static bool +cmd_fileinto_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +static const struct sieve_command_def fileinto_command = { + .identifier = "fileinto", + .type = SCT_COMMAND, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = cmd_fileinto_validate, + .generate = cmd_fileinto_generate, +}; + +/* + * Fileinto operation + */ + +static bool +ext_fileinto_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +ext_fileinto_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def fileinto_operation = { + .mnemonic = "FILEINTO", + .ext_def = &fileinto_extension, + .dump = ext_fileinto_operation_dump, + .execute = ext_fileinto_operation_execute, +}; + +/* + * Validation + */ + +static bool +cmd_fileinto_validate(struct sieve_validator *valdtr, struct sieve_command *cmd) +{ + struct sieve_ast_argument *arg = cmd->first_positional; + + if (!sieve_validate_positional_argument(valdtr, cmd, arg, "folder", + 1, SAAT_STRING)) + return FALSE; + + if (!sieve_validator_argument_activate(valdtr, cmd, arg, FALSE)) + return FALSE; + + /* Check name validity when folder argument is not a variable */ + if (sieve_argument_is_string_literal(arg)) { + const char *folder = sieve_ast_argument_strc(arg), *error; + + if (!sieve_mailbox_check_name(folder, &error)) { + sieve_command_validate_error( + valdtr, cmd, "fileinto command: " + "invalid folder name `%s' specified: %s", + str_sanitize(folder, 256), error); + return FALSE; + } + } + return TRUE; +} + +/* + * Code generation + */ + +static bool +cmd_fileinto_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd) +{ + sieve_operation_emit(cgenv->sblock, cmd->ext, &fileinto_operation); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, cmd, NULL); +} + +/* + * Code dump + */ + +static bool +ext_fileinto_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + sieve_code_dumpf(denv, "FILEINTO"); + sieve_code_descend(denv); + + if (sieve_action_opr_optional_dump(denv, address, NULL) != 0) + return FALSE; + + return sieve_opr_string_dump(denv, address, "folder"); +} + +/* + * Execution + */ + +static int +ext_fileinto_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + struct sieve_side_effects_list *slist = NULL; + string_t *folder; + const char *error; + bool trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_ACTIONS); + int ret = 0; + + /* + * Read operands + */ + + /* Optional operands (side effects only) */ + if (sieve_action_opr_optional_read(renv, address, NULL, + &ret, &slist) != 0) + return ret; + + /* Folder operand */ + ret = sieve_opr_string_read(renv, address, "folder", &folder); + if (ret <= 0) + return ret; + + /* + * Perform operation + */ + + if (trace) { + sieve_runtime_trace(renv, 0, "fileinto action"); + sieve_runtime_trace_descend(renv); + } + + if (!sieve_mailbox_check_name(str_c(folder), &error)) { + sieve_runtime_error( + renv, NULL, "fileinto command: " + "invalid folder name `%s' specified: %s", + str_c(folder), error); + return SIEVE_EXEC_FAILURE; + } + + if (trace) { + sieve_runtime_trace(renv, 0, "store message in mailbox `%s'", + str_sanitize(str_c(folder), 80)); + } + + /* Add action to result */ + if (sieve_act_store_add_to_result(renv, "fileinto", slist, + str_c(folder)) < 0) + return SIEVE_EXEC_FAILURE; + + sieve_message_snapshot(renv->msgctx); + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/ext-reject.c b/pigeonhole/src/lib-sieve/ext-reject.c new file mode 100644 index 0000000..649c1f2 --- /dev/null +++ b/pigeonhole/src/lib-sieve/ext-reject.c @@ -0,0 +1,606 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension reject + * ---------------- + * + * Authors: Stephan Bosch + * Specification: RFC 5429 + * Implementation: full + * Status: testing + * + */ + +#include "lib.h" +#include "ioloop.h" +#include "hostpid.h" +#include "str-sanitize.h" +#include "message-date.h" +#include "message-size.h" +#include "istream.h" +#include "istream-header-filter.h" + +#include "rfc2822.h" + +#include "sieve-common.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-code.h" +#include "sieve-actions.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-binary.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-result.h" +#include "sieve-message.h" +#include "sieve-smtp.h" + +/* + * Forward declarations + */ + +static const struct sieve_command_def reject_command; +static const struct sieve_operation_def reject_operation; + +static const struct sieve_command_def ereject_command; +static const struct sieve_operation_def ereject_operation; + +/* + * Extensions + */ + +static bool +ext_reject_validator_validate(const struct sieve_extension *ext, + struct sieve_validator *valdtr, void *context, + struct sieve_ast_argument *require_arg, + bool required); +static int +ext_reject_interpreter_run(const struct sieve_extension *this_ext, + const struct sieve_runtime_env *renv, + void *context, bool deferred); + +/* Reject */ + +static bool +ext_reject_validator_load(const struct sieve_extension *ext, + struct sieve_validator *valdtr); +static bool +ext_reject_interpreter_load(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_extension_def reject_extension = { + .name = "reject", + .validator_load = ext_reject_validator_load, + .interpreter_load = ext_reject_interpreter_load, + SIEVE_EXT_DEFINE_OPERATION(reject_operation) +}; +const struct sieve_validator_extension +reject_validator_extension = { + .ext = &reject_extension, + .validate = ext_reject_validator_validate +}; +const struct sieve_interpreter_extension +reject_interpreter_extension = { + .ext_def = &reject_extension, + .run = ext_reject_interpreter_run +}; + +static bool +ext_reject_validator_load(const struct sieve_extension *ext, + struct sieve_validator *valdtr) +{ + /* Register new command */ + sieve_validator_register_command(valdtr, ext, &reject_command); + + sieve_validator_extension_register(valdtr, ext, + &reject_validator_extension, NULL); + return TRUE; +} + +static bool +ext_reject_interpreter_load(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv, + sieve_size_t *address ATTR_UNUSED) +{ + sieve_interpreter_extension_register(renv->interp, ext, + &reject_interpreter_extension, + NULL); + return TRUE; +} + +/* EReject */ + +static bool +ext_ereject_validator_load(const struct sieve_extension *ext, + struct sieve_validator *valdtr); +static bool +ext_ereject_interpreter_load(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_extension_def ereject_extension = { + .name = "ereject", + .validator_load = ext_ereject_validator_load, + .interpreter_load = ext_ereject_interpreter_load, + SIEVE_EXT_DEFINE_OPERATION(ereject_operation) +}; +const struct sieve_validator_extension +ereject_validator_extension = { + .ext = &ereject_extension, + .validate = ext_reject_validator_validate +}; +const struct sieve_interpreter_extension +ereject_interpreter_extension = { + .ext_def = &ereject_extension, + .run = ext_reject_interpreter_run +}; + +static bool +ext_ereject_validator_load(const struct sieve_extension *ext, + struct sieve_validator *valdtr) +{ + /* Register new command */ + sieve_validator_register_command(valdtr, ext, &ereject_command); + + sieve_validator_extension_register(valdtr, ext, + &ereject_validator_extension, NULL); + return TRUE; +} + +static bool +ext_ereject_interpreter_load(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv, + sieve_size_t *address ATTR_UNUSED) +{ + sieve_interpreter_extension_register(renv->interp, ext, + &ereject_interpreter_extension, + NULL); + return TRUE; +} + +/* Environment checking */ + +static bool +ext_reject_validator_validate(const struct sieve_extension *ext, + struct sieve_validator *valdtr, + void *context ATTR_UNUSED, + struct sieve_ast_argument *require_arg, + bool required) +{ + if (required) { + enum sieve_compile_flags flags = + sieve_validator_compile_flags(valdtr); + + if ((flags & SIEVE_COMPILE_FLAG_NO_ENVELOPE) != 0) { + sieve_argument_validate_error( + valdtr, require_arg, + "the %s extension cannot be used in this context " + "(needs access to message envelope)", + sieve_extension_name(ext)); + return FALSE; + } + } + return TRUE; +} + +static int +ext_reject_interpreter_run(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv, + void *context ATTR_UNUSED, bool deferred) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + + if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) != 0) { + if (!deferred) { + sieve_runtime_error( + renv, NULL, + "the %s extension cannot be used in this context " + "(needs access to message envelope)", + sieve_extension_name(ext)); + } + return SIEVE_EXEC_FAILURE; + } + return SIEVE_EXEC_OK; +} + +/* + * Commands + */ + +/* Forward declarations */ + +static bool +cmd_reject_validate(struct sieve_validator *valdtr, struct sieve_command *cmd); +static bool +cmd_reject_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd); + +/* Reject command + * + * Syntax: + * reject <reason: string> + */ + +static const struct sieve_command_def reject_command = { + .identifier = "reject", + .type = SCT_COMMAND, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = cmd_reject_validate, + .generate = cmd_reject_generate +}; + +/* EReject command + * + * Syntax: + * ereject <reason: string> + */ + +static const struct sieve_command_def ereject_command = { + .identifier = "ereject", + .type = SCT_COMMAND, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = cmd_reject_validate, + .generate = cmd_reject_generate, +}; + +/* + * Operations + */ + +/* Forward declarations */ + +static bool +ext_reject_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +ext_reject_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +/* Reject operation */ + +static const struct sieve_operation_def reject_operation = { + .mnemonic = "REJECT", + .ext_def = &reject_extension, + .dump = ext_reject_operation_dump, + .execute = ext_reject_operation_execute +}; + +/* EReject operation */ + +static const struct sieve_operation_def ereject_operation = { + .mnemonic = "EREJECT", + .ext_def = &ereject_extension, + .dump = ext_reject_operation_dump, + .execute = ext_reject_operation_execute +}; + +/* + * Reject action + */ + +static int +act_reject_check_duplicate(const struct sieve_runtime_env *renv, + const struct sieve_action *act, + const struct sieve_action *act_other); +int act_reject_check_conflict(const struct sieve_runtime_env *renv, + const struct sieve_action *act, + const struct sieve_action *act_other); +static void +act_reject_print(const struct sieve_action *action, + const struct sieve_result_print_env *rpenv, bool *keep); +static int +act_reject_start(const struct sieve_action_exec_env *aenv, void **tr_context); +static int +act_reject_execute(const struct sieve_action_exec_env *aenv, void *tr_context, + bool *keep); +static int +act_reject_commit(const struct sieve_action_exec_env *aenv, void *tr_context); + +const struct sieve_action_def act_reject = { + .name = "reject", + .flags = SIEVE_ACTFLAG_SENDS_RESPONSE, + .check_duplicate = act_reject_check_duplicate, + .check_conflict = act_reject_check_conflict, + .print = act_reject_print, + .start = act_reject_start, + .execute = act_reject_execute, + .commit = act_reject_commit, +}; + +struct act_reject_context { + const char *reason; + bool ereject; +}; + +/* + * Validation + */ + +static bool +cmd_reject_validate(struct sieve_validator *valdtr, struct sieve_command *cmd) +{ + struct sieve_ast_argument *arg = cmd->first_positional; + + if (!sieve_validate_positional_argument(valdtr, cmd, arg, "reason", + 1, SAAT_STRING)) + return FALSE; + + return sieve_validator_argument_activate(valdtr, cmd, arg, FALSE); +} + +/* + * Code generation + */ + +static bool +cmd_reject_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd) +{ + if (sieve_command_is(cmd, reject_command)) { + sieve_operation_emit(cgenv->sblock, cmd->ext, + &reject_operation); + } else { + sieve_operation_emit(cgenv->sblock, cmd->ext, + &ereject_operation); + } + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, cmd, NULL); +} + +/* + * Code dump + */ + +static bool +ext_reject_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + sieve_code_dumpf(denv, "%s", sieve_operation_mnemonic(denv->oprtn)); + sieve_code_descend(denv); + + if (sieve_action_opr_optional_dump(denv, address, NULL) != 0) + return FALSE; + + return sieve_opr_string_dump(denv, address, "reason"); +} + +/* + * Interpretation + */ + +static int +ext_reject_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + const struct sieve_operation *oprtn = renv->oprtn; + const struct sieve_extension *this_ext = oprtn->ext; + struct sieve_side_effects_list *slist = NULL; + struct act_reject_context *act; + string_t *reason; + pool_t pool; + int ret; + + /* + * Read data + */ + + /* Optional operands (side effects only) */ + if (sieve_action_opr_optional_read(renv, address, NULL, + &ret, &slist) != 0) + return ret; + + /* Read rejection reason */ + if ((ret = sieve_opr_string_read(renv, address, "reason", + &reason)) <= 0) + return ret; + + /* + * Perform operation + */ + + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_ACTIONS)) { + if (sieve_operation_is(oprtn, ereject_operation)) + sieve_runtime_trace(renv, 0, "ereject action"); + else + sieve_runtime_trace(renv, 0, "reject action"); + + sieve_runtime_trace_descend(renv); + sieve_runtime_trace(renv, 0, "reject message with reason `%s'", + str_sanitize(str_c(reason), 64)); + } + + /* Add reject action to the result */ + pool = sieve_result_pool(renv->result); + act = p_new(pool, struct act_reject_context, 1); + act->reason = p_strdup(pool, str_c(reason)); + act->ereject = sieve_operation_is(oprtn, ereject_operation); + + if (sieve_result_add_action(renv, this_ext, + (act->ereject ? "ereject" : "reject"), + &act_reject, slist, (void *)act, + 0, FALSE) < 0) + return SIEVE_EXEC_FAILURE; + return SIEVE_EXEC_OK; +} + +/* + * Action implementation + */ + +struct act_reject_transaction { + bool ignore_reject:1; +}; + +static int +act_reject_check_duplicate(const struct sieve_runtime_env *renv ATTR_UNUSED, + const struct sieve_action *act, + const struct sieve_action *act_other) +{ + if (!sieve_action_is_executed(act_other, renv->result)) { + sieve_runtime_error( + renv, act->location, + "duplicate reject/ereject action not allowed " + "(previously triggered one was here: %s)", + act_other->location); + return -1; + } + + return 1; +} + +int act_reject_check_conflict(const struct sieve_runtime_env *renv, + const struct sieve_action *act, + const struct sieve_action *act_other) +{ + if ((act_other->def->flags & SIEVE_ACTFLAG_TRIES_DELIVER) > 0) { + if (!sieve_action_is_executed(act_other, renv->result)) { + sieve_runtime_error( + renv, act->location, + "reject/ereject action conflicts with other action: " + "the %s action (%s) tries to deliver the message", + act_other->def->name, act_other->location); + return -1; + } + } + + if ((act_other->def->flags & SIEVE_ACTFLAG_SENDS_RESPONSE) > 0) { + struct act_reject_context *rj_ctx; + + if (!sieve_action_is_executed(act_other, renv->result)) { + sieve_runtime_error( + renv, act->location, + "reject/ereject action conflicts with other action: " + "the %s action (%s) also sends a response to the sender", + act_other->def->name, act_other->location); + return -1; + } + + /* Conflicting action was already executed, transform reject + * into discard equivalent. + */ + rj_ctx = (struct act_reject_context *)act->context; + rj_ctx->reason = NULL; + } + + return 0; +} + +static void +act_reject_print(const struct sieve_action *action, + const struct sieve_result_print_env *rpenv, bool *keep) +{ + struct act_reject_context *rj_ctx = + (struct act_reject_context *)action->context; + + if (rj_ctx->reason != NULL) { + sieve_result_action_printf( + rpenv, "reject message with reason: %s", + str_sanitize(rj_ctx->reason, 128)); + } else { + sieve_result_action_printf( + rpenv, + "reject message without sending a response (discard)"); + } + + *keep = FALSE; +} + +static int +act_reject_start(const struct sieve_action_exec_env *aenv, void **tr_context) +{ + struct act_reject_transaction *trans; + pool_t pool = sieve_result_pool(aenv->result); + + /* Create transaction context */ + trans = p_new(pool, struct act_reject_transaction, 1); + *tr_context = trans; + + return SIEVE_EXEC_OK; +} + +static int +act_reject_execute(const struct sieve_action_exec_env *aenv, + void *tr_context, bool *keep) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + struct act_reject_context *rj_ctx = + (struct act_reject_context *)aenv->action->context; + struct act_reject_transaction *trans = tr_context; + const struct smtp_address *sender, *recipient; + + sender = sieve_message_get_sender(aenv->msgctx); + recipient = sieve_message_get_orig_recipient(aenv->msgctx); + + if ((eenv->flags & SIEVE_EXECUTE_FLAG_SKIP_RESPONSES) != 0) { + sieve_result_global_log( + aenv, "not sending reject message (skipped)"); + trans->ignore_reject = TRUE; + return SIEVE_EXEC_OK; + } + if (smtp_address_isnull(recipient)) { + sieve_result_global_warning( + aenv, "reject action aborted: envelope recipient is <>"); + trans->ignore_reject = TRUE; + return SIEVE_EXEC_OK; + } + if (rj_ctx->reason == NULL) { + sieve_result_global_log( + aenv, "not sending reject message " + "(would cause second response to sender)"); + trans->ignore_reject = TRUE; + *keep = FALSE; + return SIEVE_EXEC_OK; + } + if (smtp_address_isnull(sender)) { + sieve_result_global_log( + aenv, "not sending reject message to <>"); + trans->ignore_reject = TRUE; + *keep = FALSE; + return SIEVE_EXEC_OK; + } + + *keep = FALSE; + return SIEVE_EXEC_OK; +} + +static int +act_reject_commit(const struct sieve_action_exec_env *aenv, + void *tr_context ATTR_UNUSED) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + struct act_reject_context *rj_ctx = + (struct act_reject_context *)aenv->action->context; + struct act_reject_transaction *trans = tr_context; + const struct smtp_address *sender, *recipient; + int ret; + + sender = sieve_message_get_sender(aenv->msgctx); + recipient = sieve_message_get_orig_recipient(aenv->msgctx); + + if (trans->ignore_reject) + return SIEVE_EXEC_OK; + + if ((ret = sieve_action_reject_mail(aenv, recipient, + rj_ctx->reason)) <= 0) + return ret; + + eenv->exec_status->significant_action_executed = TRUE; + + struct event_passthrough *e = sieve_action_create_finish_event(aenv); + + sieve_result_event_log(aenv, e->event(), + "rejected message from <%s> (%s)", + smtp_address_encode(sender), + (rj_ctx->ereject ? "ereject" : "reject")); + + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/mcht-contains.c b/pigeonhole/src/lib-sieve/mcht-contains.c new file mode 100644 index 0000000..a9b3190 --- /dev/null +++ b/pigeonhole/src/lib-sieve/mcht-contains.c @@ -0,0 +1,66 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Match-type ':contains' + */ + +#include "lib.h" + +#include "sieve-match-types.h" +#include "sieve-comparators.h" +#include "sieve-match.h" + +#include <string.h> +#include <stdio.h> + +/* + * Forward declarations + */ + +static int mcht_contains_match_key + (struct sieve_match_context *mctx, const char *val, size_t val_size, + const char *key, size_t key_size); + +/* + * Match-type object + */ + +const struct sieve_match_type_def contains_match_type = { + SIEVE_OBJECT("contains", + &match_type_operand, SIEVE_MATCH_TYPE_CONTAINS), + .validate_context = sieve_match_substring_validate_context, + .match_key = mcht_contains_match_key +}; + +/* + * Match-type implementation + */ + +/* FIXME: Naive substring match implementation. Should switch to more + * efficient algorithm if large values need to be searched (e.g. message body). + */ +static int mcht_contains_match_key +(struct sieve_match_context *mctx, const char *val, size_t val_size, + const char *key, size_t key_size) +{ + const struct sieve_comparator *cmp = mctx->comparator; + const char *vend = (const char *) val + val_size; + const char *kend = (const char *) key + key_size; + const char *vp = val; + const char *kp = key; + + if ( val_size == 0 ) + return ( key_size == 0 ? 1 : 0 ); + + if ( cmp->def == NULL || cmp->def->char_match == NULL ) + return 0; + + while ( (vp < vend) && (kp < kend) ) { + if ( !cmp->def->char_match(cmp, &vp, vend, &kp, kend) ) + vp++; + } + + return ( kp == kend ? 1 : 0 ); +} + + diff --git a/pigeonhole/src/lib-sieve/mcht-is.c b/pigeonhole/src/lib-sieve/mcht-is.c new file mode 100644 index 0000000..db374bb --- /dev/null +++ b/pigeonhole/src/lib-sieve/mcht-is.c @@ -0,0 +1,52 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Match-type ':is': + */ + +#include "lib.h" + +#include "sieve-match-types.h" +#include "sieve-comparators.h" +#include "sieve-match.h" + +#include <string.h> +#include <stdio.h> + +/* + * Forward declarations + */ + +static int mcht_is_match_key + (struct sieve_match_context *mctx, const char *val, size_t val_size, + const char *key, size_t key_size); + +/* + * Match-type object + */ + +const struct sieve_match_type_def is_match_type = { + SIEVE_OBJECT("is", + &match_type_operand, SIEVE_MATCH_TYPE_IS), + .match_key = mcht_is_match_key +}; + +/* + * Match-type implementation + */ + +static int mcht_is_match_key +(struct sieve_match_context *mctx ATTR_UNUSED, + const char *val, size_t val_size, + const char *key, size_t key_size) +{ + if ( val_size == 0 ) + return ( key_size == 0 ? 1 : 0 ); + + if ( mctx->comparator->def != NULL && mctx->comparator->def->compare != NULL ) + return (mctx->comparator->def->compare(mctx->comparator, + val, val_size, key, key_size) == 0 ? 1 : 0 ); + + return 0; +} + diff --git a/pigeonhole/src/lib-sieve/mcht-matches.c b/pigeonhole/src/lib-sieve/mcht-matches.c new file mode 100644 index 0000000..050fce9 --- /dev/null +++ b/pigeonhole/src/lib-sieve/mcht-matches.c @@ -0,0 +1,440 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Match-type ':matches' + */ + +#include "lib.h" +#include "str.h" + +#include "sieve-match-types.h" +#include "sieve-comparators.h" +#include "sieve-match.h" + +#include <string.h> +#include <stdio.h> + +/* + * Forward declarations + */ + +static int mcht_matches_match_key + (struct sieve_match_context *mctx, const char *val, size_t val_size, + const char *key, size_t key_size); + +/* + * Match-type object + */ + +const struct sieve_match_type_def matches_match_type = { + SIEVE_OBJECT("matches", + &match_type_operand, SIEVE_MATCH_TYPE_MATCHES), + .validate_context = sieve_match_substring_validate_context, + .match_key = mcht_matches_match_key +}; + +/* + * Match-type implementation + */ + +/* Quick 'n dirty debug */ +//#define MATCH_DEBUG +#ifdef MATCH_DEBUG +#define debug_printf(...) printf ("match debug: " __VA_ARGS__) +#else +#define debug_printf(...) +#endif + +/* FIXME: Naive implementation, substitute this with dovecot src/lib/str-find.c + */ +static inline bool _string_find(const struct sieve_comparator *cmp, + const char **valp, const char *vend, const char **keyp, const char *kend) +{ + while ( (*valp < vend) && (*keyp < kend) ) { + if ( !cmp->def->char_match(cmp, valp, vend, keyp, kend) ) + (*valp)++; + } + + return (*keyp == kend); +} + +static char _scan_key_section + (string_t *section, const char **wcardp, const char *key_end) +{ + /* Find next wildcard and resolve escape sequences */ + str_truncate(section, 0); + while ( *wcardp < key_end && **wcardp != '*' && **wcardp != '?') { + if ( **wcardp == '\\' ) { + (*wcardp)++; + } + str_append_c(section, **wcardp); + (*wcardp)++; + } + + /* Record wildcard character or \0 */ + if ( *wcardp < key_end ) { + return **wcardp; + } + + i_assert( *wcardp == key_end ); + return '\0'; +} + +static int mcht_matches_match_key +(struct sieve_match_context *mctx, const char *val, size_t val_size, + const char *key, size_t key_size) +{ + const struct sieve_comparator *cmp = mctx->comparator; + struct sieve_match_values *mvalues; + string_t *mvalue = NULL, *mchars = NULL; + string_t *section, *subsection; + const char *vend, *kend, *vp, *kp, *wp, *pvp; + bool backtrack = FALSE; /* TRUE: match of '?'-connected sections failed */ + char wcard = '\0'; /* Current wildcard */ + char next_wcard = '\0'; /* Next widlcard */ + unsigned int key_offset = 0; + + if ( cmp->def == NULL || cmp->def->char_match == NULL ) + return 0; + + /* Key sections */ + section = t_str_new(32); /* Section (after beginning or *) */ + subsection = t_str_new(32); /* Sub-section (after ?) */ + + /* Mark end of value and key */ + vend = (const char *) val + val_size; + kend = (const char *) key + key_size; + + /* Initialize pointers */ + vp = val; /* Value pointer */ + kp = key; /* Key pointer */ + wp = key; /* Wildcard (key) pointer */ + + /* Start match values list if requested */ + if ( (mvalues = sieve_match_values_start(mctx->runenv)) != NULL ) { + /* Skip ${0} for now; added when match succeeds */ + sieve_match_values_add(mvalues, NULL); + + mvalue = t_str_new(32); /* Match value (*) */ + mchars = t_str_new(32); /* Match characters (.?..?.??) */ + } + + /* Match the pattern: + * <pattern> = <section>*<section>*<section>... + * <section> = <sub-section>?<sub-section>?<sub-section>... + * + * Escape sequences \? and \* need special attention. + */ + + debug_printf("=== Start ===\n"); + debug_printf(" key: %s\n", t_strdup_until(key, kend)); + debug_printf(" value: %s\n", t_strdup_until(val, vend)); + + /* Loop until either key or value ends */ + while (kp < kend && vp < vend ) { + const char *needle, *nend; + + if ( !backtrack ) { + /* Search the next '*' wildcard in the key string */ + + wcard = next_wcard; + + /* Find the needle to look for in the string */ + key_offset = 0; + for (;;) { + next_wcard = _scan_key_section(section, &wp, kend); + + if ( wcard == '\0' || str_len(section) > 0 ) + break; + + if ( next_wcard == '*' ) { + break; + } + + if ( wp < kend ) + wp++; + else + break; + key_offset++; + } + + debug_printf("found wildcard '%c' at pos [%d]\n", + next_wcard, (int) (wp-key)); + + if ( mvalues != NULL ) + str_truncate(mvalue, 0); + } else { + /* Backtracked; '*' wildcard is retained */ + debug_printf("backtracked"); + backtrack = FALSE; + } + + /* Determine what we are looking for */ + needle = str_c(section); + nend = PTR_OFFSET(needle, str_len(section)); + + debug_printf(" section needle: '%s'\n", t_strdup_until(needle, nend)); + debug_printf(" section key: '%s'\n", t_strdup_until(kp, kend)); + debug_printf(" section remnant: '%s'\n", t_strdup_until(wp, kend)); + debug_printf(" value remnant: '%s'\n", t_strdup_until(vp, vend)); + debug_printf(" key offset: %d\n", key_offset); + + pvp = vp; + if ( next_wcard == '\0' ) { + if ( wcard == '\0' ) { + /* No current wildcard; match needs to happen right at the beginning */ + debug_printf("next_wcard = NULL && wcard = NUL; needle should be equal to value.\n"); + + if ( (vend - vp) != (nend - needle) || + !cmp->def->char_match(cmp, &vp, vend, &needle, nend) ) { + debug_printf(" key not equal to value\n"); + break; + } + + } else { + const char *qp, *qend; + size_t slen; + + /* No more wildcards; find the needle substring at the end of string */ + debug_printf("next_wcard = NUL; must find needle at end\n"); + + /* Check if the value is still large enough */ + slen = str_len(section); + if ( (vp + slen) > vend ) { + debug_printf(" wont match: value is too short\n"); + break; + } + + /* Move value pointer to where the needle should be */ + vp = vend - slen; + + /* Record match values */ + qend = vp; + qp = vp - key_offset; + + if ( mvalues != NULL ) + str_append_data(mvalue, pvp, qp-pvp); + + /* Compare needle to end of value string */ + if ( !cmp->def->char_match(cmp, &vp, vend, &needle, nend) ) { + debug_printf(" match at end failed\n"); + break; + } + + /* Add match values */ + if ( mvalues != NULL ) { + /* Append '*' match value */ + sieve_match_values_add(mvalues, mvalue); + + /* Append any initial '?' match values */ + for ( ; qp < qend; qp++ ) + sieve_match_values_add_char(mvalues, *qp); + } + } + + /* Finish match */ + kp = kend; + vp = vend; + + debug_printf(" matched end of value\n"); + break; + } else { + /* Next wildcard found; match needle before next wildcard */ + + const char *prv = NULL; /* Stored value pointer for backtrack */ + const char *prk = NULL; /* Stored key pointer for backtrack */ + const char *prw = NULL; /* Stored wildcard pointer for backtrack */ + const char *chars; + + /* Reset '?' match values */ + if ( mvalues != NULL ) + str_truncate(mchars, 0); + + if ( wcard == '\0' ) { + /* No current wildcard; match needs to happen right at the beginning */ + debug_printf("wcard = NUL; needle should be found at the beginning.\n"); + debug_printf(" begin needle: '%s'\n", t_strdup_until(needle, nend)); + debug_printf(" begin value: '%s'\n", t_strdup_until(vp, vend)); + + if ( !cmp->def->char_match(cmp, &vp, vend, &needle, nend) ) { + debug_printf(" failed to find needle at beginning\n"); + break; + } + + } else { + /* Current wildcard present; match needle between current and next wildcard */ + debug_printf("wcard != NUL; must find needle at an offset (>= %d).\n", + key_offset); + + /* Match may happen at any offset (>= key offset): find substring */ + vp += key_offset; + if ( (vp >= vend) || !_string_find(cmp, &vp, vend, &needle, nend) ) { + debug_printf(" failed to find needle at an offset\n"); + break; + } + + prv = vp - str_len(section); + prk = kp; + prw = wp; + + /* Append match values */ + if ( mvalues != NULL ) { + const char *qend = vp - str_len(section); + const char *qp = qend - key_offset; + + /* Append '*' match value */ + str_append_data(mvalue, pvp, qp-pvp); + + /* Append any initial '?' match values (those that caused the key + * offset. + */ + for ( ; qp < qend; qp++ ) + str_append_c(mchars, *qp); + } + } + + /* Update wildcard and key pointers for next wildcard scan */ + if ( wp < kend ) wp++; + kp = wp; + + /* Scan successive '?' wildcards */ + while ( next_wcard == '?' ) { + debug_printf("next_wcard = '?'; need to match arbitrary character\n"); + + /* Add match value */ + if ( mvalues != NULL ) + str_append_c(mchars, *vp); + + vp++; + + /* Scan for next '?' wildcard */ + next_wcard = _scan_key_section(subsection, &wp, kend); + debug_printf("found next wildcard '%c' at pos [%d] (fixed match)\n", + next_wcard, (int) (wp-key)); + + /* Determine what we are looking for */ + needle = str_c(subsection); + nend = needle + str_len(subsection); + + debug_printf(" sub key: '%s'\n", t_strdup_until(needle, nend)); + debug_printf(" value remnant: '%s'\n", vp <= vend ? t_strdup_until(vp, vend) : ""); + + /* Try matching the needle at fixed position */ + if ( (needle == nend && next_wcard == '\0' && vp < vend ) || + !cmp->def->char_match(cmp, &vp, vend, &needle, nend) ) { + + /* Match failed: now we have a problem. We need to backtrack to the previous + * '*' wildcard occurrence and start scanning for the next possible match. + */ + + debug_printf(" failed fixed match\n"); + + /* Start backtrack */ + if ( prv != NULL && prv + 1 < vend ) { + /* Restore pointers */ + vp = prv; + kp = prk; + wp = prw; + + /* Skip forward one value character to scan the next possible match */ + if ( mvalues != NULL ) + str_append_c(mvalue, *vp); + vp++; + + /* Set wildcard state appropriately */ + wcard = '*'; + next_wcard = '?'; + + /* Backtrack */ + backtrack = TRUE; + + debug_printf(" BACKTRACK\n"); + } + + /* Break '?' wildcard scanning loop */ + break; + } + + /* Update wildcard and key pointers for next wildcard scan */ + if ( wp < kend ) wp++; + kp = wp; + } + + if ( !backtrack ) { + unsigned int i; + + if ( next_wcard == '?' ) { + debug_printf("failed to match '?'\n"); + break; + } + + if ( mvalues != NULL ) { + if ( prv != NULL ) + sieve_match_values_add(mvalues, mvalue); + + chars = (const char *) str_data(mchars); + + for ( i = 0; i < str_len(mchars); i++ ) { + sieve_match_values_add_char(mvalues, chars[i]); + } + } + + if ( next_wcard != '*' ) { + debug_printf("failed to match at end of string\n"); + break; + } + } + } + + /* Check whether string ends in a wildcard + * (avoid scanning the rest of the string) + */ + if ( kp == kend && next_wcard == '*' ) { + /* Add the rest of the string as match value */ + if ( mvalues != NULL ) { + str_truncate(mvalue, 0); + str_append_data(mvalue, vp, vend-vp); + sieve_match_values_add(mvalues, mvalue); + } + + /* Finish match */ + kp = kend; + vp = vend; + + debug_printf("key ends with '*'\n"); + break; + } + + debug_printf("== Loop ==\n"); + } + + /* Eat away a trailing series of *s */ + if ( vp == vend ) { + while ( kp < kend && *kp == '*' ) kp++; + } + + /* By definition, the match is only successful if both value and key pattern + * are exhausted. + */ + + debug_printf("=== Finish ===\n"); + debug_printf(" result: %s\n", (kp == kend && vp == vend) ? "true" : "false"); + + if (kp == kend && vp == vend) { + /* Activate new match values after successful match */ + if ( mvalues != NULL ) { + /* Set ${0} */ + string_t *matched = str_new_const(pool_datastack_create(), val, val_size); + sieve_match_values_set(mvalues, 0, matched); + + /* Commit new match values */ + sieve_match_values_commit(mctx->runenv, &mvalues); + } + return 1; + } + + /* No match; drop collected match values */ + sieve_match_values_abort(&mvalues); + return 0; +} + diff --git a/pigeonhole/src/lib-sieve/plugins/Makefile.am b/pigeonhole/src/lib-sieve/plugins/Makefile.am new file mode 100644 index 0000000..2a1c054 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/Makefile.am @@ -0,0 +1,32 @@ +if BUILD_UNFINISHED +UNFINISHED = +endif + +SUBDIRS = \ + vacation \ + subaddress \ + comparator-i-ascii-numeric \ + relational \ + regex \ + imap4flags \ + copy \ + include \ + body \ + variables \ + enotify \ + notify \ + environment \ + mailbox \ + date \ + spamvirustest \ + ihave \ + editheader \ + duplicate \ + index \ + metadata \ + mime \ + special-use \ + vnd.dovecot \ + $(UNFINISHED) + + diff --git a/pigeonhole/src/lib-sieve/plugins/Makefile.in b/pigeonhole/src/lib-sieve/plugins/Makefile.in new file mode 100644 index 0000000..8443114 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/Makefile.in @@ -0,0 +1,719 @@ +# 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/plugins +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 $(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 = +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 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +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 +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +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@ +@BUILD_UNFINISHED_TRUE@UNFINISHED = +SUBDIRS = \ + vacation \ + subaddress \ + comparator-i-ascii-numeric \ + relational \ + regex \ + imap4flags \ + copy \ + include \ + body \ + variables \ + enotify \ + notify \ + environment \ + mailbox \ + date \ + spamvirustest \ + ihave \ + editheader \ + duplicate \ + index \ + metadata \ + mime \ + special-use \ + vnd.dovecot \ + $(UNFINISHED) + +all: all-recursive + +.SUFFIXES: +$(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/plugins/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/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): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(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-recursive + +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-recursive + +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 + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +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-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-libtool cscopelist-am ctags \ + ctags-am distclean 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 \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean 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/plugins/body/Makefile.am b/pigeonhole/src/lib-sieve/plugins/body/Makefile.am new file mode 100644 index 0000000..251b8d6 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/body/Makefile.am @@ -0,0 +1,16 @@ +noinst_LTLIBRARIES = libsieve_ext_body.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +tsts = \ + tst-body.c + +libsieve_ext_body_la_SOURCES = \ + ext-body-common.c \ + $(tsts) \ + ext-body.c + +noinst_HEADERS = \ + ext-body-common.h diff --git a/pigeonhole/src/lib-sieve/plugins/body/Makefile.in b/pigeonhole/src/lib-sieve/plugins/body/Makefile.in new file mode 100644 index 0000000..5d58bbd --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/body/Makefile.in @@ -0,0 +1,692 @@ +# 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/plugins/body +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_ext_body_la_LIBADD = +am__objects_1 = tst-body.lo +am_libsieve_ext_body_la_OBJECTS = ext-body-common.lo $(am__objects_1) \ + ext-body.lo +libsieve_ext_body_la_OBJECTS = $(am_libsieve_ext_body_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)/ext-body-common.Plo \ + ./$(DEPDIR)/ext-body.Plo ./$(DEPDIR)/tst-body.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_ext_body_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_body_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_ext_body.la +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +tsts = \ + tst-body.c + +libsieve_ext_body_la_SOURCES = \ + ext-body-common.c \ + $(tsts) \ + ext-body.c + +noinst_HEADERS = \ + ext-body-common.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/plugins/body/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/body/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_ext_body.la: $(libsieve_ext_body_la_OBJECTS) $(libsieve_ext_body_la_DEPENDENCIES) $(EXTRA_libsieve_ext_body_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_body_la_OBJECTS) $(libsieve_ext_body_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-body-common.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-body.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-body.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)/ext-body-common.Plo + -rm -f ./$(DEPDIR)/ext-body.Plo + -rm -f ./$(DEPDIR)/tst-body.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)/ext-body-common.Plo + -rm -f ./$(DEPDIR)/ext-body.Plo + -rm -f ./$(DEPDIR)/tst-body.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/plugins/body/ext-body-common.c b/pigeonhole/src/lib-sieve/plugins/body/ext-body-common.c new file mode 100644 index 0000000..c19940e --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/body/ext-body-common.c @@ -0,0 +1,102 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "mempool.h" +#include "buffer.h" +#include "array.h" +#include "str.h" +#include "istream.h" +#include "mail-storage.h" + +#include "sieve-common.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-message.h" +#include "sieve-interpreter.h" + +#include "ext-body-common.h" + + +/* + * Body part stringlist + */ + +static int ext_body_stringlist_next_item + (struct sieve_stringlist *_strlist, string_t **str_r); +static void ext_body_stringlist_reset + (struct sieve_stringlist *_strlist); + +struct ext_body_stringlist { + struct sieve_stringlist strlist; + + struct sieve_message_part_data *body_parts; + struct sieve_message_part_data *body_parts_iter; +}; + +int ext_body_get_part_list +(const struct sieve_runtime_env *renv, enum tst_body_transform transform, + const char * const *content_types, struct sieve_stringlist **strlist_r) +{ + static const char * const _no_content_types[] = { "", NULL }; + struct ext_body_stringlist *strlist; + struct sieve_message_part_data *body_parts = NULL; + int ret; + + *strlist_r = NULL; + + if ( content_types == NULL ) content_types = _no_content_types; + + switch ( transform ) { + case TST_BODY_TRANSFORM_RAW: + if ( (ret=sieve_message_body_get_raw(renv, &body_parts)) <= 0 ) + return ret; + break; + case TST_BODY_TRANSFORM_CONTENT: + if ( (ret=sieve_message_body_get_content + (renv, content_types, &body_parts)) <= 0 ) + return ret; + break; + case TST_BODY_TRANSFORM_TEXT: + if ( (ret=sieve_message_body_get_text(renv, &body_parts)) <= 0 ) + return ret; + break; + default: + i_unreached(); + } + + strlist = t_new(struct ext_body_stringlist, 1); + strlist->strlist.runenv = renv; + strlist->strlist.next_item = ext_body_stringlist_next_item; + strlist->strlist.reset = ext_body_stringlist_reset; + strlist->body_parts = body_parts; + strlist->body_parts_iter = body_parts; + + *strlist_r = &strlist->strlist; + return SIEVE_EXEC_OK; +} + +static int ext_body_stringlist_next_item +(struct sieve_stringlist *_strlist, string_t **str_r) +{ + struct ext_body_stringlist *strlist = + (struct ext_body_stringlist *)_strlist; + + *str_r = NULL; + + if ( strlist->body_parts_iter->content == NULL ) return 0; + + *str_r = t_str_new_const + (strlist->body_parts_iter->content, strlist->body_parts_iter->size); + strlist->body_parts_iter++; + return 1; +} + +static void ext_body_stringlist_reset +(struct sieve_stringlist *_strlist) +{ + struct ext_body_stringlist *strlist = + (struct ext_body_stringlist *)_strlist; + + strlist->body_parts_iter = strlist->body_parts; +} diff --git a/pigeonhole/src/lib-sieve/plugins/body/ext-body-common.h b/pigeonhole/src/lib-sieve/plugins/body/ext-body-common.h new file mode 100644 index 0000000..290ca13 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/body/ext-body-common.h @@ -0,0 +1,40 @@ +#ifndef EXT_BODY_COMMON_H +#define EXT_BODY_COMMON_H + +/* + * Types + */ + +enum tst_body_transform { + TST_BODY_TRANSFORM_RAW, + TST_BODY_TRANSFORM_CONTENT, + TST_BODY_TRANSFORM_TEXT +}; + +/* + * Extension + */ + +extern const struct sieve_extension_def body_extension; + +/* + * Commands + */ + +extern const struct sieve_command_def body_test; + +/* + * Operations + */ + +extern const struct sieve_operation_def body_operation; + +/* + * Message body part extraction + */ + +int ext_body_get_part_list + (const struct sieve_runtime_env *renv, enum tst_body_transform transform, + const char * const *content_types, struct sieve_stringlist **strlist_r); + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/body/ext-body.c b/pigeonhole/src/lib-sieve/plugins/body/ext-body.c new file mode 100644 index 0000000..27218bd --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/body/ext-body.c @@ -0,0 +1,54 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension body + * ------------------ + * + * Authors: Stephan Bosch + * Original CMUSieve implementation by Timo Sirainen + * Specification: RFC 5173 + * Implementation: full + * Status: testing + * + */ + +#include "lib.h" +#include "array.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-address-parts.h" + +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-binary.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "ext-body-common.h" + +/* + * Extension + */ + +static bool ext_body_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr); + +const struct sieve_extension_def body_extension = { + .name = "body", + .validator_load = ext_body_validator_load, + SIEVE_EXT_DEFINE_OPERATION(body_operation) +}; + +static bool ext_body_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + /* Register new test */ + sieve_validator_register_command(valdtr, ext, &body_test); + + return TRUE; +} + + diff --git a/pigeonhole/src/lib-sieve/plugins/body/tst-body.c b/pigeonhole/src/lib-sieve/plugins/body/tst-body.c new file mode 100644 index 0000000..f08dd54 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/body/tst-body.c @@ -0,0 +1,385 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-address-parts.h" + +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-binary.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-match.h" + +#include "ext-body-common.h" + +/* + * Body test + * + * Syntax + * body [COMPARATOR] [MATCH-TYPE] [BODY-TRANSFORM] + * <key-list: string-list> + */ + +static bool tst_body_registered + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool tst_body_validate + (struct sieve_validator *valdtr, struct sieve_command *tst); +static bool tst_body_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx); + +const struct sieve_command_def body_test = { + .identifier = "body", + .type = SCT_TEST, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = tst_body_registered, + .validate = tst_body_validate, + .generate = tst_body_generate +}; + +/* + * Body operation + */ + +static bool ext_body_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int ext_body_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def body_operation = { + .mnemonic = "body", + .ext_def = &body_extension, + .dump = ext_body_operation_dump, + .execute = ext_body_operation_execute +}; + +/* + * Optional operands + */ + +enum tst_body_optional { + OPT_BODY_TRANSFORM = SIEVE_MATCH_OPT_LAST +}; + +/* + * Tagged arguments + */ + +/* Forward declarations */ + +static bool tag_body_transform_validate + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd); +static bool tag_body_transform_generate + (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *cmd); + +/* Argument objects */ + +static const struct sieve_argument_def body_raw_tag = { + .identifier = "raw", + .validate = tag_body_transform_validate, + .generate = tag_body_transform_generate +}; + +static const struct sieve_argument_def body_content_tag = { + .identifier = "content", + .validate = tag_body_transform_validate, + .generate = tag_body_transform_generate +}; + +static const struct sieve_argument_def body_text_tag = { + .identifier = "text", + .validate = tag_body_transform_validate, + .generate = tag_body_transform_generate +}; + +/* Argument implementation */ + +static bool tag_body_transform_validate +(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + enum tst_body_transform transform; + struct sieve_ast_argument *tag = *arg; + + /* BODY-TRANSFORM: + * :raw + * / :content <content-types: string-list> + * / :text + */ + if ( (bool) cmd->data ) { + sieve_argument_validate_error(valdtr, *arg, + "the :raw, :content and :text arguments for the body test are mutually " + "exclusive, but more than one was specified"); + return FALSE; + } + + /* Skip tag */ + *arg = sieve_ast_argument_next(*arg); + + /* :content tag has a string-list argument */ + if ( sieve_argument_is(tag, body_raw_tag) ) + transform = TST_BODY_TRANSFORM_RAW; + + else if ( sieve_argument_is(tag, body_text_tag) ) + transform = TST_BODY_TRANSFORM_TEXT; + + else if ( sieve_argument_is(tag, body_content_tag) ) { + /* Check syntax: + * :content <content-types: string-list> + */ + if ( !sieve_validate_tag_parameter + (valdtr, cmd, tag, *arg, NULL, 0, SAAT_STRING_LIST, FALSE) ) { + return FALSE; + } + + /* Assign tag parameters */ + tag->parameters = *arg; + *arg = sieve_ast_arguments_detach(*arg,1); + + transform = TST_BODY_TRANSFORM_CONTENT; + } else + return FALSE; + + /* Signal the presence of this tag */ + cmd->data = (void *) TRUE; + + /* Assign context data */ + tag->argument->data = (void *) transform; + + return TRUE; +} + +/* + * Command Registration + */ + +static bool tst_body_registered +(struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + /* The order of these is not significant */ + sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR); + sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE); + + sieve_validator_register_tag + (valdtr, cmd_reg, ext, &body_raw_tag, OPT_BODY_TRANSFORM); + sieve_validator_register_tag + (valdtr, cmd_reg, ext, &body_content_tag, OPT_BODY_TRANSFORM); + sieve_validator_register_tag + (valdtr, cmd_reg, ext, &body_text_tag, OPT_BODY_TRANSFORM); + + return TRUE; +} + +/* + * Validation + */ + +static bool tst_body_validate +(struct sieve_validator *valdtr, struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + const struct sieve_match_type mcht_default = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + const struct sieve_comparator cmp_default = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "key list", 1, SAAT_STRING_LIST) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) + return FALSE; + + /* Validate the key argument to a specified match type */ + return sieve_match_type_validate + (valdtr, tst, arg, &mcht_default, &cmp_default); +} + +/* + * Code generation + */ + +static bool tst_body_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + (void)sieve_operation_emit(cgenv->sblock, cmd->ext, &body_operation); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, cmd, NULL); +} + +static bool tag_body_transform_generate +(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *cmd ATTR_UNUSED) +{ + enum tst_body_transform transform = + POINTER_CAST_TO(arg->argument->data, enum tst_body_transform); + + sieve_binary_emit_byte(cgenv->sblock, transform); + sieve_generate_argument_parameters(cgenv, cmd, arg); + + return TRUE; +} + +/* + * Code dump + */ + +static bool ext_body_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + unsigned int transform; + int opt_code = 0; + + sieve_code_dumpf(denv, "BODY"); + sieve_code_descend(denv); + + /* Handle any optional arguments */ + for (;;) { + int opt; + + if ( (opt=sieve_match_opr_optional_dump(denv, address, &opt_code)) + < 0 ) + return FALSE; + + if ( opt == 0 ) break; + + switch ( opt_code ) { + case OPT_BODY_TRANSFORM: + if ( !sieve_binary_read_byte(denv->sblock, address, &transform) ) + return FALSE; + + switch ( transform ) { + case TST_BODY_TRANSFORM_RAW: + sieve_code_dumpf(denv, "BODY-TRANSFORM: RAW"); + break; + case TST_BODY_TRANSFORM_TEXT: + sieve_code_dumpf(denv, "BODY-TRANSFORM: TEXT"); + break; + case TST_BODY_TRANSFORM_CONTENT: + sieve_code_dumpf(denv, "BODY-TRANSFORM: CONTENT"); + + sieve_code_descend(denv); + if ( !sieve_opr_stringlist_dump(denv, address, "content types") ) + return FALSE; + sieve_code_ascend(denv); + break; + default: + return FALSE; + } + break; + default: + return FALSE; + } + }; + + return sieve_opr_stringlist_dump(denv, address, "key list"); +} + +/* + * Interpretation + */ + +static int ext_body_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + int opt_code = 0; + struct sieve_comparator cmp = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + struct sieve_match_type mcht = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + unsigned int transform = TST_BODY_TRANSFORM_TEXT; + struct sieve_stringlist *ctype_list, *value_list, *key_list; + bool mvalues_active; + const char * const *content_types = NULL; + int match, ret; + + /* + * Read operands + */ + + /* Optional operands */ + + ctype_list = NULL; + for (;;) { + int opt; + + if ( (opt=sieve_match_opr_optional_read + (renv, address, &opt_code, &ret, &cmp, &mcht)) < 0 ) + return ret; + + if ( opt == 0 ) break; + + switch ( opt_code ) { + case OPT_BODY_TRANSFORM: + if ( !sieve_binary_read_byte(renv->sblock, address, &transform) || + transform > TST_BODY_TRANSFORM_TEXT ) { + sieve_runtime_trace_error(renv, "invalid body transform type"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if ( transform == TST_BODY_TRANSFORM_CONTENT && + (ret=sieve_opr_stringlist_read + (renv, address, "content-type-list", &ctype_list)) <= 0 ) + return ret; + + break; + + default: + sieve_runtime_trace_error(renv, "unknown optional operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + } + + /* Read key-list */ + + if ( (ret=sieve_opr_stringlist_read(renv, address, "key-list", &key_list)) + <= 0 ) + return ret; + + if ( ctype_list != NULL && sieve_stringlist_read_all + (ctype_list, pool_datastack_create(), &content_types) < 0 ) { + sieve_runtime_trace_error(renv, "failed to read content-type-list operand"); + return ctype_list->exec_status; + } + + /* + * Perform operation + */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "body test"); + + /* Extract requested parts */ + if ( (ret=ext_body_get_part_list(renv, + (enum tst_body_transform) transform, content_types,&value_list)) <= 0 ) + return ret; + + /* Disable match values processing as required by RFC */ + mvalues_active = sieve_match_values_set_enabled(renv, FALSE); + + /* Perform match */ + match = sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret); + + /* Restore match values processing */ + (void)sieve_match_values_set_enabled(renv, mvalues_active); + + if ( match < 0 ) + return ret; + + /* Set test result for subsequent conditional jump */ + sieve_interpreter_set_test_result(renv->interp, match > 0); + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/plugins/comparator-i-ascii-numeric/Makefile.am b/pigeonhole/src/lib-sieve/plugins/comparator-i-ascii-numeric/Makefile.am new file mode 100644 index 0000000..8e385c5 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/comparator-i-ascii-numeric/Makefile.am @@ -0,0 +1,8 @@ +noinst_LTLIBRARIES = libsieve_ext_comparator-i-ascii-numeric.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +libsieve_ext_comparator_i_ascii_numeric_la_SOURCES = \ + ext-cmp-i-ascii-numeric.c diff --git a/pigeonhole/src/lib-sieve/plugins/comparator-i-ascii-numeric/Makefile.in b/pigeonhole/src/lib-sieve/plugins/comparator-i-ascii-numeric/Makefile.in new file mode 100644 index 0000000..35ef51f --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/comparator-i-ascii-numeric/Makefile.in @@ -0,0 +1,674 @@ +# 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/plugins/comparator-i-ascii-numeric +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 $(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_ext_comparator_i_ascii_numeric_la_LIBADD = +am_libsieve_ext_comparator_i_ascii_numeric_la_OBJECTS = \ + ext-cmp-i-ascii-numeric.lo +libsieve_ext_comparator_i_ascii_numeric_la_OBJECTS = \ + $(am_libsieve_ext_comparator_i_ascii_numeric_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)/ext-cmp-i-ascii-numeric.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_ext_comparator_i_ascii_numeric_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_comparator_i_ascii_numeric_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__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_ext_comparator-i-ascii-numeric.la +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +libsieve_ext_comparator_i_ascii_numeric_la_SOURCES = \ + ext-cmp-i-ascii-numeric.c + +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/plugins/comparator-i-ascii-numeric/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/comparator-i-ascii-numeric/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_ext_comparator-i-ascii-numeric.la: $(libsieve_ext_comparator_i_ascii_numeric_la_OBJECTS) $(libsieve_ext_comparator_i_ascii_numeric_la_DEPENDENCIES) $(EXTRA_libsieve_ext_comparator_i_ascii_numeric_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_comparator_i_ascii_numeric_la_OBJECTS) $(libsieve_ext_comparator_i_ascii_numeric_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-cmp-i-ascii-numeric.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) +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)/ext-cmp-i-ascii-numeric.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)/ext-cmp-i-ascii-numeric.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/plugins/comparator-i-ascii-numeric/ext-cmp-i-ascii-numeric.c b/pigeonhole/src/lib-sieve/plugins/comparator-i-ascii-numeric/ext-cmp-i-ascii-numeric.c new file mode 100644 index 0000000..20ec38b --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/comparator-i-ascii-numeric/ext-cmp-i-ascii-numeric.c @@ -0,0 +1,160 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension comparator-i;ascii-numeric + * ------------------------------------ + * + * Author: Stephan Bosch + * Specification: RFC 2244 + * Implementation: full + * Status: testing + * + */ + +#include "sieve-common.h" + +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-comparators.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" + +#include <ctype.h> + +/* + * Forward declarations + */ + +static const struct sieve_operand_def my_comparator_operand; + +const struct sieve_comparator_def i_ascii_numeric_comparator; + +/* + * Extension + */ + +static bool ext_cmp_i_ascii_numeric_validator_load + (const struct sieve_extension *ext, struct sieve_validator *validator); + +const struct sieve_extension_def comparator_i_ascii_numeric_extension = { + .name = "comparator-i;ascii-numeric", + .validator_load = ext_cmp_i_ascii_numeric_validator_load, + SIEVE_EXT_DEFINE_OPERAND(my_comparator_operand) +}; + +static bool ext_cmp_i_ascii_numeric_validator_load +(const struct sieve_extension *ext, struct sieve_validator *validator) +{ + sieve_comparator_register(validator, ext, &i_ascii_numeric_comparator); + return TRUE; +} + +/* + * Operand + */ + +static const struct sieve_extension_objects ext_comparators = + SIEVE_EXT_DEFINE_COMPARATOR(i_ascii_numeric_comparator); + +static const struct sieve_operand_def my_comparator_operand = { + .name = "comparator-i;ascii-numeric", + .ext_def = &comparator_i_ascii_numeric_extension, + .class = &sieve_comparator_operand_class, + .interface = &ext_comparators +}; + +/* + * Comparator + */ + +/* Forward declarations */ + +static int cmp_i_ascii_numeric_compare + (const struct sieve_comparator *cmp, + const char *val1, size_t val1_size, const char *val2, size_t val2_size); + +/* Comparator object */ + +const struct sieve_comparator_def i_ascii_numeric_comparator = { + SIEVE_OBJECT("i;ascii-numeric", + &my_comparator_operand, 0), + .flags = + SIEVE_COMPARATOR_FLAG_ORDERING | + SIEVE_COMPARATOR_FLAG_EQUALITY, + .compare = cmp_i_ascii_numeric_compare +}; + +/* Comparator implementation */ + +static int cmp_i_ascii_numeric_compare + (const struct sieve_comparator *cmp ATTR_UNUSED, + const char *val, size_t val_size, const char *key, size_t key_size) +{ + const char *vend = val + val_size; + const char *kend = key + key_size; + const char *vp = val; + const char *kp = key; + int digits, i; + + /* RFC 4790: All input is valid; strings that do not start with a digit + * represent positive infinity. + */ + if ( !i_isdigit(*vp) ) { + if ( i_isdigit(*kp) ) { + /* Value is greater */ + return 1; + } + } else { + if ( !i_isdigit(*kp) ) { + /* Value is less */ + return -1; + } + } + + /* Ignore leading zeros */ + + while ( *vp == '0' && vp < vend ) + vp++; + + while ( *kp == '0' && kp < kend ) + kp++; + + /* Check whether both numbers are equally long in terms of digits */ + + digits = 0; + while ( vp < vend && kp < kend && i_isdigit(*vp) && i_isdigit(*kp) ) { + vp++; + kp++; + digits++; + } + + if ( vp == vend || !i_isdigit(*vp) ) { + if ( kp != kend && i_isdigit(*kp) ) { + /* Value is less */ + return -1; + } + } else { + /* Value is greater */ + return 1; + } + + /* Equally long: compare digits */ + + vp -= digits; + kp -= digits; + i = 0; + while ( i < digits ) { + if ( *vp > *kp ) + return 1; + else if ( *vp < *kp ) + return -1; + + kp++; + vp++; + i++; + } + + return 0; +} + diff --git a/pigeonhole/src/lib-sieve/plugins/copy/Makefile.am b/pigeonhole/src/lib-sieve/plugins/copy/Makefile.am new file mode 100644 index 0000000..483032a --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/copy/Makefile.am @@ -0,0 +1,18 @@ +noinst_LTLIBRARIES = libsieve_ext_copy.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +libsieve_ext_copy_la_SOURCES = \ + ext-copy.c + +public_headers = \ + sieve-ext-copy.h + +headers = + +pkginc_libdir=$(dovecot_pkgincludedir)/sieve +pkginc_lib_HEADERS = $(public_headers) +noinst_HEADERS = $(headers) + diff --git a/pigeonhole/src/lib-sieve/plugins/copy/Makefile.in b/pigeonhole/src/lib-sieve/plugins/copy/Makefile.in new file mode 100644 index 0000000..9824089 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/copy/Makefile.in @@ -0,0 +1,735 @@ +# 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/plugins/copy +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) \ + $(pkginc_lib_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_ext_copy_la_LIBADD = +am_libsieve_ext_copy_la_OBJECTS = ext-copy.lo +libsieve_ext_copy_la_OBJECTS = $(am_libsieve_ext_copy_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)/ext-copy.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_ext_copy_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_copy_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(pkginc_libdir)" +HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +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_ext_copy.la +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +libsieve_ext_copy_la_SOURCES = \ + ext-copy.c + +public_headers = \ + sieve-ext-copy.h + +headers = +pkginc_libdir = $(dovecot_pkgincludedir)/sieve +pkginc_lib_HEADERS = $(public_headers) +noinst_HEADERS = $(headers) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/copy/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/copy/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_ext_copy.la: $(libsieve_ext_copy_la_OBJECTS) $(libsieve_ext_copy_la_DEPENDENCIES) $(EXTRA_libsieve_ext_copy_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_copy_la_OBJECTS) $(libsieve_ext_copy_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-copy.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) + @$(NORMAL_INSTALL) + @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ + done + +uninstall-pkginc_libHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/ext-copy.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pkginc_libHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/ext-copy.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pkginc_libHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstLTLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-pkginc_libHEADERS install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-pkginc_libHEADERS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/pigeonhole/src/lib-sieve/plugins/copy/ext-copy.c b/pigeonhole/src/lib-sieve/plugins/copy/ext-copy.c new file mode 100644 index 0000000..e7db9dd --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/copy/ext-copy.c @@ -0,0 +1,183 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension copy + * -------------- + * + * Authors: Stephan Bosch + * Specification: RFC 3894 + * Implementation: full + * Status: testing + * + */ + +#include "sieve-common.h" + +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-actions.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-result.h" + +#include "sieve-ext-copy.h" + +/* + * Forward declarations + */ + +static const struct sieve_argument_def copy_tag; +static const struct sieve_operand_def copy_side_effect_operand; + +/* + * Extension + */ + +static bool +ext_copy_validator_load(const struct sieve_extension *ext, + struct sieve_validator *valdtr); + +const struct sieve_extension_def copy_extension = { + .name = "copy", + .validator_load = ext_copy_validator_load, + SIEVE_EXT_DEFINE_OPERAND(copy_side_effect_operand), +}; + +static bool +ext_copy_validator_load(const struct sieve_extension *ext, + struct sieve_validator *valdtr) +{ + /* Register copy tag with redirect and fileinto commands and we don't + care whether these commands are registered or even whether they will + be registered at all. The validator handles either situation + gracefully. + */ + sieve_validator_register_external_tag(valdtr, "redirect", ext, + ©_tag, SIEVE_OPT_SIDE_EFFECT); + sieve_validator_register_external_tag(valdtr, "fileinto", ext, + ©_tag, SIEVE_OPT_SIDE_EFFECT); + return TRUE; +} + +/* + * Side effect + */ + +static void +seff_copy_print(const struct sieve_side_effect *seffect, + const struct sieve_action *action, + const struct sieve_result_print_env *rpenv, bool *keep); +static int +seff_copy_post_execute(const struct sieve_side_effect *seffect ATTR_UNUSED, + const struct sieve_action_exec_env *aenv ATTR_UNUSED, + void *tr_context ATTR_UNUSED, + void *se_tr_context ATTR_UNUSED, bool *keep); + +const struct sieve_side_effect_def copy_side_effect = { + SIEVE_OBJECT("copy", ©_side_effect_operand, 0), + .to_action = &act_store, + .print = seff_copy_print, + .post_execute = seff_copy_post_execute, +}; + +/* + * Tagged argument + */ + +static bool +tag_copy_validate(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, struct sieve_command *cmd); +static bool +tag_copy_generate(const struct sieve_codegen_env *cgenv, + struct sieve_ast_argument *arg, struct sieve_command *cmd); + +static const struct sieve_argument_def copy_tag = { + .identifier = "copy", + .validate = tag_copy_validate, + .generate = tag_copy_generate, +}; + +/* + * Operand + */ + +static const struct sieve_extension_objects ext_side_effects = + SIEVE_EXT_DEFINE_SIDE_EFFECT(copy_side_effect); + +static const struct sieve_operand_def copy_side_effect_operand = { + .name = "copy operand", + .ext_def = ©_extension, + .class = &sieve_side_effect_operand_class, + .interface = &ext_side_effects, +}; + +/* + * Tag registration + */ + +void sieve_ext_copy_register_tag(struct sieve_validator *valdtr, + const struct sieve_extension *copy_ext, + const char *command) +{ + if (sieve_validator_extension_loaded(valdtr, copy_ext)) { + sieve_validator_register_external_tag( + valdtr, command, copy_ext, ©_tag, + SIEVE_OPT_SIDE_EFFECT); + } +} + +/* + * Tag validation + */ + +static bool +tag_copy_validate(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_ast_argument **arg ATTR_UNUSED, + struct sieve_command *cmd ATTR_UNUSED) +{ + *arg = sieve_ast_argument_next(*arg); + + return TRUE; +} + +/* + * Code generation + */ + +static bool +tag_copy_generate(const struct sieve_codegen_env *cgenv, + struct sieve_ast_argument *arg, + struct sieve_command *cmd ATTR_UNUSED) +{ + if (sieve_ast_argument_type(arg) != SAAT_TAG) + return FALSE; + + sieve_opr_side_effect_emit(cgenv->sblock, sieve_argument_ext(arg), + ©_side_effect); + return TRUE; +} + +/* + * Side effect implementation + */ + +static void +seff_copy_print(const struct sieve_side_effect *seffect ATTR_UNUSED, + const struct sieve_action *action ATTR_UNUSED, + const struct sieve_result_print_env *rpenv, bool *keep) +{ + sieve_result_seffect_printf(rpenv, "preserve implicit keep"); + *keep = TRUE; +} + +static int +seff_copy_post_execute(const struct sieve_side_effect *seffect ATTR_UNUSED, + const struct sieve_action_exec_env *aenv ATTR_UNUSED, + void *tr_context ATTR_UNUSED, + void *se_tr_context ATTR_UNUSED, bool *keep) +{ + *keep = TRUE; + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/plugins/copy/sieve-ext-copy.h b/pigeonhole/src/lib-sieve/plugins/copy/sieve-ext-copy.h new file mode 100644 index 0000000..faf19dc --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/copy/sieve-ext-copy.h @@ -0,0 +1,21 @@ +#ifndef SIEVE_EXT_COPY_H +#define SIEVE_EXT_COPY_H + +/* sieve_ext_copy_get_extension(): + * Get the extension struct for the copy extension. + */ +static inline const struct sieve_extension *sieve_ext_copy_get_extension +(struct sieve_instance *svinst) +{ + return sieve_extension_get_by_name(svinst, "copy"); +} + +/* sieve_ext_copy_register_tag(): + * Register the :copy tagged argument for a command other than fileinto and + * redirect. + */ +void sieve_ext_copy_register_tag + (struct sieve_validator *valdtr, const struct sieve_extension *copy_ext, + const char *command); + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/date/Makefile.am b/pigeonhole/src/lib-sieve/plugins/date/Makefile.am new file mode 100644 index 0000000..bf257ac --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/date/Makefile.am @@ -0,0 +1,16 @@ +noinst_LTLIBRARIES = libsieve_ext_date.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +tests = \ + tst-date.c + +libsieve_ext_date_la_SOURCES = \ + $(tests) \ + ext-date-common.c \ + ext-date.c + +noinst_HEADERS = \ + ext-date-common.h diff --git a/pigeonhole/src/lib-sieve/plugins/date/Makefile.in b/pigeonhole/src/lib-sieve/plugins/date/Makefile.in new file mode 100644 index 0000000..0bc3ec0 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/date/Makefile.in @@ -0,0 +1,692 @@ +# 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/plugins/date +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_ext_date_la_LIBADD = +am__objects_1 = tst-date.lo +am_libsieve_ext_date_la_OBJECTS = $(am__objects_1) ext-date-common.lo \ + ext-date.lo +libsieve_ext_date_la_OBJECTS = $(am_libsieve_ext_date_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)/ext-date-common.Plo \ + ./$(DEPDIR)/ext-date.Plo ./$(DEPDIR)/tst-date.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_ext_date_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_date_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_ext_date.la +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +tests = \ + tst-date.c + +libsieve_ext_date_la_SOURCES = \ + $(tests) \ + ext-date-common.c \ + ext-date.c + +noinst_HEADERS = \ + ext-date-common.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/plugins/date/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/date/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_ext_date.la: $(libsieve_ext_date_la_OBJECTS) $(libsieve_ext_date_la_DEPENDENCIES) $(EXTRA_libsieve_ext_date_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_date_la_OBJECTS) $(libsieve_ext_date_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-date-common.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-date.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-date.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)/ext-date-common.Plo + -rm -f ./$(DEPDIR)/ext-date.Plo + -rm -f ./$(DEPDIR)/tst-date.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)/ext-date-common.Plo + -rm -f ./$(DEPDIR)/ext-date.Plo + -rm -f ./$(DEPDIR)/tst-date.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/plugins/date/ext-date-common.c b/pigeonhole/src/lib-sieve/plugins/date/ext-date-common.c new file mode 100644 index 0000000..7493b87 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/date/ext-date-common.c @@ -0,0 +1,593 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "utc-offset.h" +#include "str.h" +#include "iso8601-date.h" +#include "message-date.h" + +#include "sieve-common.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-interpreter.h" +#include "sieve-message.h" + +#include "ext-date-common.h" + +#include <time.h> +#include <ctype.h> + +struct ext_date_context { + time_t current_date; + int zone_offset; +}; + +/* + * Runtime initialization + */ + +static int ext_date_runtime_init +(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv, + void *context ATTR_UNUSED, bool deferred ATTR_UNUSED) +{ + struct ext_date_context *dctx; + pool_t pool; + struct timeval msg_time; + time_t current_date; + struct tm *tm; + int zone_offset; + + /* Get current time at instance main script is started */ + sieve_message_context_time(renv->msgctx, &msg_time); + current_date = msg_time.tv_sec; + + tm = localtime(¤t_date); + zone_offset = utc_offset(tm, current_date); + + /* Create context */ + pool = sieve_message_context_pool(renv->msgctx); + dctx = p_new(pool, struct ext_date_context, 1); + dctx->current_date = current_date; + dctx->zone_offset = zone_offset; + + sieve_message_context_extension_set + (renv->msgctx, ext, (void *) dctx); + return SIEVE_EXEC_OK; +} + +static struct sieve_interpreter_extension +date_interpreter_extension = { + .ext_def = &date_extension, + .run = ext_date_runtime_init +}; + +bool ext_date_interpreter_load +(const struct sieve_extension *ext, const struct sieve_runtime_env *renv, + sieve_size_t *address ATTR_UNUSED) +{ + /* Register runtime hook to obtain stript start timestamp */ + if ( renv->msgctx == NULL || + sieve_message_context_extension_get(renv->msgctx, ext) == NULL ) { + sieve_interpreter_extension_register + (renv->interp, ext, &date_interpreter_extension, NULL); + } + + return TRUE; +} + +/* + * Zone string + */ + +bool ext_date_parse_timezone +(const char *zone, int *zone_offset_r) +{ + const unsigned char *str = (const unsigned char *) zone; + size_t len = strlen(zone); + + if (len == 5 && (*str == '+' || *str == '-')) { + int offset; + + if (!i_isdigit(str[1]) || !i_isdigit(str[2]) || + !i_isdigit(str[3]) || !i_isdigit(str[4])) + return FALSE; + + offset = ((str[1]-'0') * 10 + (str[2]-'0')) * 60 + + (str[3]-'0') * 10 + (str[4]-'0'); + + if ( zone_offset_r != NULL ) + *zone_offset_r = *str == '+' ? offset : -offset; + + return TRUE; + } + + return FALSE; +} + +/* + * Current date + */ + +time_t ext_date_get_current_date +(const struct sieve_runtime_env *renv, int *zone_offset_r) +{ + const struct sieve_extension *this_ext = renv->oprtn->ext; + struct ext_date_context *dctx = (struct ext_date_context *) + sieve_message_context_extension_get(renv->msgctx, this_ext); + + if ( dctx == NULL ) { + ext_date_runtime_init(this_ext, renv, NULL, FALSE); + dctx = (struct ext_date_context *) + sieve_message_context_extension_get(renv->msgctx, this_ext); + + i_assert(dctx != NULL); + } + + /* Read script start timestamp from message context */ + + if ( zone_offset_r != NULL ) + *zone_offset_r = dctx->zone_offset; + + return dctx->current_date; +} + +/* + * Date parts + */ + +/* "year" => the year, "0000" .. "9999". + */ + +static const char *ext_date_year_part_get(struct tm *tm, int zone_offset); + +static const struct ext_date_part year_date_part = { + "year", + ext_date_year_part_get +}; + +/* "month" => the month, "01" .. "12". + */ + +static const char *ext_date_month_part_get(struct tm *tm, int zone_offset); + +static const struct ext_date_part month_date_part = { + "month", + ext_date_month_part_get +}; + +/* "day" => the day, "01" .. "31". + */ + +static const char *ext_date_day_part_get(struct tm *tm, int zone_offset); + +static const struct ext_date_part day_date_part = { + "day", + ext_date_day_part_get +}; + +/* "date" => the date in "yyyy-mm-dd" format. + */ + +static const char *ext_date_date_part_get(struct tm *tm, int zone_offset); + +static const struct ext_date_part date_date_part = { + "date", + ext_date_date_part_get +}; + +/* "julian" => the Modified Julian Day, that is, the date + * expressed as an integer number of days since + * 00:00 UTC on November 17, 1858 (using the Gregorian + * calendar). This corresponds to the regular + * Julian Day minus 2400000.5. Sample routines to + * convert to and from modified Julian dates are + * given in Appendix A. + */ + +static const char *ext_date_julian_part_get(struct tm *tm, int zone_offset); + +static const struct ext_date_part julian_date_part = { + "julian", + ext_date_julian_part_get +}; + +/* "hour" => the hour, "00" .. "23". + */ +static const char *ext_date_hour_part_get(struct tm *tm, int zone_offset); + +static const struct ext_date_part hour_date_part = { + "hour", + ext_date_hour_part_get +}; + +/* "minute" => the minute, "00" .. "59". + */ +static const char *ext_date_minute_part_get(struct tm *tm, int zone_offset); + +static const struct ext_date_part minute_date_part = { + "minute", + ext_date_minute_part_get +}; + +/* "second" => the second, "00" .. "60". + */ +static const char *ext_date_second_part_get(struct tm *tm, int zone_offset); + +static const struct ext_date_part second_date_part = { + "second", + ext_date_second_part_get +}; + +/* "time" => the time in "hh:mm:ss" format. + */ +static const char *ext_date_time_part_get(struct tm *tm, int zone_offset); + +static const struct ext_date_part time_date_part = { + "time", + ext_date_time_part_get +}; + +/* "iso8601" => the date and time in restricted ISO 8601 format. + */ +static const char *ext_date_iso8601_part_get(struct tm *tm, int zone_offset); + +static const struct ext_date_part iso8601_date_part = { + "iso8601", + ext_date_iso8601_part_get +}; + +/* "std11" => the date and time in a format appropriate + * for use in a Date: header field [RFC2822]. + */ +static const char *ext_date_std11_part_get(struct tm *tm, int zone_offset); + +static const struct ext_date_part std11_date_part = { + "std11", + ext_date_std11_part_get +}; + +/* "zone" => the time zone in use. If the user specified a + * time zone with ":zone", "zone" will + * contain that value. If :originalzone is specified + * this value will be the original zone specified + * in the date-time value. If neither argument is + * specified the value will be the server's default + * time zone in offset format "+hhmm" or "-hhmm". An + * offset of 0 (Zulu) always has a positive sign. + */ +static const char *ext_date_zone_part_get(struct tm *tm, int zone_offset); + +static const struct ext_date_part zone_date_part = { + "zone", + ext_date_zone_part_get +}; + +/* "weekday" => the day of the week expressed as an integer between + * "0" and "6". "0" is Sunday, "1" is Monday, etc. + */ +static const char *ext_date_weekday_part_get(struct tm *tm, int zone_offset); + +static const struct ext_date_part weekday_date_part = { + "weekday", + ext_date_weekday_part_get +}; + +/* + * Date part extraction + */ + +static const struct ext_date_part *date_parts[] = { + &year_date_part, &month_date_part, &day_date_part, &date_date_part, + &julian_date_part, &hour_date_part, &minute_date_part, &second_date_part, + &time_date_part, &iso8601_date_part, &std11_date_part, &zone_date_part, + &weekday_date_part +}; + +unsigned int date_parts_count = N_ELEMENTS(date_parts); + +const struct ext_date_part *ext_date_part_find(const char *part) +{ + unsigned int i; + + for ( i = 0; i < date_parts_count; i++ ) { + if ( strcasecmp(date_parts[i]->identifier, part) == 0 ) { + return date_parts[i]; + } + } + + return NULL; +} + +const char *ext_date_part_extract +(const struct ext_date_part *dpart, struct tm *tm, int zone_offset) +{ + if ( dpart == NULL || dpart->get_string == NULL ) + return NULL; + + return dpart->get_string(tm, zone_offset); +} + +/* + * Date part implementations + */ + +static const char *month_names[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +static const char *weekday_names[] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; + +static const char *ext_date_year_part_get +(struct tm *tm, int zone_offset ATTR_UNUSED) +{ + return t_strdup_printf("%04d", tm->tm_year + 1900); +} + +static const char *ext_date_month_part_get +(struct tm *tm, int zone_offset ATTR_UNUSED) +{ + return t_strdup_printf("%02d", tm->tm_mon + 1); +} + +static const char *ext_date_day_part_get +(struct tm *tm, int zone_offset ATTR_UNUSED) +{ + return t_strdup_printf("%02d", tm->tm_mday); +} + +static const char *ext_date_date_part_get +(struct tm *tm, int zone_offset ATTR_UNUSED) +{ + return t_strdup_printf("%04d-%02d-%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); +} + +static const char *ext_date_julian_part_get +(struct tm *tm, int zone_offset ATTR_UNUSED) +{ + int year = tm->tm_year+1900; + int month = tm->tm_mon+1; + int day = tm->tm_mday; + int c, ya, jd; + + /* Modified from RFC 5260 Appendix A (refer to Errata) */ + + if ( month > 2 ) + month -= 3; + else { + month += 9; + year--; + } + + c = year / 100; + ya = year - c * 100; + + jd = c * 146097 / 4 + ya * 1461 / 4 + (month * 153 + 2) / 5 + day + 1721119; + + return t_strdup_printf("%d", jd - 2400001); +} + +static const char *ext_date_hour_part_get +(struct tm *tm, int zone_offset ATTR_UNUSED) +{ + return t_strdup_printf("%02d", tm->tm_hour); +} + +static const char *ext_date_minute_part_get +(struct tm *tm, int zone_offset ATTR_UNUSED) +{ + return t_strdup_printf("%02d", tm->tm_min); +} + +static const char *ext_date_second_part_get +(struct tm *tm, int zone_offset ATTR_UNUSED) +{ + return t_strdup_printf("%02d", tm->tm_sec); +} + +static const char *ext_date_time_part_get +(struct tm *tm, int zone_offset ATTR_UNUSED) +{ + return t_strdup_printf("%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); +} + +static const char *ext_date_iso8601_part_get +(struct tm *tm, int zone_offset) +{ + /* From RFC: `The restricted ISO 8601 format is specified by the date-time + * ABNF production given in [RFC3339], Section 5.6, with the added + * restrictions that the letters "T" and "Z" MUST be in upper case, and + * a time zone offset of zero MUST be represented by "Z" and not "+00:00". + */ + if ( zone_offset == 0 ) + zone_offset = INT_MAX; + + return iso8601_date_create_tm(tm, zone_offset); +} + + +static const char *ext_date_std11_part_get +(struct tm *tm, int zone_offset) +{ + return t_strdup_printf("%s, %02d %s %04d %02d:%02d:%02d %s", + weekday_names[tm->tm_wday], + tm->tm_mday, + month_names[tm->tm_mon], + tm->tm_year+1900, + tm->tm_hour, tm->tm_min, tm->tm_sec, + ext_date_zone_part_get(tm, zone_offset)); +} + +static const char *ext_date_zone_part_get +(struct tm *tm ATTR_UNUSED, int zone_offset) +{ + bool negative; + int offset = zone_offset; + + if (zone_offset >= 0) + negative = FALSE; + else { + negative = TRUE; + offset = -offset; + } + + return t_strdup_printf + ("%c%02d%02d", negative ? '-' : '+', offset / 60, offset % 60); +} + +static const char *ext_date_weekday_part_get +(struct tm *tm, int zone_offset ATTR_UNUSED) +{ + return t_strdup_printf("%d", tm->tm_wday); +} + +/* + * Date stringlist + */ + +/* Forward declarations */ + +static int ext_date_stringlist_next_item + (struct sieve_stringlist *_strlist, string_t **str_r); +static void ext_date_stringlist_reset + (struct sieve_stringlist *_strlist); + +/* Stringlist object */ + +struct ext_date_stringlist { + struct sieve_stringlist strlist; + + struct sieve_stringlist *field_values; + int time_zone; + const struct ext_date_part *date_part; + + time_t local_time; + int local_zone; + + bool read:1; +}; + +struct sieve_stringlist *ext_date_stringlist_create +(const struct sieve_runtime_env *renv, struct sieve_stringlist *field_values, + int time_zone, const struct ext_date_part *dpart) +{ + struct ext_date_stringlist *strlist; + + strlist = t_new(struct ext_date_stringlist, 1); + strlist->strlist.runenv = renv; + strlist->strlist.exec_status = SIEVE_EXEC_OK; + strlist->strlist.next_item = ext_date_stringlist_next_item; + strlist->strlist.reset = ext_date_stringlist_reset; + strlist->field_values = field_values; + strlist->time_zone = time_zone; + strlist->date_part = dpart; + + strlist->local_time = ext_date_get_current_date(renv, &strlist->local_zone); + + return &strlist->strlist; +} + +/* Stringlist implementation */ + +static int ext_date_stringlist_next_item +(struct sieve_stringlist *_strlist, string_t **str_r) +{ + struct ext_date_stringlist *strlist = + (struct ext_date_stringlist *) _strlist; + bool got_date = FALSE; + time_t date_value; + const char *part_value = NULL; + int original_zone; + + /* Check whether the item was already read */ + if ( strlist->read ) return 0; + + if ( strlist->field_values != NULL ) { + string_t *hdr_item; + const char *header_value, *date_string; + int ret; + + /* Use header field value */ + + /* Read first */ + if ( (ret=sieve_stringlist_next_item(strlist->field_values, &hdr_item)) + <= 0 ) + return ret; + + /* Extract the date string value */ + + header_value = str_c(hdr_item); + date_string = strrchr(header_value, ';'); + + if ( date_string == NULL ) { + /* Direct header value */ + date_string = header_value; + } else { + /* Delimited by ';', e.g. a Received: header */ + date_string++; + } + + /* Parse the date value */ + if ( message_date_parse((const unsigned char *) date_string, + strlen(date_string), &date_value, &original_zone) ) { + got_date = TRUE; + } + } else { + /* Use time stamp recorded at the time the script first started */ + date_value = strlist->local_time; + original_zone = strlist->local_zone; + got_date = TRUE; + } + + if ( got_date ) { + int wanted_zone; + struct tm *date_tm; + + /* Apply wanted timezone */ + + switch ( strlist->time_zone ) { + case EXT_DATE_TIMEZONE_LOCAL: + wanted_zone = strlist->local_zone; + break; + case EXT_DATE_TIMEZONE_ORIGINAL: + wanted_zone = original_zone; + break; + default: + wanted_zone = strlist->time_zone; + } + + date_value += wanted_zone * 60; + + /* Convert timestamp to struct tm */ + + if ( (date_tm=gmtime(&date_value)) != NULL ) { + /* Extract the date part */ + part_value = ext_date_part_extract + (strlist->date_part, date_tm, wanted_zone); + } + } + + strlist->read = TRUE; + + if ( part_value == NULL ) + return 0; + + *str_r = t_str_new_const(part_value, strlen(part_value)); + return 1; +} + +static void ext_date_stringlist_reset +(struct sieve_stringlist *_strlist) +{ + struct ext_date_stringlist *strlist = + (struct ext_date_stringlist *) _strlist; + + if ( strlist->field_values != NULL ) + sieve_stringlist_reset(strlist->field_values); + strlist->read = FALSE; +} diff --git a/pigeonhole/src/lib-sieve/plugins/date/ext-date-common.h b/pigeonhole/src/lib-sieve/plugins/date/ext-date-common.h new file mode 100644 index 0000000..116af3e --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/date/ext-date-common.h @@ -0,0 +1,80 @@ +#ifndef EXT_DATE_COMMON_H +#define EXT_DATE_COMMON_H + +#include "sieve-common.h" + +#include <time.h> + +/* + * Extension + */ + +extern const struct sieve_extension_def date_extension; + +bool ext_date_interpreter_load + (const struct sieve_extension *ext, const struct sieve_runtime_env *renv, + sieve_size_t *address ATTR_UNUSED); + +/* + * Tests + */ + +extern const struct sieve_command_def date_test; +extern const struct sieve_command_def currentdate_test; + +/* + * Operations + */ + +enum ext_date_opcode { + EXT_DATE_OPERATION_DATE, + EXT_DATE_OPERATION_CURRENTDATE +}; + +extern const struct sieve_operation_def date_operation; +extern const struct sieve_operation_def currentdate_operation; + +/* + * Zone string + */ + +bool ext_date_parse_timezone(const char *zone, int *zone_offset_r); + +/* + * Current date + */ + +time_t ext_date_get_current_date + (const struct sieve_runtime_env *renv, int *zone_offset_r); + +/* + * Date part + */ + +struct ext_date_part { + const char *identifier; + + const char *(*get_string)(struct tm *tm, int zone_offset); +}; + +const struct ext_date_part *ext_date_part_find(const char *part); + +const char *ext_date_part_extract + (const struct ext_date_part *dpart, struct tm *tm, int zone_offset); + +/* + * Date stringlist + */ + +enum ext_date_timezone_special { + EXT_DATE_TIMEZONE_LOCAL = 100, + EXT_DATE_TIMEZONE_ORIGINAL = 101 +}; + +struct sieve_stringlist *ext_date_stringlist_create +(const struct sieve_runtime_env *renv, struct sieve_stringlist *field_values, + int time_zone, const struct ext_date_part *dpart); + + + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/date/ext-date.c b/pigeonhole/src/lib-sieve/plugins/date/ext-date.c new file mode 100644 index 0000000..3880d82 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/date/ext-date.c @@ -0,0 +1,62 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension date + * ------------------ + * + * Authors: Stephan Bosch + * Specification: RFC 5260 + * Implementation: full + * Status: testing + * + */ + +#include "lib.h" +#include "array.h" + +#include "sieve-common.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-address-parts.h" + +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-binary.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "ext-date-common.h" + +/* + * Extension + */ + +static bool ext_date_validator_load +(const struct sieve_extension *ext, struct sieve_validator *validator); + +const struct sieve_operation_def *ext_date_operations[] = { + &date_operation, + ¤tdate_operation +}; + +const struct sieve_extension_def date_extension = { + .name = "date", + .validator_load = ext_date_validator_load, + .interpreter_load = ext_date_interpreter_load, + SIEVE_EXT_DEFINE_OPERATIONS(ext_date_operations) +}; + +static bool ext_date_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + /* Register new test */ + sieve_validator_register_command(valdtr, ext, &date_test); + sieve_validator_register_command(valdtr, ext, ¤tdate_test); + + return TRUE; +} + + diff --git a/pigeonhole/src/lib-sieve/plugins/date/tst-date.c b/pigeonhole/src/lib-sieve/plugins/date/tst-date.c new file mode 100644 index 0000000..60ee92c --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/date/tst-date.c @@ -0,0 +1,496 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str-sanitize.h" + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-code.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-address-parts.h" +#include "sieve-message.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-match.h" + +#include "ext-date-common.h" + +#include <time.h> + +/* + * Tests + */ + +static bool tst_date_validate + (struct sieve_validator *valdtr, struct sieve_command *tst); +static bool tst_date_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx); + +/* Date test + * + * Syntax: + * date [<":zone" <time-zone: string>> / ":originalzone"] + * [COMPARATOR] [MATCH-TYPE] <header-name: string> + * <date-part: string> <key-list: string-list> + */ + +static bool tst_date_registered + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); + +const struct sieve_command_def date_test = { + .identifier = "date", + .type = SCT_TEST, + .positional_args = 3, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = tst_date_registered, + .validate = tst_date_validate, + .generate = tst_date_generate +}; + +/* Currentdate test + * + * Syntax: + * currentdate [":zone" <time-zone: string>] + * [COMPARATOR] [MATCH-TYPE] + * <date-part: string> <key-list: string-list> + */ + +static bool tst_currentdate_registered + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); + +const struct sieve_command_def currentdate_test = { + .identifier = "currentdate", + .type = SCT_TEST, + .positional_args = 2, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = tst_currentdate_registered, + .validate = tst_date_validate, + .generate = tst_date_generate +}; + +/* + * Tagged arguments + */ + +/* Forward declarations */ + +static bool tag_zone_validate + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd); +static bool tag_zone_generate + (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *cmd); + +/* Argument objects */ + +static const struct sieve_argument_def date_zone_tag = { + .identifier = "zone", + .validate = tag_zone_validate, + .generate = tag_zone_generate +}; + +static const struct sieve_argument_def date_originalzone_tag = { + .identifier = "originalzone", + .validate = tag_zone_validate, + .generate = tag_zone_generate +}; + +/* + * Date operation + */ + +static bool tst_date_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int tst_date_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def date_operation = { + .mnemonic = "DATE", + .ext_def = &date_extension, + .code = EXT_DATE_OPERATION_DATE, + .dump = tst_date_operation_dump, + .execute = tst_date_operation_execute +}; + +const struct sieve_operation_def currentdate_operation = { + .mnemonic = "CURRENTDATE", + .ext_def = &date_extension, + .code = EXT_DATE_OPERATION_CURRENTDATE, + .dump = tst_date_operation_dump, + .execute = tst_date_operation_execute +}; + +/* + * Optional operands + */ + +enum tst_date_optional { + OPT_DATE_ZONE = SIEVE_AM_OPT_LAST, + OPT_DATE_LAST +}; + +/* + * Tag implementation + */ + +static bool tag_zone_validate +(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + + if ( (bool) cmd->data ) { + if ( sieve_command_is(cmd, date_test) ) { + sieve_argument_validate_error(valdtr, *arg, + "multiple :zone or :originalzone arguments specified for " + "the currentdate test"); + } else { + sieve_argument_validate_error(valdtr, *arg, + "multiple :zone arguments specified for the currentdate test"); + } + return FALSE; + } + + /* Skip tag */ + *arg = sieve_ast_argument_next(*arg); + + /* :content tag has a string-list argument */ + if ( sieve_argument_is(tag, date_zone_tag) ) { + + /* Check syntax: + * :zone <time-zone: string> + */ + if ( !sieve_validate_tag_parameter + (valdtr, cmd, tag, *arg, NULL, 0, SAAT_STRING, FALSE) ) { + return FALSE; + } + + /* Check it */ + if ( sieve_argument_is_string_literal(*arg) ) { + const char *zone = sieve_ast_argument_strc(*arg); + + if ( !ext_date_parse_timezone(zone, NULL) ) { + sieve_argument_validate_warning(valdtr, *arg, + "specified :zone argument '%s' is not a valid timezone", + str_sanitize(zone, 40)); + } + } + + /* Assign tag parameters */ + tag->parameters = *arg; + *arg = sieve_ast_arguments_detach(*arg,1); + } + + cmd->data = (void *) TRUE; + + return TRUE; +} + +/* + * Test registration + */ + +static bool tst_date_registered +(struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR); + sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE); + + sieve_validator_register_tag + (valdtr, cmd_reg, ext, &date_zone_tag, OPT_DATE_ZONE); + sieve_validator_register_tag + (valdtr, cmd_reg, ext, &date_originalzone_tag, OPT_DATE_ZONE); + + return TRUE; +} + +static bool tst_currentdate_registered +(struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR); + sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE); + + sieve_validator_register_tag + (valdtr, cmd_reg, ext, &date_zone_tag, OPT_DATE_ZONE); + + return TRUE; +} + +/* + * Validation + */ + +static bool tst_date_validate +(struct sieve_validator *valdtr, struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + unsigned int arg_offset = 0 ; + const struct sieve_match_type mcht_default = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + const struct sieve_comparator cmp_default = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + + /* Check header name */ + + if ( sieve_command_is(tst, date_test) ) { + arg_offset = 1; + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "header name", 1, SAAT_STRING) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) + return FALSE; + + if ( !sieve_command_verify_headers_argument(valdtr, arg) ) + return FALSE; + + arg = sieve_ast_argument_next(arg); + } + + /* Check date part */ + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "date part", arg_offset + 1, SAAT_STRING) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) + return FALSE; + + if ( sieve_argument_is_string_literal(arg) ) { + const char * part = sieve_ast_argument_strc(arg); + + if ( ext_date_part_find(part) == NULL ) { + sieve_argument_validate_warning + (valdtr, arg, "specified date part `%s' is not known", + str_sanitize(part, 80)); + } + } + + arg = sieve_ast_argument_next(arg); + + /* Check key list */ + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "key list", arg_offset + 2, SAAT_STRING_LIST) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) + return FALSE; + + /* Validate the key argument to a specified match type */ + return sieve_match_type_validate + (valdtr, tst, arg, &mcht_default, &cmp_default); +} + +/* + * Code generation + */ + +static bool tst_date_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *tst) +{ + if ( sieve_command_is(tst, date_test) ) + sieve_operation_emit(cgenv->sblock, tst->ext, &date_operation); + else if ( sieve_command_is(tst, currentdate_test) ) + sieve_operation_emit(cgenv->sblock, tst->ext, ¤tdate_operation); + else + i_unreached(); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, tst, NULL); +} + +static bool tag_zone_generate +(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *cmd) +{ + if ( arg->parameters == NULL ) { + sieve_opr_omitted_emit(cgenv->sblock); + return TRUE; + } + + return sieve_generate_argument_parameters(cgenv, cmd, arg); +} + +/* + * Code dump + */ + +static bool tst_date_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + int opt_code = 0; + const struct sieve_operation *op = denv->oprtn; + + sieve_code_dumpf(denv, "%s", sieve_operation_mnemonic(op)); + sieve_code_descend(denv); + + /* Handle any optional arguments */ + for (;;) { + int opt; + + if ( (opt=sieve_message_opr_optional_dump(denv, address, &opt_code)) < 0 ) + return FALSE; + + if ( opt == 0 ) break; + + switch ( opt_code ) { + case OPT_DATE_ZONE: + if ( !sieve_opr_string_dump_ex(denv, address, "zone", "ORIGINAL") ) + return FALSE; + break; + default: + return FALSE; + } + } + + if ( sieve_operation_is(op, date_operation) && + !sieve_opr_string_dump(denv, address, "header name") ) + return FALSE; + + return + sieve_opr_string_dump(denv, address, "date part") && + sieve_opr_stringlist_dump(denv, address, "key list"); +} + + +/* + * Code execution + */ + +static int tst_date_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + const struct sieve_operation *op = renv->oprtn; + int opt_code = 0; + struct sieve_match_type mcht = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + struct sieve_comparator cmp = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + ARRAY_TYPE(sieve_message_override) svmos; + string_t *date_part = NULL, *zone = NULL; + struct sieve_stringlist *hdr_list = NULL, *hdr_value_list; + struct sieve_stringlist *value_list, *key_list; + bool zone_specified = FALSE, zone_literal = TRUE; + const struct ext_date_part *dpart; + int time_zone; + int match, ret; + + /* Read optional operands */ + for (;;) { + int opt; + + /* Optional operands */ + i_zero(&svmos); + if ( (opt=sieve_message_opr_optional_read + (renv, address, &opt_code, &ret, NULL, &mcht, &cmp, &svmos)) < 0 ) + return ret; + + if ( opt == 0 ) break; + + switch ( opt_code ) { + case OPT_DATE_ZONE: + if ( (ret=sieve_opr_string_read_ex + (renv, address, "zone", TRUE, &zone, &zone_literal)) <= 0 ) + return ret; + + zone_specified = TRUE; + break; + default: + sieve_runtime_trace_error(renv, "unknown optional operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + } + + if ( sieve_operation_is(op, date_operation) ) { + /* Read header name as stringlist */ + if ( (ret=sieve_opr_stringlist_read + (renv, address, "header-name", &hdr_list)) <= 0 ) + return ret; + } + + /* Read date part */ + if ( (ret=sieve_opr_string_read(renv, address, "date-part", &date_part)) + <= 0 ) + return ret; + + /* Read key-list */ + if ( (ret=sieve_opr_stringlist_read(renv, address, "key-list", &key_list)) + <= 0 ) + return ret; + + /* Determine what time zone to use in the result */ + if ( !zone_specified ) { + time_zone = EXT_DATE_TIMEZONE_LOCAL; + } else if ( zone == NULL ) { + time_zone = EXT_DATE_TIMEZONE_ORIGINAL; + } else if ( !ext_date_parse_timezone(str_c(zone), &time_zone) ) { + if ( !zone_literal ) + sieve_runtime_warning(renv, NULL, + "specified :zone argument `%s' is not a valid timezone " + "(using local zone)", str_sanitize(str_c(zone), 40)); + time_zone = EXT_DATE_TIMEZONE_LOCAL; + } + + if ( (dpart=ext_date_part_find(str_c(date_part))) == NULL ) { + sieve_runtime_warning(renv, NULL, + "specified date part argument `%s' is not known", + str_sanitize(str_c(date_part), 40)); + sieve_interpreter_set_test_result(renv->interp, FALSE); + return SIEVE_EXEC_OK; + } + + /* + * Perform test + */ + + if ( sieve_operation_is(op, date_operation) ) { + + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "date test"); + + /* Get header */ + sieve_runtime_trace_descend(renv); + if ( (ret=sieve_message_get_header_fields + (renv, hdr_list, &svmos, FALSE, &hdr_value_list)) <= 0 ) + return ret; + sieve_runtime_trace_ascend(renv); + + /* Create value stringlist */ + value_list = ext_date_stringlist_create + (renv, hdr_value_list, time_zone, dpart); + + } else if ( sieve_operation_is(op, currentdate_operation) ) { + /* Use time stamp recorded at the time the script first started */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "currentdatedate test"); + + /* Create value stringlist */ + value_list = ext_date_stringlist_create(renv, NULL, time_zone, dpart); + } else { + i_unreached(); + } + + /* Perform match */ + if ( (match=sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret)) < 0 ) + return ret; + + /* Set test result for subsequent conditional jump */ + sieve_interpreter_set_test_result(renv->interp, match > 0); + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/plugins/duplicate/Makefile.am b/pigeonhole/src/lib-sieve/plugins/duplicate/Makefile.am new file mode 100644 index 0000000..7469b98 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/duplicate/Makefile.am @@ -0,0 +1,20 @@ +noinst_LTLIBRARIES = libsieve_ext_duplicate.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +tests = \ + tst-duplicate.c + +extensions = \ + ext-duplicate.c + +libsieve_ext_duplicate_la_SOURCES = \ + $(tests) \ + $(extensions) \ + ext-duplicate-common.c + +noinst_HEADERS = \ + ext-duplicate-common.h + diff --git a/pigeonhole/src/lib-sieve/plugins/duplicate/Makefile.in b/pigeonhole/src/lib-sieve/plugins/duplicate/Makefile.in new file mode 100644 index 0000000..d29f834 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/duplicate/Makefile.in @@ -0,0 +1,697 @@ +# 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/plugins/duplicate +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_ext_duplicate_la_LIBADD = +am__objects_1 = tst-duplicate.lo +am__objects_2 = ext-duplicate.lo +am_libsieve_ext_duplicate_la_OBJECTS = $(am__objects_1) \ + $(am__objects_2) ext-duplicate-common.lo +libsieve_ext_duplicate_la_OBJECTS = \ + $(am_libsieve_ext_duplicate_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)/ext-duplicate-common.Plo \ + ./$(DEPDIR)/ext-duplicate.Plo ./$(DEPDIR)/tst-duplicate.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_ext_duplicate_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_duplicate_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_ext_duplicate.la +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +tests = \ + tst-duplicate.c + +extensions = \ + ext-duplicate.c + +libsieve_ext_duplicate_la_SOURCES = \ + $(tests) \ + $(extensions) \ + ext-duplicate-common.c + +noinst_HEADERS = \ + ext-duplicate-common.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/plugins/duplicate/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/duplicate/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_ext_duplicate.la: $(libsieve_ext_duplicate_la_OBJECTS) $(libsieve_ext_duplicate_la_DEPENDENCIES) $(EXTRA_libsieve_ext_duplicate_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_duplicate_la_OBJECTS) $(libsieve_ext_duplicate_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-duplicate-common.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-duplicate.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-duplicate.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)/ext-duplicate-common.Plo + -rm -f ./$(DEPDIR)/ext-duplicate.Plo + -rm -f ./$(DEPDIR)/tst-duplicate.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)/ext-duplicate-common.Plo + -rm -f ./$(DEPDIR)/ext-duplicate.Plo + -rm -f ./$(DEPDIR)/tst-duplicate.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/plugins/duplicate/ext-duplicate-common.c b/pigeonhole/src/lib-sieve/plugins/duplicate/ext-duplicate-common.c new file mode 100644 index 0000000..5af7e04 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/duplicate/ext-duplicate-common.c @@ -0,0 +1,298 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "md5.h" +#include "ioloop.h" +#include "str.h" +#include "str-sanitize.h" +#include "array.h" + +#include "sieve-common.h" +#include "sieve-settings.h" +#include "sieve-error.h" +#include "sieve-extensions.h" +#include "sieve-message.h" +#include "sieve-code.h" +#include "sieve-runtime.h" +#include "sieve-interpreter.h" +#include "sieve-actions.h" +#include "sieve-result.h" + +#include "ext-duplicate-common.h" + +/* + * Extension configuration + */ + +#define EXT_DUPLICATE_DEFAULT_PERIOD (12*60*60) +#define EXT_DUPLICATE_DEFAULT_MAX_PERIOD (2*24*60*60) + +bool ext_duplicate_load(const struct sieve_extension *ext, void **context) +{ + struct sieve_instance *svinst = ext->svinst; + struct ext_duplicate_config *config; + sieve_number_t default_period, max_period; + + if (*context != NULL) + ext_duplicate_unload(ext); + + if (!sieve_setting_get_duration_value( + svinst, "sieve_duplicate_default_period", &default_period)) + default_period = EXT_DUPLICATE_DEFAULT_PERIOD; + if (!sieve_setting_get_duration_value( + svinst, "sieve_duplicate_max_period", &max_period)) { + max_period = EXT_DUPLICATE_DEFAULT_MAX_PERIOD; + } + + config = i_new(struct ext_duplicate_config, 1); + config->default_period = default_period; + config->max_period = max_period; + + *context = (void *) config; + return TRUE; +} + +void ext_duplicate_unload(const struct sieve_extension *ext) +{ + struct ext_duplicate_config *config = + (struct ext_duplicate_config *)ext->context; + + i_free(config); +} + +/* + * Duplicate_mark action + */ + +struct act_duplicate_mark_data { + const char *handle; + unsigned int period; + unsigned char hash[MD5_RESULTLEN]; + bool last:1; +}; + +static void +act_duplicate_mark_print(const struct sieve_action *action, + const struct sieve_result_print_env *rpenv, + bool *keep); +static void +act_duplicate_mark_finish(const struct sieve_action_exec_env *aenv, + void *tr_context, int status); + +static const struct sieve_action_def act_duplicate_mark = { + .name = "duplicate_mark", + .print = act_duplicate_mark_print, + .finish = act_duplicate_mark_finish +}; + +static void +act_duplicate_mark_print(const struct sieve_action *action, + const struct sieve_result_print_env *rpenv, + bool *keep ATTR_UNUSED) +{ + struct act_duplicate_mark_data *data = + (struct act_duplicate_mark_data *)action->context; + const char *last = (data->last ? " last" : ""); + + if (data->handle != NULL) { + sieve_result_action_printf( + rpenv, "track%s duplicate with handle: %s", + last, str_sanitize(data->handle, 128)); + } else { + sieve_result_action_printf(rpenv, "track%s duplicate", last); + } +} + +static void +act_duplicate_mark_finish(const struct sieve_action_exec_env *aenv, + void *tr_context ATTR_UNUSED, int status) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + struct act_duplicate_mark_data *data = + (struct act_duplicate_mark_data *)aenv->action->context; + + if (status != SIEVE_EXEC_OK) { + e_debug(aenv->event, "Not marking duplicate (status=%s)", + sieve_execution_exitcode_to_str(status)); + return; + } + + e_debug(aenv->event, "Marking duplicate"); + + /* Message was handled successfully, so track duplicate for this + * message. + */ + eenv->exec_status->significant_action_executed = TRUE; + sieve_action_duplicate_mark(aenv, data->hash, sizeof(data->hash), + ioloop_time + data->period); +} + +/* + * Duplicate checking + */ + +struct ext_duplicate_handle { + const char *handle; + bool last:1; + bool duplicate:1; +}; + +struct ext_duplicate_hash { + unsigned char hash[MD5_RESULTLEN]; + ARRAY(struct ext_duplicate_handle) handles; +}; + +struct ext_duplicate_context { + ARRAY(struct ext_duplicate_hash) hashes; +}; + +static void +ext_duplicate_hash(string_t *handle, const char *value, size_t value_len, + bool last, unsigned char hash_r[]) +{ + static const char *id = "sieve duplicate"; + struct md5_context md5ctx; + + md5_init(&md5ctx); + md5_update(&md5ctx, id, strlen(id)); + if (last) + md5_update(&md5ctx, "0", 1); + else + md5_update(&md5ctx, "+", 1); + if (handle != NULL) { + md5_update(&md5ctx, "h-", 2); + md5_update(&md5ctx, str_c(handle), str_len(handle)); + } else { + md5_update(&md5ctx, "default", 7); + } + md5_update(&md5ctx, value, value_len); + md5_final(&md5ctx, hash_r); +} + +int ext_duplicate_check(const struct sieve_runtime_env *renv, string_t *handle, + const char *value, size_t value_len, + sieve_number_t period, bool last, bool *duplicate_r) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + const struct sieve_extension *this_ext = renv->oprtn->ext; + struct ext_duplicate_context *rctx; + bool duplicate = FALSE; + pool_t msg_pool = NULL, result_pool = NULL; + unsigned char hash[MD5_RESULTLEN]; + struct ext_duplicate_hash *hash_record = NULL; + struct ext_duplicate_handle *handle_record = NULL; + struct act_duplicate_mark_data *act; + int ret; + + *duplicate_r = FALSE; + + if (!sieve_execute_duplicate_check_available(eenv)) { + sieve_runtime_warning( + renv, NULL, "duplicate test: " + "duplicate checking not available in this context"); + return SIEVE_EXEC_OK; + } + + if (value == NULL) + return SIEVE_EXEC_OK; + + /* Create hash */ + ext_duplicate_hash(handle, value, value_len, last, hash); + + /* Get context; find out whether duplicate was checked earlier */ + rctx = (struct ext_duplicate_context *) + sieve_message_context_extension_get(renv->msgctx, this_ext); + + if (rctx == NULL) { + /* Create context */ + msg_pool = sieve_message_context_pool(renv->msgctx); + rctx = p_new(msg_pool, struct ext_duplicate_context, 1); + sieve_message_context_extension_set(renv->msgctx, this_ext, + (void *)rctx); + } else if (array_is_created(&rctx->hashes)) { + struct ext_duplicate_hash *record; + + array_foreach_modifiable(&rctx->hashes, record) { + if (memcmp(record->hash, hash, MD5_RESULTLEN) == 0) { + hash_record = record; + break; + } + } + } + if (hash_record != NULL) { + const struct ext_duplicate_handle *rhandle; + array_foreach(&hash_record->handles, rhandle) { + const char *handle_str = + (handle == NULL ? NULL : str_c(handle)); + if (null_strcmp(rhandle->handle, handle_str) == 0 && + rhandle->last == last) + return (rhandle->duplicate ? + SIEVE_DUPLICATE_CHECK_RESULT_EXISTS : + SIEVE_DUPLICATE_CHECK_RESULT_NOT_FOUND); + } + } + + result_pool = sieve_result_pool(renv->result); + act = p_new(result_pool, struct act_duplicate_mark_data, 1); + if (handle != NULL) + act->handle = p_strdup(result_pool, str_c(handle)); + act->period = period; + memcpy(act->hash, hash, MD5_RESULTLEN); + act->last = last; + + /* Check duplicate */ + ret = sieve_execute_duplicate_check(eenv, hash, sizeof(hash), + &duplicate); + if (ret >= SIEVE_EXEC_OK && !duplicate && last) { + unsigned char no_last_hash[MD5_RESULTLEN]; + + /* Check for entry without :last */ + ext_duplicate_hash(handle, value, value_len, + FALSE, no_last_hash); + ret = sieve_execute_duplicate_check( + eenv, no_last_hash, sizeof(no_last_hash), + &duplicate); + } + if (ret < SIEVE_EXEC_OK) { + sieve_runtime_critical( + renv, NULL, "failed to check for duplicate", + "failed to check for duplicate%s", + (ret == SIEVE_EXEC_TEMP_FAILURE ? + " (temporary failure)" : "")); + return ret; + } + + /* We may only mark the message as duplicate when Sieve script executes + * successfully; therefore defer this operation until successful result + * execution. + */ + if (!duplicate || last) { + if (sieve_result_add_action(renv, NULL, NULL, + &act_duplicate_mark, + NULL, (void *) act, 0, FALSE) < 0) + return SIEVE_EXEC_FAILURE; + } + + /* Cache result */ + if (msg_pool == NULL) + msg_pool = sieve_message_context_pool(renv->msgctx); + if (hash_record == NULL) { + if (!array_is_created(&rctx->hashes)) + p_array_init(&rctx->hashes, msg_pool, 64); + hash_record = array_append_space(&rctx->hashes); + memcpy(hash_record->hash, hash, MD5_RESULTLEN); + p_array_init(&hash_record->handles, msg_pool, 64); + } + + handle_record = array_append_space(&hash_record->handles); + if (handle != NULL) + handle_record->handle = p_strdup(msg_pool, str_c(handle)); + handle_record->last = last; + handle_record->duplicate = duplicate; + + *duplicate_r = duplicate; + + return SIEVE_EXEC_OK; +} + diff --git a/pigeonhole/src/lib-sieve/plugins/duplicate/ext-duplicate-common.h b/pigeonhole/src/lib-sieve/plugins/duplicate/ext-duplicate-common.h new file mode 100644 index 0000000..c802b08 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/duplicate/ext-duplicate-common.h @@ -0,0 +1,41 @@ +#ifndef EXT_DUPLICATE_COMMON_H +#define EXT_DUPLICATE_COMMON_H + +#include "sieve-common.h" + +/* + * Extension + */ + +struct ext_duplicate_config { + unsigned int default_period; + unsigned int max_period; +}; + +bool ext_duplicate_load(const struct sieve_extension *ext, void **context); +void ext_duplicate_unload(const struct sieve_extension *ext); + +extern const struct sieve_extension_def duplicate_extension; +extern const struct sieve_extension_def vnd_duplicate_extension; + +/* + * Tests + */ + +extern const struct sieve_command_def tst_duplicate; + +/* + * Operations + */ + +extern const struct sieve_operation_def tst_duplicate_operation; + +/* + * Duplicate checking + */ + +int ext_duplicate_check(const struct sieve_runtime_env *renv, string_t *handle, + const char *value, size_t value_len, + sieve_number_t period, bool last, bool *duplicate_r); + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/duplicate/ext-duplicate.c b/pigeonhole/src/lib-sieve/plugins/duplicate/ext-duplicate.c new file mode 100644 index 0000000..54de5e5 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/duplicate/ext-duplicate.c @@ -0,0 +1,107 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension duplicate + * ------------------- + * + * Authors: Stephan Bosch + * Specification: vendor-defined; spec-bosch-sieve-duplicate + * Implementation: full + * Status: experimental + * + */ + +/* Extension vnd.dovecot.duplicate + * ------------------------------- + * + * Authors: Stephan Bosch + * Specification: vendor-defined; spec-bosch-sieve-duplicate + * Implementation: full, but deprecated; provided for backwards compatibility + * Status: experimental + * + */ + +#include "lib.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-binary.h" + +#include "sieve-validator.h" + +#include "ext-duplicate-common.h" + +/* + * Extensions + */ + +static bool ext_duplicate_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); + +const struct sieve_extension_def duplicate_extension = { + .name = "duplicate", + .load = ext_duplicate_load, + .unload = ext_duplicate_unload, + .validator_load = ext_duplicate_validator_load, + SIEVE_EXT_DEFINE_OPERATION(tst_duplicate_operation) +}; + +const struct sieve_extension_def vnd_duplicate_extension = { + .name = "vnd.dovecot.duplicate", + .load = ext_duplicate_load, + .unload = ext_duplicate_unload, + .validator_load = ext_duplicate_validator_load, + SIEVE_EXT_DEFINE_OPERATION(tst_duplicate_operation) +}; + +/* + * Validation + */ + +static bool ext_duplicate_validator_check_conflict + (const struct sieve_extension *ext, + struct sieve_validator *valdtr, void *context, + struct sieve_ast_argument *require_arg, + const struct sieve_extension *ext_other, + bool required); + +const struct sieve_validator_extension +duplicate_validator_extension = { + .ext = &vnd_duplicate_extension, + .check_conflict = ext_duplicate_validator_check_conflict +}; + +static bool ext_duplicate_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + /* Register validator extension to check for conflict between + vnd.dovecot.duplicate and duplicate extensions */ + if ( sieve_extension_is(ext, vnd_duplicate_extension) ) { + sieve_validator_extension_register + (valdtr, ext, &duplicate_validator_extension, NULL); + } + + /* Register duplicate test */ + sieve_validator_register_command(valdtr, ext, &tst_duplicate); + + return TRUE; +} + +static bool ext_duplicate_validator_check_conflict +(const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_validator *valdtr, void *context ATTR_UNUSED, + struct sieve_ast_argument *require_arg, + const struct sieve_extension *ext_other, + bool required ATTR_UNUSED) +{ + /* Check for conflict with duplicate extension */ + if ( sieve_extension_name_is(ext_other, "duplicate") ) { + sieve_argument_validate_error(valdtr, require_arg, + "the (deprecated) vnd.dovecot.duplicate extension " + "cannot be used together with the duplicate extension"); + return FALSE; + } + + return TRUE; +} + diff --git a/pigeonhole/src/lib-sieve/plugins/duplicate/tst-duplicate.c b/pigeonhole/src/lib-sieve/plugins/duplicate/tst-duplicate.c new file mode 100644 index 0000000..d211e04 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/duplicate/tst-duplicate.c @@ -0,0 +1,449 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "mail-storage.h" + +#include "sieve-common.h" +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "ext-duplicate-common.h" + +/* Duplicate test + * + * Syntax: + * Usage: "duplicate" [":handle" <handle: string>] + * [":header" <header-name: string> / + * ":uniqueid" <value: string>] + * [":seconds" <timeout: number>] [":last"] + */ + +static bool +tst_duplicate_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool +tst_duplicate_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +const struct sieve_command_def tst_duplicate = { + .identifier = "duplicate", + .type = SCT_TEST, + .positional_args = 0, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = tst_duplicate_registered, + .generate = tst_duplicate_generate +}; + +/* + * Duplicate test tags + */ + +static bool +tst_duplicate_validate_number_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd); +static bool +tst_duplicate_validate_string_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd); + +static const struct sieve_argument_def duplicate_seconds_tag = { + .identifier = "seconds", + .validate = tst_duplicate_validate_number_tag +}; + +static const struct sieve_argument_def duplicate_header_tag = { + .identifier = "header", + .validate = tst_duplicate_validate_string_tag +}; + +static const struct sieve_argument_def duplicate_uniqueid_tag = { + .identifier = "uniqueid", + .validate = tst_duplicate_validate_string_tag +}; + +static const struct sieve_argument_def duplicate_value_tag = { + .identifier = "value", /* vnd.dovecot.duplicate (deprecated) */ + .validate = tst_duplicate_validate_string_tag +}; + +static const struct sieve_argument_def duplicate_handle_tag = { + .identifier = "handle", + .validate = tst_duplicate_validate_string_tag +}; + +static const struct sieve_argument_def duplicate_last_tag = { + .identifier = "last" +}; + +/* Codes for optional arguments */ + +enum tst_duplicate_optional { + OPT_END, + OPT_SECONDS, + OPT_HEADER, + OPT_UNIQUEID, + OPT_LAST, + OPT_HANDLE +}; + +/* + * Duplicate operation + */ + +static bool +tst_duplicate_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +tst_duplicate_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def tst_duplicate_operation = { + .mnemonic = "DUPLICATE", + .ext_def = &duplicate_extension, + .dump = tst_duplicate_operation_dump, + .execute = tst_duplicate_operation_execute +}; + +/* + * Tag validation + */ + +static bool +tst_duplicate_validate_number_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + const struct sieve_extension *ext = sieve_argument_ext(*arg); + const struct ext_duplicate_config *config = + (const struct ext_duplicate_config *)ext->context; + struct sieve_ast_argument *tag = *arg; + sieve_number_t seconds; + + /* Detach the tag itself */ + *arg = sieve_ast_arguments_detach(*arg,1); + + /* Check syntax: + * :seconds number + */ + if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0, + SAAT_NUMBER, FALSE)) + return FALSE; + + seconds = sieve_ast_argument_number(*arg); + /* Enforce :days <= max_period */ + if (config->max_period > 0 && seconds > config->max_period) { + seconds = config->max_period; + + sieve_argument_validate_warning( + valdtr, *arg, + "specified :seconds value '%llu' is over the maximum", + (unsigned long long)seconds); + } + + sieve_ast_argument_number_set(*arg, seconds); + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + return TRUE; +} + +static bool +tst_duplicate_validate_string_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + const struct sieve_extension *ext = cmd->ext; + struct sieve_ast_argument *tag = *arg; + + /* Detach the tag itself */ + *arg = sieve_ast_arguments_detach(*arg,1); + + /* Check syntax: + * :header <header-name: string> + * :value <value: string> + * :handle <handle: string> + */ + if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0, + SAAT_STRING, FALSE)) + return FALSE; + + if (!sieve_argument_is(tag, duplicate_handle_tag) && (bool)cmd->data) { + sieve_argument_validate_error( + valdtr, *arg, + "conflicting :header and %s arguments specified " + "for the duplicate test", + (sieve_extension_is(ext, duplicate_extension) ? + ":uniqueid" : ":value")); + return FALSE; + } + + /* :header <header-name: string> */ + if (sieve_argument_is(tag, duplicate_header_tag)) { + if (!sieve_command_verify_headers_argument(valdtr, *arg)) + return FALSE; + cmd->data = (void *)TRUE; + /* :handle <handle: string> */ + } else if (sieve_argument_is(tag, duplicate_handle_tag)) { + /* nothing to be done */ + } else if (sieve_argument_is(tag, duplicate_uniqueid_tag)) { + i_assert(sieve_extension_is(ext, duplicate_extension)); + cmd->data = (void *)TRUE; + /* :value <value: string> (vnd.dovecot.duplicate) */ + } else if (sieve_argument_is(tag, duplicate_value_tag)) { + i_assert(sieve_extension_is(ext, vnd_duplicate_extension)); + cmd->data = (void *)TRUE; + } else { + i_unreached(); + } + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + return TRUE; +} + +/* + * Command registration + */ + +static bool +tst_duplicate_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &duplicate_seconds_tag, OPT_SECONDS); + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &duplicate_last_tag, OPT_LAST); + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &duplicate_header_tag, OPT_HEADER); + if (sieve_extension_is(ext, duplicate_extension)) { + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &duplicate_uniqueid_tag, + OPT_UNIQUEID); + } else { + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &duplicate_value_tag, + OPT_UNIQUEID); + } + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &duplicate_handle_tag, OPT_HANDLE); + return TRUE; +} + +/* + * Code generation + */ + +static bool +tst_duplicate_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd) +{ + sieve_operation_emit(cgenv->sblock, cmd->ext, &tst_duplicate_operation); + + if (!sieve_generate_arguments(cgenv, cmd, NULL)) + return FALSE; + return TRUE; +} + +/* + * Code dump + */ + +static bool +tst_duplicate_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + const struct sieve_extension *ext = denv->oprtn->ext; + int opt_code = 0; + + sieve_code_dumpf(denv, "DUPLICATE"); + sieve_code_descend(denv); + + /* Dump optional operands */ + + for (;;) { + int opt; + bool opok = TRUE; + + if ((opt = sieve_opr_optional_dump(denv, address, + &opt_code)) < 0) + return FALSE; + + if (opt == 0) + break; + + switch (opt_code) { + case OPT_SECONDS: + opok = sieve_opr_number_dump(denv, address, "seconds"); + break; + case OPT_LAST: + sieve_code_dumpf(denv, "last"); + break; + case OPT_HEADER: + opok = sieve_opr_string_dump(denv, address, "header"); + break; + case OPT_UNIQUEID: + if (sieve_extension_is(ext, duplicate_extension)) { + opok = sieve_opr_string_dump(denv, address, + "uniqueid"); + } else { + opok = sieve_opr_string_dump(denv, address, + "value"); + } + break; + case OPT_HANDLE: + opok = sieve_opr_string_dump(denv, address, "handle"); + break; + default: + return FALSE; + } + + if (!opok) + return FALSE; + } + + return TRUE; +} + +/* + * Code execution + */ + +static int +tst_duplicate_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address ATTR_UNUSED) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + const struct sieve_extension *ext = renv->oprtn->ext; + const struct ext_duplicate_config *config = + (const struct ext_duplicate_config *)ext->context; + struct mail *mail = eenv->msgdata->mail; + int opt_code = 0; + string_t *handle = NULL, *header = NULL, *uniqueid = NULL; + const char *val = NULL; + size_t val_len = 0; + sieve_number_t seconds = config->default_period; + bool last = FALSE, duplicate = FALSE; + int ret; + + /* + * Read operands + */ + + /* Optional operands */ + + for (;;) { + int opt; + + if ((opt = sieve_opr_optional_read(renv, address, + &opt_code)) < 0) + return SIEVE_EXEC_BIN_CORRUPT; + + if (opt == 0) + break; + + switch (opt_code) { + case OPT_SECONDS: + ret = sieve_opr_number_read(renv, address, "seconds", + &seconds); + break; + case OPT_LAST: + last = TRUE; + ret = SIEVE_EXEC_OK; + break; + case OPT_HEADER: + ret = sieve_opr_string_read(renv, address, "header", + &header); + break; + case OPT_UNIQUEID: + if (sieve_extension_is(ext, duplicate_extension)) { + ret = sieve_opr_string_read(renv, address, + "uniqueid", + &uniqueid); + } else { + ret = sieve_opr_string_read(renv, address, + "value", &uniqueid); + } + break; + case OPT_HANDLE: + ret = sieve_opr_string_read(renv, address, + "handle", &handle); + break; + default: + sieve_runtime_trace_error( + renv, "unknown optional operand"); + ret = SIEVE_EXEC_BIN_CORRUPT; + } + + if (ret <= 0) + return ret; + } + + /* + * Perform operation + */ + + /* Trace */ + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "duplicate test"); + sieve_runtime_trace_descend(renv); + + /* Get value */ + if (uniqueid != NULL) { + val = str_c(uniqueid); + val_len = str_len(uniqueid); + } else { + if (header == NULL) { + ret = mail_get_message_id(mail, &val); + if (ret < 0) { + return sieve_runtime_mail_error( + renv, mail, "duplicate test: " + "failed to read header field `message-id'"); + } + } else { + ret = mail_get_first_header_utf8(mail, str_c(header), + &val); + if (ret < 0) { + return sieve_runtime_mail_error( + renv, mail, "duplicate test: " + "failed to read header field `%s'", + str_c(header)); + } + } + + if (ret > 0) + val_len = strlen(val); + } + + /* Check duplicate */ + if (val == NULL) { + duplicate = FALSE; + } else { + ret = ext_duplicate_check(renv, handle, val, val_len, + seconds, last, &duplicate); + if (ret < SIEVE_EXEC_OK) + return ret; + } + + /* Trace */ + if (duplicate) { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "message is a duplicate"); + } else { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "message is not a duplicate"); + } + + /* Set test result for subsequent conditional jump */ + sieve_interpreter_set_test_result(renv->interp, duplicate); + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/plugins/editheader/Makefile.am b/pigeonhole/src/lib-sieve/plugins/editheader/Makefile.am new file mode 100644 index 0000000..6824ec0 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/editheader/Makefile.am @@ -0,0 +1,19 @@ +noinst_LTLIBRARIES = libsieve_ext_editheader.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + -I$(srcdir)/../../util \ + $(LIBDOVECOT_INCLUDE) + +commands = \ + cmd-addheader.c \ + cmd-deleteheader.c + +libsieve_ext_editheader_la_SOURCES = \ + $(commands) \ + ext-editheader.c \ + ext-editheader-common.c + +noinst_HEADERS = \ + ext-editheader-limits.h \ + ext-editheader-common.h diff --git a/pigeonhole/src/lib-sieve/plugins/editheader/Makefile.in b/pigeonhole/src/lib-sieve/plugins/editheader/Makefile.in new file mode 100644 index 0000000..75d9571 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/editheader/Makefile.in @@ -0,0 +1,701 @@ +# 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/plugins/editheader +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_ext_editheader_la_LIBADD = +am__objects_1 = cmd-addheader.lo cmd-deleteheader.lo +am_libsieve_ext_editheader_la_OBJECTS = $(am__objects_1) \ + ext-editheader.lo ext-editheader-common.lo +libsieve_ext_editheader_la_OBJECTS = \ + $(am_libsieve_ext_editheader_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)/cmd-addheader.Plo \ + ./$(DEPDIR)/cmd-deleteheader.Plo \ + ./$(DEPDIR)/ext-editheader-common.Plo \ + ./$(DEPDIR)/ext-editheader.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_ext_editheader_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_editheader_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_ext_editheader.la +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + -I$(srcdir)/../../util \ + $(LIBDOVECOT_INCLUDE) + +commands = \ + cmd-addheader.c \ + cmd-deleteheader.c + +libsieve_ext_editheader_la_SOURCES = \ + $(commands) \ + ext-editheader.c \ + ext-editheader-common.c + +noinst_HEADERS = \ + ext-editheader-limits.h \ + ext-editheader-common.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/plugins/editheader/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/editheader/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_ext_editheader.la: $(libsieve_ext_editheader_la_OBJECTS) $(libsieve_ext_editheader_la_DEPENDENCIES) $(EXTRA_libsieve_ext_editheader_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_editheader_la_OBJECTS) $(libsieve_ext_editheader_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-addheader.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-deleteheader.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-editheader-common.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-editheader.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)/cmd-addheader.Plo + -rm -f ./$(DEPDIR)/cmd-deleteheader.Plo + -rm -f ./$(DEPDIR)/ext-editheader-common.Plo + -rm -f ./$(DEPDIR)/ext-editheader.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)/cmd-addheader.Plo + -rm -f ./$(DEPDIR)/cmd-deleteheader.Plo + -rm -f ./$(DEPDIR)/ext-editheader-common.Plo + -rm -f ./$(DEPDIR)/ext-editheader.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/plugins/editheader/cmd-addheader.c b/pigeonhole/src/lib-sieve/plugins/editheader/cmd-addheader.c new file mode 100644 index 0000000..f39cbb7 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/editheader/cmd-addheader.c @@ -0,0 +1,337 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str-sanitize.h" +#include "mail-storage.h" + +#include "rfc2822.h" +#include "edit-mail.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-code.h" +#include "sieve-message.h" + +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-binary.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "ext-editheader-common.h" + +/* + * Addheader command + * + * Syntax + * "addheader" [":last"] <field-name: string> <value: string> + */ + +static bool cmd_addheader_registered + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool cmd_addheader_validate + (struct sieve_validator *valdtr, struct sieve_command *tst); +static bool cmd_addheader_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx); + +const struct sieve_command_def addheader_command = { + .identifier = "addheader", + .type = SCT_COMMAND, + .positional_args = 2, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = cmd_addheader_registered, + .validate = cmd_addheader_validate, + .generate = cmd_addheader_generate +}; + +/* + * Addheader command tags + */ + +/* Argument objects */ + +static const struct sieve_argument_def addheader_last_tag = { + .identifier = "last" +}; + +/* Codes for optional arguments */ + +enum cmd_addheader_optional { + OPT_END, + OPT_LAST +}; + +/* + * Addheader operation + */ + +static bool cmd_addheader_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int cmd_addheader_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def addheader_operation = { + .mnemonic = "ADDHEADER", + .ext_def = &editheader_extension, + .code = EXT_EDITHEADER_OPERATION_ADDHEADER, + .dump = cmd_addheader_operation_dump, + .execute = cmd_addheader_operation_execute +}; + +/* + * Utility + */ + +static bool _str_contains_nul(const string_t *str) +{ + const unsigned char *p, *pend; + + p = str_data(str); + pend = p + str_len(str); + while (p < pend) { + if (*p == '\0') + return TRUE; + p++; + } + return FALSE; +} + +/* + * Validation + */ + +static bool cmd_addheader_validate +(struct sieve_validator *valdtr, struct sieve_command *cmd) +{ + struct sieve_ast_argument *arg = cmd->first_positional; + + /* Check field-name syntax */ + + if ( !sieve_validate_positional_argument + (valdtr, cmd, arg, "field-name", 1, SAAT_STRING) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, cmd, arg, FALSE) ) + return FALSE; + + if ( sieve_argument_is_string_literal(arg) ) { + string_t *fname = sieve_ast_argument_str(arg); + + if ( !rfc2822_header_field_name_verify(str_c(fname), str_len(fname)) ) { + sieve_argument_validate_error + (valdtr, arg, "addheader command: specified field name `%s' is invalid", + str_sanitize(str_c(fname), 80)); + return FALSE; + } + + if ( !ext_editheader_header_allow_add + (cmd->ext, str_c(fname)) ) { + sieve_argument_validate_warning + (valdtr, arg, "addheader command: " + "adding specified header field `%s' is forbidden; " + "modification will be denied", + str_sanitize(str_c(fname), 80)); + } + } + + /* Check value syntax */ + + arg = sieve_ast_argument_next(arg); + + if ( !sieve_validate_positional_argument + (valdtr, cmd, arg, "value", 2, SAAT_STRING) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, cmd, arg, FALSE) ) + return FALSE; + + if ( sieve_argument_is_string_literal(arg) ) { + string_t *fvalue = sieve_ast_argument_str(arg); + + if ( _str_contains_nul(fvalue) ) { + sieve_argument_validate_error(valdtr, arg, + "addheader command: specified value `%s' is invalid " + "(contains NUL character)", str_sanitize(str_c(fvalue), 80)); + return FALSE; + } + + if ( !rfc2822_header_field_body_verify + (str_c(fvalue), str_len(fvalue), TRUE, TRUE) ) { + sieve_argument_validate_warning(valdtr, arg, + "addheader command: specified value `%s' is invalid", + str_sanitize(str_c(fvalue), 80)); + } + + if ( ext_editheader_header_too_large(cmd->ext, str_len(fvalue)) ) { + sieve_argument_validate_error(valdtr, arg, "addheader command: " + "specified header value `%s' is too large (%zu bytes)", + str_sanitize(str_c(fvalue), 80), str_len(fvalue)); + return SIEVE_EXEC_FAILURE; + } + } + + return TRUE; +} + +/* + * Command registration + */ + +static bool cmd_addheader_registered +(struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + sieve_validator_register_tag + (valdtr, cmd_reg, ext, &addheader_last_tag, OPT_LAST); + return TRUE; +} + +/* + * Code generation + */ + +static bool cmd_addheader_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + (void)sieve_operation_emit(cgenv->sblock, cmd->ext, &addheader_operation); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, cmd, NULL); +} + +/* + * Code dump + */ + +static bool cmd_addheader_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + int opt_code = 0; + + sieve_code_dumpf(denv, "addheader"); + sieve_code_descend(denv); + + /* Dump optional operands */ + + for (;;) { + int opt; + + if ( (opt=sieve_opr_optional_dump(denv, address, &opt_code)) < 0 ) + return FALSE; + + if ( opt == 0 ) break; + + if ( opt_code == OPT_LAST ) { + sieve_code_dumpf(denv, "last"); + } else { + return FALSE; + } + } + + return + sieve_opr_string_dump(denv, address, "field-name") && + sieve_opr_string_dump(denv, address, "value"); +} + +/* + * Interpretation + */ + +static int cmd_addheader_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + const struct sieve_extension *this_ext = renv->oprtn->ext; + string_t *field_name; + string_t *value; + struct edit_mail *edmail; + bool last = FALSE; + int opt_code = 0; + int ret; + + /* + * Read operands + */ + + /* Optional operands */ + + for (;;) { + int opt; + + if ( (opt=sieve_opr_optional_read(renv, address, &opt_code)) < 0 ) + return SIEVE_EXEC_BIN_CORRUPT; + + if ( opt == 0 ) break; + + switch ( opt_code ) { + case OPT_LAST: + last = TRUE; + break; + default: + sieve_runtime_trace_error(renv, "unknown optional operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + } + + /* Read message */ + + if ( (ret=sieve_opr_string_read + (renv, address, "field-name", &field_name)) <= 0 ) + return ret; + + if ( (ret=sieve_opr_string_read + (renv, address, "value", &value)) <= 0 ) + return ret; + + /* + * Verify arguments + */ + + if ( !rfc2822_header_field_name_verify + (str_c(field_name), str_len(field_name)) ) { + sieve_runtime_error(renv, NULL, "addheader action: " + "specified field name `%s' is invalid", + str_sanitize(str_c(field_name), 80)); + return SIEVE_EXEC_FAILURE; + } + + if ( !ext_editheader_header_allow_add + (this_ext, str_c(field_name)) ) { + sieve_runtime_warning(renv, NULL, "addheader action: " + "adding specified header field `%s' is forbidden; " + "modification denied", str_sanitize(str_c(field_name), 80)); + return SIEVE_EXEC_OK; + } + + if ( _str_contains_nul(value) ) { + sieve_runtime_error(renv, NULL, "addheader action: " + "specified value `%s' is invalid (contains NUL character)", + str_sanitize(str_c(value), 80)); + return SIEVE_EXEC_FAILURE; + } + + if ( ext_editheader_header_too_large(this_ext, str_len(value)) ) { + sieve_runtime_error(renv, NULL, "addheader action: " + "specified header value `%s' is too large (%zu bytes)", + str_sanitize(str_c(value), 80), str_len(value)); + return SIEVE_EXEC_FAILURE; + } + + /* + * Perform operation + */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "addheader \"%s: %s\"", + str_sanitize(str_c(field_name), 80), str_sanitize(str_c(value), 80)); + + edmail = sieve_message_edit(renv->msgctx); + edit_mail_header_add(edmail, + rfc2822_header_field_name_sanitize(str_c(field_name)), + str_c(value), last); + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/plugins/editheader/cmd-deleteheader.c b/pigeonhole/src/lib-sieve/plugins/editheader/cmd-deleteheader.c new file mode 100644 index 0000000..a6964b7 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/editheader/cmd-deleteheader.c @@ -0,0 +1,551 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str-sanitize.h" +#include "mail-storage.h" + +#include "rfc2822.h" +#include "edit-mail.h" + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-code.h" +#include "sieve-message.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" + +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-match.h" + +#include "ext-editheader-common.h" + +/* + * Deleteheader command + * + * Syntax: + * deleteheader [":index" <fieldno: number> [":last"]] + * [COMPARATOR] [MATCH-TYPE] + * <field-name: string> [<value-patterns: string-list>] + */ + +static bool cmd_deleteheader_registered + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool cmd_deleteheader_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); +static bool cmd_deleteheader_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd); + +const struct sieve_command_def deleteheader_command = { + .identifier = "deleteheader", + .type = SCT_COMMAND, + .positional_args = -1, /* We check positional arguments ourselves */ + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = cmd_deleteheader_registered, + .validate = cmd_deleteheader_validate, + .generate = cmd_deleteheader_generate +}; + +/* + * Deleteheader command tags + */ + +/* Forward declarations */ + +static bool cmd_deleteheader_validate_index_tag + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd); +static bool cmd_deleteheader_validate_last_tag + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd); + +/* Argument objects */ + +static const struct sieve_argument_def deleteheader_index_tag = { + .identifier = "index", + .validate = cmd_deleteheader_validate_index_tag +}; + +static const struct sieve_argument_def deleteheader_last_tag = { + .identifier = "last", + .validate = cmd_deleteheader_validate_last_tag +}; + +/* Codes for optional arguments */ + +enum cmd_deleteheader_optional { + OPT_INDEX = SIEVE_MATCH_OPT_LAST, + OPT_LAST +}; + +/* + * Deleteheader operation + */ + +static bool cmd_deleteheader_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int cmd_deleteheader_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def deleteheader_operation = { + .mnemonic = "DELETEHEADER", + .ext_def = &editheader_extension, + .code = EXT_EDITHEADER_OPERATION_DELETEHEADER, + .dump = cmd_deleteheader_operation_dump, + .execute = cmd_deleteheader_operation_execute +}; + +/* + * Command registration + */ + +static bool cmd_deleteheader_registered +(struct sieve_validator *valdtr, const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_command_registration *cmd_reg) +{ + /* The order of these is not significant */ + sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR); + sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE); + + sieve_validator_register_tag + (valdtr, cmd_reg, ext, &deleteheader_index_tag, OPT_INDEX); + sieve_validator_register_tag + (valdtr, cmd_reg, ext, &deleteheader_last_tag, OPT_LAST); + + return TRUE; +} + +/* + * Command validation context + */ + +struct cmd_deleteheader_context_data { + struct sieve_ast_argument *arg_index; + struct sieve_ast_argument *arg_last; +}; + +/* + * Tag validation + */ + +static struct cmd_deleteheader_context_data * +cmd_deleteheader_get_context +(struct sieve_command *cmd) +{ + struct cmd_deleteheader_context_data *ctx_data = + (struct cmd_deleteheader_context_data *)cmd->data; + + if ( ctx_data != NULL ) return ctx_data; + + ctx_data = p_new + (sieve_command_pool(cmd), struct cmd_deleteheader_context_data, 1); + cmd->data = (void *)ctx_data; + + return ctx_data; +} + +static bool cmd_deleteheader_validate_index_tag +(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + struct cmd_deleteheader_context_data *ctx_data; + sieve_number_t index; + + /* Detach the tag itself */ + *arg = sieve_ast_arguments_detach(*arg,1); + + /* Check syntax: + * :index number + */ + if ( !sieve_validate_tag_parameter + (valdtr, cmd, tag, *arg, NULL, 0, SAAT_NUMBER, FALSE) ) { + return FALSE; + } + + index = sieve_ast_argument_number(*arg); + if ( index > INT_MAX ) { + sieve_argument_validate_warning(valdtr, *arg, + "the :%s tag for the %s %s has a parameter value '%llu' " + "exceeding the maximum (%d)", + sieve_argument_identifier(tag), sieve_command_identifier(cmd), + sieve_command_type_name(cmd), (unsigned long long) index, + INT_MAX); + return FALSE; + } + + ctx_data = cmd_deleteheader_get_context(cmd); + ctx_data->arg_index = *arg; + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + + return TRUE; +} + +static bool cmd_deleteheader_validate_last_tag +(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct cmd_deleteheader_context_data *ctx_data; + + ctx_data = cmd_deleteheader_get_context(cmd); + ctx_data->arg_last = *arg; + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + + return TRUE; +} + +/* + * Validation + */ + +static bool cmd_deleteheader_validate +(struct sieve_validator *valdtr, struct sieve_command *cmd) +{ + struct sieve_ast_argument *arg = cmd->first_positional; + struct cmd_deleteheader_context_data *ctx_data = + (struct cmd_deleteheader_context_data *)cmd->data; + struct sieve_comparator cmp_default = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + struct sieve_match_type mcht_default = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + + if ( ctx_data != NULL ) { + if ( ctx_data->arg_last != NULL && ctx_data->arg_index == NULL ) { + sieve_argument_validate_error(valdtr, ctx_data->arg_last, + "the :last tag for the %s %s cannot be specified " + "without the :index tag", + sieve_command_identifier(cmd), sieve_command_type_name(cmd)); + } + } + + /* Field name argument */ + + if ( arg == NULL ) { + sieve_command_validate_error(valdtr, cmd, + "the %s %s expects at least one positional argument, but none was found", + sieve_command_identifier(cmd), sieve_command_type_name(cmd)); + return FALSE; + } + + if ( !sieve_validate_positional_argument + (valdtr, cmd, arg, "field name", 1, SAAT_STRING_LIST) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, cmd, arg, FALSE) ) + return FALSE; + + if ( sieve_argument_is_string_literal(arg) ) { + string_t *fname = sieve_ast_argument_str(arg); + + if ( !rfc2822_header_field_name_verify(str_c(fname), str_len(fname)) ) { + sieve_argument_validate_error(valdtr, arg, "deleteheader command:" + "specified field name `%s' is invalid", + str_sanitize(str_c(fname), 80)); + return FALSE; + } + + if ( !ext_editheader_header_allow_delete + (cmd->ext, str_c(fname)) ) { + sieve_argument_validate_warning + (valdtr, arg, "deleteheader command: " + "deleting specified header field `%s' is forbidden; " + "modification will be denied", + str_sanitize(str_c(fname), 80)); + } + } + + /* Value patterns argument */ + + arg = sieve_ast_argument_next(arg); + if ( arg == NULL ) { + /* There is none; let's not generate code for useless match arguments */ + sieve_match_type_arguments_remove(valdtr, cmd); + + return TRUE; + } + + if ( !sieve_validate_positional_argument + (valdtr, cmd, arg, "value patterns", 2, SAAT_STRING_LIST) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, cmd, arg, FALSE) ) + return FALSE; + + /* Validate the value patterns to a specified match type */ + return sieve_match_type_validate + (valdtr, cmd, arg, &mcht_default, &cmp_default); +} + +/* + * Code generation + */ + +static bool cmd_deleteheader_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + sieve_operation_emit(cgenv->sblock, cmd->ext, &deleteheader_operation); + + /* Generate arguments */ + if ( !sieve_generate_arguments(cgenv, cmd, NULL) ) + return FALSE; + + /* Emit a placeholder when the value-patterns argument is missing */ + if ( sieve_ast_argument_next(cmd->first_positional) == NULL ) + sieve_opr_omitted_emit(cgenv->sblock); + + return TRUE; +} + +/* + * Code dump + */ + +static bool cmd_deleteheader_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + int opt_code = 0; + + sieve_code_dumpf(denv, "DELETEHEADER"); + sieve_code_descend(denv); + + /* Optional operands */ + for (;;) { + int opt; + + if ( (opt=sieve_match_opr_optional_dump(denv, address, &opt_code)) < 0 ) + return FALSE; + + if ( opt == 0 ) break; + + switch ( opt_code ) { + case OPT_INDEX: + if ( !sieve_opr_number_dump(denv, address, "index") ) + return FALSE; + break; + case OPT_LAST: + sieve_code_dumpf(denv, "last"); + break; + default: + return FALSE; + } + }; + + if ( !sieve_opr_string_dump(denv, address, "field name") ) + return FALSE; + + return sieve_opr_stringlist_dump_ex(denv, address, "value patterns", ""); +} + +/* + * Code execution + */ + +static int cmd_deleteheader_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + const struct sieve_extension *this_ext = renv->oprtn->ext; + int opt_code = 0; + struct sieve_comparator cmp = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + struct sieve_match_type mcht = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + string_t *field_name; + struct sieve_stringlist *vpattern_list = NULL; + struct edit_mail *edmail; + sieve_number_t index_offset = 0; + bool index_last = FALSE; + bool trace = FALSE; + int ret; + + /* + * Read operands + */ + + for (;;) { + int opt; + + if ( (opt=sieve_match_opr_optional_read + (renv, address, &opt_code, &ret, &cmp, &mcht)) < 0 ) + return ret; + + if ( opt == 0 ) break; + + switch ( opt_code ) { + case OPT_INDEX: + if ( (ret=sieve_opr_number_read(renv, address, "index", &index_offset)) + <= 0 ) + return ret; + + if ( index_offset > INT_MAX ) { + sieve_runtime_trace_error(renv, "index is > %d", INT_MAX); + return SIEVE_EXEC_BIN_CORRUPT; + } + + break; + case OPT_LAST: + index_last = TRUE; + break; + default: + sieve_runtime_trace_error(renv, "unknown optional operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + } + + /* Read field-name */ + if ( (ret=sieve_opr_string_read(renv, address, "field-name", &field_name)) + <= 0 ) + return ret; + + /* Read value-patterns */ + if ( (ret=sieve_opr_stringlist_read_ex + (renv, address, "value-patterns", TRUE, &vpattern_list)) <= 0 ) + return ret; + + /* + * Verify arguments + */ + + if ( !rfc2822_header_field_name_verify + (str_c(field_name), str_len(field_name)) ) { + sieve_runtime_error(renv, NULL, "deleteheader action: " + "specified field name `%s' is invalid", + str_sanitize(str_c(field_name), 80)); + return SIEVE_EXEC_FAILURE; + } + + if ( !ext_editheader_header_allow_delete + (this_ext, str_c(field_name)) ) { + sieve_runtime_warning(renv, NULL, "deleteheader action: " + "deleting specified header field `%s' is forbidden; " + "modification denied", + str_sanitize(str_c(field_name), 80)); + return SIEVE_EXEC_OK; + } + + /* + * Execute command + */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "deleteheader command"); + + /* Start editing the mail */ + edmail = sieve_message_edit(renv->msgctx); + + trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS); + + /* Either do string matching or just kill all/indexed notify action(s) */ + if ( vpattern_list != NULL ) { + struct edit_mail_header_iter *edhiter; + struct sieve_match_context *mctx; + + if ( trace ) { + sieve_runtime_trace_descend(renv); + if ( index_offset != 0 ) { + sieve_runtime_trace(renv, 0, + "deleting matching occurrences of header `%s' at index %llu%s", + str_c(field_name), (unsigned long long)index_offset, + ( index_last ? " from last": "")); + } else { + sieve_runtime_trace(renv, 0, + "deleting matching occurrences of header `%s'", str_c(field_name)); + } + } + + /* Iterate through all headers and delete those that match */ + if ( (ret=edit_mail_headers_iterate_init + (edmail, str_c(field_name), index_last, &edhiter)) > 0 ) + { + int mret = 0; + sieve_number_t pos = 0; + + /* Initialize match */ + mctx = sieve_match_begin(renv, &mcht, &cmp); + + /* Match */ + for (;;) { + pos++; + + /* Check index if any */ + if ( index_offset == 0 || pos == index_offset ) { + const char *value; + int match; + + /* Match value against all value patterns */ + edit_mail_headers_iterate_get(edhiter, &value); + if ( (match=sieve_match_value + (mctx, value, strlen(value), vpattern_list)) < 0 ) + break; + + if ( match > 0 ) { + /* Remove it and iterate to next */ + sieve_runtime_trace(renv, 0, "deleting header with value `%s'", + value); + + if ( !edit_mail_headers_iterate_remove(edhiter) ) break; + continue; + } + } + + if ( !edit_mail_headers_iterate_next(edhiter) ) + break; + } + + /* Finish match */ + mret = sieve_match_end(&mctx, &ret); + + edit_mail_headers_iterate_deinit(&edhiter); + + if ( mret < 0 ) + return ret; + } + + if ( ret == 0 ) { + sieve_runtime_trace(renv, 0, "header `%s' not found", str_c(field_name)); + } else if ( ret < 0 ) { + sieve_runtime_warning(renv, NULL, "deleteheader action: " + "failed to delete occurrences of header `%s' (this should not happen!)", + str_c(field_name)); + } + + } else { + int index = ( index_last ? -((int)index_offset) : ((int)index_offset) ); + + if ( trace ) { + sieve_runtime_trace_descend(renv); + if ( index_offset != 0 ) { + sieve_runtime_trace(renv, 0, "deleting header `%s' at index %llu%s", + str_c(field_name), (unsigned long long)index_offset, + ( index_last ? " from last": "")); + } else { + sieve_runtime_trace(renv, 0, "deleting header `%s'", str_c(field_name)); + } + } + + /* Delete all occurrences of header */ + ret = edit_mail_header_delete(edmail, str_c(field_name), index); + + if ( ret < 0 ) { + sieve_runtime_warning(renv, NULL, "deleteheader action: " + "failed to delete occurrences of header `%s' (this should not happen!)", + str_c(field_name)); + } else if ( trace ) { + sieve_runtime_trace(renv, 0, "deleted %d occurrences of header `%s'", + ret, str_c(field_name)); + } + + } + + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader-common.c b/pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader-common.c new file mode 100644 index 0000000..89fb180 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader-common.c @@ -0,0 +1,211 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "mempool.h" +#include "array.h" + +#include "rfc2822.h" + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-settings.h" +#include "sieve-extensions.h" + +#include "ext-editheader-limits.h" +#include "ext-editheader-common.h" + +/* + * Extension configuration + */ + +struct ext_editheader_header { + const char *name; + + bool forbid_add:1; + bool forbid_delete:1; +}; + +struct ext_editheader_config { + pool_t pool; + + ARRAY(struct ext_editheader_header) headers; + + size_t max_header_size; +}; + +static struct ext_editheader_header * +ext_editheader_config_header_find(struct ext_editheader_config *ext_config, + const char *hname) +{ + struct ext_editheader_header *headers; + unsigned int count, i; + + headers = array_get_modifiable(&ext_config->headers, &count); + for (i = 0; i < count; i++) { + if (strcasecmp(hname, headers[i].name) == 0) + return &headers[i]; + } + return NULL; +} + +static void +ext_editheader_config_headers(struct sieve_instance *svinst, + struct ext_editheader_config *ext_config, + const char *setting, bool forbid_add, + bool forbid_delete) +{ + const char *setval; + + setval = sieve_setting_get(svinst, setting); + if (setval != NULL) { + const char **headers = t_strsplit_spaces(setval, " \t"); + + while (*headers != NULL) { + struct ext_editheader_header *header; + + if (!rfc2822_header_field_name_verify( + *headers, strlen(*headers))) { + e_warning(svinst->event, "editheader: " + "setting %s contains invalid header field name " + "`%s' (ignored)", + setting, *headers); + headers++; + continue; + } + + header = ext_editheader_config_header_find( + ext_config, *headers); + if (header == NULL) { + header = array_append_space( + &ext_config->headers); + header->name = p_strdup(ext_config->pool, + *headers); + } + + if (forbid_add) + header->forbid_add = TRUE; + if (forbid_delete) + header->forbid_delete = TRUE; + + headers++; + } + } +} + +bool ext_editheader_load(const struct sieve_extension *ext, void **context) +{ + struct ext_editheader_config *ext_config; + struct sieve_instance *svinst = ext->svinst; + size_t max_header_size; + pool_t pool; + + if (*context != NULL) { + ext_editheader_unload(ext); + *context = NULL; + } + + T_BEGIN { + pool = pool_alloconly_create("editheader_config", 1024); + ext_config = p_new(pool, struct ext_editheader_config, 1); + ext_config->pool = pool; + ext_config->max_header_size = + EXT_EDITHEADER_DEFAULT_MAX_HEADER_SIZE; + + p_array_init(&ext_config->headers, pool, 16); + + ext_editheader_config_headers( + svinst, ext_config, + "sieve_editheader_protected", TRUE, TRUE); + ext_editheader_config_headers( + svinst, ext_config, + "sieve_editheader_forbid_add", TRUE, FALSE); + ext_editheader_config_headers( + svinst, ext_config, + "sieve_editheader_forbid_delete", FALSE, TRUE); + + if (sieve_setting_get_size_value( + svinst, "sieve_editheader_max_header_size", + &max_header_size)) { + if (max_header_size < EXT_EDITHEADER_MINIMUM_MAX_HEADER_SIZE) { + e_warning(svinst->event, "editheader: " + "value of sieve_editheader_max_header_size setting " + "(=%zu) is less than the minimum (=%zu) " + "(ignored)", max_header_size, + (size_t)EXT_EDITHEADER_MINIMUM_MAX_HEADER_SIZE); + } else { + ext_config->max_header_size = max_header_size; + } + } + } T_END; + + *context = (void *)ext_config; + return TRUE; +} + +void ext_editheader_unload(const struct sieve_extension *ext) +{ + struct ext_editheader_config *ext_config = + (struct ext_editheader_config *)ext->context; + + if (ext_config != NULL) + pool_unref(&ext_config->pool); +} + +/* + * Protected headers + */ + +bool ext_editheader_header_allow_add(const struct sieve_extension *ext, + const char *hname) +{ + struct ext_editheader_config *ext_config = + (struct ext_editheader_config *)ext->context; + const struct ext_editheader_header *header; + + if (strcasecmp(hname, "subject") == 0) + return TRUE; + if (strcasecmp(hname, "x-sieve-redirected-from") == 0) + return FALSE; + + header = ext_editheader_config_header_find(ext_config, hname); + if (header == NULL) + return TRUE; + + return !header->forbid_add; +} + +bool ext_editheader_header_allow_delete(const struct sieve_extension *ext, + const char *hname) +{ + struct ext_editheader_config *ext_config = + (struct ext_editheader_config *)ext->context; + const struct ext_editheader_header *header; + + if (strcasecmp(hname, "received") == 0 || + strcasecmp(hname, "auto-submitted") == 0) + return FALSE; + if (strcasecmp(hname, "x-sieve-redirected-from") == 0) + return FALSE; + if (strcasecmp(hname, "subject") == 0) + return TRUE; + + header = ext_editheader_config_header_find(ext_config, hname); + if (header == NULL) + return TRUE; + + return !header->forbid_delete; +} + +/* + * Limits + */ + +bool ext_editheader_header_too_large(const struct sieve_extension *ext, + size_t size) +{ + struct ext_editheader_config *ext_config = + (struct ext_editheader_config *)ext->context; + + return size > ext_config->max_header_size; +} diff --git a/pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader-common.h b/pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader-common.h new file mode 100644 index 0000000..8a6cc36 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader-common.h @@ -0,0 +1,48 @@ +#ifndef EXT_EDITHEADER_COMMON_H +#define EXT_EDITHEADER_COMMON_H + +/* + * Commands + */ + +extern const struct sieve_command_def addheader_command; +extern const struct sieve_command_def deleteheader_command; + +/* + * Operations + */ + +enum ext_imap4flags_opcode { + EXT_EDITHEADER_OPERATION_ADDHEADER, + EXT_EDITHEADER_OPERATION_DELETEHEADER, +}; + +extern const struct sieve_operation_def addheader_operation; +extern const struct sieve_operation_def deleteheader_operation; + +/* + * Extension + */ + +extern const struct sieve_extension_def editheader_extension; + +bool ext_editheader_load(const struct sieve_extension *ext, void **context); +void ext_editheader_unload(const struct sieve_extension *ext); + +/* + * Protected headers + */ + +bool ext_editheader_header_allow_add(const struct sieve_extension *ext, + const char *hname); +bool ext_editheader_header_allow_delete(const struct sieve_extension *ext, + const char *hname); + +/* + * Limits + */ + +bool ext_editheader_header_too_large(const struct sieve_extension *ext, + size_t size); + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader-limits.h b/pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader-limits.h new file mode 100644 index 0000000..a8e834b --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader-limits.h @@ -0,0 +1,7 @@ +#ifndef EXT_EDITHEADER_LIMITS_H +#define EXT_EDITHEADER_LIMITS_H + +#define EXT_EDITHEADER_MINIMUM_MAX_HEADER_SIZE 1024 +#define EXT_EDITHEADER_DEFAULT_MAX_HEADER_SIZE 2048 + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader.c b/pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader.c new file mode 100644 index 0000000..dc8eb12 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader.c @@ -0,0 +1,66 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension debug + * --------------- + * + * Authors: Stephan Bosch + * Specification: RFC 5293 + * Implementation: full + * Status: testing + * + */ + +#include "lib.h" +#include "array.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-address-parts.h" + +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-binary.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "ext-editheader-common.h" + +/* + * Operations + */ + +const struct sieve_operation_def *editheader_operations[] = { + &addheader_operation, + &deleteheader_operation +}; + +/* + * Extension + */ + +static bool ext_editheader_validator_load + (const struct sieve_extension *ext, struct sieve_validator *validator); + +const struct sieve_extension_def editheader_extension = { + .name = "editheader", + .load = ext_editheader_load, + .unload = ext_editheader_unload, + .validator_load = ext_editheader_validator_load, + SIEVE_EXT_DEFINE_OPERATIONS(editheader_operations) +}; + +static bool ext_editheader_validator_load +(const struct sieve_extension *ext, struct sieve_validator *validator) +{ + /* Register new commands */ + sieve_validator_register_command(validator, ext, &addheader_command); + sieve_validator_register_command(validator, ext, &deleteheader_command); + + return TRUE; +} + + + diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/Makefile.am b/pigeonhole/src/lib-sieve/plugins/enotify/Makefile.am new file mode 100644 index 0000000..4b65630 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/enotify/Makefile.am @@ -0,0 +1,44 @@ +SUBDIRS = mailto + +noinst_LTLIBRARIES = libsieve_ext_enotify.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + -I$(srcdir)/../variables \ + $(LIBDOVECOT_INCLUDE) + +commands = \ + cmd-notify.c + +tests = \ + tst-valid-notify-method.c \ + tst-notify-method-capability.c + +var_modifiers = \ + vmodf-encodeurl.c + +notify_methods = \ + ./mailto/libsieve_ext_enotify_mailto.la + +libsieve_ext_enotify_la_DEPENDENCIES = \ + $(notify_methods) +libsieve_ext_enotify_la_LIBADD = \ + $(notify_methods) + +libsieve_ext_enotify_la_SOURCES = \ + ext-enotify.c \ + ext-enotify-common.c \ + $(commands) \ + $(tests) \ + $(var_modifiers) + +public_headers = \ + sieve-ext-enotify.h + +headers = \ + ext-enotify-limits.h \ + ext-enotify-common.h + +pkginc_libdir=$(dovecot_pkgincludedir)/sieve +pkginc_lib_HEADERS = $(public_headers) +noinst_HEADERS = $(headers) diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/Makefile.in b/pigeonhole/src/lib-sieve/plugins/enotify/Makefile.in new file mode 100644 index 0000000..fea2423 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/enotify/Makefile.in @@ -0,0 +1,904 @@ +# 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/plugins/enotify +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) \ + $(pkginc_lib_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) +am__objects_1 = cmd-notify.lo +am__objects_2 = tst-valid-notify-method.lo \ + tst-notify-method-capability.lo +am__objects_3 = vmodf-encodeurl.lo +am_libsieve_ext_enotify_la_OBJECTS = ext-enotify.lo \ + ext-enotify-common.lo $(am__objects_1) $(am__objects_2) \ + $(am__objects_3) +libsieve_ext_enotify_la_OBJECTS = \ + $(am_libsieve_ext_enotify_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)/cmd-notify.Plo \ + ./$(DEPDIR)/ext-enotify-common.Plo ./$(DEPDIR)/ext-enotify.Plo \ + ./$(DEPDIR)/tst-notify-method-capability.Plo \ + ./$(DEPDIR)/tst-valid-notify-method.Plo \ + ./$(DEPDIR)/vmodf-encodeurl.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_ext_enotify_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_enotify_la_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(pkginc_libdir)" +HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS) +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +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 +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +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@ +SUBDIRS = mailto +noinst_LTLIBRARIES = libsieve_ext_enotify.la +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + -I$(srcdir)/../variables \ + $(LIBDOVECOT_INCLUDE) + +commands = \ + cmd-notify.c + +tests = \ + tst-valid-notify-method.c \ + tst-notify-method-capability.c + +var_modifiers = \ + vmodf-encodeurl.c + +notify_methods = \ + ./mailto/libsieve_ext_enotify_mailto.la + +libsieve_ext_enotify_la_DEPENDENCIES = \ + $(notify_methods) + +libsieve_ext_enotify_la_LIBADD = \ + $(notify_methods) + +libsieve_ext_enotify_la_SOURCES = \ + ext-enotify.c \ + ext-enotify-common.c \ + $(commands) \ + $(tests) \ + $(var_modifiers) + +public_headers = \ + sieve-ext-enotify.h + +headers = \ + ext-enotify-limits.h \ + ext-enotify-common.h + +pkginc_libdir = $(dovecot_pkgincludedir)/sieve +pkginc_lib_HEADERS = $(public_headers) +noinst_HEADERS = $(headers) +all: all-recursive + +.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/plugins/enotify/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/enotify/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_ext_enotify.la: $(libsieve_ext_enotify_la_OBJECTS) $(libsieve_ext_enotify_la_DEPENDENCIES) $(EXTRA_libsieve_ext_enotify_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_enotify_la_OBJECTS) $(libsieve_ext_enotify_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-notify.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-enotify-common.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-enotify.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-notify-method-capability.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-valid-notify-method.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vmodf-encodeurl.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) + @$(NORMAL_INSTALL) + @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ + done + +uninstall-pkginc_libHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(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-recursive + +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-recursive + +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 + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +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-recursive + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/cmd-notify.Plo + -rm -f ./$(DEPDIR)/ext-enotify-common.Plo + -rm -f ./$(DEPDIR)/ext-enotify.Plo + -rm -f ./$(DEPDIR)/tst-notify-method-capability.Plo + -rm -f ./$(DEPDIR)/tst-valid-notify-method.Plo + -rm -f ./$(DEPDIR)/vmodf-encodeurl.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-pkginc_libHEADERS + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f ./$(DEPDIR)/cmd-notify.Plo + -rm -f ./$(DEPDIR)/ext-enotify-common.Plo + -rm -f ./$(DEPDIR)/ext-enotify.Plo + -rm -f ./$(DEPDIR)/tst-notify-method-capability.Plo + -rm -f ./$(DEPDIR)/tst-valid-notify-method.Plo + -rm -f ./$(DEPDIR)/vmodf-encodeurl.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-pkginc_libHEADERS + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-am clean clean-generic clean-libtool \ + clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-pkginc_libHEADERS \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am \ + uninstall-pkginc_libHEADERS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/cmd-notify.c b/pigeonhole/src/lib-sieve/plugins/enotify/cmd-notify.c new file mode 100644 index 0000000..4fe121a --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/enotify/cmd-notify.c @@ -0,0 +1,621 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str-sanitize.h" + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-actions.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-result.h" + +#include "ext-enotify-common.h" + +/* + * Forward declarations + */ + +static const struct sieve_argument_def notify_importance_tag; +static const struct sieve_argument_def notify_from_tag; +static const struct sieve_argument_def notify_options_tag; +static const struct sieve_argument_def notify_message_tag; + +/* + * Notify command + * + * Syntax: + * notify [":from" string] + * [":importance" <"1" / "2" / "3">] + * [":options" string-list] + * [":message" string] + * <method: string> + */ + +static bool +cmd_notify_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool +cmd_notify_pre_validate(struct sieve_validator *validator, + struct sieve_command *cmd); +static bool +cmd_notify_validate(struct sieve_validator *valdtr, struct sieve_command *cmd); +static bool +cmd_notify_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +const struct sieve_command_def notify_command = { + .identifier = "notify", + .type = SCT_COMMAND, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = cmd_notify_registered, + .pre_validate = cmd_notify_pre_validate, + .validate = cmd_notify_validate, + .generate = cmd_notify_generate, +}; + +/* + * Notify command tags + */ + +/* Forward declarations */ + +static bool +cmd_notify_validate_string_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd); +static bool +cmd_notify_validate_stringlist_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd); +static bool +cmd_notify_validate_importance_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd); + +/* Argument objects */ + +static const struct sieve_argument_def notify_from_tag = { + .identifier = "from", + .validate = cmd_notify_validate_string_tag +}; + +static const struct sieve_argument_def notify_options_tag = { + .identifier = "options", + .validate = cmd_notify_validate_stringlist_tag +}; + +static const struct sieve_argument_def notify_message_tag = { + .identifier = "message", + .validate = cmd_notify_validate_string_tag +}; + +static const struct sieve_argument_def notify_importance_tag = { + .identifier = "importance", + .validate = cmd_notify_validate_importance_tag +}; + +/* + * Notify operation + */ + +static bool +cmd_notify_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +cmd_notify_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def notify_operation = { + .mnemonic = "NOTIFY", + .ext_def = &enotify_extension, + .code = EXT_ENOTIFY_OPERATION_NOTIFY, + .dump = cmd_notify_operation_dump, + .execute = cmd_notify_operation_execute +}; + +/* + * Notify action + */ + +/* Forward declarations */ + +static int +act_notify_check_duplicate(const struct sieve_runtime_env *renv, + const struct sieve_action *act, + const struct sieve_action *act_other); +static void +act_notify_print(const struct sieve_action *action, + const struct sieve_result_print_env *rpenv, + bool *keep); +static int +act_notify_commit(const struct sieve_action_exec_env *aenv, void *tr_context); + +/* Action object */ + +const struct sieve_action_def act_notify = { + .name = "notify", + .check_duplicate =act_notify_check_duplicate, + .print = act_notify_print, + .commit = act_notify_commit, +}; + +/* + * Command validation context + */ + +struct cmd_notify_context_data { + struct sieve_ast_argument *from; + struct sieve_ast_argument *message; + struct sieve_ast_argument *options; +}; + +/* + * Tag validation + */ + +static bool +cmd_notify_validate_string_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + struct cmd_notify_context_data *ctx_data = + (struct cmd_notify_context_data *)cmd->data; + + /* Detach the tag itself */ + *arg = sieve_ast_arguments_detach(*arg,1); + + /* Check syntax: + * :from <string> + * :message <string> + */ + if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0, + SAAT_STRING, FALSE)) + return FALSE; + + if (sieve_argument_is(tag, notify_from_tag)) { + ctx_data->from = *arg; + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + } else if (sieve_argument_is(tag, notify_message_tag)) { + ctx_data->message = *arg; + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + } + return TRUE; +} + +static bool +cmd_notify_validate_stringlist_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + struct cmd_notify_context_data *ctx_data = + (struct cmd_notify_context_data *)cmd->data; + + /* Detach the tag itself */ + *arg = sieve_ast_arguments_detach(*arg,1); + + /* Check syntax: + * :options string-list + */ + if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0, + SAAT_STRING_LIST, FALSE)) + return FALSE; + + /* Assign context */ + ctx_data->options = *arg; + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + + return TRUE; +} + +static bool +cmd_notify_validate_importance_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd ATTR_UNUSED) +{ + const struct sieve_ast_argument *tag = *arg; + const char *impstr; + + /* Detach the tag itself */ + *arg = sieve_ast_arguments_detach(*arg,1); + + /* Check syntax: + * :importance <"1" / "2" / "3"> + */ + if (sieve_ast_argument_type(*arg) != SAAT_STRING) { + /* Not a string */ + sieve_argument_validate_error( + valdtr, *arg, + "the :importance tag for the notify command requires a string parameter, " + "but %s was found", sieve_ast_argument_name(*arg)); + return FALSE; + } + + impstr = sieve_ast_argument_strc(*arg); + if (impstr[0] < '1' || impstr[0] > '3' || impstr[1] != '\0') { + /* Invalid importance */ + sieve_argument_validate_error( + valdtr, *arg, + "invalid :importance value for notify command: %s", + impstr); + return FALSE; + } + + sieve_ast_argument_number_substitute(*arg, impstr[0] - '0'); + (*arg)->argument = sieve_argument_create((*arg)->ast, &number_argument, + tag->argument->ext, + tag->argument->id_code); + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + + return TRUE; +} + +/* + * Command registration + */ + +static bool +cmd_notify_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + sieve_validator_register_tag(valdtr, cmd_reg, ext, + ¬ify_importance_tag, + CMD_NOTIFY_OPT_IMPORTANCE); + sieve_validator_register_tag(valdtr, cmd_reg, ext, + ¬ify_from_tag, CMD_NOTIFY_OPT_FROM); + sieve_validator_register_tag(valdtr, cmd_reg, ext, + ¬ify_options_tag, + CMD_NOTIFY_OPT_OPTIONS); + sieve_validator_register_tag(valdtr, cmd_reg, ext, + ¬ify_message_tag, + CMD_NOTIFY_OPT_MESSAGE); + return TRUE; +} + +/* + * Command validation + */ + +static bool +cmd_notify_pre_validate(struct sieve_validator *validator ATTR_UNUSED, + struct sieve_command *cmd) +{ + struct cmd_notify_context_data *ctx_data; + + /* Assign context */ + ctx_data = p_new(sieve_command_pool(cmd), + struct cmd_notify_context_data, 1); + cmd->data = ctx_data; + + return TRUE; +} + +static bool +cmd_notify_validate(struct sieve_validator *valdtr, struct sieve_command *cmd) +{ + struct sieve_ast_argument *arg = cmd->first_positional; + struct cmd_notify_context_data *ctx_data = + (struct cmd_notify_context_data *)cmd->data; + + if (!sieve_validate_positional_argument(valdtr, cmd, arg, "method", 1, + SAAT_STRING)) + return FALSE; + + if (!sieve_validator_argument_activate(valdtr, cmd, arg, FALSE)) + return FALSE; + + return ext_enotify_compile_check_arguments( + valdtr, cmd, arg, ctx_data->message, ctx_data->from, + ctx_data->options); +} + +/* + * Code generation + */ + +static bool +cmd_notify_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd) +{ + sieve_operation_emit(cgenv->sblock, cmd->ext, ¬ify_operation); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, cmd, NULL); +} + +/* + * Code dump + */ + +static bool +cmd_notify_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + int opt_code = 0; + + sieve_code_dumpf(denv, "NOTIFY"); + sieve_code_descend(denv); + + /* Dump optional operands */ + + for (;;) { + int opt; + bool opok = TRUE; + + if ((opt = sieve_opr_optional_dump(denv, address, + &opt_code)) < 0) + return FALSE; + + if (opt == 0) + break; + + switch (opt_code) { + case CMD_NOTIFY_OPT_IMPORTANCE: + opok = sieve_opr_number_dump(denv, address, + "importance"); + break; + case CMD_NOTIFY_OPT_FROM: + opok = sieve_opr_string_dump(denv, address, "from"); + break; + case CMD_NOTIFY_OPT_OPTIONS: + opok = sieve_opr_stringlist_dump(denv, address, + "options"); + break; + case CMD_NOTIFY_OPT_MESSAGE: + opok = sieve_opr_string_dump(denv, address, "message"); + break; + default: + return FALSE; + } + + if (!opok) + return FALSE; + } + + /* Dump method operand */ + return sieve_opr_string_dump(denv, address, "method"); +} + +/* + * Code execution + */ + +static int +cmd_notify_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + const struct sieve_extension *this_ext = renv->oprtn->ext; + struct sieve_side_effects_list *slist = NULL; + struct sieve_enotify_action *act; + void *method_context; + pool_t pool; + int opt_code = 0; + sieve_number_t importance = 2; + struct sieve_stringlist *options = NULL; + const struct sieve_enotify_method *method; + string_t *method_uri, *message = NULL, *from = NULL; + int ret; + + /* + * Read operands + */ + + /* Optional operands */ + + for (;;) { + int opt; + + if ((opt = sieve_opr_optional_read(renv, address, + &opt_code)) < 0) + return SIEVE_EXEC_BIN_CORRUPT; + + if (opt == 0) break; + + switch (opt_code) { + case CMD_NOTIFY_OPT_IMPORTANCE: + ret = sieve_opr_number_read(renv, address, "importance", + &importance); + break; + case CMD_NOTIFY_OPT_FROM: + ret = sieve_opr_string_read(renv, address, "from", + &from); + break; + case CMD_NOTIFY_OPT_MESSAGE: + ret = sieve_opr_string_read(renv, address, "message", + &message); + break; + case CMD_NOTIFY_OPT_OPTIONS: + ret = sieve_opr_stringlist_read(renv, address, + "options", &options); + break; + default: + sieve_runtime_trace_error( + renv, "unknown optional operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if (ret <= 0) + return ret; + } + + /* Method operand */ + + if ((ret = sieve_opr_string_read(renv, address, "method", + &method_uri)) <= 0) + return ret; + + /* + * Perform operation + */ + + /* Enforce 0 < importance < 4 (just to be sure) */ + + if (importance < 1) + importance = 1; + else if (importance > 3) + importance = 3; + + /* Trace */ + + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_ACTIONS)) { + sieve_runtime_trace(renv, 0, "notify action"); + sieve_runtime_trace_descend(renv); + sieve_runtime_trace(renv, 0, "notify with uri `%s'", + str_sanitize(str_c(method_uri), 80)); + } + + /* Check operands */ + + if ((ret = ext_enotify_runtime_check_operands(renv, method_uri, message, + from, options, &method, + &method_context)) > 0) + { + /* Add notify action to the result */ + pool = sieve_result_pool(renv->result); + act = p_new(pool, struct sieve_enotify_action, 1); + act->method = method; + act->method_context = method_context; + act->importance = importance; + if (message != NULL) + act->message = p_strdup(pool, str_c(message)); + if (from != NULL) + act->from = p_strdup(pool, str_c(from)); + + if (sieve_result_add_action(renv, this_ext, "notify", + &act_notify, slist, + (void *)act, 0, FALSE) < 0) + return SIEVE_EXEC_FAILURE; + + return SIEVE_EXEC_OK; + } + return ret; +} + +/* + * Action + */ + +/* Runtime verification */ + +static int +act_notify_check_duplicate(const struct sieve_runtime_env *renv, + const struct sieve_action *act, + const struct sieve_action *act_other) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + const struct sieve_enotify_action *nact, *nact_other; + const struct sieve_enotify_method_def *nmth_def; + struct sieve_enotify_env nenv; + int result; + + if (act->context == NULL || act_other->context == NULL) + return 0; + + nact = (const struct sieve_enotify_action *)act->context; + nact_other = (const struct sieve_enotify_action *)act_other->context; + + if (nact->method == NULL || nact->method->def == NULL) + return 0; + + nmth_def = nact->method->def; + if (nmth_def->action_check_duplicates == NULL) + return 0; + + i_zero(&nenv); + nenv.svinst = eenv->svinst; + nenv.method = nact->method; + nenv.ehandler = renv->ehandler; + nenv.location = act->location; + nenv.event = event_create(nenv.svinst->event); + event_set_append_log_prefix(nenv.event, "notify: "); + + result = nmth_def->action_check_duplicates(&nenv, nact, nact_other); + + event_unref(&nenv.event); + + return result; +} + +/* Result printing */ + +static void +act_notify_print(const struct sieve_action *action, + const struct sieve_result_print_env *rpenv, + bool *keep ATTR_UNUSED) +{ + const struct sieve_enotify_action *act = + (const struct sieve_enotify_action *)action->context; + const struct sieve_enotify_method *method; + + method = act->method; + + if (method->def != NULL) { + sieve_result_action_printf( + rpenv, "send notification with method '%s:':", + method->def->identifier); + + if (method->def->action_print != NULL) { + struct sieve_enotify_print_env penv; + + i_zero(&penv); + penv.result_penv = rpenv; + + method->def->action_print(&penv, act); + } + } +} + +/* Result execution */ + +static int +act_notify_commit(const struct sieve_action_exec_env *aenv, + void *tr_context ATTR_UNUSED) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + const struct sieve_enotify_action *act = + (const struct sieve_enotify_action *)aenv->action->context; + const struct sieve_enotify_method *method = act->method; + struct sieve_enotify_exec_env nenv; + int ret = 0; + + if (method->def != NULL && method->def->action_execute != NULL) { + /* Compose log structure */ + i_zero(&nenv); + nenv.svinst = eenv->svinst; + nenv.flags = eenv->flags; + nenv.method = method; + nenv.scriptenv = eenv->scriptenv; + nenv.msgdata = eenv->msgdata; + nenv.msgctx = aenv->msgctx; + + nenv.ehandler = aenv->ehandler; + nenv.event = aenv->event; + + ret = method->def->action_execute(&nenv, act); + if (ret >= 0) + eenv->exec_status->significant_action_executed = TRUE; + } + + return (ret >= 0 ? SIEVE_EXEC_OK : SIEVE_EXEC_TEMP_FAILURE); +} diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify-common.c b/pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify-common.c new file mode 100644 index 0000000..e0539f0 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify-common.c @@ -0,0 +1,718 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "str-sanitize.h" +#include "array.h" + +#include "sieve-common.h" +#include "sieve-ast.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-interpreter.h" +#include "sieve-result.h" + +#include "ext-enotify-limits.h" +#include "ext-enotify-common.h" + +#include <ctype.h> + +/* FIXME: (from draft RFC) + + Header/envelope tests [Sieve] together with Sieve variables can be used to + extract the list of users to receive notifications from the incoming email + message or its envelope. This is potentially quite dangerous, as this can be + used for Deny Of Service attacks on recipients controlled by the message + sender. For this reason implementations SHOULD NOT allow use of variables + containing values extracted from the email message in the method parameter to + the notify action. Note that violation of this SHOULD NOT may result in* the + creation of an open relay, i.e. any sender would be able to create specially + crafted email messages that would result in notifications delivered to + recipients under the control of the sender. In worst case this might result + in financial loss by user controlling the Sieve script and/or by recipients + of notifications (e.g. if a notification is an SMS message). + + --> This is currently not possible to check. + */ + +/* + * Notify capability + */ + +static const char * +ext_notify_get_methods_string(const struct sieve_extension *ntfy_ext); + +const struct sieve_extension_capabilities notify_capabilities = { + "notify", + ext_notify_get_methods_string +}; + +/* + * Core notification methods + */ + +extern const struct sieve_enotify_method_def mailto_notify; + +/* + * Notify method registry + */ + +static const struct sieve_enotify_method * +ext_enotify_method_register(struct sieve_instance *svinst, + struct ext_enotify_context *ectx, + const struct sieve_enotify_method_def *nmth_def) +{ + struct sieve_enotify_method *nmth; + int nmth_id = (int)array_count(&ectx->notify_methods); + + nmth = array_append_space(&ectx->notify_methods); + nmth->def = nmth_def; + nmth->id = nmth_id; + nmth->svinst = svinst; + + if (nmth_def->load != NULL) + nmth_def->load(nmth, &nmth->context); + + return nmth; +} + +void ext_enotify_methods_init(struct sieve_instance *svinst, + struct ext_enotify_context *ectx) +{ + p_array_init(&ectx->notify_methods, default_pool, 4); + + ext_enotify_method_register(svinst, ectx, &mailto_notify); +} + +void ext_enotify_methods_deinit(struct ext_enotify_context *ectx) +{ + const struct sieve_enotify_method *methods; + unsigned int meth_count, i; + + methods = array_get(&ectx->notify_methods, &meth_count); + for (i = 0; i < meth_count; i++) { + if (methods[i].def != NULL && methods[i].def->unload != NULL) + methods[i].def->unload(&methods[i]); + } + + array_free(&ectx->notify_methods); +} + +const struct sieve_enotify_method * +sieve_enotify_method_register(struct sieve_instance *svinst, + const struct sieve_enotify_method_def *nmth_def) +{ + const struct sieve_extension *ntfy_ext = + sieve_extension_get_by_name(svinst, "enotify"); + + if (ntfy_ext != NULL) { + struct ext_enotify_context *ectx = + (struct ext_enotify_context *) ntfy_ext->context; + + return ext_enotify_method_register(svinst, ectx, nmth_def); + } + return NULL; +} + +void sieve_enotify_method_unregister(const struct sieve_enotify_method *nmth) +{ + struct sieve_instance *svinst = nmth->svinst; + const struct sieve_extension *ntfy_ext = + sieve_extension_get_by_name(svinst, "enotify"); + + if (ntfy_ext != NULL) { + struct ext_enotify_context *ectx = + (struct ext_enotify_context *) ntfy_ext->context; + int nmth_id = nmth->id; + + if (nmth_id >= 0 && + nmth_id < (int)array_count(&ectx->notify_methods)) { + struct sieve_enotify_method *nmth_mod = + array_idx_modifiable(&ectx->notify_methods, + nmth_id); + + nmth_mod->def = NULL; + } + } +} + +const struct sieve_enotify_method * +ext_enotify_method_find(const struct sieve_extension *ntfy_ext, + const char *identifier) +{ + struct ext_enotify_context *ectx = + (struct ext_enotify_context *)ntfy_ext->context; + unsigned int meth_count, i; + const struct sieve_enotify_method *methods; + + methods = array_get(&ectx->notify_methods, &meth_count); + for (i = 0; i < meth_count; i++) { + if (methods[i].def == NULL) + continue; + + if (strcasecmp(methods[i].def->identifier, identifier) == 0) + return &methods[i]; + } + return NULL; +} + +static const char * +ext_notify_get_methods_string(const struct sieve_extension *ntfy_ext) +{ + struct ext_enotify_context *ectx = + (struct ext_enotify_context *) ntfy_ext->context; + unsigned int meth_count, i; + const struct sieve_enotify_method *methods; + string_t *result = t_str_new(128); + + methods = array_get(&ectx->notify_methods, &meth_count); + if (meth_count > 0) { + for (i = 0; i < meth_count; i++) { + if (str_len(result) > 0) + str_append_c(result, ' '); + if (methods[i].def != NULL) + str_append(result, methods[i].def->identifier); + } + return str_c(result); + } + return NULL; +} + +/* + * Compile-time argument validation + */ + +static const char *ext_enotify_uri_scheme_parse(const char **uri_p) +{ + string_t *scheme = t_str_new(EXT_ENOTIFY_MAX_SCHEME_LEN); + const char *p = *uri_p; + unsigned int len = 0; + + /* RFC 3968: + + scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + + FIXME: we do not allow '%' in schemes. Is this correct? + */ + + if (!i_isalpha(*p)) + return NULL; + + str_append_c(scheme, *p); + p++; + + while (*p != '\0' && len < EXT_ENOTIFY_MAX_SCHEME_LEN) { + if (!i_isalnum(*p) && *p != '+' && *p != '-' && *p != '.') + break; + + str_append_c(scheme, *p); + p++; + len++; + } + + if (*p != ':') + return NULL; + p++; + + *uri_p = p; + return str_c(scheme); +} + +static bool +ext_enotify_option_parse(struct sieve_enotify_env *nenv, + const char *option, bool name_only, + const char **opt_name_r, const char **opt_value_r) +{ + const char *p = option; + + /* "<optionname>=<value>". + + l-d = ALPHA / DIGIT + l-d-p = l-d / "." / "-" / "_" + optionname = l-d *l-d-p + value = *(%x01-09 / %x0B-0C / %x0E-FF) + */ + + /* + * Parse option name + */ + + /* optionname = l-d *l-d-p + */ + + /* Explicitly report empty option as such */ + if (*p == '\0') { + sieve_enotify_error(nenv, "empty option specified"); + return FALSE; + } + + /* l-d = ALPHA / DIGIT */ + if (i_isalnum(*p)) { + p++; + + /* l-d-p = l-d / "." / "-" / "_" */ + while (i_isalnum(*p) || *p == '.' || *p == '-' || *p == '_') + p++; + } + + /* Parsing must end at '=' and we must parse at least one character */ + if (*p != '=' || p == option) { + sieve_enotify_error( + nenv, "invalid option name specified in option '%s'", + str_sanitize(option, 80)); + return FALSE; + } + + /* Assign option name */ + if (opt_name_r != NULL) + *opt_name_r = t_strdup_until(option, p); + + /* Skip '=' */ + p++; + + /* Exit now if only the option name is of interest */ + if (name_only) + return TRUE; + + /* + * Parse option value + */ + + /* value = *(%x01-09 / %x0B-0C / %x0E-FF) */ + while (*p != '\0' && *p != 0x0A && *p != 0x0D) + p++; + + /* Parse must end at end of string */ + if (*p != '\0') { + sieve_enotify_error( + nenv, "notify command: " + "invalid option value specified in option '%s'", + str_sanitize(option, 80)); + return FALSE; + } + + /* Assign option value */ + if (opt_value_r != NULL) + *opt_value_r = p; + + return TRUE; +} + +struct _ext_enotify_option_check_context { + struct sieve_instance *svinst; + struct sieve_validator *valdtr; + const struct sieve_enotify_method *method; +}; + +static int +_ext_enotify_option_check(void *context, struct sieve_ast_argument *arg) +{ + struct _ext_enotify_option_check_context *optn_context = + (struct _ext_enotify_option_check_context *) context; + struct sieve_validator *valdtr = optn_context->valdtr; + const struct sieve_enotify_method *method = optn_context->method; + struct sieve_enotify_env nenv; + const char *option = sieve_ast_argument_strc(arg); + const char *opt_name = NULL, *opt_value = NULL; + bool check = TRUE; + int result = 1; + + /* Compose log structure */ + i_zero(&nenv); + nenv.svinst = optn_context->svinst; + nenv.method = method; + nenv.ehandler = sieve_validator_error_handler(valdtr); + nenv.location = sieve_error_script_location( + sieve_validator_script(valdtr), arg->source_line); + nenv.event = event_create(nenv.svinst->event); + event_set_append_log_prefix(nenv.event, "notify command: "); + + /* Parse option */ + if (!sieve_argument_is_string_literal(arg)) { + /* Variable string: partial option parse + + If the string item is not a string literal, it cannot be + validated fully at compile time. We can however check whether + the '=' is in the string specification and whether the part + before the '=' is a valid option name. In that case, the + method option check function is called with the value + parameter equal to NULL, meaning that it should only check + the validity of the option itself and not the assigned value. + */ + if (!ext_enotify_option_parse(NULL, option, TRUE, + &opt_name, &opt_value)) + check = FALSE; + } else { + /* Literal string: full option parse */ + if (!ext_enotify_option_parse(&nenv, option, FALSE, + &opt_name, &opt_value)) + result = -1; + } + + /* Call method's option check function */ + if (result > 0 && check && method->def != NULL && + method->def->compile_check_option != NULL) { + result = (method->def->compile_check_option(&nenv, opt_name, + opt_value) ? + 1 : -1 ); + } + + event_unref(&nenv.event); + return result; +} + +bool ext_enotify_compile_check_arguments(struct sieve_validator *valdtr, + struct sieve_command *cmd, + struct sieve_ast_argument *uri_arg, + struct sieve_ast_argument *msg_arg, + struct sieve_ast_argument *from_arg, + struct sieve_ast_argument *options_arg) +{ + const struct sieve_extension *this_ext = cmd->ext; + struct sieve_instance *svinst = this_ext->svinst; + const char *uri = sieve_ast_argument_strc(uri_arg); + const char *scheme; + const struct sieve_enotify_method *method; + struct sieve_enotify_env nenv; + bool result = TRUE; + + /* If the uri string is not a constant literal, we cannot determine + which method is used, so we bail out successfully and defer checking + to runtime. + */ + if (!sieve_argument_is_string_literal(uri_arg)) + return TRUE; + + /* Parse scheme part of URI */ + if ((scheme = ext_enotify_uri_scheme_parse(&uri)) == NULL) { + sieve_argument_validate_error( + valdtr, uri_arg, "notify command: " + "invalid scheme part for method URI '%s'", + str_sanitize(sieve_ast_argument_strc(uri_arg), 80)); + return FALSE; + } + + /* Find used method with the parsed scheme identifier */ + if ((method = ext_enotify_method_find(this_ext, scheme)) == NULL) { + sieve_argument_validate_error( + valdtr, uri_arg, "notify command: " + "invalid method '%s'", scheme); + return FALSE; + } + + if (method->def == NULL) + return TRUE; + + /* Compose log structure */ + i_zero(&nenv); + nenv.svinst = svinst; + nenv.method = method; + + /* Check URI itself */ + if (result && method->def->compile_check_uri != NULL) { + /* Set log location to location of URI argument */ + nenv.ehandler = sieve_validator_error_handler(valdtr); + nenv.location = sieve_error_script_location( + sieve_validator_script(valdtr), uri_arg->source_line); + nenv.event = event_create(nenv.svinst->event); + event_set_append_log_prefix(nenv.event, "notify command: "); + + /* Execute method check function */ + result = method->def->compile_check_uri( + &nenv, sieve_ast_argument_strc(uri_arg), uri); + } + + /* Check :message argument */ + if (result && msg_arg != NULL && + sieve_argument_is_string_literal(msg_arg) && + method->def->compile_check_message != NULL ) { + /* Set log location to location of :message argument */ + event_unref(&nenv.event); + nenv.ehandler = sieve_validator_error_handler(valdtr); + nenv.location = sieve_error_script_location( + sieve_validator_script(valdtr), msg_arg->source_line); + nenv.event = event_create(nenv.svinst->event); + event_set_append_log_prefix(nenv.event, "notify command: "); + + /* Execute method check function */ + result = method->def->compile_check_message( + &nenv, sieve_ast_argument_str(msg_arg)); + } + + /* Check :from argument */ + if (result && from_arg != NULL && + sieve_argument_is_string_literal(from_arg) && + method->def->compile_check_from != NULL ) { + /* Set log location to location of :from argument */ + event_unref(&nenv.event); + nenv.ehandler = sieve_validator_error_handler(valdtr); + nenv.location = sieve_error_script_location( + sieve_validator_script(valdtr), from_arg->source_line); + nenv.event = event_create(nenv.svinst->event); + event_set_append_log_prefix(nenv.event, "notify command: "); + + /* Execute method check function */ + result = method->def->compile_check_from( + &nenv, sieve_ast_argument_str(from_arg)); + } + + event_unref(&nenv.event); + + /* Check :options argument */ + if (result && options_arg != NULL) { + struct sieve_ast_argument *option = options_arg; + struct _ext_enotify_option_check_context optn_context = { + svinst, valdtr, method }; + + /* Parse and check options */ + result = (sieve_ast_stringlist_map( + &option, (void *) &optn_context, + _ext_enotify_option_check) > 0); + + /* Discard argument if options are not accepted by method */ + if (result && method->def->compile_check_option == NULL) { + sieve_argument_validate_warning( + valdtr, options_arg, "notify command: " + "method '%s' accepts no options", scheme); + (void)sieve_ast_arguments_detach(options_arg, 1); + } + } + return result; +} + +/* + * Runtime operand checking + */ + +bool ext_enotify_runtime_method_validate(const struct sieve_runtime_env *renv, + string_t *method_uri) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + const struct sieve_extension *this_ext = renv->oprtn->ext; + const struct sieve_enotify_method *method; + const char *uri = str_c(method_uri); + const char *scheme; + bool result = TRUE; + + /* Get the method */ + + if ((scheme = ext_enotify_uri_scheme_parse(&uri)) == NULL) + return FALSE; + if ((method = ext_enotify_method_find(this_ext, scheme)) == NULL) + return FALSE; + + /* Validate the provided URI */ + + if (method->def != NULL && method->def->runtime_check_uri != NULL) { + struct sieve_enotify_env nenv; + + i_zero(&nenv); + nenv.svinst = eenv->svinst; + nenv.method = method; + nenv.ehandler = renv->ehandler; + nenv.location = sieve_runtime_get_full_command_location(renv), + nenv.event = event_create(nenv.svinst->event); + event_set_append_log_prefix(nenv.event, + "valid_notify_method test: "); + + /* Use the method check function to validate the URI */ + result = method->def->runtime_check_uri( + &nenv, str_c(method_uri), uri); + + event_unref(&nenv.event); + } + + return result; +} + +static const struct +sieve_enotify_method *ext_enotify_get_method( + const struct sieve_runtime_env *renv, string_t *method_uri, + const char **uri_body_r) +{ + const struct sieve_extension *this_ext = renv->oprtn->ext; + const struct sieve_enotify_method *method; + const char *uri = str_c(method_uri); + const char *scheme; + + /* Parse part before ':' of the uri (the scheme) and use it to identify + notify method. + */ + if ((scheme = ext_enotify_uri_scheme_parse(&uri)) == NULL) { + sieve_runtime_error( + renv, NULL, "invalid scheme part for method URI '%s'", + str_sanitize(str_c(method_uri), 80)); + return NULL; + } + + /* Find the notify method */ + if ((method = ext_enotify_method_find(this_ext, scheme)) == NULL) { + sieve_runtime_error(renv, NULL, "invalid notify method '%s'", + scheme); + return NULL; + } + + /* Return the parse pointer and the found method */ + *uri_body_r = uri; + return method; +} + +const char * +ext_enotify_runtime_get_method_capability(const struct sieve_runtime_env *renv, + string_t *method_uri, + const char *capability) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + const struct sieve_enotify_method *method; + const char *uri_body; + const char *result = NULL; + + /* Get method */ + method = ext_enotify_get_method(renv, method_uri, &uri_body); + if ( method == NULL ) return NULL; + + /* Get requested capability */ + if (method->def != NULL && + method->def->runtime_get_method_capability != NULL) { + struct sieve_enotify_env nenv; + + i_zero(&nenv); + nenv.svinst = eenv->svinst; + nenv.method = method; + nenv.ehandler = renv->ehandler; + nenv.location = sieve_runtime_get_full_command_location(renv), + nenv.event = event_create(nenv.svinst->event); + event_set_append_log_prefix(nenv.event, + "notify_method_capability test: "); + + /* Execute method function to acquire capability value */ + result = method->def->runtime_get_method_capability( + &nenv, str_c(method_uri), uri_body, capability); + + event_unref(&nenv.event); + } + + return result; +} + +int ext_enotify_runtime_check_operands( + const struct sieve_runtime_env *renv, string_t *method_uri, + string_t *message, string_t *from, struct sieve_stringlist *options, + const struct sieve_enotify_method **method_r, void **method_context) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + const struct sieve_enotify_method *method; + const char *uri_body; + + /* Get method */ + method = ext_enotify_get_method(renv, method_uri, &uri_body); + if (method == NULL) + return SIEVE_EXEC_FAILURE; + + /* Check provided operands */ + if (method->def != NULL && + method->def->runtime_check_operands != NULL) { + struct sieve_enotify_env nenv; + int result = SIEVE_EXEC_OK; + + i_zero(&nenv); + nenv.svinst = eenv->svinst; + nenv.method = method; + nenv.ehandler = renv->ehandler; + nenv.location = sieve_runtime_get_full_command_location(renv), + nenv.event = event_create(nenv.svinst->event); + event_set_append_log_prefix(nenv.event, "notify_action: "); + + /* Execute check function */ + if (method->def->runtime_check_operands( + &nenv, str_c(method_uri), uri_body, message, from, + sieve_result_pool(renv->result), method_context)) { + + /* Check any provided options */ + if (options != NULL) { + string_t *option = NULL; + int ret; + + /* Iterate through all provided options */ + while ((ret = sieve_stringlist_next_item( + options, &option)) > 0) { + const char *opt_name = NULL; + const char *opt_value = NULL; + + /* Parse option into <optionname> and + <value> */ + if (ext_enotify_option_parse( + &nenv, str_c(option), FALSE, + &opt_name, &opt_value)) { + + /* Set option */ + if (method->def->runtime_set_option != NULL) { + (void)method->def->runtime_set_option( + &nenv, *method_context, + opt_name, opt_value); + } + } + } + + /* Check for binary corruptions encountered + during string list iteration */ + if (ret >= 0) { + *method_r = method; + } else { + /* Binary corrupt */ + sieve_runtime_trace_error( + renv, "invalid item in options string list"); + result = SIEVE_EXEC_BIN_CORRUPT; + } + + } else { + /* No options */ + *method_r = method; + } + + } else { + /* Operand check failed */ + result = SIEVE_EXEC_FAILURE; + } + + event_unref(&nenv.event); + return result; + } + + /* No check function defined: a most unlikely situation */ + *method_context = NULL; + *method_r = method; + return SIEVE_EXEC_OK; +} + +/* + * Notify method printing + */ + +void sieve_enotify_method_printf(const struct sieve_enotify_print_env *penv, + const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + sieve_result_vprintf(penv->result_penv, fmt, args); + va_end(args); +} + +/* + * Action execution + */ + +struct event_passthrough * +sieve_enotify_create_finish_event(const struct sieve_enotify_exec_env *nenv) +{ + struct event_passthrough *e = + event_create_passthrough(nenv->event)-> + set_name("sieve_action_finished"); + + return e; +} + diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify-common.h b/pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify-common.h new file mode 100644 index 0000000..ec43202 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify-common.h @@ -0,0 +1,122 @@ +#ifndef EXT_ENOTIFY_COMMON_H +#define EXT_ENOTIFY_COMMON_H + +#include "lib.h" +#include "array.h" + +#include "sieve-common.h" + +#include "sieve-ext-variables.h" + +#include "sieve-ext-enotify.h" + +/* + * Extension + */ + +extern const struct sieve_extension_def enotify_extension; +extern const struct sieve_extension_capabilities notify_capabilities; + +struct ext_enotify_context { + const struct sieve_extension *var_ext; + ARRAY(struct sieve_enotify_method) notify_methods; +}; + + +/* + * Commands + */ + +extern const struct sieve_command_def notify_command; + +/* Codes for optional arguments */ + +enum cmd_notify_optional { + CMD_NOTIFY_OPT_END, + CMD_NOTIFY_OPT_FROM, + CMD_NOTIFY_OPT_OPTIONS, + CMD_NOTIFY_OPT_MESSAGE, + CMD_NOTIFY_OPT_IMPORTANCE +}; + +/* + * Tests + */ + +extern const struct sieve_command_def valid_notify_method_test; +extern const struct sieve_command_def notify_method_capability_test; + +/* + * Operations + */ + +extern const struct sieve_operation_def notify_operation; +extern const struct sieve_operation_def valid_notify_method_operation; +extern const struct sieve_operation_def notify_method_capability_operation; + +enum ext_variables_opcode { + EXT_ENOTIFY_OPERATION_NOTIFY, + EXT_ENOTIFY_OPERATION_VALID_NOTIFY_METHOD, + EXT_ENOTIFY_OPERATION_NOTIFY_METHOD_CAPABILITY +}; + +/* + * Operands + */ + +extern const struct sieve_operand_def encodeurl_operand; + +/* + * Modifiers + */ + +extern const struct sieve_variables_modifier_def encodeurl_modifier; + +/* + * Notify methods + */ + +void ext_enotify_methods_init(struct sieve_instance *svinst, + struct ext_enotify_context *ectx); +void ext_enotify_methods_deinit(struct ext_enotify_context *ectx); + +const struct sieve_enotify_method * +ext_enotify_method_find(const struct sieve_extension *ntfy_ext, + const char *identifier); + +/* + * Validation + */ + +bool ext_enotify_compile_check_arguments( + struct sieve_validator *valdtr, struct sieve_command *cmd, + struct sieve_ast_argument *uri_arg, struct sieve_ast_argument *msg_arg, + struct sieve_ast_argument *from_arg, + struct sieve_ast_argument *options_arg); + +/* + * Runtime + */ + +bool ext_enotify_runtime_method_validate(const struct sieve_runtime_env *renv, + string_t *method_uri); + +const char * +ext_enotify_runtime_get_method_capability(const struct sieve_runtime_env *renv, + string_t *method_uri, + const char *capability); + +int ext_enotify_runtime_check_operands( + const struct sieve_runtime_env *renv, string_t *method_uri, + string_t *message, string_t *from, struct sieve_stringlist *options, + const struct sieve_enotify_method **method_r, void **method_context); + +/* + * Method printing + */ + +struct sieve_enotify_print_env { + const struct sieve_result_print_env *result_penv; +}; + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify-limits.h b/pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify-limits.h new file mode 100644 index 0000000..aac48df --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify-limits.h @@ -0,0 +1,6 @@ +#ifndef EXT_ENOTIFY_LIMITS_H +#define EXT_ENOTIFY_LIMITS_H + +#define EXT_ENOTIFY_MAX_SCHEME_LEN 32 + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify.c b/pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify.c new file mode 100644 index 0000000..df479b3 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify.c @@ -0,0 +1,103 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension enotify + * ------------------ + * + * Authors: Stephan Bosch + * Specification: RFC 5435 + * Implementation: full + * Status: testing + * + */ + +#include "sieve-common.h" + +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-actions.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-result.h" + +#include "sieve-ext-variables.h" + +#include "ext-enotify-common.h" + +/* + * Operations + */ + +const struct sieve_operation_def *ext_enotify_operations[] = { + ¬ify_operation, + &valid_notify_method_operation, + ¬ify_method_capability_operation +}; + +/* + * Extension + */ + +static bool ext_enotify_load(const struct sieve_extension *ext, void **context); +static void ext_enotify_unload(const struct sieve_extension *ext); +static bool ext_enotify_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); + +const struct sieve_extension_def enotify_extension = { + .name = "enotify", + .load = ext_enotify_load, + .unload = ext_enotify_unload, + .validator_load = ext_enotify_validator_load, + SIEVE_EXT_DEFINE_OPERATIONS(ext_enotify_operations), + SIEVE_EXT_DEFINE_OPERAND(encodeurl_operand) +}; + +static bool ext_enotify_load(const struct sieve_extension *ext, void **context) +{ + struct ext_enotify_context *ectx; + + if ( *context != NULL ) { + ext_enotify_unload(ext); + } + + ectx = i_new(struct ext_enotify_context, 1); + ectx->var_ext = sieve_ext_variables_get_extension(ext->svinst); + *context = (void *) ectx; + + ext_enotify_methods_init(ext->svinst, ectx); + + sieve_extension_capabilities_register(ext, ¬ify_capabilities); + + return TRUE; +} + +static void ext_enotify_unload(const struct sieve_extension *ext) +{ + struct ext_enotify_context *ectx = + (struct ext_enotify_context *) ext->context; + + ext_enotify_methods_deinit(ectx); + + i_free(ectx); +} + +static bool ext_enotify_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + struct ext_enotify_context *ectx = + (struct ext_enotify_context *) ext->context; + + /* Register new commands */ + sieve_validator_register_command(valdtr, ext, ¬ify_command); + sieve_validator_register_command(valdtr, ext, &valid_notify_method_test); + sieve_validator_register_command(valdtr, ext, ¬ify_method_capability_test); + + /* Register new set modifier for variables extension */ + sieve_variables_modifier_register + (ectx->var_ext, valdtr, ext, &encodeurl_modifier); + + return TRUE; +} + diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/mailto/Makefile.am b/pigeonhole/src/lib-sieve/plugins/enotify/mailto/Makefile.am new file mode 100644 index 0000000..83f129c --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/enotify/mailto/Makefile.am @@ -0,0 +1,16 @@ +noinst_LTLIBRARIES = libsieve_ext_enotify_mailto.la + +AM_CPPFLAGS = \ + -I$(srcdir)/.. \ + -I$(srcdir)/../../.. \ + -I$(srcdir)/../../../util \ + $(LIBDOVECOT_INCLUDE) + +libsieve_ext_enotify_mailto_la_SOURCES = \ + uri-mailto.c \ + ntfy-mailto.c + +noinst_HEADERS = \ + uri-mailto.h + + diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/mailto/Makefile.in b/pigeonhole/src/lib-sieve/plugins/enotify/mailto/Makefile.in new file mode 100644 index 0000000..49aa41a --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/enotify/mailto/Makefile.in @@ -0,0 +1,687 @@ +# 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/plugins/enotify/mailto +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_ext_enotify_mailto_la_LIBADD = +am_libsieve_ext_enotify_mailto_la_OBJECTS = uri-mailto.lo \ + ntfy-mailto.lo +libsieve_ext_enotify_mailto_la_OBJECTS = \ + $(am_libsieve_ext_enotify_mailto_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)/ntfy-mailto.Plo \ + ./$(DEPDIR)/uri-mailto.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_ext_enotify_mailto_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_enotify_mailto_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_ext_enotify_mailto.la +AM_CPPFLAGS = \ + -I$(srcdir)/.. \ + -I$(srcdir)/../../.. \ + -I$(srcdir)/../../../util \ + $(LIBDOVECOT_INCLUDE) + +libsieve_ext_enotify_mailto_la_SOURCES = \ + uri-mailto.c \ + ntfy-mailto.c + +noinst_HEADERS = \ + uri-mailto.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/plugins/enotify/mailto/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/enotify/mailto/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_ext_enotify_mailto.la: $(libsieve_ext_enotify_mailto_la_OBJECTS) $(libsieve_ext_enotify_mailto_la_DEPENDENCIES) $(EXTRA_libsieve_ext_enotify_mailto_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_enotify_mailto_la_OBJECTS) $(libsieve_ext_enotify_mailto_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfy-mailto.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/uri-mailto.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)/ntfy-mailto.Plo + -rm -f ./$(DEPDIR)/uri-mailto.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)/ntfy-mailto.Plo + -rm -f ./$(DEPDIR)/uri-mailto.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/plugins/enotify/mailto/ntfy-mailto.c b/pigeonhole/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c new file mode 100644 index 0000000..4e104d1 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c @@ -0,0 +1,794 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Notify method mailto + * -------------------- + * + * Authors: Stephan Bosch + * Specification: RFC 5436 + * Implementation: full + * Status: testing + * + */ + +/* FIXME: URI syntax conforms to something somewhere in between RFC 2368 and + * draft-duerst-mailto-bis-05.txt. Should fully migrate to new specification + * when it matures. This requires modifications to the address parser (no + * whitespace allowed within the address itself) and UTF-8 support will be + * required in the URL. + */ + +#include "lib.h" +#include "array.h" +#include "str.h" +#include "ioloop.h" +#include "str-sanitize.h" +#include "ostream.h" +#include "message-date.h" +#include "mail-storage.h" + +#include "sieve-common.h" +#include "sieve-address.h" +#include "sieve-address-source.h" +#include "sieve-message.h" +#include "sieve-smtp.h" +#include "sieve-settings.h" + +#include "sieve-ext-enotify.h" + +#include "rfc2822.h" + +#include "uri-mailto.h" + +/* + * Configuration + */ + +#define NTFY_MAILTO_MAX_RECIPIENTS 8 +#define NTFY_MAILTO_MAX_HEADERS 16 +#define NTFY_MAILTO_MAX_SUBJECT 256 + +/* + * Mailto notification configuration + */ + +struct ntfy_mailto_config { + pool_t pool; + struct sieve_address_source envelope_from; +}; + +/* + * Mailto notification method + */ + +static bool ntfy_mailto_load + (const struct sieve_enotify_method *nmth, void **context); +static void ntfy_mailto_unload + (const struct sieve_enotify_method *nmth); + +static bool ntfy_mailto_compile_check_uri + (const struct sieve_enotify_env *nenv, const char *uri, const char *uri_body); +static bool ntfy_mailto_compile_check_from + (const struct sieve_enotify_env *nenv, string_t *from); + +static const char *ntfy_mailto_runtime_get_notify_capability + (const struct sieve_enotify_env *nenv, const char *uri, const char *uri_body, + const char *capability); +static bool ntfy_mailto_runtime_check_uri + (const struct sieve_enotify_env *nenv, const char *uri, const char *uri_body); +static bool ntfy_mailto_runtime_check_operands + (const struct sieve_enotify_env *nenv, const char *uri,const char *uri_body, + string_t *message, string_t *from, pool_t context_pool, + void **method_context); + +static int ntfy_mailto_action_check_duplicates + (const struct sieve_enotify_env *nenv, + const struct sieve_enotify_action *nact, + const struct sieve_enotify_action *nact_other); + +static void ntfy_mailto_action_print + (const struct sieve_enotify_print_env *penv, + const struct sieve_enotify_action *nact); + +static int ntfy_mailto_action_execute + (const struct sieve_enotify_exec_env *nenv, + const struct sieve_enotify_action *nact); + +const struct sieve_enotify_method_def mailto_notify = { + "mailto", + ntfy_mailto_load, + ntfy_mailto_unload, + ntfy_mailto_compile_check_uri, + NULL, + ntfy_mailto_compile_check_from, + NULL, + ntfy_mailto_runtime_check_uri, + ntfy_mailto_runtime_get_notify_capability, + ntfy_mailto_runtime_check_operands, + NULL, + ntfy_mailto_action_check_duplicates, + ntfy_mailto_action_print, + ntfy_mailto_action_execute +}; + +/* + * Reserved and unique headers + */ + +static const char *_reserved_headers[] = { + "auto-submitted", + "received", + "message-id", + "data", + "bcc", + "in-reply-to", + "references", + "resent-date", + "resent-from", + "resent-sender", + "resent-to", + "resent-cc", + "resent-bcc", + "resent-msg-id", + "from", + "sender", + NULL +}; + +static const char *_unique_headers[] = { + "reply-to", + NULL +}; + +/* + * Method context data + */ + +struct ntfy_mailto_context { + struct uri_mailto *uri; + const struct smtp_address *from_address; +}; + +/* + * Method registration + */ + +static bool ntfy_mailto_load +(const struct sieve_enotify_method *nmth, void **context) +{ + struct sieve_instance *svinst = nmth->svinst; + struct ntfy_mailto_config *config; + pool_t pool; + + if ( *context != NULL ) { + ntfy_mailto_unload(nmth); + } + + pool = pool_alloconly_create("ntfy_mailto_config", 256); + config = p_new(pool, struct ntfy_mailto_config, 1); + config->pool = pool; + + (void)sieve_address_source_parse_from_setting (svinst, + config->pool, "sieve_notify_mailto_envelope_from", + &config->envelope_from); + + *context = (void *) config; + + return TRUE; +} + +static void ntfy_mailto_unload +(const struct sieve_enotify_method *nmth) +{ + struct ntfy_mailto_config *config = + (struct ntfy_mailto_config *) nmth->context; + + pool_unref(&config->pool); +} + +/* + * URI parsing + */ + +struct ntfy_mailto_uri_env { + const struct sieve_enotify_env *nenv; + + struct event *event; + + struct uri_mailto_log uri_log; +}; + +static void ATTR_FORMAT(5, 0) +ntfy_mailto_uri_logv(void *context, enum log_type log_type, + const char *csrc_filename, unsigned int csrc_linenum, + const char *fmt, va_list args) +{ + struct ntfy_mailto_uri_env *nmuenv = context; + const struct sieve_enotify_env *nenv = nmuenv->nenv; + + sieve_event_logv(nenv->svinst, nenv->ehandler, nmuenv->event, + log_type, csrc_filename, csrc_linenum, + nenv->location, 0, fmt, args); +} + +static void +ntfy_mailto_uri_env_init(struct ntfy_mailto_uri_env *nmuenv, + const struct sieve_enotify_env *nenv) +{ + i_zero(nmuenv); + nmuenv->nenv = nenv; + nmuenv->event = event_create(nenv->event); + event_set_append_log_prefix(nmuenv->event, "mailto URI: "); + + nmuenv->uri_log.context = nmuenv; + nmuenv->uri_log.logv = ntfy_mailto_uri_logv; +} + +static void +ntfy_mailto_uri_env_deinit(struct ntfy_mailto_uri_env *nmuenv) +{ + event_unref(&nmuenv->event); +} + +/* + * Validation + */ + + +static bool ntfy_mailto_compile_check_uri +(const struct sieve_enotify_env *nenv, const char *uri ATTR_UNUSED, + const char *uri_body) +{ + struct ntfy_mailto_uri_env nmuenv; + bool result; + + ntfy_mailto_uri_env_init(&nmuenv, nenv); + result = uri_mailto_validate( + uri_body, _reserved_headers, _unique_headers, + NTFY_MAILTO_MAX_RECIPIENTS, NTFY_MAILTO_MAX_HEADERS, + &nmuenv.uri_log); + ntfy_mailto_uri_env_deinit(&nmuenv); + + return result; +} + +static bool ntfy_mailto_compile_check_from +(const struct sieve_enotify_env *nenv, string_t *from) +{ + const char *error; + bool result = FALSE; + + T_BEGIN { + result = sieve_address_validate_str(from, &error); + if ( !result ) { + sieve_enotify_error(nenv, + "specified :from address '%s' is invalid for " + "the mailto method: %s", + str_sanitize(str_c(from), 128), error); + } + } T_END; + + return result; +} + +/* + * Runtime + */ + +struct ntfy_mailto_runtime_env { + const struct sieve_enotify_env *nenv; + + struct event *event; +}; + +static const char *ntfy_mailto_runtime_get_notify_capability +(const struct sieve_enotify_env *nenv ATTR_UNUSED, const char *uri ATTR_UNUSED, + const char *uri_body, const char *capability) +{ + if ( !uri_mailto_validate(uri_body, _reserved_headers, _unique_headers, + NTFY_MAILTO_MAX_RECIPIENTS, NTFY_MAILTO_MAX_HEADERS, NULL) ) { + return NULL; + } + + if ( strcasecmp(capability, "online") == 0 ) + return "maybe"; + + return NULL; +} + +static bool ntfy_mailto_runtime_check_uri +(const struct sieve_enotify_env *nenv ATTR_UNUSED, const char *uri ATTR_UNUSED, + const char *uri_body) +{ + return uri_mailto_validate + (uri_body, _reserved_headers, _unique_headers, + NTFY_MAILTO_MAX_RECIPIENTS, NTFY_MAILTO_MAX_HEADERS, NULL); +} + +static bool ntfy_mailto_runtime_check_operands +(const struct sieve_enotify_env *nenv, const char *uri ATTR_UNUSED, + const char *uri_body, string_t *message ATTR_UNUSED, string_t *from, + pool_t context_pool, void **method_context) +{ + struct ntfy_mailto_context *mtctx; + struct uri_mailto *parsed_uri; + const struct smtp_address *address; + struct ntfy_mailto_uri_env nmuenv; + const char *error; + + /* Need to create context before validation to have arrays present */ + mtctx = p_new(context_pool, struct ntfy_mailto_context, 1); + + /* Validate :from */ + if ( from != NULL ) { + T_BEGIN { + address = sieve_address_parse_str(from, &error); + if ( address == NULL ) { + sieve_enotify_error(nenv, + "specified :from address '%s' is invalid for " + "the mailto method: %s", + str_sanitize(str_c(from), 128), error); + } else + mtctx->from_address = + smtp_address_clone(context_pool, address); + } T_END; + + if ( address == NULL ) return FALSE; + } + + ntfy_mailto_uri_env_init(&nmuenv, nenv); + parsed_uri = uri_mailto_parse(uri_body, context_pool, + _reserved_headers, _unique_headers, + NTFY_MAILTO_MAX_RECIPIENTS, + NTFY_MAILTO_MAX_HEADERS, + &nmuenv.uri_log); + ntfy_mailto_uri_env_deinit(&nmuenv); + + if (parsed_uri == NULL) + return FALSE; + + mtctx->uri = parsed_uri; + *method_context = (void *) mtctx; + return TRUE; +} + +/* + * Action duplicates + */ + +static int ntfy_mailto_action_check_duplicates +(const struct sieve_enotify_env *nenv ATTR_UNUSED, + const struct sieve_enotify_action *nact, + const struct sieve_enotify_action *nact_other) +{ + struct ntfy_mailto_context *mtctx = + (struct ntfy_mailto_context *) nact->method_context; + struct ntfy_mailto_context *mtctx_other = + (struct ntfy_mailto_context *) nact_other->method_context; + const struct uri_mailto_recipient *new_rcpts, *old_rcpts; + unsigned int new_count, old_count, i, j; + unsigned int del_start = 0, del_len = 0; + + new_rcpts = array_get(&mtctx->uri->recipients, &new_count); + old_rcpts = array_get(&mtctx_other->uri->recipients, &old_count); + + for ( i = 0; i < new_count; i++ ) { + for ( j = 0; j < old_count; j++ ) { + if ( smtp_address_equals + (new_rcpts[i].address, old_rcpts[j].address) ) + break; + } + + if ( j == old_count ) { + /* Not duplicate */ + if ( del_len > 0 ) { + /* Perform pending deletion */ + array_delete(&mtctx->uri->recipients, del_start, del_len); + + /* Make sure the loop integrity is maintained */ + i -= del_len; + new_rcpts = array_get(&mtctx->uri->recipients, &new_count); + } + del_len = 0; + } else { + /* Mark deletion */ + if ( del_len == 0 ) + del_start = i; + del_len++; + } + } + + /* Perform pending deletion */ + if ( del_len > 0 ) { + array_delete(&mtctx->uri->recipients, del_start, del_len); + } + + return ( array_count(&mtctx->uri->recipients) > 0 ? 0 : 1 ); +} + +/* + * Action printing + */ + +static void ntfy_mailto_action_print +(const struct sieve_enotify_print_env *penv, + const struct sieve_enotify_action *nact) +{ + unsigned int count, i; + const struct uri_mailto_recipient *recipients; + const struct uri_mailto_header_field *headers; + struct ntfy_mailto_context *mtctx = + (struct ntfy_mailto_context *) nact->method_context; + + /* Print main method parameters */ + + sieve_enotify_method_printf + (penv, " => importance : %llu\n", + (unsigned long long)nact->importance); + + if ( nact->message != NULL ) + sieve_enotify_method_printf + (penv, " => subject : %s\n", nact->message); + else if ( mtctx->uri->subject != NULL ) + sieve_enotify_method_printf + (penv, " => subject : %s\n", mtctx->uri->subject); + + if ( nact->from != NULL ) + sieve_enotify_method_printf + (penv, " => from : %s\n", nact->from); + + /* Print mailto: recipients */ + + sieve_enotify_method_printf(penv, " => recipients :\n" ); + + recipients = array_get(&mtctx->uri->recipients, &count); + if ( count == 0 ) { + sieve_enotify_method_printf(penv, " NONE, action has no effect\n"); + } else { + for ( i = 0; i < count; i++ ) { + if ( recipients[i].carbon_copy ) + sieve_enotify_method_printf + (penv, " + Cc: %s\n", recipients[i].full); + else + sieve_enotify_method_printf + (penv, " + To: %s\n", recipients[i].full); + } + } + + /* Print accepted headers for notification message */ + + headers = array_get(&mtctx->uri->headers, &count); + if ( count > 0 ) { + sieve_enotify_method_printf(penv, " => headers :\n" ); + for ( i = 0; i < count; i++ ) { + sieve_enotify_method_printf(penv, " + %s: %s\n", + headers[i].name, headers[i].body); + } + } + + /* Print body for notification message */ + + if ( mtctx->uri->body != NULL ) + sieve_enotify_method_printf + (penv, " => body : \n--\n%s\n--\n", mtctx->uri->body); + + /* Finish output with an empty line */ + + sieve_enotify_method_printf(penv, "\n"); +} + +/* + * Action execution + */ + +static bool _contains_8bit(const char *msg) +{ + const unsigned char *s = (const unsigned char *)msg; + + for (; *s != '\0'; s++) { + if ((*s & 0x80) != 0) + return TRUE; + } + + return FALSE; +} + +static int ntfy_mailto_send +(const struct sieve_enotify_exec_env *nenv, + const struct sieve_enotify_action *nact, + const struct smtp_address *owner_email) +{ + struct sieve_instance *svinst = nenv->svinst; + const struct sieve_message_data *msgdata = nenv->msgdata; + const struct sieve_script_env *senv = nenv->scriptenv; + struct ntfy_mailto_context *mtctx = + (struct ntfy_mailto_context *) nact->method_context; + struct ntfy_mailto_config *mth_config = + (struct ntfy_mailto_config *)nenv->method->context; + struct sieve_address_source env_from = + mth_config->envelope_from; + const char *from = NULL; + const struct smtp_address *from_smtp = NULL; + const char *subject = mtctx->uri->subject; + const char *body = mtctx->uri->body; + string_t *to, *cc, *all; + const struct uri_mailto_recipient *recipients; + const struct uri_mailto_header_field *headers; + struct sieve_smtp_context *sctx; + struct ostream *output; + string_t *msg; + unsigned int count, i, hcount, h; + const char *outmsgid, *error; + int ret; + + /* Get recipients */ + recipients = array_get(&mtctx->uri->recipients, &count); + if ( count == 0 ) { + sieve_enotify_warning(nenv, + "notify mailto uri specifies no recipients; action has no effect"); + return 0; + } + + /* Just to be sure */ + if ( !sieve_smtp_available(senv) ) { + sieve_enotify_global_warning(nenv, + "notify mailto method has no means to send mail"); + return 0; + } + + /* Determine which sender to use + + From RFC 5436, Section 2.3: + + The ":from" tag overrides the default sender of the notification + message. "Sender", here, refers to the value used in the [RFC5322] + "From" header. Implementations MAY also use this value in the + [RFC5321] "MAIL FROM" command (the "envelope sender"), or they may + prefer to establish a mailbox that receives bounces from notification + messages. + */ + if ( (nenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0 ) { + from_smtp = sieve_message_get_sender(nenv->msgctx); + if ( from_smtp == NULL ) { + /* "<>" */ + i_zero(&env_from); + env_from.type = SIEVE_ADDRESS_SOURCE_EXPLICIT; + } + } + from = nact->from; + if ( (ret=sieve_address_source_get_address(&env_from, svinst, + senv, nenv->msgctx, nenv->flags, &from_smtp)) < 0 ) { + from_smtp = NULL; + } else if ( ret == 0 ) { + if ( mtctx->from_address != NULL ) + from_smtp = mtctx->from_address; + else if ( svinst->user_email != NULL ) + from_smtp = svinst->user_email; + else { + from_smtp = sieve_get_postmaster_smtp(senv); + if (from == NULL) + from = sieve_get_postmaster_address(senv); + } + } + + /* Determine message from address */ + if ( from == NULL ) { + if ( from_smtp == NULL ) + from = sieve_get_postmaster_address(senv); + else { + from = t_strdup_printf("<%s>", + smtp_address_encode(from_smtp)); + } + } + + /* Determine subject */ + if ( nact->message != NULL ) { + /* FIXME: handle UTF-8 */ + subject = str_sanitize(nact->message, NTFY_MAILTO_MAX_SUBJECT); + } else if ( subject == NULL ) { + const char *const *hsubject; + + /* Fetch subject from original message */ + if ( mail_get_headers_utf8 + (msgdata->mail, "subject", &hsubject) > 0 ) + subject = str_sanitize(t_strdup_printf("Notification: %s", hsubject[0]), + NTFY_MAILTO_MAX_SUBJECT); + else + subject = "Notification: (no subject)"; + } + + /* Compose To and Cc headers */ + to = NULL; + cc = NULL; + all = t_str_new(256); + for ( i = 0; i < count; i++ ) { + if ( recipients[i].carbon_copy ) { + if ( cc == NULL ) { + cc = t_str_new(256); + str_append(cc, recipients[i].full); + } else { + str_append(cc, ", "); + str_append(cc, recipients[i].full); + } + } else { + if ( to == NULL ) { + to = t_str_new(256); + str_append(to, recipients[i].full); + } else { + str_append(to, ", "); + str_append(to, recipients[i].full); + } + } + if ( i < 3) { + if ( i > 0 ) + str_append(all, ", "); + str_append(all, + smtp_address_encode_path(recipients[i].address)); + } else if (i == 3) { + str_printfa(all, ", ... (%u total)", count); + } + } + + msg = t_str_new(512); + outmsgid = sieve_message_get_new_id(svinst); + + rfc2822_header_write(msg, "X-Sieve", SIEVE_IMPLEMENTATION); + rfc2822_header_write(msg, "Message-ID", outmsgid); + rfc2822_header_write(msg, "Date", message_date_create(ioloop_time)); + rfc2822_header_utf8_printf(msg, "Subject", "%s", subject); + + rfc2822_header_write_address(msg, "From", from); + + if ( to != NULL ) + rfc2822_header_write_address(msg, "To", str_c(to)); + if ( cc != NULL ) + rfc2822_header_write_address(msg, "Cc", str_c(cc)); + + rfc2822_header_printf(msg, "Auto-Submitted", + "auto-notified; owner-email=\"%s\"", + smtp_address_encode(owner_email)); + rfc2822_header_write(msg, "Precedence", "bulk"); + + /* Set importance */ + switch ( nact->importance ) { + case 1: + rfc2822_header_write(msg, "X-Priority", "1 (Highest)"); + rfc2822_header_write(msg, "Importance", "High"); + break; + case 3: + rfc2822_header_write(msg, "X-Priority", "5 (Lowest)"); + rfc2822_header_write(msg, "Importance", "Low"); + break; + case 2: + default: + rfc2822_header_write(msg, "X-Priority", "3 (Normal)"); + rfc2822_header_write(msg, "Importance", "Normal"); + break; + } + + /* Add custom headers */ + + headers = array_get(&mtctx->uri->headers, &hcount); + for ( h = 0; h < hcount; h++ ) { + const char *name = rfc2822_header_field_name_sanitize(headers[h].name); + + rfc2822_header_write(msg, name, headers[h].body); + } + + /* Generate message body */ + + rfc2822_header_write(msg, "MIME-Version", "1.0"); + if ( body != NULL ) { + if (_contains_8bit(body)) { + rfc2822_header_write + (msg, "Content-Type", "text/plain; charset=utf-8"); + rfc2822_header_write(msg, "Content-Transfer-Encoding", "8bit"); + } else { + rfc2822_header_write + (msg, "Content-Type", "text/plain; charset=us-ascii"); + rfc2822_header_write(msg, "Content-Transfer-Encoding", "7bit"); + } + str_printfa(msg, "\r\n%s\r\n", body); + + } else { + rfc2822_header_write + (msg, "Content-Type", "text/plain; charset=US-ASCII"); + rfc2822_header_write(msg, "Content-Transfer-Encoding", "7bit"); + + str_append(msg, "\r\nNotification of new message.\r\n"); + } + + sctx = sieve_smtp_start(senv, from_smtp); + + /* Send message to all recipients */ + for ( i = 0; i < count; i++ ) + sieve_smtp_add_rcpt(sctx, recipients[i].address); + + output = sieve_smtp_send(sctx); + o_stream_nsend(output, str_data(msg), str_len(msg)); + + if ( (ret=sieve_smtp_finish(sctx, &error)) <= 0 ) { + if (ret < 0) { + sieve_enotify_global_error(nenv, + "failed to send mail notification to %s: %s (temporary failure)", + str_c(all), str_sanitize(error, 512)); + } else { + sieve_enotify_global_log_error(nenv, + "failed to send mail notification to %s: %s (permanent failure)", + str_c(all), str_sanitize(error, 512)); + } + } else { + struct event_passthrough *e = + sieve_enotify_create_finish_event(nenv)-> + add_str("notify_target", str_c(all)); + + sieve_enotify_event_log(nenv, e->event(), + "sent mail notification to %s", + str_c(all)); + } + + return 0; +} + +static int ntfy_mailto_action_execute +(const struct sieve_enotify_exec_env *nenv, + const struct sieve_enotify_action *nact) +{ + struct sieve_instance *svinst = nenv->svinst; + const struct sieve_script_env *senv = nenv->scriptenv; + struct mail *mail = nenv->msgdata->mail; + const struct smtp_address *owner_email; + const char *const *hdsp; + int ret; + + owner_email = svinst->user_email; + if ( owner_email == NULL && + (nenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0 ) + owner_email = sieve_message_get_final_recipient(nenv->msgctx); + if ( owner_email == NULL ) { + owner_email = sieve_get_postmaster_smtp(senv); + } + i_assert( owner_email != NULL ); + + /* Is the message an automatic reply ? */ + if ( (ret=mail_get_headers(mail, "auto-submitted", &hdsp)) < 0 ) { + sieve_enotify_critical(nenv, + "mailto notification: " + "failed to read `auto-submitted' header field", + "mailto notification: " + "failed to read `auto-submitted' header field: %s", + mailbox_get_last_internal_error(mail->box, NULL)); + return -1; + } + + /* Theoretically multiple headers could exist, so lets make sure */ + if ( ret > 0 ) { + while ( *hdsp != NULL ) { + if ( strcasecmp(*hdsp, "no") != 0 ) { + const struct smtp_address *sender = NULL; + const char *from; + + if ( (nenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0 ) + sender = sieve_message_get_sender(nenv->msgctx); + from = (sender == NULL ? "" : t_strdup_printf + (" from <%s>", smtp_address_encode(sender))); + + sieve_enotify_global_info(nenv, + "not sending notification " + "for auto-submitted message%s", from); + return 0; + } + hdsp++; + } + } + + T_BEGIN { + ret = ntfy_mailto_send(nenv, nact, owner_email); + } T_END; + + return ret; +} + + + + diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/mailto/uri-mailto.c b/pigeonhole/src/lib-sieve/plugins/enotify/mailto/uri-mailto.c new file mode 100644 index 0000000..c5a0953 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/enotify/mailto/uri-mailto.c @@ -0,0 +1,683 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* FIXME: URI syntax conforms to something somewhere in between RFC 2368 and + draft-duerst-mailto-bis-05.txt. Should fully migrate to new + specification when it matures. This requires modifications to the + address parser (no whitespace allowed within the address itself) and + UTF-8 support will be required in the URL. + */ + +#include "lib.h" +#include "array.h" +#include "str.h" +#include "str-sanitize.h" + +#include "rfc2822.h" + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-address.h" +#include "sieve-message.h" + +#include "uri-mailto.h" + +/* Parser object */ + +struct uri_mailto_parser { + pool_t pool; + const struct uri_mailto_log *log; + + struct uri_mailto *uri; + + const char **reserved_headers; + const char **unique_headers; + + int max_recipients; + int max_headers; +}; + +/* Error handling */ + +#define uri_mailto_error(PARSER, ...) \ + uri_mailto_log((PARSER), LOG_TYPE_ERROR, \ + __FILE__, __LINE__, __VA_ARGS__) +#define uri_mailto_warning(PARSER, ...) \ + uri_mailto_log((PARSER), LOG_TYPE_WARNING, \ + __FILE__, __LINE__, __VA_ARGS__) + +static inline void ATTR_FORMAT(5, 6) +uri_mailto_log(struct uri_mailto_parser *parser, enum log_type log_type, + const char *csrc_filename, unsigned int csrc_linenum, + const char *fmt, ...) +{ + va_list args; + + if (parser->log == NULL || parser->log->logv == NULL) + return; + + va_start(args, fmt); + parser->log->logv(parser->log->context, log_type, + csrc_filename, csrc_linenum, fmt, args); + va_end(args); +} + + +/* + * Error handling + */ + +/* + * Reserved and unique headers + */ + +static inline bool +uri_mailto_header_is_reserved(struct uri_mailto_parser *parser, + const char *field_name) +{ + const char **hdr = parser->reserved_headers; + + if (hdr == NULL) + return FALSE; + + /* Check whether it is reserved */ + while (*hdr != NULL) { + if (strcasecmp(field_name, *hdr) == 0) + return TRUE; + hdr++; + } + return FALSE; +} + +static inline bool +uri_mailto_header_is_unique(struct uri_mailto_parser *parser, + const char *field_name) +{ + const char **hdr = parser->unique_headers; + + if (hdr == NULL) + return FALSE; + + /* Check whether it is supposed to be unique */ + while (*hdr != NULL) { + if (strcasecmp(field_name, *hdr) == 0) + return TRUE; + hdr++; + } + return FALSE; +} + +/* + * Low-level URI parsing. + * + * FIXME: much of this implementation will be common to other URI schemes. This + * should be merged into a common implementation. + */ + +static const char _qchar_lookup[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10 + 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 20 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, // 30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 50 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, // 70 + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 90 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // F0 +}; + +static inline bool _is_qchar(char c) +{ + return ((_qchar_lookup[(unsigned char)c] & 0x01) != 0); +} + +static inline int _decode_hex_digit(unsigned char digit) +{ + switch (digit) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return (int) digit - '0'; + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + return (int) digit - 'a' + 0x0a; + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + return (int) digit - 'A' + 0x0A; + } + return -1; +} + +static bool _parse_hex_value(const char **in, char *out) +{ + int value, digit; + + if ((digit = _decode_hex_digit((unsigned char)**in)) < 0) + return FALSE; + + value = digit << 4; + (*in)++; + + if ((digit = _decode_hex_digit((unsigned char)**in)) < 0) + return FALSE; + + value |= digit; + (*in)++; + + if (value == 0) + return FALSE; + + *out = (char)value; + return TRUE; +} + +/* + * URI recipient parsing + */ + +static bool +uri_mailto_add_valid_recipient(struct uri_mailto_parser *parser, + string_t *recipient, bool cc) +{ + struct uri_mailto *uri = parser->uri; + struct uri_mailto_recipient *new_recipient; + struct uri_mailto_recipient *rcpts; + unsigned int count, i; + const char *error; + const struct smtp_address *address; + + /* Verify recipient */ + if ((address = sieve_address_parse_str(recipient, &error)) == NULL) { + uri_mailto_error(parser, "invalid recipient '%s': %s", + str_sanitize(str_c(recipient), 80), error); + return FALSE; + } + + /* Add recipient to the uri */ + if (uri != NULL) { + /* Get current recipients */ + rcpts = array_get_modifiable(&uri->recipients, &count); + + /* Enforce limits */ + if (parser->max_recipients > 0 && + (int)count >= parser->max_recipients) { + if ((int)count == parser->max_recipients) { + uri_mailto_warning( + parser, + "more than the maximum %u recipients specified; " + "rest is discarded", + parser->max_recipients); + } + return TRUE; + } + + /* Check for duplicate first */ + for (i = 0; i < count; i++) { + if (smtp_address_equals(rcpts[i].address, address)) { + /* Upgrade existing Cc: recipient to a To: + recipient if possible */ + rcpts[i].carbon_copy = + (rcpts[i].carbon_copy && cc); + + uri_mailto_warning( + parser, + "ignored duplicate recipient '%s'", + str_sanitize(str_c(recipient), 80)); + return TRUE; + } + } + + /* Add */ + new_recipient = array_append_space(&uri->recipients); + new_recipient->carbon_copy = cc; + new_recipient->full = p_strdup(parser->pool, str_c(recipient)); + new_recipient->address = + smtp_address_clone(parser->pool, address); + } + + return TRUE; +} + +static bool +uri_mailto_parse_recipients(struct uri_mailto_parser *parser, + const char **uri_p) +{ + string_t *to = t_str_new(128); + const char *p = *uri_p; + + if (*p == '\0' || *p == '?') + return TRUE; + + while (*p != '\0' && *p != '?') { + if (*p == '%') { + /* % encoded character */ + char ch; + + p++; + + /* Parse 2-digit hex value */ + if (!_parse_hex_value(&p, &ch)) { + uri_mailto_error(parser, "invalid %% encoding"); + return FALSE; + } + + /* Check for delimiter */ + if (ch == ',') { + /* Verify and add recipient */ + if (!uri_mailto_add_valid_recipient( + parser, to, FALSE)) + return FALSE; + + /* Reset for next recipient */ + str_truncate(to, 0); + } else { + /* Content character */ + str_append_c(to, ch); + } + } else { + if (*p == ':' || *p == ';' || *p == ',' || + !_is_qchar(*p)) { + uri_mailto_error( + parser, + "invalid character '%c' in 'to' part", *p); + return FALSE; + } + + /* Content character */ + str_append_c(to, *p); + p++; + } + } + + i_assert(*p == '\0' || *p == '?'); + + /* Verify and add recipient */ + if (!uri_mailto_add_valid_recipient(parser, to, FALSE)) + return FALSE; + + *uri_p = p; + return TRUE; +} + +static bool +uri_mailto_parse_header_recipients(struct uri_mailto_parser *parser, + string_t *rcpt_header, bool cc) +{ + string_t *to = t_str_new(128); + const char *p = (const char *)str_data(rcpt_header); + const char *pend = p + str_len(rcpt_header); + + while (p < pend) { + if (*p == ',') { + /* Verify and add recipient */ + if (!uri_mailto_add_valid_recipient(parser, to, cc)) + return FALSE; + + /* Reset for next recipient */ + str_truncate(to, 0); + } else { + /* Content character */ + str_append_c(to, *p); + } + p++; + } + + /* Verify and add recipient */ + if (!uri_mailto_add_valid_recipient(parser, to, cc)) + return FALSE; + + return TRUE; +} + +/* URI header parsing */ + +static bool +uri_mailto_header_is_duplicate(struct uri_mailto_parser *parser, + const char *field_name) +{ + struct uri_mailto *uri = parser->uri; + + if (uri == NULL) + return FALSE; + + if (uri_mailto_header_is_unique(parser, field_name)) { + const struct uri_mailto_header_field *hdrs; + unsigned int count, i; + + hdrs = array_get(&uri->headers, &count); + for (i = 0; i < count; i++) { + if (strcasecmp(hdrs[i].name, field_name) == 0) + return TRUE; + } + } + return FALSE; +} + +static bool +uri_mailto_parse_headers(struct uri_mailto_parser *parser, const char **uri_p) +{ + struct uri_mailto *uri = parser->uri; + unsigned int header_count = 0; + string_t *field = t_str_new(128); + const char *p = *uri_p; + + while (*p != '\0') { + enum { + _HNAME_IGNORED, + _HNAME_GENERIC, + _HNAME_TO, + _HNAME_CC, + _HNAME_SUBJECT, + _HNAME_BODY, + } hname_type = _HNAME_GENERIC; + struct uri_mailto_header_field *hdrf = NULL; + const char *field_name; + + /* Parse field name */ + while (*p != '\0' && *p != '=') { + char ch = *p; + p++; + + if (ch == '%') { + /* Encoded, parse 2-digit hex value */ + if (!_parse_hex_value(&p, &ch)) { + uri_mailto_error(parser, + "invalid %% encoding"); + return FALSE; + } + } else if (ch != '=' && !_is_qchar(ch)) { + uri_mailto_error( + parser, + "invalid character '%c' in header field name part", + ch); + return FALSE; + } + + str_append_c(field, ch); + } + if (*p != '\0') + p++; + + /* Verify field name */ + if (!rfc2822_header_field_name_verify(str_c(field), + str_len(field))) { + uri_mailto_error(parser, "invalid header field name"); + return FALSE; + } + + if (parser->max_headers > -1 && + (int)header_count >= parser->max_headers) { + /* Refuse to accept more headers than allowed by policy + */ + if ((int)header_count == parser->max_headers) { + uri_mailto_warning( + parser, + "more than the maximum %u headers specified; " + "rest is discarded", + parser->max_headers); + } + + hname_type = _HNAME_IGNORED; + } else { + /* Add new header field to array and assign its name */ + + field_name = str_c(field); + if (strcasecmp(field_name, "to") == 0) + hname_type = _HNAME_TO; + else if (strcasecmp(field_name, "cc") == 0) + hname_type = _HNAME_CC; + else if (strcasecmp(field_name, "subject") == 0) + hname_type = _HNAME_SUBJECT; + else if (strcasecmp(field_name, "body") == 0) + hname_type = _HNAME_BODY; + else if (!uri_mailto_header_is_reserved(parser, field_name)) { + if (uri != NULL) { + if (!uri_mailto_header_is_duplicate(parser, field_name)) { + hdrf = array_append_space(&uri->headers); + hdrf->name = p_strdup(parser->pool, field_name); + } else { + uri_mailto_warning( + parser, + "ignored duplicate for unique header field '%s'", + str_sanitize(field_name, 32)); + hname_type = _HNAME_IGNORED; + } + } else { + hname_type = _HNAME_IGNORED; + } + } else { + uri_mailto_warning( + parser, + "ignored reserved header field '%s'", + str_sanitize(field_name, 32)); + hname_type = _HNAME_IGNORED; + } + } + + header_count++; + + /* Reset for field body */ + str_truncate(field, 0); + + /* Parse field body */ + while (*p != '\0' && *p != '&') { + char ch = *p; + p++; + + if (ch == '%') { + /* Encoded, parse 2-digit hex value */ + if (!_parse_hex_value(&p, &ch)) { + uri_mailto_error(parser, + "invalid %% encoding"); + return FALSE; + } + } else if (ch != '=' && !_is_qchar(ch)) { + uri_mailto_error( + parser, + "invalid character '%c' in header field value part", + ch); + return FALSE; + } + str_append_c(field, ch); + } + if (*p != '\0') + p++; + + /* Verify field body */ + if (hname_type == _HNAME_BODY) { + // FIXME: verify body ... + } else { + if (!rfc2822_header_field_body_verify( + str_c(field), str_len(field), FALSE, FALSE)) { + uri_mailto_error(parser, + "invalid header field body"); + return FALSE; + } + } + + /* Assign field body */ + + switch (hname_type) { + case _HNAME_IGNORED: + break; + case _HNAME_TO: + /* Gracefully allow duplicate To fields */ + if (!uri_mailto_parse_header_recipients( + parser, field, FALSE)) + return FALSE; + break; + case _HNAME_CC: + /* Gracefully allow duplicate Cc fields */ + if (!uri_mailto_parse_header_recipients( + parser, field, TRUE)) + return FALSE; + break; + case _HNAME_SUBJECT: + /* Ignore duplicate subject field */ + if (uri != NULL) { + if (uri->subject == NULL) { + uri->subject = + p_strdup(parser->pool, str_c(field)); + } else { + uri_mailto_warning( + parser, + "ignored duplicate subject field"); + } + } + break; + case _HNAME_BODY: + /* Ignore duplicate body field */ + if (uri != NULL) { + if (uri->body == NULL) { + uri->body = p_strdup( + parser->pool, str_c(field)); + } else { + uri_mailto_warning( + parser, "ignored duplicate body field"); + } + } + break; + case _HNAME_GENERIC: + if (uri != NULL && hdrf != NULL) + hdrf->body = p_strdup(parser->pool, str_c(field)); + break; + } + + /* Reset for next name */ + str_truncate(field, 0); + } + + /* Skip '&' */ + if (*p != '\0') + p++; + + *uri_p = p; + return TRUE; +} + +static bool +uri_mailto_parse_uri(struct uri_mailto_parser *parser, const char *uri_body) +{ + const char *p = uri_body; + + /* + * mailtoURI = "mailto:" [ to ] [ hfields ] + * to = [ addr-spec *("%2C" addr-spec ) ] + * hfields = "?" hfield *( "&" hfield ) + * hfield = hfname "=" hfvalue + * hfname = *qchar + * hfvalue = *qchar + * addr-spec = local-part "@" domain + * local-part = dot-atom / quoted-string + * qchar = unreserved / pct-encoded / some-delims + * some-delims = "!" / "$" / "'" / "(" / ")" / "*" + * / "+" / "," / ";" / ":" / "@" + * + * to ~= *tqchar + * tqchar ~= <qchar> without ";" and ":" + * + * Scheme 'mailto:' already parsed, starting parse after colon + */ + + /* First extract to-part by searching for '?' and decoding % items + */ + + if (!uri_mailto_parse_recipients(parser, &p)) + return FALSE; + + if (*p == '\0') + return TRUE; + i_assert(*p == '?'); + p++; + + /* Extract hfield items */ + + while (*p != '\0') { + /* Extract hfield item by searching for '&' and decoding '%' + items */ + if (!uri_mailto_parse_headers(parser, &p)) + return FALSE; + } + return TRUE; +} + +/* + * Validation + */ + +bool uri_mailto_validate(const char *uri_body, + const char **reserved_headers, + const char **unique_headers, int max_recipients, + int max_headers, const struct uri_mailto_log *log) +{ + struct uri_mailto_parser parser; + + i_zero(&parser); + parser.log = log; + parser.max_recipients = max_recipients; + parser.max_headers = max_headers; + parser.reserved_headers = reserved_headers; + parser.unique_headers = unique_headers; + + /* If no errors are reported, we don't need to record any data */ + if (log != NULL) { + parser.pool = pool_datastack_create(); + + parser.uri = p_new(parser.pool, struct uri_mailto, 1); + p_array_init(&parser.uri->recipients, parser.pool, max_recipients); + p_array_init(&parser.uri->headers, parser.pool, max_headers); + } + + if (!uri_mailto_parse_uri(&parser, uri_body)) + return FALSE; + + if (log != NULL) { + if (array_count(&parser.uri->recipients) == 0) { + uri_mailto_warning( + &parser, + "notification URI specifies no recipients"); + } + } + return TRUE; +} + +/* + * Parsing + */ + +struct uri_mailto * +uri_mailto_parse(const char *uri_body, pool_t pool, + const char **reserved_headers, const char **unique_headers, + int max_recipients, int max_headers, + const struct uri_mailto_log *log) +{ + struct uri_mailto_parser parser; + + parser.pool = pool; + parser.log = log; + parser.max_recipients = max_recipients; + parser.max_headers = max_headers; + parser.reserved_headers = reserved_headers; + parser.unique_headers = unique_headers; + + parser.uri = p_new(pool, struct uri_mailto, 1); + p_array_init(&parser.uri->recipients, pool, max_recipients); + p_array_init(&parser.uri->headers, pool, max_headers); + + if (!uri_mailto_parse_uri(&parser, uri_body)) + return NULL; + + if (log != NULL) { + if (array_count(&parser.uri->recipients) == 0) { + uri_mailto_warning( + &parser, + "notification URI specifies no recipients"); + } + } + return parser.uri; +} diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/mailto/uri-mailto.h b/pigeonhole/src/lib-sieve/plugins/enotify/mailto/uri-mailto.h new file mode 100644 index 0000000..8e9e057 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/enotify/mailto/uri-mailto.h @@ -0,0 +1,49 @@ +#ifndef URI_MAILTO_H +#define URI_MAILTO_H + +/* + * Types + */ + +struct uri_mailto_header_field { + const char *name; + const char *body; +}; + +struct uri_mailto_recipient { + const char *full; + const struct smtp_address *address; + bool carbon_copy; +}; + +ARRAY_DEFINE_TYPE(recipients, struct uri_mailto_recipient); +ARRAY_DEFINE_TYPE(headers, struct uri_mailto_header_field); + +struct uri_mailto_log { + void *context; + + void (*logv)(void *context, enum log_type log_type, + const char *csrc_filename, unsigned int csrc_linenum, + const char *fmt, va_list args) ATTR_FORMAT(5, 0); +}; + +struct uri_mailto { + ARRAY_TYPE(recipients) recipients; + ARRAY_TYPE(headers) headers; + const char *subject; + const char *body; +}; + +bool uri_mailto_validate + (const char *uri_body, const char **reserved_headers, + const char **unique_headers, int max_recipients, int max_headers, + const struct uri_mailto_log *log); + +struct uri_mailto *uri_mailto_parse +(const char *uri_body, pool_t pool, const char **reserved_headers, + const char **unique_headers, int max_recipients, int max_headers, + const struct uri_mailto_log *log); + +#endif + + diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/sieve-ext-enotify.h b/pigeonhole/src/lib-sieve/plugins/enotify/sieve-ext-enotify.h new file mode 100644 index 0000000..fd8b574 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/enotify/sieve-ext-enotify.h @@ -0,0 +1,197 @@ +#ifndef SIEVE_EXT_ENOTIFY_H +#define SIEVE_EXT_ENOTIFY_H + +#include "lib.h" +#include "compat.h" +#include <stdarg.h> + +#include "sieve-common.h" +#include "sieve-error.h" + +/* + * Forward declarations + */ + +struct sieve_enotify_method; +struct sieve_enotify_env; +struct sieve_enotify_action; +struct sieve_enotify_print_env; +struct sieve_enotify_exec_env; + +/* + * Notify method definition + */ + +struct sieve_enotify_method_def { + const char *identifier; + + /* Registration */ + bool (*load) + (const struct sieve_enotify_method *nmth, void **context); + void (*unload) + (const struct sieve_enotify_method *nmth); + + /* Validation */ + bool (*compile_check_uri) + (const struct sieve_enotify_env *nenv, const char *uri, + const char *uri_body); + bool (*compile_check_message) + (const struct sieve_enotify_env *nenv, string_t *message); + bool (*compile_check_from) + (const struct sieve_enotify_env *nenv, string_t *from); + bool (*compile_check_option) + (const struct sieve_enotify_env *nenv, const char *option, + const char *value); + + /* Runtime */ + bool (*runtime_check_uri) + (const struct sieve_enotify_env *nenv, const char *uri, + const char *uri_body); + const char *(*runtime_get_method_capability) + (const struct sieve_enotify_env *nenv, const char *uri, + const char *uri_body, const char *capability); + bool (*runtime_check_operands) + (const struct sieve_enotify_env *nenv, const char *uri, + const char *uri_body, string_t *message, string_t *from, + pool_t context_pool, void **method_context); + bool (*runtime_set_option) + (const struct sieve_enotify_env *nenv, void *method_context, + const char *option, const char *value); + + /* Action duplicates */ + int (*action_check_duplicates) + (const struct sieve_enotify_env *nenv, + const struct sieve_enotify_action *nact, + const struct sieve_enotify_action *nact_other); + + /* Action print */ + void (*action_print) + (const struct sieve_enotify_print_env *penv, + const struct sieve_enotify_action *nact); + + /* Action execution + (returns 0 if all is ok and -1 for temporary error) + */ + int (*action_execute) + (const struct sieve_enotify_exec_env *nenv, + const struct sieve_enotify_action *nact); +}; + +/* + * Notify method instance + */ + +struct sieve_enotify_method { + const struct sieve_enotify_method_def *def; + int id; + + struct sieve_instance *svinst; + void *context; +}; + +const struct sieve_enotify_method *sieve_enotify_method_register + (struct sieve_instance *svinst, + const struct sieve_enotify_method_def *nmth_def); +void sieve_enotify_method_unregister + (const struct sieve_enotify_method *nmth); + +/* + * Notify method environment + */ + +struct sieve_enotify_env { + struct sieve_instance *svinst; + + const struct sieve_enotify_method *method; + + struct sieve_error_handler *ehandler; + const char *location; + struct event *event; +}; + +/* + * Notify method printing + */ + +void sieve_enotify_method_printf + (const struct sieve_enotify_print_env *penv, const char *fmt, ...) + ATTR_FORMAT(2, 3); + +/* + * Notify execution environment + */ + +struct sieve_enotify_exec_env { + struct sieve_instance *svinst; + enum sieve_execute_flags flags; + + const struct sieve_enotify_method *method; + + const struct sieve_script_env *scriptenv; + const struct sieve_message_data *msgdata; + struct sieve_message_context *msgctx; + + struct sieve_error_handler *ehandler; + const char *location; + struct event *event; +}; + +struct event_passthrough * +sieve_enotify_create_finish_event(const struct sieve_enotify_exec_env *nenv); + +/* + * Notify action + */ + +struct sieve_enotify_action { + const struct sieve_enotify_method *method; + void *method_context; + + sieve_number_t importance; + const char *message; + const char *from; +}; + +/* + * Error handling + */ + +#define sieve_enotify_error(ENV, ...) \ + sieve_event_log((ENV)->svinst, (ENV)->ehandler, (ENV)->event, \ + LOG_TYPE_ERROR, (ENV)->location, 0, __VA_ARGS__ ) +#define sieve_enotify_warning(ENV, ...) \ + sieve_event_log((ENV)->svinst, (ENV)->ehandler, (ENV)->event, \ + LOG_TYPE_WARNING, \ + (ENV)->location, 0, __VA_ARGS__ ) +#define sieve_enotify_info(ENV, ...) \ + sieve_event_log((ENV)->svinst, (ENV)->ehandler, (ENV)->event, \ + LOG_TYPE_INFO, \ + (ENV)->location, 0, __VA_ARGS__ ) +#define sieve_enotify_critical(ENV, ...) \ + sieve_critical((ENV)->svinst, (ENV)->ehandler, NULL, __VA_ARGS__ ) + +#define sieve_enotify_global_error(ENV, ...) \ + sieve_event_log((ENV)->svinst, (ENV)->ehandler, (ENV)->event, \ + LOG_TYPE_ERROR, (ENV)->location, \ + SIEVE_ERROR_FLAG_GLOBAL, __VA_ARGS__ ) +#define sieve_enotify_global_warning(ENV, ...) \ + sieve_event_log((ENV)->svinst, (ENV)->ehandler, (ENV)->event, \ + LOG_TYPE_WARNING, (ENV)->location, \ + SIEVE_ERROR_FLAG_GLOBAL, __VA_ARGS__ ) +#define sieve_enotify_global_info(ENV, ...) \ + sieve_event_log((ENV)->svinst, (ENV)->ehandler, (ENV)->event, \ + LOG_TYPE_INFO, (ENV)->location, \ + SIEVE_ERROR_FLAG_GLOBAL, __VA_ARGS__ ) + +#define sieve_enotify_event_log(ENV, EVENT, ...) \ + sieve_event_log((ENV)->svinst, (ENV)->ehandler, (EVENT), \ + LOG_TYPE_INFO, (ENV)->location, \ + SIEVE_ERROR_FLAG_GLOBAL, __VA_ARGS__ ) + +#define sieve_enotify_global_log_error(ENV, ...) \ + sieve_event_log((ENV)->svinst, (ENV)->ehandler, (ENV)->event, \ + LOG_TYPE_ERROR, (ENV)->location, \ + (SIEVE_ERROR_FLAG_GLOBAL | \ + SIEVE_ERROR_FLAG_GLOBAL_MAX_INFO), __VA_ARGS__ ) +#endif + diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/tst-notify-method-capability.c b/pigeonhole/src/lib-sieve/plugins/enotify/tst-notify-method-capability.c new file mode 100644 index 0000000..8a6c071 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/enotify/tst-notify-method-capability.c @@ -0,0 +1,233 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-match.h" + +#include "ext-enotify-common.h" + +/* + * String test + * + * Syntax: + * notify_method_capability [COMPARATOR] [MATCH-TYPE] + * <notification-uri: string> + * <notification-capability: string> + * <key-list: string-list> + */ + +static bool tst_notifymc_registered + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool tst_notifymc_validate + (struct sieve_validator *valdtr, struct sieve_command *tst); +static bool tst_notifymc_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx); + +const struct sieve_command_def notify_method_capability_test = { + .identifier = "notify_method_capability", + .type = SCT_TEST, + .positional_args = 3, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = tst_notifymc_registered, + .validate = tst_notifymc_validate, + .generate = tst_notifymc_generate +}; + +/* + * String operation + */ + +static bool tst_notifymc_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int tst_notifymc_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def notify_method_capability_operation = { + .mnemonic = "NOTIFY_METHOD_CAPABILITY", + .ext_def = &enotify_extension, + .code = EXT_ENOTIFY_OPERATION_NOTIFY_METHOD_CAPABILITY, + .dump = tst_notifymc_operation_dump, + .execute = tst_notifymc_operation_execute +}; + +/* + * Optional arguments + */ + +enum tst_notifymc_optional { + OPT_END, + OPT_COMPARATOR, + OPT_MATCH_TYPE +}; + +/* + * Test registration + */ + +static bool tst_notifymc_registered +(struct sieve_validator *valdtr, const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_command_registration *cmd_reg) +{ + /* The order of these is not significant */ + sieve_comparators_link_tag(valdtr, cmd_reg, OPT_COMPARATOR); + sieve_match_types_link_tags(valdtr, cmd_reg, OPT_MATCH_TYPE); + + return TRUE; +} + +/* + * Test validation + */ + +static bool tst_notifymc_validate +(struct sieve_validator *valdtr, struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + const struct sieve_match_type mcht_default = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + const struct sieve_comparator cmp_default = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "notification-uri", 1, SAAT_STRING) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) + return FALSE; + + arg = sieve_ast_argument_next(arg); + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "notification-capability", 2, SAAT_STRING) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) + return FALSE; + + arg = sieve_ast_argument_next(arg); + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "key-list", 3, SAAT_STRING_LIST) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) + return FALSE; + + /* Validate the key argument to a specified match type */ + return sieve_match_type_validate + (valdtr, tst, arg, &mcht_default, &cmp_default); +} + +/* + * Test generation + */ + +static bool tst_notifymc_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + sieve_operation_emit + (cgenv->sblock, cmd->ext, ¬ify_method_capability_operation); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, cmd, NULL); +} + +/* + * Code dump + */ + +static bool tst_notifymc_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + sieve_code_dumpf(denv, "NOTIFY_METHOD_CAPABILITY"); + sieve_code_descend(denv); + + /* Handle any optional arguments */ + if ( sieve_match_opr_optional_dump(denv, address, NULL) != 0 ) + return FALSE; + + return + sieve_opr_string_dump(denv, address, "notify uri") && + sieve_opr_string_dump(denv, address, "notify capability") && + sieve_opr_stringlist_dump(denv, address, "key list"); +} + +/* + * Code execution + */ + +static int tst_notifymc_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + struct sieve_match_type mcht = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + struct sieve_comparator cmp = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + string_t *notify_uri, *notify_capability; + struct sieve_stringlist *value_list, *key_list; + const char *cap_value; + int match, ret; + + /* + * Read operands + */ + + /* Handle match-type and comparator operands */ + if ( sieve_match_opr_optional_read + (renv, address, NULL, &ret, &cmp, &mcht) < 0 ) + return ret; + + /* Read notify uri */ + if ( (ret=sieve_opr_string_read(renv, address, "notify-uri", ¬ify_uri)) + <= 0 ) + return ret; + + /* Read notify capability */ + if ( (ret=sieve_opr_string_read + (renv, address, "notify-capability", ¬ify_capability)) <= 0 ) + return ret; + + /* Read key-list */ + if ( (ret=sieve_opr_stringlist_read(renv, address, "key-list", &key_list)) + <= 0) + return ret; + + /* + * Perform operation + */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "notify_method_capability test"); + + cap_value = ext_enotify_runtime_get_method_capability + (renv, notify_uri, str_c(notify_capability)); + + if ( cap_value != NULL ) { + value_list = sieve_single_stringlist_create_cstr(renv, cap_value, TRUE); + + /* Perform match */ + if ( (match=sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret)) + < 0 ) + return ret; + } else { + match = 0; + } + + /* Set test result for subsequent conditional jump */ + sieve_interpreter_set_test_result(renv->interp, match > 0); + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/tst-valid-notify-method.c b/pigeonhole/src/lib-sieve/plugins/enotify/tst-valid-notify-method.c new file mode 100644 index 0000000..becc41c --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/enotify/tst-valid-notify-method.c @@ -0,0 +1,144 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-match.h" + +#include "ext-enotify-common.h" + +/* + * Valid_notify_method test + * + * Syntax: + * valid_notify_method <notification-uris: string-list> + */ + +static bool tst_vnotifym_validate + (struct sieve_validator *valdtr, struct sieve_command *tst); +static bool tst_vnotifym_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx); + +const struct sieve_command_def valid_notify_method_test = { + .identifier = "valid_notify_method", + .type = SCT_TEST, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = tst_vnotifym_validate, + .generate = tst_vnotifym_generate +}; + +/* + * Valid_notify_method operation + */ + +static bool tst_vnotifym_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int tst_vnotifym_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def valid_notify_method_operation = { + .mnemonic = "VALID_NOTIFY_METHOD", + .ext_def = &enotify_extension, + .code = EXT_ENOTIFY_OPERATION_VALID_NOTIFY_METHOD, + .dump = tst_vnotifym_operation_dump, + .execute = tst_vnotifym_operation_execute +}; + +/* + * Test validation + */ + +static bool tst_vnotifym_validate + (struct sieve_validator *valdtr, struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "notification-uris", 1, SAAT_STRING_LIST) ) { + return FALSE; + } + + return sieve_validator_argument_activate(valdtr, tst, arg, FALSE); +} + +/* + * Test generation + */ + +static bool tst_vnotifym_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + sieve_operation_emit(cgenv->sblock, cmd->ext, &valid_notify_method_operation); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, cmd, NULL); +} + +/* + * Code dump + */ + +static bool tst_vnotifym_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + sieve_code_dumpf(denv, "VALID_NOTIFY_METHOD"); + sieve_code_descend(denv); + + return + sieve_opr_stringlist_dump(denv, address, "notify-uris"); +} + +/* + * Code execution + */ + +static int tst_vnotifym_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + struct sieve_stringlist *notify_uris; + string_t *uri_item; + bool all_valid = TRUE; + int ret; + + /* + * Read operands + */ + + /* Read notify uris */ + if ( (ret=sieve_opr_stringlist_read + (renv, address, "notify-uris", ¬ify_uris)) <= 0 ) + return ret; + + /* + * Perform operation + */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "valid_notify_method test"); + + uri_item = NULL; + while ( (ret=sieve_stringlist_next_item(notify_uris, &uri_item)) > 0 ) { + if ( !ext_enotify_runtime_method_validate(renv, uri_item) ) { + all_valid = FALSE; + break; + } + } + + if ( ret < 0 ) { + sieve_runtime_trace_error(renv, "invalid method uri item"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + sieve_interpreter_set_test_result(renv->interp, all_valid); + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/vmodf-encodeurl.c b/pigeonhole/src/lib-sieve/plugins/enotify/vmodf-encodeurl.c new file mode 100644 index 0000000..801f882 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/enotify/vmodf-encodeurl.c @@ -0,0 +1,119 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "unichar.h" +#include "str.h" + +#include "sieve-common.h" +#include "sieve-code.h" + +#include "sieve-ext-variables.h" + +#include "ext-enotify-common.h" + +/* + * Encodeurl modifier + */ + +static bool +mod_encodeurl_modify(const struct sieve_variables_modifier *modf, + string_t *in, string_t **result); + +const struct sieve_variables_modifier_def encodeurl_modifier = { + SIEVE_OBJECT("encodeurl", &encodeurl_operand, 0), + 15, + mod_encodeurl_modify +}; + +/* + * Modifier operand + */ + +static const struct sieve_extension_objects ext_enotify_modifiers = + SIEVE_VARIABLES_DEFINE_MODIFIER(encodeurl_modifier); + +const struct sieve_operand_def encodeurl_operand = { + .name = "modifier", + .ext_def = &enotify_extension, + .class = &sieve_variables_modifier_operand_class, + .interface = &ext_enotify_modifiers +}; + +/* + * Modifier implementation + */ + +static const char _uri_reserved_lookup[256] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 00 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 10 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, // 20 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, // 30 + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, // 50 + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, // 70 + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 90 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // F0 +}; + +static bool +mod_encodeurl_modify(const struct sieve_variables_modifier *modf, + string_t *in, string_t **result) +{ + size_t max_var_size = + sieve_variables_get_max_variable_size(modf->var_ext); + const unsigned char *p, *poff, *pend; + size_t new_size; + + if ( str_len(in) == 0 ) { + *result = in; + return TRUE; + } + + /* allocate new string */ + new_size = str_len(in) + 32; + if (new_size > max_var_size) + new_size = max_var_size; + *result = t_str_new(new_size + 1); + + /* escape string */ + p = str_data(in); + pend = p + str_len(in); + poff = p; + while (p < pend) { + unsigned int i, n = uni_utf8_char_bytes(*p); + + if (n > 1 || (_uri_reserved_lookup[*p] & 0x01) != 0) { + str_append_data(*result, poff, p - poff); + poff = p; + + if (str_len(*result) + 3 * n > max_var_size) + break; + + str_printfa(*result, "%%%02X", *p); + for (i = 1; i < n && p < pend; i++) { + p++; + poff++; + str_printfa(*result, "%%%02X", *p); + } + + poff++; + } else if ((str_len(*result) + (p - poff) + 1) > max_var_size) { + break; + } + p++; + } + + str_append_data(*result, poff, p - poff); + return TRUE; +} + + diff --git a/pigeonhole/src/lib-sieve/plugins/environment/Makefile.am b/pigeonhole/src/lib-sieve/plugins/environment/Makefile.am new file mode 100644 index 0000000..916362e --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/environment/Makefile.am @@ -0,0 +1,24 @@ +noinst_LTLIBRARIES = libsieve_ext_environment.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +tests = \ + tst-environment.c + +libsieve_ext_environment_la_SOURCES = \ + $(tests) \ + ext-environment-common.c \ + ext-environment.c + +public_headers = \ + sieve-ext-environment.h + +headers = \ + ext-environment-common.h + +pkginc_libdir=$(dovecot_pkgincludedir)/sieve +pkginc_lib_HEADERS = $(public_headers) +noinst_HEADERS = $(headers) + diff --git a/pigeonhole/src/lib-sieve/plugins/environment/Makefile.in b/pigeonhole/src/lib-sieve/plugins/environment/Makefile.in new file mode 100644 index 0000000..68424f4 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/environment/Makefile.in @@ -0,0 +1,753 @@ +# 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/plugins/environment +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) \ + $(pkginc_lib_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_ext_environment_la_LIBADD = +am__objects_1 = tst-environment.lo +am_libsieve_ext_environment_la_OBJECTS = $(am__objects_1) \ + ext-environment-common.lo ext-environment.lo +libsieve_ext_environment_la_OBJECTS = \ + $(am_libsieve_ext_environment_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)/ext-environment-common.Plo \ + ./$(DEPDIR)/ext-environment.Plo \ + ./$(DEPDIR)/tst-environment.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_ext_environment_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_environment_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(pkginc_libdir)" +HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +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_ext_environment.la +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +tests = \ + tst-environment.c + +libsieve_ext_environment_la_SOURCES = \ + $(tests) \ + ext-environment-common.c \ + ext-environment.c + +public_headers = \ + sieve-ext-environment.h + +headers = \ + ext-environment-common.h + +pkginc_libdir = $(dovecot_pkgincludedir)/sieve +pkginc_lib_HEADERS = $(public_headers) +noinst_HEADERS = $(headers) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/environment/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/environment/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_ext_environment.la: $(libsieve_ext_environment_la_OBJECTS) $(libsieve_ext_environment_la_DEPENDENCIES) $(EXTRA_libsieve_ext_environment_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_environment_la_OBJECTS) $(libsieve_ext_environment_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-environment-common.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-environment.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-environment.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) + @$(NORMAL_INSTALL) + @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ + done + +uninstall-pkginc_libHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/ext-environment-common.Plo + -rm -f ./$(DEPDIR)/ext-environment.Plo + -rm -f ./$(DEPDIR)/tst-environment.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pkginc_libHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/ext-environment-common.Plo + -rm -f ./$(DEPDIR)/ext-environment.Plo + -rm -f ./$(DEPDIR)/tst-environment.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pkginc_libHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstLTLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-pkginc_libHEADERS install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-pkginc_libHEADERS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/pigeonhole/src/lib-sieve/plugins/environment/ext-environment-common.c b/pigeonhole/src/lib-sieve/plugins/environment/ext-environment-common.c new file mode 100644 index 0000000..28d4251 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/environment/ext-environment-common.c @@ -0,0 +1,339 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "hash.h" + +#include "sieve-common.h" +#include "sieve-extensions.h" +#include "sieve-interpreter.h" + +#include "ext-environment-common.h" + +struct ext_environment_interpreter_context; + +/* + * Core environment items + */ + +static const struct sieve_environment_item *core_env_items[] = { + &domain_env_item, + &host_env_item, + &location_env_item, + &phase_env_item, + &name_env_item, + &version_env_item +}; + +static unsigned int core_env_items_count = N_ELEMENTS(core_env_items); + +static void +sieve_environment_item_insert(struct ext_environment_interpreter_context *ctx, + const struct sieve_environment_item *item); + +/* + * Validator context + */ + +struct ext_environment_interpreter_context { + HASH_TABLE(const char *, + const struct sieve_environment_item *) name_items; + ARRAY(const struct sieve_environment_item *) prefix_items; + + bool active:1; +}; + +static void +ext_environment_interpreter_extension_free(const struct sieve_extension *ext, + struct sieve_interpreter *interp, + void *context); + +struct sieve_interpreter_extension environment_interpreter_extension = { + .ext_def = &environment_extension, + .free = ext_environment_interpreter_extension_free, +}; + +static struct ext_environment_interpreter_context * +ext_environment_interpreter_context_create( + const struct sieve_extension *this_ext, + struct sieve_interpreter *interp) +{ + pool_t pool = sieve_interpreter_pool(interp); + struct ext_environment_interpreter_context *ctx; + + ctx = p_new(pool, struct ext_environment_interpreter_context, 1); + + hash_table_create(&ctx->name_items, default_pool, 0, str_hash, strcmp); + i_array_init(&ctx->prefix_items, 16); + + sieve_interpreter_extension_register(interp, this_ext, + &environment_interpreter_extension, + (void *)ctx); + return ctx; +} + +static void +ext_environment_interpreter_extension_free( + const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_interpreter *interp ATTR_UNUSED, void *context) +{ + struct ext_environment_interpreter_context *ctx = + (struct ext_environment_interpreter_context *)context; + + hash_table_destroy(&ctx->name_items); + array_free(&ctx->prefix_items); +} + +static struct ext_environment_interpreter_context * +ext_environment_interpreter_context_get(const struct sieve_extension *this_ext, + struct sieve_interpreter *interp) +{ + struct ext_environment_interpreter_context *ctx = + (struct ext_environment_interpreter_context *) + sieve_interpreter_extension_get_context(interp, this_ext); + + if (ctx == NULL) { + ctx = ext_environment_interpreter_context_create( + this_ext, interp); + } + return ctx; +} + +void ext_environment_interpreter_init(const struct sieve_extension *this_ext, + struct sieve_interpreter *interp) +{ + struct ext_environment_interpreter_context *ctx; + unsigned int i; + + /* Create our context */ + ctx = ext_environment_interpreter_context_get(this_ext, interp); + + for (i = 0; i < core_env_items_count; i++) + sieve_environment_item_insert(ctx, core_env_items[i]); + + ctx->active = TRUE; +} + +bool sieve_ext_environment_is_active(const struct sieve_extension *env_ext, + struct sieve_interpreter *interp) +{ + struct ext_environment_interpreter_context *ctx = + ext_environment_interpreter_context_get(env_ext, interp); + + return (ctx != NULL && ctx->active); +} + +/* + * Registration + */ + +static void +sieve_environment_item_insert(struct ext_environment_interpreter_context *ctx, + const struct sieve_environment_item *item) +{ + if (!item->prefix) + hash_table_insert(ctx->name_items, item->name, item); + else + array_append(&ctx->prefix_items, &item, 1); +} + +void sieve_environment_item_register(const struct sieve_extension *env_ext, + struct sieve_interpreter *interp, + const struct sieve_environment_item *item) +{ + struct ext_environment_interpreter_context *ctx; + + i_assert(sieve_extension_is(env_ext, environment_extension)); + ctx = ext_environment_interpreter_context_get(env_ext, interp); + + sieve_environment_item_insert(ctx, item); +} + +/* + * Retrieval + */ + +static const struct sieve_environment_item * +ext_environment_item_lookup(struct ext_environment_interpreter_context *ctx, + const char **_name) +{ + const struct sieve_environment_item *item; + const char *name = *_name; + + item = hash_table_lookup(ctx->name_items, name); + if (item != NULL) + return item; + + array_foreach_elem(&ctx->prefix_items, item) { + size_t prefix_len; + + i_assert(item->prefix); + prefix_len = strlen(item->name); + + if (str_begins(name, item->name)) { + if (name[prefix_len] == '.') { + *_name = &name[prefix_len+1]; + return item; + } else if (name[prefix_len] == '\0') { + *_name = &name[prefix_len+1]; + return item; + } + } + } + return NULL; +} + +const char * +ext_environment_item_get_value(const struct sieve_extension *env_ext, + const struct sieve_runtime_env *renv, + const char *name) +{ + struct ext_environment_interpreter_context *ctx; + const struct sieve_environment_item *item; + + i_assert(sieve_extension_is(env_ext, environment_extension)); + ctx = ext_environment_interpreter_context_get(env_ext, renv->interp); + + item = ext_environment_item_lookup(ctx, &name); + if (item == NULL) + return NULL; + + if (item->value != NULL) + return item->value; + if (item->get_value != NULL) + return item->get_value(renv, name); + return NULL; +} + +/* + * Default environment items + */ + +/* "domain": + + The primary DNS domain associated with the Sieve execution context, usually + but not always a proper suffix of the host name. + */ + +static const char * +envit_domain_get_value(const struct sieve_runtime_env *renv, + const char *name ATTR_UNUSED) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + + return eenv->svinst->domainname; +} + +const struct sieve_environment_item domain_env_item = { + .name = "domain", + .get_value = envit_domain_get_value, +}; + +/* "host": + + The fully-qualified domain name of the host where the Sieve script is + executing. + */ + +static const char * +envit_host_get_value(const struct sieve_runtime_env *renv, + const char *name ATTR_UNUSED) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + + return eenv->svinst->hostname; +} + +const struct sieve_environment_item host_env_item = { + .name = "host", + .get_value = envit_host_get_value, +}; + +/* "location": + + Sieve evaluation can be performed at various different points as messages + are processed. This item provides additional information about the type of + service that is evaluating the script. Possible values are: + "MTA" - the Sieve script is being evaluated by a Message Transfer Agent + "MDA" - evaluation is being performed by a Mail Delivery Agent + "MUA" - evaluation is being performed by a Mail User Agent (right...) + "MS" - evaluation is being performed by a Message Store + */ + +static const char * +envit_location_get_value(const struct sieve_runtime_env *renv, + const char *name ATTR_UNUSED) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + + switch (eenv->svinst->env_location ) { + case SIEVE_ENV_LOCATION_MDA: + return "MDA"; + case SIEVE_ENV_LOCATION_MTA: + return "MTA"; + case SIEVE_ENV_LOCATION_MS: + return "MS"; + default: + break; + } + return NULL; +} + +const struct sieve_environment_item location_env_item = { + .name = "location", + .get_value = envit_location_get_value +}; + +/* "phase": + + The point relative to final delivery where the Sieve script is being + evaluated. Possible values are "pre", "during", and "post", referring + respectively to processing before, during, and after final delivery has + taken place. + */ + +static const char * +envit_phase_get_value(const struct sieve_runtime_env *renv, + const char *name ATTR_UNUSED) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + + switch (eenv->svinst->delivery_phase) { + case SIEVE_DELIVERY_PHASE_PRE: + return "pre"; + case SIEVE_DELIVERY_PHASE_DURING: + return "during"; + case SIEVE_DELIVERY_PHASE_POST: + return "post"; + default: + break; + } + return NULL; +} + +const struct sieve_environment_item phase_env_item = { + .name = "phase", + .get_value = envit_phase_get_value +}; + +/* "name": + + The product name associated with the Sieve interpreter. + */ + +const struct sieve_environment_item name_env_item = { + .name = "name", + .value = PIGEONHOLE_NAME" Sieve" +}; + +/* "version": + + The product version associated with the Sieve interpreter. The meaning of the + product version string is product-specific and should always be considered + in the context of the product name given by the "name" item. + */ + +const struct sieve_environment_item version_env_item = { + .name = "version", + .value = PIGEONHOLE_VERSION, +}; diff --git a/pigeonhole/src/lib-sieve/plugins/environment/ext-environment-common.h b/pigeonhole/src/lib-sieve/plugins/environment/ext-environment-common.h new file mode 100644 index 0000000..288b82e --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/environment/ext-environment-common.h @@ -0,0 +1,53 @@ +#ifndef EXT_ENVIRONMENT_COMMON_H +#define EXT_ENVIRONMENT_COMMON_H + +#include "lib.h" + +#include "sieve-common.h" + +#include "sieve-ext-environment.h" + +/* + * Extension + */ + +extern const struct sieve_extension_def environment_extension; + +/* + * Commands + */ + +extern const struct sieve_command_def tst_environment; + +/* + * Operations + */ + +extern const struct sieve_operation_def tst_environment_operation; + +/* + * Environment items + */ + +extern const struct sieve_environment_item domain_env_item; +extern const struct sieve_environment_item host_env_item; +extern const struct sieve_environment_item location_env_item; +extern const struct sieve_environment_item phase_env_item; +extern const struct sieve_environment_item name_env_item; +extern const struct sieve_environment_item version_env_item; + +/* + * Initialization + */ + +bool ext_environment_init(const struct sieve_extension *ext, void **context); +void ext_environment_deinit(const struct sieve_extension *ext); + +/* + * Validator context + */ + +void ext_environment_interpreter_init(const struct sieve_extension *this_ext, + struct sieve_interpreter *interp); + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/environment/ext-environment.c b/pigeonhole/src/lib-sieve/plugins/environment/ext-environment.c new file mode 100644 index 0000000..c2130a2 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/environment/ext-environment.c @@ -0,0 +1,59 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension variables + * ------------------- + * + * Authors: Stephan Bosch + * Specification: RFC 5183 + * Implementation: full + * Status: testing + * + */ + +#include "lib.h" +#include "str.h" +#include "unichar.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-binary.h" +#include "sieve-interpreter.h" + +#include "sieve-validator.h" + +#include "ext-environment-common.h" + +/* + * Extension + */ + +static bool ext_environment_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); +static bool ext_environment_interpreter_load +(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_extension_def environment_extension = { + .name = "environment", + .validator_load = ext_environment_validator_load, + .interpreter_load = ext_environment_interpreter_load, + SIEVE_EXT_DEFINE_OPERATION(tst_environment_operation) +}; + +static bool ext_environment_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + sieve_validator_register_command(valdtr, ext, &tst_environment); + return TRUE; +} + +static bool ext_environment_interpreter_load +(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv, + sieve_size_t *address ATTR_UNUSED) +{ + ext_environment_interpreter_init(ext, renv->interp); + return TRUE; +} + diff --git a/pigeonhole/src/lib-sieve/plugins/environment/sieve-ext-environment.h b/pigeonhole/src/lib-sieve/plugins/environment/sieve-ext-environment.h new file mode 100644 index 0000000..8ae0d18 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/environment/sieve-ext-environment.h @@ -0,0 +1,54 @@ +#ifndef SIEVE_EXT_ENVIRONMENT_H +#define SIEVE_EXT_ENVIRONMENT_H + +#include "sieve-common.h" + +/* + * Environment extension + */ + +/* FIXME: this is not suitable for future plugin support */ + +extern const struct sieve_extension_def environment_extension; + +static inline const struct sieve_extension * +sieve_ext_environment_get_extension +(struct sieve_instance *svinst) +{ + return sieve_extension_register + (svinst, &environment_extension, FALSE); +} + +static inline const struct sieve_extension * +sieve_ext_environment_require_extension +(struct sieve_instance *svinst) +{ + return sieve_extension_require + (svinst, &environment_extension, TRUE); +} + +bool sieve_ext_environment_is_active + (const struct sieve_extension *env_ext, + struct sieve_interpreter *interp); + +/* + * Environment item + */ + +struct sieve_environment_item { + const char *name; + bool prefix; + + const char *value; + const char *(*get_value) + (const struct sieve_runtime_env *renv, const char *name); +}; + +void sieve_environment_item_register + (const struct sieve_extension *env_ext, struct sieve_interpreter *interp, + const struct sieve_environment_item *item); +const char *ext_environment_item_get_value + (const struct sieve_extension *env_ext, + const struct sieve_runtime_env *renv, const char *name); + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/environment/tst-environment.c b/pigeonhole/src/lib-sieve/plugins/environment/tst-environment.c new file mode 100644 index 0000000..9eaa099 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/environment/tst-environment.c @@ -0,0 +1,215 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str-sanitize.h" + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-match.h" + +#include "ext-environment-common.h" + +/* + * Environment test + * + * Syntax: + * environment [COMPARATOR] [MATCH-TYPE] + * <name: string> <key-list: string-list> + */ + +static bool tst_environment_registered + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool tst_environment_validate + (struct sieve_validator *valdtr, struct sieve_command *tst); +static bool tst_environment_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd); + +const struct sieve_command_def tst_environment = { + .identifier = "environment", + .type = SCT_TEST, + .positional_args = 2, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = tst_environment_registered, + .validate = tst_environment_validate, + .generate = tst_environment_generate +}; + +/* + * Environment operation + */ + +static bool tst_environment_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int tst_environment_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def tst_environment_operation = { + .mnemonic = "ENVIRONMENT", + .ext_def = &environment_extension, + .dump = tst_environment_operation_dump, + .execute = tst_environment_operation_execute +}; + +/* + * Test registration + */ + +static bool tst_environment_registered +(struct sieve_validator *valdtr, const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_command_registration *cmd_reg) +{ + /* The order of these is not significant */ + sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR); + sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE); + + return TRUE; +} + +/* + * Test validation + */ + +static bool tst_environment_validate +(struct sieve_validator *valdtr, struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + const struct sieve_match_type mcht_default = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + const struct sieve_comparator cmp_default = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "name", 1, SAAT_STRING) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) + return FALSE; + + arg = sieve_ast_argument_next(arg); + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "key list", 2, SAAT_STRING_LIST) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) + return FALSE; + + /* Validate the key argument to a specified match type */ + return sieve_match_type_validate + (valdtr, tst, arg, &mcht_default, &cmp_default); +} + +/* + * Test generation + */ + +static bool tst_environment_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + sieve_operation_emit(cgenv->sblock, cmd->ext, &tst_environment_operation); + + /* Generate arguments */ + if ( !sieve_generate_arguments(cgenv, cmd, NULL) ) + return FALSE; + + return TRUE; +} + +/* + * Code dump + */ + +static bool tst_environment_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + sieve_code_dumpf(denv, "ENVIRONMENT"); + sieve_code_descend(denv); + + /* Optional operands */ + if ( sieve_match_opr_optional_dump(denv, address, NULL) != 0 ) + return FALSE; + + return + sieve_opr_string_dump(denv, address, "name") && + sieve_opr_stringlist_dump(denv, address, "key list"); +} + +/* + * Code execution + */ + +static int tst_environment_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + const struct sieve_extension *this_ext = renv->oprtn->ext; + struct sieve_match_type mcht = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + struct sieve_comparator cmp = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + string_t *name; + struct sieve_stringlist *value_list, *key_list; + const char *env_item; + int match, ret; + + /* + * Read operands + */ + + /* Handle match-type and comparator operands */ + if ( sieve_match_opr_optional_read + (renv, address, NULL, &ret, &cmp, &mcht) < 0 ) + return ret; + + /* Read source */ + if ( (ret=sieve_opr_string_read(renv, address, "name", &name)) <= 0 ) + return ret; + + /* Read key-list */ + if ( (ret=sieve_opr_stringlist_read(renv, address, "key-list", &key_list)) + <= 0 ) + return ret; + + /* + * Perform operation + */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "environment test"); + + env_item = ext_environment_item_get_value + (this_ext, renv, str_c(name)); + + if ( env_item != NULL ) { + /* Construct value list */ + value_list = sieve_single_stringlist_create_cstr(renv, env_item, FALSE); + + /* Perform match */ + if ( (match=sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret)) + < 0 ) + return ret; + } else { + match = 0; + + sieve_runtime_trace_descend(renv); + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "environment item `%s' not found", + str_sanitize(str_c(name), 128)); + } + + /* Set test result for subsequent conditional jump */ + sieve_interpreter_set_test_result(renv->interp, match > 0); + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/plugins/ihave/Makefile.am b/pigeonhole/src/lib-sieve/plugins/ihave/Makefile.am new file mode 100644 index 0000000..1eb0472 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/ihave/Makefile.am @@ -0,0 +1,22 @@ +noinst_LTLIBRARIES = libsieve_ext_ihave.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +tests = \ + tst-ihave.c + +commands = \ + cmd-error.c + +libsieve_ext_ihave_la_SOURCES = \ + $(tests) \ + $(commands) \ + ext-ihave-binary.c \ + ext-ihave-common.c \ + ext-ihave.c + +noinst_HEADERS = \ + ext-ihave-binary.h \ + ext-ihave-common.h diff --git a/pigeonhole/src/lib-sieve/plugins/ihave/Makefile.in b/pigeonhole/src/lib-sieve/plugins/ihave/Makefile.in new file mode 100644 index 0000000..c5b8952 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/ihave/Makefile.in @@ -0,0 +1,707 @@ +# 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/plugins/ihave +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_ext_ihave_la_LIBADD = +am__objects_1 = tst-ihave.lo +am__objects_2 = cmd-error.lo +am_libsieve_ext_ihave_la_OBJECTS = $(am__objects_1) $(am__objects_2) \ + ext-ihave-binary.lo ext-ihave-common.lo ext-ihave.lo +libsieve_ext_ihave_la_OBJECTS = $(am_libsieve_ext_ihave_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)/cmd-error.Plo \ + ./$(DEPDIR)/ext-ihave-binary.Plo \ + ./$(DEPDIR)/ext-ihave-common.Plo ./$(DEPDIR)/ext-ihave.Plo \ + ./$(DEPDIR)/tst-ihave.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_ext_ihave_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_ihave_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_ext_ihave.la +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +tests = \ + tst-ihave.c + +commands = \ + cmd-error.c + +libsieve_ext_ihave_la_SOURCES = \ + $(tests) \ + $(commands) \ + ext-ihave-binary.c \ + ext-ihave-common.c \ + ext-ihave.c + +noinst_HEADERS = \ + ext-ihave-binary.h \ + ext-ihave-common.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/plugins/ihave/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/ihave/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_ext_ihave.la: $(libsieve_ext_ihave_la_OBJECTS) $(libsieve_ext_ihave_la_DEPENDENCIES) $(EXTRA_libsieve_ext_ihave_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_ihave_la_OBJECTS) $(libsieve_ext_ihave_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-error.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-ihave-binary.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-ihave-common.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-ihave.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-ihave.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)/cmd-error.Plo + -rm -f ./$(DEPDIR)/ext-ihave-binary.Plo + -rm -f ./$(DEPDIR)/ext-ihave-common.Plo + -rm -f ./$(DEPDIR)/ext-ihave.Plo + -rm -f ./$(DEPDIR)/tst-ihave.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)/cmd-error.Plo + -rm -f ./$(DEPDIR)/ext-ihave-binary.Plo + -rm -f ./$(DEPDIR)/ext-ihave-common.Plo + -rm -f ./$(DEPDIR)/ext-ihave.Plo + -rm -f ./$(DEPDIR)/tst-ihave.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/plugins/ihave/cmd-error.c b/pigeonhole/src/lib-sieve/plugins/ihave/cmd-error.c new file mode 100644 index 0000000..6e971bd --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/ihave/cmd-error.c @@ -0,0 +1,131 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str-sanitize.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-code.h" + +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-binary.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "ext-ihave-common.h" + +/* + * Error command + * + * Syntax + * error <message: string> + */ + +static bool cmd_error_validate + (struct sieve_validator *valdtr, struct sieve_command *tst); +static bool cmd_error_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx); + +const struct sieve_command_def error_command = { + .identifier = "error", + .type = SCT_COMMAND, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = cmd_error_validate, + .generate = cmd_error_generate +}; + +/* + * Body operation + */ + +static bool cmd_error_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int cmd_error_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def cmd_error_operation = { + .mnemonic = "ERROR", + .ext_def = &ihave_extension, + .code = EXT_IHAVE_OPERATION_ERROR, + .dump = cmd_error_operation_dump, + .execute = cmd_error_operation_execute +}; + +/* + * Validation + */ + +static bool cmd_error_validate +(struct sieve_validator *valdtr, struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "message", 1, SAAT_STRING) ) { + return FALSE; + } + + return sieve_validator_argument_activate(valdtr, tst, arg, FALSE); +} + +/* + * Code generation + */ + +static bool cmd_error_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + (void)sieve_operation_emit(cgenv->sblock, cmd->ext, &cmd_error_operation); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, cmd, NULL); +} + +/* + * Code dump + */ + +static bool cmd_error_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + sieve_code_dumpf(denv, "ERROR"); + sieve_code_descend(denv); + + return sieve_opr_string_dump(denv, address, "message"); +} + +/* + * Interpretation + */ + +static int cmd_error_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + string_t *message; + int ret; + + /* + * Read operands + */ + + /* Read message */ + + if ( (ret=sieve_opr_string_read(renv, address, "message", &message)) <= 0 ) + return ret; + + /* + * Perform operation + */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "error \"%s\"", + str_sanitize(str_c(message), 80)); + + sieve_runtime_error(renv, NULL, "%s", str_c(message)); + + return SIEVE_EXEC_FAILURE; +} diff --git a/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-binary.c b/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-binary.c new file mode 100644 index 0000000..01b375e --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-binary.c @@ -0,0 +1,249 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" + +#include "sieve-common.h" +#include "sieve-extensions.h" +#include "sieve-error.h" +#include "sieve-script.h" +#include "sieve-binary.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "ext-ihave-common.h" +#include "ext-ihave-binary.h" + +/* + * Forward declarations + */ + +static bool ext_ihave_binary_pre_save + (const struct sieve_extension *ext, struct sieve_binary *sbin, + void *context, enum sieve_error *error_r); +static bool ext_ihave_binary_open + (const struct sieve_extension *ext, struct sieve_binary *sbin, + void *context); +static bool ext_ihave_binary_up_to_date + (const struct sieve_extension *ext, struct sieve_binary *sbin, + void *context, enum sieve_compile_flags cpflags); + +/* + * Binary include extension + */ + +const struct sieve_binary_extension ihave_binary_ext = { + .extension = &ihave_extension, + .binary_pre_save = ext_ihave_binary_pre_save, + .binary_open = ext_ihave_binary_open, + .binary_up_to_date = ext_ihave_binary_up_to_date +}; + +/* + * Binary context management + */ + +struct ext_ihave_binary_context { + struct sieve_binary *binary; + struct sieve_binary_block *block; + + ARRAY(const char *) missing_extensions; +}; + +static struct ext_ihave_binary_context *ext_ihave_binary_create_context +(const struct sieve_extension *this_ext, struct sieve_binary *sbin) +{ + pool_t pool = sieve_binary_pool(sbin); + + struct ext_ihave_binary_context *ctx = + p_new(pool, struct ext_ihave_binary_context, 1); + + ctx->binary = sbin; + p_array_init(&ctx->missing_extensions, pool, 64); + + sieve_binary_extension_set(sbin, this_ext, &ihave_binary_ext, ctx); + return ctx; +} + +struct ext_ihave_binary_context *ext_ihave_binary_get_context +(const struct sieve_extension *this_ext, struct sieve_binary *sbin) +{ + struct ext_ihave_binary_context *ctx = (struct ext_ihave_binary_context *) + sieve_binary_extension_get_context(sbin, this_ext); + + if ( ctx == NULL ) + ctx = ext_ihave_binary_create_context(this_ext, sbin); + + return ctx; +} + +struct ext_ihave_binary_context *ext_ihave_binary_init +(const struct sieve_extension *this_ext, struct sieve_binary *sbin, + struct sieve_ast *ast) +{ + struct ext_ihave_ast_context *ast_ctx = + ext_ihave_get_ast_context(this_ext, ast); + struct ext_ihave_binary_context *binctx; + const char *const *exts; + unsigned int i, count; + + binctx = ext_ihave_binary_get_context(this_ext, sbin); + + exts = array_get(&ast_ctx->missing_extensions, &count); + + if ( count > 0 ) { + pool_t pool = sieve_binary_pool(sbin); + + if ( binctx->block == NULL ) + binctx->block = sieve_binary_extension_create_block(sbin, this_ext); + + for ( i = 0; i < count; i++ ) { + const char *ext_name = p_strdup(pool, exts[i]); + + array_append(&binctx->missing_extensions, &ext_name, 1); + } + } + + return binctx; +} + +/* + * Binary extension + */ + +static bool ext_ihave_binary_pre_save +(const struct sieve_extension *ext, struct sieve_binary *sbin, + void *context, enum sieve_error *error_r ATTR_UNUSED) +{ + struct ext_ihave_binary_context *binctx = + (struct ext_ihave_binary_context *) context; + const char *const *exts; + unsigned int count, i; + + exts = array_get(&binctx->missing_extensions, &count); + + if ( binctx->block != NULL ) + sieve_binary_block_clear(binctx->block); + + if ( count > 0 ) { + if ( binctx->block == NULL ) + binctx->block = sieve_binary_extension_create_block(sbin, ext); + + sieve_binary_emit_unsigned(binctx->block, count); + + for ( i = 0; i < count; i++ ) { + sieve_binary_emit_cstring(binctx->block, exts[i]); + } + } + + return TRUE; +} + +static bool ext_ihave_binary_open +(const struct sieve_extension *ext, struct sieve_binary *sbin, void *context) +{ + struct sieve_instance *svinst = ext->svinst; + struct ext_ihave_binary_context *binctx = + (struct ext_ihave_binary_context *) context; + struct sieve_binary_block *sblock; + unsigned int i, count, block_id; + sieve_size_t offset; + + sblock = sieve_binary_extension_get_block(sbin, ext); + + if ( sblock != NULL ) { + binctx->block = sblock; + block_id = sieve_binary_block_get_id(sblock); + + offset = 0; + + /* Read number of missing extensions to read subsequently */ + if ( !sieve_binary_read_unsigned(sblock, &offset, &count) ) { + e_error(svinst->event, "ihave: " + "failed to read missing extension count " + "from block %d of binary %s", + block_id, sieve_binary_path(sbin)); + return FALSE; + } + + /* Read dependencies */ + for ( i = 0; i < count; i++ ) { + string_t *ext_name; + const char *name; + + if ( !sieve_binary_read_string(sblock, &offset, &ext_name) ) { + /* Binary is corrupt, recompile */ + e_error(svinst->event, "ihave: " + "failed to read missing extension name " + "from block %d of binary %s", + block_id, sieve_binary_path(sbin)); + return FALSE; + } + + name = str_c(ext_name); + array_append(&binctx->missing_extensions, &name, 1); + } + } + + return TRUE; +} + +static bool ext_ihave_binary_up_to_date +(const struct sieve_extension *ext, struct sieve_binary *sbin ATTR_UNUSED, + void *context, enum sieve_compile_flags cpflags) +{ + struct ext_ihave_binary_context *binctx = + (struct ext_ihave_binary_context *) context; + const struct sieve_extension *mext; + const char *const *mexts; + unsigned int count, i; + + mexts = array_get(&binctx->missing_extensions, &count); + for ( i = 0; i < count; i++ ) { + if ( (mext=sieve_extension_get_by_name(ext->svinst, mexts[i])) != NULL && + ((cpflags & SIEVE_COMPILE_FLAG_NOGLOBAL) == 0 || !mext->global) ) + return FALSE; + } + + return TRUE; +} + +/* + * Main extension interface + */ + +bool ext_ihave_binary_load +(const struct sieve_extension *ext, struct sieve_binary *sbin) +{ + (void)ext_ihave_binary_get_context(ext, sbin); + + return TRUE; +} + +bool ext_ihave_binary_dump +(const struct sieve_extension *ext, struct sieve_dumptime_env *denv) +{ + struct sieve_binary *sbin = denv->sbin; + struct ext_ihave_binary_context *binctx = + ext_ihave_binary_get_context(ext, sbin); + const char *const *exts; + unsigned int count, i; + + exts = array_get(&binctx->missing_extensions, &count); + + if ( count > 0 ) { + sieve_binary_dump_sectionf(denv, + "Extensions missing at compile (block: %d)", + sieve_binary_block_get_id(binctx->block)); + + for ( i = 0; i < count; i++ ) { + sieve_binary_dumpf(denv, " - %s\n", exts[i]); + } + } + + return TRUE; +} + + diff --git a/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-binary.h b/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-binary.h new file mode 100644 index 0000000..418f078 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-binary.h @@ -0,0 +1,33 @@ +#ifndef EXT_IHAVE_BINARY_H +#define EXT_IHAVE_BINARY_H + +/* + * Binary context management + */ + +struct ext_ihave_binary_context; + +struct ext_ihave_binary_context *ext_ihave_binary_get_context + (const struct sieve_extension *this_ext, struct sieve_binary *sbin); +struct ext_ihave_binary_context *ext_ihave_binary_init + (const struct sieve_extension *this_ext, struct sieve_binary *sbin, + struct sieve_ast *ast); + +/* + * Registering missing extension + */ + +void ext_ihave_binary_add_missing_extension + (struct ext_ihave_binary_context *binctx, const char *ext_name); + +/* + * Main extension interface + */ + +bool ext_ihave_binary_load + (const struct sieve_extension *ext, struct sieve_binary *sbin); +bool ext_ihave_binary_dump + (const struct sieve_extension *ext, struct sieve_dumptime_env *denv); + +#endif + diff --git a/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-common.c b/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-common.c new file mode 100644 index 0000000..1d31238 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-common.c @@ -0,0 +1,52 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" + +#include "sieve-common.h" +#include "sieve-ast.h" + +#include "ext-ihave-common.h" + +/* + * AST context management + */ + +struct ext_ihave_ast_context *ext_ihave_get_ast_context +(const struct sieve_extension *this_ext, struct sieve_ast *ast) +{ + struct ext_ihave_ast_context *actx = (struct ext_ihave_ast_context *) + sieve_ast_extension_get_context(ast, this_ext); + pool_t pool; + + if ( actx != NULL ) + return actx; + + pool = sieve_ast_pool(ast); + actx = p_new(pool, struct ext_ihave_ast_context, 1); + p_array_init(&actx->missing_extensions, pool, 64); + + sieve_ast_extension_set_context(ast, this_ext, (void *) actx); + + return actx; +} + +void ext_ihave_ast_add_missing_extension +(const struct sieve_extension *this_ext, struct sieve_ast *ast, + const char *ext_name) +{ + struct ext_ihave_ast_context *actx = + ext_ihave_get_ast_context(this_ext, ast); + const char *const *exts; + unsigned int i, count; + + exts = array_get(&actx->missing_extensions, &count); + for ( i = 0; i < count; i++ ) { + if ( strcmp(exts[i], ext_name) == 0 ) + return; + } + + array_append(&actx->missing_extensions, &ext_name, 1); +} + diff --git a/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-common.h b/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-common.h new file mode 100644 index 0000000..371d03d --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-common.h @@ -0,0 +1,52 @@ +#ifndef EXT_IHAVE_COMMON_H +#define EXT_IHAVE_COMMON_H + +#include "sieve-common.h" + +/* + * Extensions + */ + +extern const struct sieve_extension_def ihave_extension; + +/* + * Tests + */ + +extern const struct sieve_command_def ihave_test; + +/* + * Commands + */ + +extern const struct sieve_command_def error_command; + +/* + * Operations + */ + +enum ext_ihave_opcode { + EXT_IHAVE_OPERATION_IHAVE, + EXT_IHAVE_OPERATION_ERROR +}; + +extern const struct sieve_operation_def tst_ihave_operation; +extern const struct sieve_operation_def cmd_error_operation; + +/* + * AST context + */ + +struct ext_ihave_ast_context { + ARRAY(const char *) missing_extensions; +}; + +struct ext_ihave_ast_context *ext_ihave_get_ast_context + (const struct sieve_extension *this_ext, struct sieve_ast *ast); + +void ext_ihave_ast_add_missing_extension + (const struct sieve_extension *this_ext, struct sieve_ast *ast, + const char *ext_name); + + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave.c b/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave.c new file mode 100644 index 0000000..40b70eb --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave.c @@ -0,0 +1,70 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension ihave + * --------------- + * + * Authors: Stephan Bosch + * Specification: RFC 5463 + * Implementation: full + * Status: testing + * + */ + +#include "lib.h" + +#include "sieve-common.h" +#include "sieve-extensions.h" +#include "sieve-code.h" +#include "sieve-binary.h" +#include "sieve-validator.h" +#include "sieve-generator.h" + +#include "ext-ihave-common.h" +#include "ext-ihave-binary.h" + +/* + * Operations + */ + +const struct sieve_operation_def *ext_ihave_operations[] = { + &tst_ihave_operation, + &cmd_error_operation +}; + +/* + * Extension + */ + +static bool ext_ihave_validator_load + (const struct sieve_extension *ext, struct sieve_validator *validator); +static bool ext_ihave_generator_load + (const struct sieve_extension *ext, const struct sieve_codegen_env *cgenv); + +const struct sieve_extension_def ihave_extension = { + "ihave", + .version = 1, + .validator_load = ext_ihave_validator_load, + .generator_load = ext_ihave_generator_load, + .binary_load = ext_ihave_binary_load, + .binary_dump = ext_ihave_binary_dump, + SIEVE_EXT_DEFINE_OPERATIONS(ext_ihave_operations) +}; + +static bool ext_ihave_validator_load +(const struct sieve_extension *ext, struct sieve_validator *validator) +{ + sieve_validator_register_command(validator, ext, &ihave_test); + sieve_validator_register_command(validator, ext, &error_command); + + return TRUE; +} + +static bool ext_ihave_generator_load +(const struct sieve_extension *ext, const struct sieve_codegen_env *cgenv) +{ + (void)ext_ihave_binary_init(ext, cgenv->sbin, cgenv->ast); + + return TRUE; +} + diff --git a/pigeonhole/src/lib-sieve/plugins/ihave/tst-ihave.c b/pigeonhole/src/lib-sieve/plugins/ihave/tst-ihave.c new file mode 100644 index 0000000..c6c8e8f --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/ihave/tst-ihave.c @@ -0,0 +1,294 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" + +#include "sieve-common.h" +#include "sieve-stringlist.h" +#include "sieve-extensions.h" +#include "sieve-code.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" + +#include "ext-ihave-common.h" + +/* + * Ihave test + * + * Syntax: + * ihave <capabilities: string-list> + */ + +static bool +tst_ihave_validate(struct sieve_validator *valdtr, struct sieve_command *tst); +static bool +tst_ihave_validate_const(struct sieve_validator *valdtr, + struct sieve_command *tst, int *const_current, + int const_next); +static bool +tst_ihave_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *tst); + +const struct sieve_command_def ihave_test = { + .identifier = "ihave", + .type = SCT_TEST, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = tst_ihave_validate, + .validate_const = tst_ihave_validate_const, + .generate = tst_ihave_generate +}; + +/* + * Ihave operation + */ + +static bool +tst_ihave_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +tst_ihave_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def tst_ihave_operation = { + .mnemonic = "IHAVE", + .ext_def = &ihave_extension, + .code = EXT_IHAVE_OPERATION_IHAVE, + .dump = tst_ihave_operation_dump, + .execute = tst_ihave_operation_execute +}; + +/* + * Code validation + */ + +static bool +tst_ihave_validate(struct sieve_validator *valdtr, struct sieve_command *tst) +{ + struct _capability { + const struct sieve_extension *ext; + struct sieve_ast_argument *arg; + }; + struct sieve_ast_argument *arg = tst->first_positional; + struct sieve_ast_argument *stritem; + enum sieve_compile_flags cpflags = + sieve_validator_compile_flags(valdtr); + bool no_global = ((cpflags & SIEVE_COMPILE_FLAG_NOGLOBAL) != 0); + ARRAY(struct _capability) capabilities; + struct _capability capability; + const struct _capability *caps; + unsigned int i, count; + bool all_known = TRUE; + + t_array_init(&capabilities, 64); + + tst->data = (void *)FALSE; + + /* Check stringlist argument */ + if (!sieve_validate_positional_argument(valdtr, tst, arg, + "capabilities", 1, + SAAT_STRING_LIST)) + return FALSE; + + switch (sieve_ast_argument_type(arg)) { + case SAAT_STRING: + /* Single string */ + capability.arg = arg; + capability.ext = sieve_extension_get_by_name( + tst->ext->svinst, sieve_ast_argument_strc(arg)); + + if (capability.ext == NULL || + (no_global && capability.ext->global)) { + all_known = FALSE; + + ext_ihave_ast_add_missing_extension( + tst->ext, tst->ast_node->ast, + sieve_ast_argument_strc(arg)); + } else { + array_append(&capabilities, &capability, 1); + } + + break; + + case SAAT_STRING_LIST: + /* String list */ + stritem = sieve_ast_strlist_first(arg); + + while (stritem != NULL) { + capability.arg = stritem; + capability.ext = sieve_extension_get_by_name( + tst->ext->svinst, + sieve_ast_argument_strc(stritem)); + + if (capability.ext == NULL || + (no_global && capability.ext->global)) { + all_known = FALSE; + + ext_ihave_ast_add_missing_extension( + tst->ext, tst->ast_node->ast, + sieve_ast_argument_strc(stritem)); + } else { + array_append(&capabilities, &capability, 1); + } + stritem = sieve_ast_strlist_next(stritem); + } + + break; + default: + i_unreached(); + } + + if (!all_known) + return TRUE; + + /* RFC 5463, Section 4, page 4: + + The "ihave" extension is designed to be used with other extensions + that add tests, actions, comparators, or arguments. Implementations + MUST NOT allow it to be used with extensions that change the + underlying Sieve grammar, or extensions like encoded-character + [RFC5228], or variables [RFC5229] that change how the content of + Sieve scripts are interpreted. The test MUST fail and the extension + MUST NOT be enabled if such usage is attempted. + + FIXME: current implementation of this restriction is hardcoded and + therefore highly inflexible + */ + caps = array_get(&capabilities, &count); + for (i = 0; i < count; i++) { + if (sieve_extension_name_is(caps[i].ext, "variables") || + sieve_extension_name_is(caps[i].ext, "encoded-character")) + return TRUE; + } + + /* Load all extensions */ + caps = array_get(&capabilities, &count); + for (i = 0; i < count; i++) { + if (!sieve_validator_extension_load(valdtr, tst, caps[i].arg, + caps[i].ext, FALSE)) + return FALSE; + } + + if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE)) + return FALSE; + + tst->data = (void *)TRUE; + return TRUE; +} + +static bool +tst_ihave_validate_const(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_command *tst, int *const_current, + int const_next ATTR_UNUSED) +{ + if ((bool)tst->data == TRUE) + *const_current = -1; + else + *const_current = 0; + return TRUE; +} + +/* + * Code generation + */ + +bool tst_ihave_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *tst) +{ + /* Emit opcode */ + sieve_operation_emit(cgenv->sblock, tst->ext, &tst_ihave_operation); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, tst, NULL); +} + +/* + * Code dump + */ + +static bool +tst_ihave_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + sieve_code_dumpf(denv, "IHAVE"); + sieve_code_descend(denv); + + return sieve_opr_stringlist_dump(denv, address, "capabilities"); +} + +/* + * Code execution + */ + +static int +tst_ihave_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + struct sieve_instance *svinst = eenv->svinst; + struct sieve_stringlist *capabilities; + string_t *cap_item; + bool matched; + int ret; + + /* + * Read operands + */ + + /* Read capabilities */ + if ((ret = sieve_opr_stringlist_read(renv, address, "capabilities", + &capabilities)) <= 0) + return ret; + + /* + * Perform test + */ + + /* Perform the test */ + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "ihave test"); + sieve_runtime_trace_descend(renv); + + cap_item = NULL; + matched = TRUE; + while (matched && + (ret = sieve_stringlist_next_item(capabilities, + &cap_item)) > 0) { + const struct sieve_extension *ext; + int sret; + + ext = sieve_extension_get_by_name(svinst, str_c(cap_item)); + if (ext == NULL) { + sieve_runtime_trace_error( + renv, "ihave: invalid extension name"); + return SIEVE_EXEC_BIN_CORRUPT; + } + sret = sieve_interpreter_extension_start(renv->interp, ext); + if (sret == SIEVE_EXEC_FAILURE) { + sieve_runtime_trace( + renv, SIEVE_TRLVL_TESTS, + "extension `%s' not available", + sieve_extension_name(ext)); + matched = FALSE; + } else if (sret == SIEVE_EXEC_OK) { + sieve_runtime_trace( + renv, SIEVE_TRLVL_TESTS, + "extension `%s' available", + sieve_extension_name(ext)); + } else { + return sret; + } + } + if (ret < 0) { + sieve_runtime_trace_error(renv, "invalid capabilities item"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + /* Set test result for subsequent conditional jump */ + sieve_interpreter_set_test_result(renv->interp, matched); + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/plugins/imap4flags/Makefile.am b/pigeonhole/src/lib-sieve/plugins/imap4flags/Makefile.am new file mode 100644 index 0000000..e5390d4 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/imap4flags/Makefile.am @@ -0,0 +1,33 @@ +noinst_LTLIBRARIES = libsieve_ext_imap4flags.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + -I$(srcdir)/../variables \ + $(LIBDOVECOT_INCLUDE) + +commands = \ + cmd-flag.c + +tests = \ + tst-hasflag.c + +tags = \ + tag-flags.c + +libsieve_ext_imap4flags_la_SOURCES = \ + ext-imap4flags-common.c \ + $(commands) \ + $(tests) \ + $(tags) \ + ext-imap4flags.c \ + ext-imapflags.c + +public_headers = \ + sieve-ext-imap4flags.h + +headers = \ + ext-imap4flags-common.h + +pkginc_libdir=$(dovecot_pkgincludedir)/sieve +pkginc_lib_HEADERS = $(public_headers) +noinst_HEADERS = $(headers) diff --git a/pigeonhole/src/lib-sieve/plugins/imap4flags/Makefile.in b/pigeonhole/src/lib-sieve/plugins/imap4flags/Makefile.in new file mode 100644 index 0000000..77f2dc3 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/imap4flags/Makefile.in @@ -0,0 +1,776 @@ +# 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/plugins/imap4flags +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) \ + $(pkginc_lib_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_ext_imap4flags_la_LIBADD = +am__objects_1 = cmd-flag.lo +am__objects_2 = tst-hasflag.lo +am__objects_3 = tag-flags.lo +am_libsieve_ext_imap4flags_la_OBJECTS = ext-imap4flags-common.lo \ + $(am__objects_1) $(am__objects_2) $(am__objects_3) \ + ext-imap4flags.lo ext-imapflags.lo +libsieve_ext_imap4flags_la_OBJECTS = \ + $(am_libsieve_ext_imap4flags_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)/cmd-flag.Plo \ + ./$(DEPDIR)/ext-imap4flags-common.Plo \ + ./$(DEPDIR)/ext-imap4flags.Plo ./$(DEPDIR)/ext-imapflags.Plo \ + ./$(DEPDIR)/tag-flags.Plo ./$(DEPDIR)/tst-hasflag.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_ext_imap4flags_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_imap4flags_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(pkginc_libdir)" +HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +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_ext_imap4flags.la +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + -I$(srcdir)/../variables \ + $(LIBDOVECOT_INCLUDE) + +commands = \ + cmd-flag.c + +tests = \ + tst-hasflag.c + +tags = \ + tag-flags.c + +libsieve_ext_imap4flags_la_SOURCES = \ + ext-imap4flags-common.c \ + $(commands) \ + $(tests) \ + $(tags) \ + ext-imap4flags.c \ + ext-imapflags.c + +public_headers = \ + sieve-ext-imap4flags.h + +headers = \ + ext-imap4flags-common.h + +pkginc_libdir = $(dovecot_pkgincludedir)/sieve +pkginc_lib_HEADERS = $(public_headers) +noinst_HEADERS = $(headers) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/imap4flags/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/imap4flags/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_ext_imap4flags.la: $(libsieve_ext_imap4flags_la_OBJECTS) $(libsieve_ext_imap4flags_la_DEPENDENCIES) $(EXTRA_libsieve_ext_imap4flags_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_imap4flags_la_OBJECTS) $(libsieve_ext_imap4flags_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-flag.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-imap4flags-common.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-imap4flags.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-imapflags.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tag-flags.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-hasflag.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) + @$(NORMAL_INSTALL) + @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ + done + +uninstall-pkginc_libHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/cmd-flag.Plo + -rm -f ./$(DEPDIR)/ext-imap4flags-common.Plo + -rm -f ./$(DEPDIR)/ext-imap4flags.Plo + -rm -f ./$(DEPDIR)/ext-imapflags.Plo + -rm -f ./$(DEPDIR)/tag-flags.Plo + -rm -f ./$(DEPDIR)/tst-hasflag.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pkginc_libHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/cmd-flag.Plo + -rm -f ./$(DEPDIR)/ext-imap4flags-common.Plo + -rm -f ./$(DEPDIR)/ext-imap4flags.Plo + -rm -f ./$(DEPDIR)/ext-imapflags.Plo + -rm -f ./$(DEPDIR)/tag-flags.Plo + -rm -f ./$(DEPDIR)/tst-hasflag.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pkginc_libHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstLTLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-pkginc_libHEADERS install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-pkginc_libHEADERS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/pigeonhole/src/lib-sieve/plugins/imap4flags/cmd-flag.c b/pigeonhole/src/lib-sieve/plugins/imap4flags/cmd-flag.c new file mode 100644 index 0000000..2645669 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/imap4flags/cmd-flag.c @@ -0,0 +1,251 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" + +#include "sieve-code.h" +#include "sieve-stringlist.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "ext-imap4flags-common.h" + +/* + * Commands + */ + +/* Forward declarations */ + +static bool cmd_flag_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx); + +/* Setflag command + * + * Syntax: + * setflag [<variablename: string>] <list-of-flags: string-list> + */ + +const struct sieve_command_def cmd_setflag = { + .identifier = "setflag", + .type = SCT_COMMAND, + .positional_args = -1, /* We check positional arguments ourselves */ + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = ext_imap4flags_command_validate, + .generate = cmd_flag_generate +}; + +/* Addflag command + * + * Syntax: + * addflag [<variablename: string>] <list-of-flags: string-list> + */ + +const struct sieve_command_def cmd_addflag = { + .identifier = "addflag", + .type = SCT_COMMAND, + .positional_args = -1, /* We check positional arguments ourselves */ + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = ext_imap4flags_command_validate, + .generate = cmd_flag_generate +}; + + +/* Removeflag command + * + * Syntax: + * removeflag [<variablename: string>] <list-of-flags: string-list> + */ + +const struct sieve_command_def cmd_removeflag = { + .identifier = "removeflag", + .type = SCT_COMMAND, + .positional_args = -1, /* We check positional arguments ourselves */ + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = ext_imap4flags_command_validate, + .generate = cmd_flag_generate +}; + +/* + * Operations + */ + +/* Forward declarations */ + +bool cmd_flag_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int cmd_flag_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +/* Setflag operation */ + +const struct sieve_operation_def setflag_operation = { + .mnemonic = "SETFLAG", + .ext_def = &imap4flags_extension, + .code = EXT_IMAP4FLAGS_OPERATION_SETFLAG, + .dump = cmd_flag_operation_dump, + .execute = cmd_flag_operation_execute +}; + +/* Addflag operation */ + +const struct sieve_operation_def addflag_operation = { + .mnemonic = "ADDFLAG", + .ext_def = &imap4flags_extension, + .code = EXT_IMAP4FLAGS_OPERATION_ADDFLAG, + .dump = cmd_flag_operation_dump, + .execute = cmd_flag_operation_execute +}; + +/* Removeflag operation */ + +const struct sieve_operation_def removeflag_operation = { + .mnemonic = "REMOVEFLAG", + .ext_def = &imap4flags_extension, + .code = EXT_IMAP4FLAGS_OPERATION_REMOVEFLAG, + .dump = cmd_flag_operation_dump, + .execute = cmd_flag_operation_execute +}; + +/* + * Code generation + */ + +static bool cmd_flag_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + struct sieve_ast_argument *arg1, *arg2; + + /* Emit operation */ + if ( sieve_command_is(cmd, cmd_setflag) ) + sieve_operation_emit(cgenv->sblock, cmd->ext, &setflag_operation); + else if ( sieve_command_is(cmd, cmd_addflag) ) + sieve_operation_emit(cgenv->sblock, cmd->ext, &addflag_operation); + else if ( sieve_command_is(cmd, cmd_removeflag) ) + sieve_operation_emit(cgenv->sblock, cmd->ext, &removeflag_operation); + + arg1 = cmd->first_positional; + arg2 = sieve_ast_argument_next(arg1); + + if ( arg2 == NULL ) { + /* No variable */ + sieve_opr_omitted_emit(cgenv->sblock); + if ( !sieve_generate_argument(cgenv, arg1, cmd) ) + return FALSE; + } else { + /* Full command */ + if ( !sieve_generate_argument(cgenv, arg1, cmd) ) + return FALSE; + if ( !sieve_generate_argument(cgenv, arg2, cmd) ) + return FALSE; + } + return TRUE; +} + +/* + * Code dump + */ + +bool cmd_flag_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + struct sieve_operand oprnd; + + sieve_code_dumpf(denv, "%s", sieve_operation_mnemonic(denv->oprtn)); + sieve_code_descend(denv); + + sieve_code_mark(denv); + if ( !sieve_operand_read(denv->sblock, address, NULL, &oprnd) ) { + sieve_code_dumpf(denv, "ERROR: INVALID OPERAND"); + return FALSE; + } + + if ( !sieve_operand_is_omitted(&oprnd) ) { + return + sieve_opr_string_dump_data(denv, &oprnd, address, "variable name") && + sieve_opr_stringlist_dump(denv, address, "list of flags"); + } + + return + sieve_opr_stringlist_dump(denv, address, "list of flags"); +} + +/* + * Code execution + */ + +static int cmd_flag_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + const struct sieve_operation *op = renv->oprtn; + struct sieve_operand oprnd; + struct sieve_stringlist *flag_list; + struct sieve_variable_storage *storage; + unsigned int var_index; + ext_imapflag_flag_operation_t flag_op; + int ret; + + /* + * Read operands + */ + + /* Read bare operand (two types possible) */ + if ( (ret=sieve_operand_runtime_read + (renv, address, NULL, &oprnd)) <= 0 ) + return ret; + + /* Variable operand (optional) */ + if ( !sieve_operand_is_omitted(&oprnd) ) { + /* Read the variable operand */ + if ( (ret=sieve_variable_operand_read_data + (renv, &oprnd, address, "variable", &storage, &var_index)) <= 0 ) + return ret; + + /* Read flag list */ + if ( (ret=sieve_opr_stringlist_read(renv, address, "flag-list", &flag_list)) + <= 0 ) + return ret; + + /* Flag-list operand */ + } else { + storage = NULL; + var_index = 0; + + /* Read flag list */ + if ( (ret=sieve_opr_stringlist_read(renv, address, + "flag-list", &flag_list)) <= 0 ) + return ret; + } + + /* + * Perform operation + */ + + /* Determine what to do */ + + if ( sieve_operation_is(op, setflag_operation) ) { + sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "setflag command"); + flag_op = sieve_ext_imap4flags_set_flags; + } else if ( sieve_operation_is(op, addflag_operation) ) { + sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "addflag command"); + flag_op = sieve_ext_imap4flags_add_flags; + } else if ( sieve_operation_is(op, removeflag_operation) ) { + sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "removeflag command"); + flag_op = sieve_ext_imap4flags_remove_flags; + } else { + i_unreached(); + } + + sieve_runtime_trace_descend(renv); + + /* Perform requested operation */ + return flag_op(renv, op->ext, storage, var_index, flag_list); +} diff --git a/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags-common.c b/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags-common.c new file mode 100644 index 0000000..3c364b8 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags-common.c @@ -0,0 +1,733 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "str-sanitize.h" +#include "mail-storage.h" +#include "imap-arg.h" + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-code.h" +#include "sieve-stringlist.h" +#include "sieve-actions.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-result.h" +#include "sieve-dump.h" + +#include "sieve-ext-variables.h" + +#include "ext-imap4flags-common.h" + +/* + * Tagged arguments + */ + +extern const struct sieve_argument_def tag_flags; +extern const struct sieve_argument_def tag_flags_implicit; + +/* + * Common command functions + */ + +bool ext_imap4flags_command_validate +(struct sieve_validator *valdtr, struct sieve_command *cmd) +{ + struct sieve_ast_argument *arg = cmd->first_positional; + struct sieve_ast_argument *arg2; + const struct sieve_extension *var_ext; + + /* Check arguments */ + + if ( arg == NULL ) { + sieve_command_validate_error(valdtr, cmd, + "the %s %s expects at least one argument, but none was found", + sieve_command_identifier(cmd), sieve_command_type_name(cmd)); + return FALSE; + } + + if ( sieve_ast_argument_type(arg) != SAAT_STRING && + sieve_ast_argument_type(arg) != SAAT_STRING_LIST ) + { + sieve_argument_validate_error(valdtr, arg, + "the %s %s expects either a string (variable name) or " + "a string-list (list of flags) as first argument, but %s was found", + sieve_command_identifier(cmd), sieve_command_type_name(cmd), + sieve_ast_argument_name(arg)); + return FALSE; + } + + arg2 = sieve_ast_argument_next(arg); + if ( arg2 != NULL ) { + /* First, check syntax sanity */ + + if ( sieve_ast_argument_type(arg) != SAAT_STRING ) + { + if ( sieve_command_is(cmd, tst_hasflag) ) { + if ( sieve_ast_argument_type(arg) != SAAT_STRING_LIST ) { + sieve_argument_validate_error(valdtr, arg, + "if a second argument is specified for the hasflag, the first " + "must be a string-list (variable-list), but %s was found", + sieve_ast_argument_name(arg)); + return FALSE; + } + } else { + sieve_argument_validate_error(valdtr, arg, + "if a second argument is specified for the %s %s, the first " + "must be a string (variable name), but %s was found", + sieve_command_identifier(cmd), sieve_command_type_name(cmd), + sieve_ast_argument_name(arg)); + return FALSE; + } + } + + /* Then, check whether the second argument is permitted */ + + var_ext = sieve_ext_variables_get_extension(cmd->ext->svinst); + + if ( var_ext == NULL || !sieve_ext_variables_is_active(var_ext, valdtr) ) + { + sieve_argument_validate_error(valdtr,arg, + "the %s %s only allows for the specification of a " + "variable name when the variables extension is active", + sieve_command_identifier(cmd), sieve_command_type_name(cmd)); + return FALSE; + } + + if ( !sieve_variable_argument_activate(var_ext, var_ext, + valdtr, cmd, arg, !sieve_command_is(cmd, tst_hasflag) ) ) + return FALSE; + + if ( sieve_ast_argument_type(arg2) != SAAT_STRING && + sieve_ast_argument_type(arg2) != SAAT_STRING_LIST ) + { + sieve_argument_validate_error(valdtr, arg2, + "the %s %s expects a string list (list of flags) as " + "second argument when two arguments are specified, " + "but %s was found", + sieve_command_identifier(cmd), sieve_command_type_name(cmd), + sieve_ast_argument_name(arg2)); + return FALSE; + } + } else + arg2 = arg; + + if ( !sieve_validator_argument_activate(valdtr, cmd, arg2, FALSE) ) + return FALSE; + + if ( !sieve_command_is(cmd, tst_hasflag) && + sieve_argument_is_string_literal(arg2) ) { + struct ext_imap4flags_iter fiter; + const char *flag; + + /* Warn the user about validity of verifiable flags */ + ext_imap4flags_iter_init(&fiter, sieve_ast_argument_str(arg)); + + while ( (flag=ext_imap4flags_iter_get_flag(&fiter)) != NULL ) { + if ( !sieve_ext_imap4flags_flag_is_valid(flag) ) { + sieve_argument_validate_warning(valdtr, arg, + "IMAP flag '%s' specified for the %s command is invalid " + "and will be ignored (only first invalid is reported)", + str_sanitize(flag, 64), sieve_command_identifier(cmd)); + break; + } + } + } + + return TRUE; +} + +/* + * Flags tag registration + */ + +void ext_imap4flags_attach_flags_tag +(struct sieve_validator *valdtr, const struct sieve_extension *ext, + const char *command) +{ + /* Register :flags tag with the command and we don't care whether it is + * registered or even whether it will be registered at all. The validator + * handles either situation gracefully + */ + + /* Tag specified by user */ + sieve_validator_register_external_tag + (valdtr, command, ext, &tag_flags, SIEVE_OPT_SIDE_EFFECT); +} + +void sieve_ext_imap4flags_register_side_effect +(struct sieve_validator *valdtr, const struct sieve_extension *flg_ext, + const char *command) +{ + /* Implicit tag if none is specified */ + sieve_validator_register_persistent_tag + (valdtr, command, flg_ext, &tag_flags_implicit); +} + + +/* + * Result context + */ + +struct ext_imap4flags_result_context { + string_t *internal_flags; +}; + +static void _get_initial_flags +(struct sieve_result *result, string_t *flags) +{ + const struct sieve_message_data *msgdata = + sieve_result_get_message_data(result); + enum mail_flags mail_flags; + const char *const *mail_keywords; + + mail_flags = mail_get_flags(msgdata->mail); + mail_keywords = mail_get_keywords(msgdata->mail); + + if ( (mail_flags & MAIL_FLAGGED) > 0 ) + str_printfa(flags, " \\flagged"); + + if ( (mail_flags & MAIL_ANSWERED) > 0 ) + str_printfa(flags, " \\answered"); + + if ( (mail_flags & MAIL_DELETED) > 0 ) + str_printfa(flags, " \\deleted"); + + if ( (mail_flags & MAIL_SEEN) > 0 ) + str_printfa(flags, " \\seen"); + + if ( (mail_flags & MAIL_DRAFT) > 0 ) + str_printfa(flags, " \\draft"); + + while ( *mail_keywords != NULL ) { + str_printfa(flags, " %s", *mail_keywords); + mail_keywords++; + } +} + +static inline struct ext_imap4flags_result_context *_get_result_context +(const struct sieve_extension *this_ext, struct sieve_result *result) +{ + struct ext_imap4flags_result_context *rctx = + (struct ext_imap4flags_result_context *) + sieve_result_extension_get_context(result, this_ext); + + if ( rctx == NULL ) { + pool_t pool = sieve_result_pool(result); + + rctx =p_new(pool, struct ext_imap4flags_result_context, 1); + rctx->internal_flags = str_new(pool, 32); + _get_initial_flags(result, rctx->internal_flags); + + sieve_result_extension_set_context + (result, this_ext, rctx); + } + + return rctx; +} + +static string_t *_get_flags_string +(const struct sieve_extension *this_ext, struct sieve_result *result) +{ + struct ext_imap4flags_result_context *ctx = + _get_result_context(this_ext, result); + + return ctx->internal_flags; +} + +/* + * Runtime initialization + */ + +static int ext_imap4flags_runtime_init +(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv, + void *context ATTR_UNUSED, bool deferred ATTR_UNUSED) +{ + sieve_result_add_implicit_side_effect + (renv->result, NULL, TRUE, ext, &flags_side_effect, NULL); + return SIEVE_EXEC_OK; +} + +const struct sieve_interpreter_extension +imap4flags_interpreter_extension = { + .ext_def = &imap4flags_extension, + .run = ext_imap4flags_runtime_init +}; + +/* + * Flag handling + */ + +/* FIXME: This currently accepts a potentially unlimited number of + * flags, making the internal or variable flag list indefinitely long + */ + +bool sieve_ext_imap4flags_flag_is_valid(const char *flag) +{ + if ( *flag == '\0' ) + return FALSE; + + if ( *flag == '\\' ) { + /* System flag */ + const char *atom = t_str_ucase(flag); + + if ( + (strcmp(atom, "\\ANSWERED") != 0) && + (strcmp(atom, "\\FLAGGED") != 0) && + (strcmp(atom, "\\DELETED") != 0) && + (strcmp(atom, "\\SEEN") != 0) && + (strcmp(atom, "\\DRAFT") != 0) ) + { + return FALSE; + } + } else { + const char *p; + + /* Custom keyword: + * + * Syntax (IMAP4rev1, RFC 3501, Section 9. Formal Syntax) : + * flag-keyword = atom + * atom = 1*ATOM-CHAR + */ + p = flag; + while ( *p != '\0' ) { + if ( !IS_ATOM_CHAR(*p) ) + return FALSE; + p++; + } + } + + return TRUE; +} + +/* Flag iterator */ + +static void ext_imap4flags_iter_clear +(struct ext_imap4flags_iter *iter) +{ + i_zero(iter); +} + +void ext_imap4flags_iter_init +(struct ext_imap4flags_iter *iter, string_t *flags_list) +{ + ext_imap4flags_iter_clear(iter); + iter->flags_list = flags_list; +} + +static string_t *ext_imap4flags_iter_get_flag_str +(struct ext_imap4flags_iter *iter) +{ + unsigned int len; + const unsigned char *fp; + const unsigned char *fbegin; + const unsigned char *fstart; + const unsigned char *fend; + + /* Return if not initialized */ + if ( iter->flags_list == NULL ) return NULL; + + /* Return if no more flags are available */ + len = str_len(iter->flags_list); + if ( iter->offset >= len ) return NULL; + + /* Mark string boundries */ + fbegin = str_data(iter->flags_list); + fend = fbegin + len; + + /* Start of this flag */ + fstart = fbegin + iter->offset; + + /* Scan for next flag */ + fp = fstart; + for (;;) { + /* Have we reached the end or a flag boundary? */ + if ( fp >= fend || *fp == ' ' ) { + /* Did we scan more than nothing ? */ + if ( fp > fstart ) { + /* Return flag */ + string_t *flag = t_str_new(fp-fstart+1); + str_append_data(flag, fstart, fp-fstart); + + iter->last = fstart - fbegin; + iter->offset = fp - fbegin; + + return flag; + } + + fstart = fp + 1; + } + + if ( fp >= fend ) break; + + fp++; + } + + iter->last = fstart - fbegin; + iter->offset = fp - fbegin; + return NULL; +} + +const char *ext_imap4flags_iter_get_flag +(struct ext_imap4flags_iter *iter) +{ + string_t *flag = ext_imap4flags_iter_get_flag_str(iter); + + if ( flag == NULL ) return NULL; + + return str_c(flag); +} + +static void ext_imap4flags_iter_delete_last +(struct ext_imap4flags_iter *iter) +{ + iter->offset++; + if ( iter->offset > str_len(iter->flags_list) ) + iter->offset = str_len(iter->flags_list); + if ( iter->offset == str_len(iter->flags_list) && iter->last > 0 ) + iter->last--; + + str_delete(iter->flags_list, iter->last, iter->offset - iter->last); + + iter->offset = iter->last; +} + +/* Flag operations */ + +static string_t *ext_imap4flags_get_flag_variable +(const struct sieve_runtime_env *renv, + const struct sieve_extension *flg_ext, + struct sieve_variable_storage *storage, + unsigned int var_index) + ATTR_NULL(2); + +static bool flags_list_flag_exists +(string_t *flags_list, const char *flag) +{ + const char *flg; + struct ext_imap4flags_iter flit; + + ext_imap4flags_iter_init(&flit, flags_list); + + while ( (flg=ext_imap4flags_iter_get_flag(&flit)) != NULL ) { + if ( strcasecmp(flg, flag) == 0 ) + return TRUE; + } + + return FALSE; +} + +static void flags_list_flag_delete +(string_t *flags_list, const char *flag) +{ + const char *flg; + struct ext_imap4flags_iter flit; + + ext_imap4flags_iter_init(&flit, flags_list); + + while ( (flg=ext_imap4flags_iter_get_flag(&flit)) != NULL ) { + if ( strcasecmp(flg, flag) == 0 ) { + ext_imap4flags_iter_delete_last(&flit); + } + } +} + +static void flags_list_add_flags +(string_t *flags_list, string_t *flags) +{ + const char *flg; + struct ext_imap4flags_iter flit; + + ext_imap4flags_iter_init(&flit, flags); + + while ( (flg=ext_imap4flags_iter_get_flag(&flit)) != NULL ) { + if ( sieve_ext_imap4flags_flag_is_valid(flg) && + !flags_list_flag_exists(flags_list, flg) ) { + if ( str_len(flags_list) != 0 ) + str_append_c(flags_list, ' '); + str_append(flags_list, flg); + } + } +} + +static void flags_list_remove_flags +(string_t *flags_list, string_t *flags) +{ + const char *flg; + struct ext_imap4flags_iter flit; + + ext_imap4flags_iter_init(&flit, flags); + + while ( (flg=ext_imap4flags_iter_get_flag(&flit)) != NULL ) { + flags_list_flag_delete(flags_list, flg); + } +} + +static void flags_list_set_flags +(string_t *flags_list, string_t *flags) +{ + str_truncate(flags_list, 0); + flags_list_add_flags(flags_list, flags); +} + +static void flags_list_clear_flags +(string_t *flags_list) +{ + str_truncate(flags_list, 0); +} + +static string_t *ext_imap4flags_get_flag_variable +(const struct sieve_runtime_env *renv, + const struct sieve_extension *flg_ext, + struct sieve_variable_storage *storage, + unsigned int var_index) +{ + string_t *flags; + + if ( storage != NULL ) { + if ( sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS) ) { + const char *var_name, *var_id; + + (void)sieve_variable_get_identifier(storage, var_index, &var_name); + var_id = sieve_variable_get_varid(storage, var_index); + + sieve_runtime_trace(renv, 0, "update variable `%s' [%s]", + var_name, var_id); + } + + if ( !sieve_variable_get_modifiable(storage, var_index, &flags) ) + return NULL; + } else { + i_assert( sieve_extension_is(flg_ext, imap4flags_extension) ); + flags = _get_flags_string(flg_ext, renv->result); + } + + return flags; +} + +int sieve_ext_imap4flags_set_flags +(const struct sieve_runtime_env *renv, + const struct sieve_extension *flg_ext, + struct sieve_variable_storage *storage, + unsigned int var_index, + struct sieve_stringlist *flags) +{ + string_t *cur_flags = ext_imap4flags_get_flag_variable + (renv, flg_ext, storage, var_index); + + if ( cur_flags != NULL ) { + string_t *flags_item; + int ret; + + flags_list_clear_flags(cur_flags); + while ( (ret=sieve_stringlist_next_item(flags, &flags_item)) > 0 ) { + sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, + "set flags `%s'", str_c(flags_item)); + + flags_list_add_flags(cur_flags, flags_item); + } + + if ( ret < 0 ) return SIEVE_EXEC_BIN_CORRUPT; + + return SIEVE_EXEC_OK; + } + + return SIEVE_EXEC_BIN_CORRUPT; +} + +int sieve_ext_imap4flags_add_flags +(const struct sieve_runtime_env *renv, + const struct sieve_extension *flg_ext, + struct sieve_variable_storage *storage, + unsigned int var_index, + struct sieve_stringlist *flags) +{ + string_t *cur_flags = ext_imap4flags_get_flag_variable + (renv, flg_ext, storage, var_index); + + if ( cur_flags != NULL ) { + string_t *flags_item; + int ret; + + while ( (ret=sieve_stringlist_next_item(flags, &flags_item)) > 0 ) { + sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, + "add flags `%s'", str_c(flags_item)); + + flags_list_add_flags(cur_flags, flags_item); + } + + if ( ret < 0 ) return SIEVE_EXEC_BIN_CORRUPT; + + return SIEVE_EXEC_OK; + } + + return SIEVE_EXEC_BIN_CORRUPT; +} + +int sieve_ext_imap4flags_remove_flags +(const struct sieve_runtime_env *renv, + const struct sieve_extension *flg_ext, + struct sieve_variable_storage *storage, + unsigned int var_index, + struct sieve_stringlist *flags) +{ + string_t *cur_flags = ext_imap4flags_get_flag_variable + (renv, flg_ext, storage, var_index); + + if ( cur_flags != NULL ) { + string_t *flags_item; + int ret; + + while ( (ret=sieve_stringlist_next_item(flags, &flags_item)) > 0 ) { + sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, + "remove flags `%s'", str_c(flags_item)); + + flags_list_remove_flags(cur_flags, flags_item); + } + + if ( ret < 0 ) return SIEVE_EXEC_BIN_CORRUPT; + + return SIEVE_EXEC_OK; + } + + return SIEVE_EXEC_BIN_CORRUPT; +} + +/* Flag stringlist */ + +static int ext_imap4flags_stringlist_next_item + (struct sieve_stringlist *_strlist, string_t **str_r); +static void ext_imap4flags_stringlist_reset + (struct sieve_stringlist *_strlist); + +struct ext_imap4flags_stringlist { + struct sieve_stringlist strlist; + + struct sieve_stringlist *flags_list; + string_t *flags_string; + struct ext_imap4flags_iter flit; + + bool normalize:1; +}; + +static struct sieve_stringlist *ext_imap4flags_stringlist_create +(const struct sieve_runtime_env *renv, struct sieve_stringlist *flags_list, + bool normalize) +{ + struct ext_imap4flags_stringlist *strlist; + + strlist = t_new(struct ext_imap4flags_stringlist, 1); + strlist->strlist.exec_status = SIEVE_EXEC_OK; + strlist->strlist.runenv = renv; + strlist->strlist.next_item = ext_imap4flags_stringlist_next_item; + strlist->strlist.reset = ext_imap4flags_stringlist_reset; + strlist->normalize = normalize; + + strlist->flags_list = flags_list; + + return &strlist->strlist; +} + +static struct sieve_stringlist *ext_imap4flags_stringlist_create_single +(const struct sieve_runtime_env *renv, string_t *flags_string, bool normalize) +{ + struct ext_imap4flags_stringlist *strlist; + + strlist = t_new(struct ext_imap4flags_stringlist, 1); + strlist->strlist.exec_status = SIEVE_EXEC_OK; + strlist->strlist.runenv = renv; + strlist->strlist.next_item = ext_imap4flags_stringlist_next_item; + strlist->strlist.reset = ext_imap4flags_stringlist_reset; + strlist->normalize = normalize; + + if ( normalize ) { + strlist->flags_string = t_str_new(256); + flags_list_set_flags(strlist->flags_string, flags_string); + } else { + strlist->flags_string = flags_string; + } + + ext_imap4flags_iter_init(&strlist->flit, strlist->flags_string); + + return &strlist->strlist; +} + +static int ext_imap4flags_stringlist_next_item +(struct sieve_stringlist *_strlist, string_t **str_r) +{ + struct ext_imap4flags_stringlist *strlist = + (struct ext_imap4flags_stringlist *)_strlist; + + while ( (*str_r=ext_imap4flags_iter_get_flag_str(&strlist->flit)) == NULL ) { + int ret; + + if ( strlist->flags_list == NULL ) + return 0; + + if ( (ret=sieve_stringlist_next_item + (strlist->flags_list, &strlist->flags_string)) <= 0 ) + return ret; + + if ( strlist->flags_string == NULL ) + return -1; + + if ( strlist->normalize ) { + string_t *flags_string = t_str_new(256); + + flags_list_set_flags(flags_string, strlist->flags_string); + strlist->flags_string = flags_string; + } + + ext_imap4flags_iter_init(&strlist->flit, strlist->flags_string); + } + + return 1; +} + +static void ext_imap4flags_stringlist_reset +(struct sieve_stringlist *_strlist) +{ + struct ext_imap4flags_stringlist *strlist = + (struct ext_imap4flags_stringlist *)_strlist; + + if ( strlist->flags_list != NULL ) { + sieve_stringlist_reset(strlist->flags_list); + ext_imap4flags_iter_clear(&strlist->flit); + } else { + ext_imap4flags_iter_init(&strlist->flit, strlist->flags_string); + } +} + +/* Flag access */ + +struct sieve_stringlist *sieve_ext_imap4flags_get_flags +(const struct sieve_runtime_env *renv, + const struct sieve_extension *flg_ext, + struct sieve_stringlist *flags_list) +{ + if ( flags_list == NULL ) { + i_assert( sieve_extension_is(flg_ext, imap4flags_extension) ); + return ext_imap4flags_stringlist_create_single + (renv, _get_flags_string(flg_ext, renv->result), FALSE); + } + + return ext_imap4flags_stringlist_create(renv, flags_list, TRUE); +} + +void ext_imap4flags_get_implicit_flags_init +(struct ext_imap4flags_iter *iter, const struct sieve_extension *this_ext, + struct sieve_result *result) +{ + string_t *cur_flags = _get_flags_string(this_ext, result); + + ext_imap4flags_iter_init(iter, cur_flags); +} + + + + + diff --git a/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags-common.h b/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags-common.h new file mode 100644 index 0000000..4bedb85 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags-common.h @@ -0,0 +1,97 @@ +#ifndef EXT_IMAP4FLAGS_COMMON_H +#define EXT_IMAP4FLAGS_COMMON_H + +#include "lib.h" + +#include "sieve-common.h" +#include "sieve-ext-variables.h" + +#include "sieve-ext-imap4flags.h" + +/* + * Side effect + */ + +extern const struct sieve_side_effect_def flags_side_effect; + +/* + * Operands + */ + +extern const struct sieve_operand_def flags_side_effect_operand; + +/* + * Operations + */ + +enum ext_imap4flags_opcode { + EXT_IMAP4FLAGS_OPERATION_SETFLAG, + EXT_IMAP4FLAGS_OPERATION_ADDFLAG, + EXT_IMAP4FLAGS_OPERATION_REMOVEFLAG, + EXT_IMAP4FLAGS_OPERATION_HASFLAG +}; + +extern const struct sieve_operation_def setflag_operation; +extern const struct sieve_operation_def addflag_operation; +extern const struct sieve_operation_def removeflag_operation; +extern const struct sieve_operation_def hasflag_operation; + +/* + * Commands + */ + +extern const struct sieve_command_def cmd_setflag; +extern const struct sieve_command_def cmd_addflag; +extern const struct sieve_command_def cmd_removeflag; + +extern const struct sieve_command_def tst_hasflag; + +/* + * Common command functions + */ + +bool ext_imap4flags_command_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); + +/* + * Flags tagged argument + */ + +void ext_imap4flags_attach_flags_tag + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + const char *command); + +/* + * Flag management + */ + +struct ext_imap4flags_iter { + string_t *flags_list; + unsigned int offset; + unsigned int last; +}; + +void ext_imap4flags_iter_init + (struct ext_imap4flags_iter *iter, string_t *flags_list); + +const char *ext_imap4flags_iter_get_flag + (struct ext_imap4flags_iter *iter); + +/* Flag operations */ + +typedef int (*ext_imapflag_flag_operation_t) + (const struct sieve_runtime_env *renv, + const struct sieve_extension *flg_ext, + struct sieve_variable_storage *storage, + unsigned int var_index, struct sieve_stringlist *flags) + ATTR_NULL(2); + +/* Flags access */ + +void ext_imap4flags_get_implicit_flags_init + (struct ext_imap4flags_iter *iter, const struct sieve_extension *this_ext, + struct sieve_result *result); + + +#endif + diff --git a/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags.c b/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags.c new file mode 100644 index 0000000..23230c1 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags.c @@ -0,0 +1,96 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension imap4flags + * -------------------- + * + * Authors: Stephan Bosch + * Specification: RFC 5232 + * Implementation: full + * Status: testing + * + */ + +#include "lib.h" +#include "mempool.h" +#include "str.h" + +#include "sieve-common.h" + +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-actions.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" + +#include "ext-imap4flags-common.h" + +/* + * Operations + */ + +const struct sieve_operation_def *imap4flags_operations[] = { + &setflag_operation, + &addflag_operation, + &removeflag_operation, + &hasflag_operation +}; + +/* + * Extension + */ + +static bool ext_imap4flags_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); +static bool ext_imap4flags_interpreter_load + (const struct sieve_extension *ext, const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_extension_def imap4flags_extension = { + .name = "imap4flags", + .version = 1, + .validator_load = ext_imap4flags_validator_load, + .interpreter_load = ext_imap4flags_interpreter_load, + SIEVE_EXT_DEFINE_OPERATIONS(imap4flags_operations), + SIEVE_EXT_DEFINE_OPERAND(flags_side_effect_operand) +}; + +static bool ext_imap4flags_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + /* Register commands */ + sieve_validator_register_command(valdtr, ext, &cmd_setflag); + sieve_validator_register_command(valdtr, ext, &cmd_addflag); + sieve_validator_register_command(valdtr, ext, &cmd_removeflag); + sieve_validator_register_command(valdtr, ext, &tst_hasflag); + + /* Attach :flags tag to keep and fileinto commands */ + ext_imap4flags_attach_flags_tag(valdtr, ext, "keep"); + ext_imap4flags_attach_flags_tag(valdtr, ext, "fileinto"); + + /* Attach flags side-effect to keep and fileinto actions */ + sieve_ext_imap4flags_register_side_effect(valdtr, ext, "keep"); + sieve_ext_imap4flags_register_side_effect(valdtr, ext, "fileinto"); + + return TRUE; +} + +void sieve_ext_imap4flags_interpreter_load +(const struct sieve_extension *ext, const struct sieve_runtime_env *renv) +{ + sieve_interpreter_extension_register + (renv->interp, ext, &imap4flags_interpreter_extension, NULL); +} + +static bool ext_imap4flags_interpreter_load +(const struct sieve_extension *ext, const struct sieve_runtime_env *renv, + sieve_size_t *address ATTR_UNUSED) +{ + sieve_ext_imap4flags_interpreter_load(ext, renv); + return TRUE; +} + + + diff --git a/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imapflags.c b/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imapflags.c new file mode 100644 index 0000000..ba99035 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imapflags.c @@ -0,0 +1,213 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension imapflags + * -------------------- + * + * Authors: Stephan Bosch + * Specification: draft-melnikov-sieve-imapflags-03.txt + * Implementation: full, but deprecated; provided for backwards compatibility + * Status: testing + * + */ + +#include "lib.h" +#include "mempool.h" +#include "str.h" + +#include "sieve-common.h" + +#include "sieve-ast.h" +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-actions.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" + +#include "ext-imap4flags-common.h" + +/* + * Commands + */ + +static bool cmd_mark_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); + +/* Mark command + * + * Syntax: + * mark + */ + +static const struct sieve_command_def cmd_mark = { + .identifier = "mark", + .type = SCT_COMMAND, + .positional_args = 0, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = cmd_mark_validate +}; + +/* Unmark command + * + * Syntax: + * unmark + */ +static const struct sieve_command_def cmd_unmark = { + .identifier = "unmark", + .type = SCT_COMMAND, + .positional_args = 0, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = cmd_mark_validate +}; + +/* + * Extension + */ + +static bool ext_imapflags_load + (const struct sieve_extension *ext, void **context); +static bool ext_imapflags_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); +static bool ext_imapflags_interpreter_load + (const struct sieve_extension *ext, const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_extension_def imapflags_extension = { + .name = "imapflags", + .load = ext_imapflags_load, + .validator_load = ext_imapflags_validator_load, + .interpreter_load = ext_imapflags_interpreter_load +}; + +static bool ext_imapflags_load +(const struct sieve_extension *ext, void **context) +{ + if ( *context == NULL ) { + /* Make sure real extension is registered, it is needed by the binary */ + *context = (void *) + sieve_extension_require(ext->svinst, &imap4flags_extension, FALSE); + } + + return TRUE; +} + +/* + * Validator + */ + +static bool ext_imapflags_validator_check_conflict + (const struct sieve_extension *ext, + struct sieve_validator *valdtr, void *context, + struct sieve_ast_argument *require_arg, + const struct sieve_extension *other_ext, + bool required); +static bool ext_imapflags_validator_validate + (const struct sieve_extension *ext, + struct sieve_validator *valdtr, void *context, + struct sieve_ast_argument *require_arg, + bool required); + +const struct sieve_validator_extension +imapflags_validator_extension = { + .ext = &imapflags_extension, + .check_conflict = ext_imapflags_validator_check_conflict, + .validate = ext_imapflags_validator_validate +}; + +static bool ext_imapflags_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + sieve_validator_extension_register + (valdtr, ext, &imapflags_validator_extension, NULL); + + return TRUE; +} + +static bool ext_imapflags_validator_check_conflict +(const struct sieve_extension *ext, + struct sieve_validator *valdtr, void *context ATTR_UNUSED, + struct sieve_ast_argument *require_arg, + const struct sieve_extension *ext_other, + bool required ATTR_UNUSED) +{ + const struct sieve_extension *master_ext = + (const struct sieve_extension *) ext->context; + + if ( ext_other == master_ext ) { + sieve_argument_validate_error(valdtr, require_arg, + "the (deprecated) imapflags extension cannot be used " + "together with the imap4flags extension"); + return FALSE; + } + + return TRUE; +} + +static bool ext_imapflags_validator_validate +(const struct sieve_extension *ext, + struct sieve_validator *valdtr, void *context ATTR_UNUSED, + struct sieve_ast_argument *require_arg ATTR_UNUSED, + bool required ATTR_UNUSED) +{ + const struct sieve_extension *master_ext = + (const struct sieve_extension *) ext->context; + + /* No conflicts */ + + /* Register commands */ + sieve_validator_register_command(valdtr, master_ext, &cmd_setflag); + sieve_validator_register_command(valdtr, master_ext, &cmd_addflag); + sieve_validator_register_command(valdtr, master_ext, &cmd_removeflag); + + sieve_validator_register_command(valdtr, master_ext, &cmd_mark); + sieve_validator_register_command(valdtr, master_ext, &cmd_unmark); + + /* Attach flags side-effect to keep and fileinto actions */ + sieve_ext_imap4flags_register_side_effect(valdtr, master_ext, "keep"); + sieve_ext_imap4flags_register_side_effect(valdtr, master_ext, "fileinto"); + + return TRUE; +} + +/* + * Interpreter + */ + +static bool ext_imapflags_interpreter_load +(const struct sieve_extension *ext, const struct sieve_runtime_env *renv, + sieve_size_t *address ATTR_UNUSED) +{ + const struct sieve_extension *master_ext = + (const struct sieve_extension *) ext->context; + + sieve_ext_imap4flags_interpreter_load(master_ext, renv); + return TRUE; +} + +/* + * Command validation + */ + +static bool cmd_mark_validate +(struct sieve_validator *valdtr, struct sieve_command *cmd) +{ + if ( sieve_command_is(cmd, cmd_mark) ) + cmd->def = &cmd_addflag; + else + cmd->def = &cmd_removeflag; + + cmd->first_positional = sieve_ast_argument_cstring_create + (cmd->ast_node, "\\flagged", cmd->ast_node->source_line); + + if ( !sieve_validator_argument_activate + (valdtr, cmd, cmd->first_positional, FALSE) ) + return FALSE; + + return TRUE; +} diff --git a/pigeonhole/src/lib-sieve/plugins/imap4flags/sieve-ext-imap4flags.h b/pigeonhole/src/lib-sieve/plugins/imap4flags/sieve-ext-imap4flags.h new file mode 100644 index 0000000..b38a3c8 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/imap4flags/sieve-ext-imap4flags.h @@ -0,0 +1,74 @@ +#ifndef SIEVE_EXT_IMAP4FLAGS_H +#define SIEVE_EXT_IMAP4FLAGS_H + +struct sieve_variable_storage; + +/* + * Imap4flags extension + */ + +/* FIXME: this is not suitable for future plugin support */ + +extern const struct sieve_extension_def imap4flags_extension; +extern const struct sieve_interpreter_extension + imap4flags_interpreter_extension; + +static inline const struct sieve_extension * +sieve_ext_imap4flags_require_extension +(struct sieve_instance *svinst) +{ + return sieve_extension_require + (svinst, &imap4flags_extension, TRUE); +} + +void sieve_ext_imap4flags_interpreter_load +(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv); + +/* + * Action side-effect + */ + +void sieve_ext_imap4flags_register_side_effect +(struct sieve_validator *valdtr, const struct sieve_extension *flg_ext, + const char *command); + +/* + * Flag syntax + */ + +bool sieve_ext_imap4flags_flag_is_valid(const char *flag); + +/* + * Flag manipulation + */ + +int sieve_ext_imap4flags_set_flags +(const struct sieve_runtime_env *renv, + const struct sieve_extension *flg_ext, + struct sieve_variable_storage *storage, + unsigned int var_index, + struct sieve_stringlist *flags) ATTR_NULL(3); +int sieve_ext_imap4flags_add_flags +(const struct sieve_runtime_env *renv, + const struct sieve_extension *flg_ext, + struct sieve_variable_storage *storage, + unsigned int var_index, + struct sieve_stringlist *flags) ATTR_NULL(3); +int sieve_ext_imap4flags_remove_flags +(const struct sieve_runtime_env *renv, + const struct sieve_extension *flg_ext, + struct sieve_variable_storage *storage, + unsigned int var_index, + struct sieve_stringlist *flags) ATTR_NULL(3); + +/* + * Flag retrieval + */ + +struct sieve_stringlist *sieve_ext_imap4flags_get_flags +(const struct sieve_runtime_env *renv, + const struct sieve_extension *flg_ext, + struct sieve_stringlist *flags_list); + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/imap4flags/tag-flags.c b/pigeonhole/src/lib-sieve/plugins/imap4flags/tag-flags.c new file mode 100644 index 0000000..331063d --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/imap4flags/tag-flags.c @@ -0,0 +1,402 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str-sanitize.h" +#include "array.h" +#include "mail-storage.h" + +#include "sieve-code.h" +#include "sieve-stringlist.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-result.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-actions.h" +#include "sieve-dump.h" + +#include "ext-imap4flags-common.h" + +#include <ctype.h> + +/* + * Flags tagged argument + */ + +static bool +tag_flags_validate(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, struct sieve_command *cmd); +static bool +tag_flags_validate_persistent(struct sieve_validator *valdtr, + struct sieve_command *cmd, + const struct sieve_extension *ext); +static bool +tag_flags_generate(const struct sieve_codegen_env *cgenv, + struct sieve_ast_argument *arg, struct sieve_command *cmd); + +const struct sieve_argument_def tag_flags = { + .identifier = "flags", + .validate = tag_flags_validate, + .generate = tag_flags_generate, +}; + +const struct sieve_argument_def tag_flags_implicit = { + .identifier = "flags-implicit", + .validate_persistent = tag_flags_validate_persistent, + .generate = tag_flags_generate, +}; + +/* + * Side effect + */ + +static bool +seff_flags_dump_context(const struct sieve_side_effect *seffect, + const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +seff_flags_read_context(const struct sieve_side_effect *seffect, + const struct sieve_runtime_env *renv, + sieve_size_t *address, void **context); + +static int +seff_flags_merge(const struct sieve_runtime_env *renv, + const struct sieve_action *action, + const struct sieve_side_effect *old_seffect, + const struct sieve_side_effect *new_seffect, + void **old_context); + +static void +seff_flags_print(const struct sieve_side_effect *seffect, + const struct sieve_action *action, + const struct sieve_result_print_env *rpenv, bool *keep); + +static int +seff_flags_pre_execute(const struct sieve_side_effect *seffect, + const struct sieve_action_exec_env *aenv, + void *tr_context, void **se_tr_context); + +const struct sieve_side_effect_def flags_side_effect = { + SIEVE_OBJECT("flags", &flags_side_effect_operand, 0), + .to_action = &act_store, + .dump_context = seff_flags_dump_context, + .read_context = seff_flags_read_context, + .merge = seff_flags_merge, + .print = seff_flags_print, + .pre_execute = seff_flags_pre_execute, +}; + +/* + * Operand + */ + +static const struct sieve_extension_objects ext_side_effects = + SIEVE_EXT_DEFINE_SIDE_EFFECT(flags_side_effect); + +const struct sieve_operand_def flags_side_effect_operand = { + .name = "flags operand", + .ext_def = &imap4flags_extension, + .class = &sieve_side_effect_operand_class, + .interface = &ext_side_effects, +}; + +/* + * Tag validation + */ + +static bool +tag_flags_validate_persistent(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_command *cmd, + const struct sieve_extension *ext) +{ + if (sieve_command_find_argument(cmd, &tag_flags) == NULL) + sieve_command_add_dynamic_tag(cmd, ext, &tag_flags_implicit, + -1); + return TRUE; +} + +static bool +tag_flags_validate(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + + /* Detach the tag itself */ + *arg = sieve_ast_argument_next(*arg); + + /* Check syntax: + * :flags <list-of-flags: string-list> + */ + if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0, + SAAT_STRING_LIST, FALSE)) + return FALSE; + + tag->parameters = *arg; + + /* Detach parameter */ + *arg = sieve_ast_arguments_detach(*arg,1); + return TRUE; +} + +/* + * Code generation + */ + +static bool +tag_flags_generate(const struct sieve_codegen_env *cgenv, + struct sieve_ast_argument *arg, struct sieve_command *cmd) +{ + struct sieve_ast_argument *param; + + if (sieve_ast_argument_type(arg) != SAAT_TAG) + return FALSE; + + sieve_opr_side_effect_emit(cgenv->sblock, arg->argument->ext, + &flags_side_effect); + + if (sieve_argument_is(arg, tag_flags)) { + /* Explicit :flags tag */ + param = arg->parameters; + + /* Call the generation function for the argument */ + if (param->argument != NULL && param->argument->def != NULL && + param->argument->def->generate != NULL && + !param->argument->def->generate(cgenv, param, cmd)) + return FALSE; + } else if (sieve_argument_is(arg, tag_flags_implicit)) { + /* Implicit flags */ + sieve_opr_omitted_emit(cgenv->sblock); + } else { + /* Something else?! */ + i_unreached(); + } + return TRUE; +} + +/* + * Side effect implementation + */ + +/* Context data */ + +struct seff_flags_context { + ARRAY(const char *) keywords; + enum mail_flags flags; +}; + +/* Context coding */ + +static bool +seff_flags_dump_context(const struct sieve_side_effect *seffect ATTR_UNUSED, + const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + return sieve_opr_stringlist_dump_ex(denv, address, "flags", "INTERNAL"); +} + +static struct seff_flags_context * +seff_flags_get_implicit_context(const struct sieve_extension *this_ext, + struct sieve_result *result) +{ + pool_t pool = sieve_result_pool(result); + struct seff_flags_context *ctx; + const char *flag; + struct ext_imap4flags_iter flit; + + ctx = p_new(pool, struct seff_flags_context, 1); + p_array_init(&ctx->keywords, pool, 2); + + T_BEGIN { + /* Unpack */ + ext_imap4flags_get_implicit_flags_init(&flit, this_ext, result); + while ((flag = ext_imap4flags_iter_get_flag(&flit)) != NULL) { + if (flag != NULL && *flag != '\\') { + /* keyword */ + const char *keyword = p_strdup(pool, flag); + array_append(&ctx->keywords, &keyword, 1); + } else { + /* system flag */ + if (flag == NULL || + strcasecmp(flag, "\\flagged") == 0) + ctx->flags |= MAIL_FLAGGED; + else if (strcasecmp(flag, "\\answered") == 0) + ctx->flags |= MAIL_ANSWERED; + else if (strcasecmp(flag, "\\deleted") == 0) + ctx->flags |= MAIL_DELETED; + else if (strcasecmp(flag, "\\seen") == 0) + ctx->flags |= MAIL_SEEN; + else if (strcasecmp(flag, "\\draft") == 0) + ctx->flags |= MAIL_DRAFT; + } + } + } T_END; + + return ctx; +} + +static int +seff_flags_do_read_context(const struct sieve_side_effect *seffect, + const struct sieve_runtime_env *renv, + sieve_size_t *address, void **se_context) +{ + pool_t pool = sieve_result_pool(renv->result); + struct seff_flags_context *ctx; + string_t *flags_item; + struct sieve_stringlist *flag_list = NULL; + int ret; + + ret = sieve_opr_stringlist_read_ex(renv, address, "flags", TRUE, + &flag_list); + if (ret <= 0) + return ret; + + if (flag_list == NULL) { + /* Flag list is omitted, use current value of internal + * variable to construct side effect context. + */ + *se_context = seff_flags_get_implicit_context( + SIEVE_OBJECT_EXTENSION(seffect), renv->result); + return SIEVE_EXEC_OK; + } + + ctx = p_new(pool, struct seff_flags_context, 1); + p_array_init(&ctx->keywords, pool, 2); + + /* Unpack */ + flags_item = NULL; + while ((ret = sieve_stringlist_next_item(flag_list, &flags_item)) > 0) { + const char *flag; + struct ext_imap4flags_iter flit; + + ext_imap4flags_iter_init(&flit, flags_item); + + while ((flag = ext_imap4flags_iter_get_flag(&flit)) != NULL) { + if (flag != NULL && *flag != '\\') { + /* keyword */ + const char *keyword = p_strdup(pool, flag); + + /* FIXME: should check for duplicates (cannot + trust variables) */ + array_append(&ctx->keywords, &keyword, 1); + } else { + /* system flag */ + if (flag == NULL || + strcasecmp(flag, "\\flagged") == 0) + ctx->flags |= MAIL_FLAGGED; + else if (strcasecmp(flag, "\\answered") == 0) + ctx->flags |= MAIL_ANSWERED; + else if (strcasecmp(flag, "\\deleted") == 0) + ctx->flags |= MAIL_DELETED; + else if (strcasecmp(flag, "\\seen") == 0) + ctx->flags |= MAIL_SEEN; + else if (strcasecmp(flag, "\\draft") == 0) + ctx->flags |= MAIL_DRAFT; + } + } + } + + if (ret < 0) + return flag_list->exec_status; + + *se_context = (void *)ctx; + return SIEVE_EXEC_OK; +} + +static int +seff_flags_read_context(const struct sieve_side_effect *seffect, + const struct sieve_runtime_env *renv, + sieve_size_t *address, void **se_context) +{ + int ret; + + T_BEGIN { + ret = seff_flags_do_read_context(seffect, renv, address, + se_context); + } T_END; + + return ret; +} + +/* Result verification */ + +static int +seff_flags_merge(const struct sieve_runtime_env *renv ATTR_UNUSED, + const struct sieve_action *action ATTR_UNUSED, + const struct sieve_side_effect *old_seffect ATTR_UNUSED, + const struct sieve_side_effect *new_seffect, + void **old_context) +{ + if (new_seffect != NULL) + *old_context = new_seffect->context; + return 1; +} + +/* Result printing */ + +static void +seff_flags_print(const struct sieve_side_effect *seffect, + const struct sieve_action *action ATTR_UNUSED, + const struct sieve_result_print_env *rpenv, + bool *keep ATTR_UNUSED) +{ + struct sieve_result *result = rpenv->result; + struct seff_flags_context *ctx = + (struct seff_flags_context *)seffect->context; + unsigned int i; + + if (ctx == NULL) { + ctx = seff_flags_get_implicit_context( + SIEVE_OBJECT_EXTENSION(seffect), result); + } + + if (ctx->flags != 0 || array_count(&ctx->keywords) > 0) { + T_BEGIN { + string_t *flags = t_str_new(128); + + if ((ctx->flags & MAIL_FLAGGED) > 0) + str_printfa(flags, " \\flagged"); + if ((ctx->flags & MAIL_ANSWERED) > 0) + str_printfa(flags, " \\answered"); + if ((ctx->flags & MAIL_DELETED) > 0) + str_printfa(flags, " \\deleted"); + if ((ctx->flags & MAIL_SEEN) > 0) + str_printfa(flags, " \\seen"); + if ((ctx->flags & MAIL_DRAFT) > 0) + str_printfa(flags, " \\draft"); + + for (i = 0; i < array_count(&ctx->keywords); i++) { + const char *const *keyword = + array_idx(&ctx->keywords, i); + str_printfa(flags, " %s", + str_sanitize(*keyword, 64)); + } + + sieve_result_seffect_printf(rpenv, "add IMAP flags:%s", + str_c(flags)); + } T_END; + } +} + +/* Result execution */ + +static int +seff_flags_pre_execute(const struct sieve_side_effect *seffect, + const struct sieve_action_exec_env *aenv, + void *tr_context, void **se_tr_context ATTR_UNUSED) +{ + struct seff_flags_context *ctx = seffect->context; + const char *const *keywords; + + if (ctx == NULL) { + ctx = seff_flags_get_implicit_context( + SIEVE_OBJECT_EXTENSION(seffect), aenv->result); + } + + (void)array_append_space(&ctx->keywords); + keywords = array_idx(&ctx->keywords, 0); + + sieve_act_store_add_flags(aenv, tr_context, keywords, ctx->flags); + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/plugins/imap4flags/tst-hasflag.c b/pigeonhole/src/lib-sieve/plugins/imap4flags/tst-hasflag.c new file mode 100644 index 0000000..23f2acc --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/imap4flags/tst-hasflag.c @@ -0,0 +1,248 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" + +#include "sieve-commands.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-match.h" + +#include "ext-imap4flags-common.h" + +/* + * Hasflag test + * + * Syntax: + * hasflag [MATCH-TYPE] [COMPARATOR] [<variable-list: string-list>] + * <list-of-flags: string-list> + */ + +static bool tst_hasflag_registered + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool tst_hasflag_validate + (struct sieve_validator *valdtr, struct sieve_command *tst); +static bool tst_hasflag_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd); + +const struct sieve_command_def tst_hasflag = { + .identifier = "hasflag", + .type = SCT_TEST, + .positional_args = -1, /* We check positional arguments ourselves */ + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = tst_hasflag_registered, + .validate = tst_hasflag_validate, + .generate = tst_hasflag_generate +}; + +/* + * Hasflag operation + */ + +static bool tst_hasflag_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int tst_hasflag_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def hasflag_operation = { + .mnemonic = "HASFLAG", + .ext_def = &imap4flags_extension, + .code = EXT_IMAP4FLAGS_OPERATION_HASFLAG, + .dump = tst_hasflag_operation_dump, + .execute = tst_hasflag_operation_execute +}; + +/* + * Optional arguments + */ + +enum tst_hasflag_optional { + OPT_VARIABLES = SIEVE_MATCH_OPT_LAST, +}; + +/* + * Tag registration + */ + +static bool tst_hasflag_registered +(struct sieve_validator *valdtr, const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_command_registration *cmd_reg) +{ + /* The order of these is not significant */ + sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR); + sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE); + + return TRUE; +} + +/* + * Validation + */ + +static bool tst_hasflag_validate +(struct sieve_validator *valdtr, struct sieve_command *tst) +{ + struct sieve_ast_argument *vars = tst->first_positional; + struct sieve_ast_argument *keys = sieve_ast_argument_next(vars); + const struct sieve_match_type mcht_default = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + const struct sieve_comparator cmp_default = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + + if ( !ext_imap4flags_command_validate(valdtr, tst) ) + return FALSE; + + if ( keys == NULL ) { + keys = vars; + vars = NULL; + } else { + vars->argument->id_code = OPT_VARIABLES; + } + + /* Validate the key argument to a specified match type */ + return sieve_match_type_validate + (valdtr, tst, keys, &mcht_default, &cmp_default); +} + +/* + * Code generation + */ + +static bool tst_hasflag_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + sieve_operation_emit(cgenv->sblock, cmd->ext, &hasflag_operation); + + /* Generate arguments */ + if ( !sieve_generate_arguments(cgenv, cmd, NULL) ) + return FALSE; + + return TRUE; +} + +/* + * Code dump + */ + +static bool tst_hasflag_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + int opt_code = 0; + + sieve_code_dumpf(denv, "HASFLAG"); + sieve_code_descend(denv); + + /* Optional operands */ + + for (;;) { + bool opok = TRUE; + int opt; + + if ( (opt=sieve_match_opr_optional_dump(denv, address, &opt_code)) + < 0 ) + return FALSE; + + if ( opt == 0 ) break; + + switch ( opt_code ) { + case OPT_VARIABLES: + opok = sieve_opr_stringlist_dump(denv, address, "variables"); + break; + default: + return FALSE; + } + + if ( !opok ) return FALSE; + } + + return + sieve_opr_stringlist_dump(denv, address, "list of flags"); +} + +/* + * Interpretation + */ + +static int tst_hasflag_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + const struct sieve_operation *op = renv->oprtn; + int opt_code = 0; + struct sieve_comparator cmp = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + struct sieve_match_type mcht = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + struct sieve_stringlist *flag_list, *variables_list, *value_list, *key_list; + int match, ret; + + /* + * Read operands + */ + + /* Optional operands */ + + variables_list = NULL; + for (;;) { + int opt; + + if ( (opt=sieve_match_opr_optional_read + (renv, address, &opt_code, &ret, &cmp, &mcht)) < 0 ) + return ret; + + if ( opt == 0 ) break; + + switch ( opt_code ) { + case OPT_VARIABLES: + ret = sieve_opr_stringlist_read + (renv, address, "variables-list", &variables_list); + break; + default: + sieve_runtime_trace_error(renv, "invalid optional operand"); + ret = SIEVE_EXEC_BIN_CORRUPT; + } + + if ( ret <= 0 ) return ret; + } + + /* Fixed operands */ + + if ( (ret=sieve_opr_stringlist_read(renv, address, "flag-list", &flag_list)) + <= 0 ) + return ret; + + /* + * Perform operation + */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "hasflag test"); + + value_list = sieve_ext_imap4flags_get_flags + (renv, op->ext, variables_list); + + if ( sieve_match_type_is(&mcht, is_match_type) || + sieve_match_type_is(&mcht, contains_match_type) ) { + key_list = sieve_ext_imap4flags_get_flags + (renv, op->ext, flag_list); + } else { + key_list = flag_list; + } + + /* Perform match */ + if ( (match=sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret)) < 0 ) + return ret; + + /* Set test result for subsequent conditional jump */ + sieve_interpreter_set_test_result(renv->interp, match > 0); + return SIEVE_EXEC_OK; +} + + diff --git a/pigeonhole/src/lib-sieve/plugins/include/Makefile.am b/pigeonhole/src/lib-sieve/plugins/include/Makefile.am new file mode 100644 index 0000000..5ce1599 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/include/Makefile.am @@ -0,0 +1,24 @@ +noinst_LTLIBRARIES = libsieve_ext_include.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + -I$(srcdir)/../variables \ + $(LIBDOVECOT_INCLUDE) + +cmds = \ + cmd-include.c \ + cmd-return.c \ + cmd-global.c + +libsieve_ext_include_la_SOURCES = \ + $(cmds) \ + ext-include-common.c \ + ext-include-binary.c \ + ext-include-variables.c \ + ext-include.c + +noinst_HEADERS = \ + ext-include-common.h \ + ext-include-limits.h \ + ext-include-binary.h \ + ext-include-variables.h diff --git a/pigeonhole/src/lib-sieve/plugins/include/Makefile.in b/pigeonhole/src/lib-sieve/plugins/include/Makefile.in new file mode 100644 index 0000000..06f003f --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/include/Makefile.in @@ -0,0 +1,718 @@ +# 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/plugins/include +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_ext_include_la_LIBADD = +am__objects_1 = cmd-include.lo cmd-return.lo cmd-global.lo +am_libsieve_ext_include_la_OBJECTS = $(am__objects_1) \ + ext-include-common.lo ext-include-binary.lo \ + ext-include-variables.lo ext-include.lo +libsieve_ext_include_la_OBJECTS = \ + $(am_libsieve_ext_include_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)/cmd-global.Plo \ + ./$(DEPDIR)/cmd-include.Plo ./$(DEPDIR)/cmd-return.Plo \ + ./$(DEPDIR)/ext-include-binary.Plo \ + ./$(DEPDIR)/ext-include-common.Plo \ + ./$(DEPDIR)/ext-include-variables.Plo \ + ./$(DEPDIR)/ext-include.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_ext_include_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_include_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_ext_include.la +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + -I$(srcdir)/../variables \ + $(LIBDOVECOT_INCLUDE) + +cmds = \ + cmd-include.c \ + cmd-return.c \ + cmd-global.c + +libsieve_ext_include_la_SOURCES = \ + $(cmds) \ + ext-include-common.c \ + ext-include-binary.c \ + ext-include-variables.c \ + ext-include.c + +noinst_HEADERS = \ + ext-include-common.h \ + ext-include-limits.h \ + ext-include-binary.h \ + ext-include-variables.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/plugins/include/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/include/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_ext_include.la: $(libsieve_ext_include_la_OBJECTS) $(libsieve_ext_include_la_DEPENDENCIES) $(EXTRA_libsieve_ext_include_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_include_la_OBJECTS) $(libsieve_ext_include_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-global.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-include.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-return.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-include-binary.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-include-common.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-include-variables.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-include.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)/cmd-global.Plo + -rm -f ./$(DEPDIR)/cmd-include.Plo + -rm -f ./$(DEPDIR)/cmd-return.Plo + -rm -f ./$(DEPDIR)/ext-include-binary.Plo + -rm -f ./$(DEPDIR)/ext-include-common.Plo + -rm -f ./$(DEPDIR)/ext-include-variables.Plo + -rm -f ./$(DEPDIR)/ext-include.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)/cmd-global.Plo + -rm -f ./$(DEPDIR)/cmd-include.Plo + -rm -f ./$(DEPDIR)/cmd-return.Plo + -rm -f ./$(DEPDIR)/ext-include-binary.Plo + -rm -f ./$(DEPDIR)/ext-include-common.Plo + -rm -f ./$(DEPDIR)/ext-include-variables.Plo + -rm -f ./$(DEPDIR)/ext-include.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/plugins/include/cmd-global.c b/pigeonhole/src/lib-sieve/plugins/include/cmd-global.c new file mode 100644 index 0000000..0ef37eb --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/include/cmd-global.c @@ -0,0 +1,329 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" + +#include "sieve-common.h" +#include "sieve-code.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-binary.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "sieve-ext-variables.h" + +#include "ext-include-common.h" +#include "ext-include-binary.h" +#include "ext-include-variables.h" + +/* + * Commands + */ + +static bool cmd_global_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); +static bool cmd_global_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd); + +const struct sieve_command_def cmd_global = { + .identifier = "global", + .type = SCT_COMMAND, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = cmd_global_validate, + .generate = cmd_global_generate, +}; + +/* DEPRICATED: + */ + +/* Import command + * + * Syntax + * import + */ +const struct sieve_command_def cmd_import = { + .identifier = "import", + .type = SCT_COMMAND, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = cmd_global_validate, + .generate = cmd_global_generate, +}; + +/* Export command + * + * Syntax + * export + */ +const struct sieve_command_def cmd_export = { + .identifier = "export", + .type = SCT_COMMAND, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = cmd_global_validate, + .generate = cmd_global_generate, +}; + +/* + * Operations + */ + +static bool opc_global_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int opc_global_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +/* Global operation */ + +const struct sieve_operation_def global_operation = { + .mnemonic = "GLOBAL", + .ext_def = &include_extension, + .code = EXT_INCLUDE_OPERATION_GLOBAL, + .dump = opc_global_dump, + .execute = opc_global_execute +}; + +/* + * Validation + */ + +static inline struct sieve_argument *_create_variable_argument +(struct sieve_command *cmd, struct sieve_variable *var) +{ + struct sieve_argument *argument = sieve_argument_create + (cmd->ast_node->ast, NULL, cmd->ext, 0); + + argument->data = (void *) var; + + return argument; +} + +static bool cmd_global_validate +(struct sieve_validator *valdtr, struct sieve_command *cmd) +{ + const struct sieve_extension *this_ext = cmd->ext; + struct sieve_ast_argument *arg = cmd->first_positional; + struct sieve_command *prev = sieve_command_prev(cmd); + + /* DEPRECATED: Check valid command placement */ + if ( !sieve_command_is(cmd, cmd_global) ) { + if ( !sieve_command_is_toplevel(cmd) || + ( !sieve_command_is_first(cmd) && prev != NULL && + !sieve_command_is(prev, cmd_require) && + !sieve_command_is(prev, cmd_import) && + !sieve_command_is(prev, cmd_export) ) ) { + sieve_command_validate_error(valdtr, cmd, + "the DEPRECATED %s command can only be placed at top level " + "at the beginning of the file after any require or " + "import/export commands", + sieve_command_identifier(cmd)); + return FALSE; + } + } + + /* Check for use of variables extension */ + if ( !ext_include_validator_have_variables(this_ext, valdtr) ) { + sieve_command_validate_error(valdtr, cmd, + "%s command requires that variables extension is active", + sieve_command_identifier(cmd)); + return FALSE; + } + + /* Register global variable */ + if ( sieve_ast_argument_type(arg) == SAAT_STRING ) { + /* Single string */ + const char *identifier = sieve_ast_argument_strc(arg); + struct sieve_variable *var; + + if ( (var=ext_include_variable_import_global + (valdtr, cmd, identifier)) == NULL ) + return FALSE; + + arg->argument = _create_variable_argument(cmd, var); + + } else if ( sieve_ast_argument_type(arg) == SAAT_STRING_LIST ) { + /* String list */ + struct sieve_ast_argument *stritem = sieve_ast_strlist_first(arg); + + while ( stritem != NULL ) { + const char *identifier = sieve_ast_argument_strc(stritem); + struct sieve_variable *var; + + if ( (var=ext_include_variable_import_global + (valdtr, cmd, identifier)) == NULL ) + return FALSE; + + stritem->argument = _create_variable_argument(cmd, var); + + stritem = sieve_ast_strlist_next(stritem); + } + } else { + /* Something else */ + sieve_argument_validate_error(valdtr, arg, + "the %s command accepts a single string or string list argument, " + "but %s was found", sieve_command_identifier(cmd), + sieve_ast_argument_name(arg)); + return FALSE; + } + + /* Join global commands with predecessors if possible */ + if ( sieve_commands_equal(prev, cmd) ) { + /* Join this command's string list with the previous one */ + prev->first_positional = sieve_ast_stringlist_join + (prev->first_positional, cmd->first_positional); + + if ( prev->first_positional == NULL ) { + /* Not going to happen unless MAXINT stringlist items are specified */ + sieve_command_validate_error(valdtr, cmd, + "compiler reached AST limit (script too complex)"); + return FALSE; + } + + /* Detach this command node */ + sieve_ast_node_detach(cmd->ast_node); + } + + return TRUE; +} + +/* + * Code generation + */ + +static bool cmd_global_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + struct sieve_ast_argument *arg = cmd->first_positional; + + sieve_operation_emit(cgenv->sblock, cmd->ext, &global_operation); + + if ( sieve_ast_argument_type(arg) == SAAT_STRING ) { + /* Single string */ + struct sieve_variable *var = (struct sieve_variable *) arg->argument->data; + + (void)sieve_binary_emit_unsigned(cgenv->sblock, 1); + (void)sieve_binary_emit_unsigned(cgenv->sblock, var->index); + + } else if ( sieve_ast_argument_type(arg) == SAAT_STRING_LIST ) { + /* String list */ + struct sieve_ast_argument *stritem = sieve_ast_strlist_first(arg); + + (void)sieve_binary_emit_unsigned + (cgenv->sblock, sieve_ast_strlist_count(arg)); + + while ( stritem != NULL ) { + struct sieve_variable *var = + (struct sieve_variable *) stritem->argument->data; + + (void)sieve_binary_emit_unsigned(cgenv->sblock, var->index); + + stritem = sieve_ast_strlist_next(stritem); + } + } else { + i_unreached(); + } + + return TRUE; +} + +/* + * Code dump + */ + +static bool opc_global_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + const struct sieve_extension *this_ext = denv->oprtn->ext; + unsigned int count, i, var_count; + struct sieve_variable_scope_binary *global_vars; + struct sieve_variable_scope *global_scope; + struct sieve_variable * const *vars; + + if ( !sieve_binary_read_unsigned(denv->sblock, address, &count) ) + return FALSE; + + sieve_code_dumpf(denv, "GLOBAL (count: %u):", count); + + global_vars = ext_include_binary_get_global_scope(this_ext, denv->sbin); + global_scope = sieve_variable_scope_binary_get(global_vars); + vars = sieve_variable_scope_get_variables(global_scope, &var_count); + + sieve_code_descend(denv); + + for ( i = 0; i < count; i++ ) { + unsigned int index; + + sieve_code_mark(denv); + if ( !sieve_binary_read_unsigned(denv->sblock, address, &index) || + index >= var_count ) + return FALSE; + + sieve_code_dumpf(denv, "%d: VAR[%d]: '%s'", i, index, vars[index]->identifier); + } + + return TRUE; +} + +/* + * Execution + */ + +static int opc_global_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + const struct sieve_extension *this_ext = renv->oprtn->ext; + struct sieve_variable_scope_binary *global_vars; + struct sieve_variable_scope *global_scope; + struct sieve_variable_storage *storage; + struct sieve_variable * const *vars; + unsigned int var_count, count, i; + + if ( !sieve_binary_read_unsigned(renv->sblock, address, &count) ) { + sieve_runtime_trace_error(renv, + "global: count operand invalid"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + global_vars = ext_include_binary_get_global_scope(this_ext, renv->sbin); + global_scope = sieve_variable_scope_binary_get(global_vars); + vars = sieve_variable_scope_get_variables(global_scope, &var_count); + storage = ext_include_interpreter_get_global_variables + (this_ext, renv->interp); + + for ( i = 0; i < count; i++ ) { + unsigned int index; + + if ( !sieve_binary_read_unsigned(renv->sblock, address, &index) ) { + sieve_runtime_trace_error(renv, + "global: variable index operand invalid"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if ( index >= var_count ) { + sieve_runtime_trace_error(renv, + "global: variable index %u is invalid in global storage (> %u)", + index, var_count); + return SIEVE_EXEC_BIN_CORRUPT; + } + + sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, + "global: exporting variable '%s' [gvid: %u, vid: %u]", + vars[index]->identifier, i, index); + + /* Make sure variable is initialized (export) */ + (void)sieve_variable_get_modifiable(storage, index, NULL); + } + + return SIEVE_EXEC_OK; +} + + diff --git a/pigeonhole/src/lib-sieve/plugins/include/cmd-include.c b/pigeonhole/src/lib-sieve/plugins/include/cmd-include.c new file mode 100644 index 0000000..92aefb4 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/include/cmd-include.c @@ -0,0 +1,406 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str-sanitize.h" + +#include "sieve-common.h" +#include "sieve-script.h" +#include "sieve-storage.h" +#include "sieve-ast.h" +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-binary.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "ext-include-common.h" +#include "ext-include-binary.h" + +/* + * Include command + * + * Syntax: + * include [LOCATION] [":once"] [":optional"] <value: string> + * + * [LOCATION]: + * ":personal" / ":global" + */ + +static bool cmd_include_registered + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool cmd_include_pre_validate + (struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *cmd); +static bool cmd_include_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); +static bool cmd_include_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx); + +const struct sieve_command_def cmd_include = { + .identifier = "include", + .type = SCT_COMMAND, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = cmd_include_registered, + .pre_validate = cmd_include_pre_validate, + .validate = cmd_include_validate, + .generate = cmd_include_generate +}; + +/* + * Include operation + */ + +static bool opc_include_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int opc_include_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def include_operation = { + .mnemonic = "include", + .ext_def = &include_extension, + .code = EXT_INCLUDE_OPERATION_INCLUDE, + .dump = opc_include_dump, + .execute = opc_include_execute +}; + +/* + * Context structures + */ + +struct cmd_include_context_data { + enum ext_include_script_location location; + + struct sieve_script *script; + enum ext_include_flags flags; + + bool location_assigned:1; +}; + +/* + * Tagged arguments + */ + +static bool cmd_include_validate_location_tag + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd); + +static const struct sieve_argument_def include_personal_tag = { + .identifier = "personal", + .validate = cmd_include_validate_location_tag +}; + +static const struct sieve_argument_def include_global_tag = { + .identifier = "global", + .validate = cmd_include_validate_location_tag +}; + +static bool cmd_include_validate_boolean_tag + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd); + +static const struct sieve_argument_def include_once_tag = { + .identifier = "once", + .validate = cmd_include_validate_boolean_tag +}; + +static const struct sieve_argument_def include_optional_tag = { + .identifier = "optional", + .validate = cmd_include_validate_boolean_tag +}; + +/* + * Tag validation + */ + +static bool cmd_include_validate_location_tag +(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct cmd_include_context_data *ctx_data = + (struct cmd_include_context_data *) cmd->data; + + if ( ctx_data->location_assigned) { + sieve_argument_validate_error(valdtr, *arg, + "include: cannot use location tags ':personal' and ':global' " + "multiple times"); + return FALSE; + } + + if ( sieve_argument_is(*arg, include_personal_tag) ) + ctx_data->location = EXT_INCLUDE_LOCATION_PERSONAL; + else if ( sieve_argument_is(*arg, include_global_tag) ) + ctx_data->location = EXT_INCLUDE_LOCATION_GLOBAL; + else + return FALSE; + + ctx_data->location_assigned = TRUE; + + /* Delete this tag (for now) */ + *arg = sieve_ast_arguments_detach(*arg, 1); + + return TRUE; +} + +static bool cmd_include_validate_boolean_tag +(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct cmd_include_context_data *ctx_data = + (struct cmd_include_context_data *) cmd->data; + + if ( sieve_argument_is(*arg, include_once_tag) ) + ctx_data->flags |= EXT_INCLUDE_FLAG_ONCE; + else + ctx_data->flags |= EXT_INCLUDE_FLAG_OPTIONAL; + + /* Delete this tag (for now) */ + *arg = sieve_ast_arguments_detach(*arg, 1); + + return TRUE; +} + +/* + * Command registration + */ + +static bool cmd_include_registered +(struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_personal_tag, 0); + sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_global_tag, 0); + sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_once_tag, 0); + sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_optional_tag, 0); + + return TRUE; +} + +/* + * Command validation + */ + +static bool cmd_include_pre_validate +(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *cmd) +{ + struct cmd_include_context_data *ctx_data; + + /* Assign context */ + ctx_data = p_new(sieve_command_pool(cmd), struct cmd_include_context_data, 1); + ctx_data->location = EXT_INCLUDE_LOCATION_PERSONAL; + cmd->data = ctx_data; + + return TRUE; +} + +static bool cmd_include_validate +(struct sieve_validator *valdtr, struct sieve_command *cmd) +{ + const struct sieve_extension *this_ext = cmd->ext; + struct sieve_ast_argument *arg = cmd->first_positional; + struct cmd_include_context_data *ctx_data = + (struct cmd_include_context_data *) cmd->data; + struct sieve_storage *storage; + struct sieve_script *script; + const char *script_name; + enum sieve_error error = SIEVE_ERROR_NONE; + int ret; + + /* Check argument */ + if ( !sieve_validate_positional_argument + (valdtr, cmd, arg, "value", 1, SAAT_STRING) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, cmd, arg, FALSE) ) + return FALSE; + + /* + * Variables are not allowed. + */ + if ( !sieve_argument_is_string_literal(arg) ) { + sieve_argument_validate_error(valdtr, arg, + "the include command requires a constant string for its value argument"); + return FALSE; + } + + /* Find the script */ + + script_name = sieve_ast_argument_strc(arg); + + if ( !sieve_script_name_is_valid(script_name) ) { + sieve_argument_validate_error(valdtr, arg, + "include: invalid script name '%s'", + str_sanitize(script_name, 80)); + return FALSE; + } + + storage = ext_include_get_script_storage + (this_ext, ctx_data->location, script_name, &error); + if ( storage == NULL ) { + // FIXME: handle ':optional' in this case + if (error == SIEVE_ERROR_NOT_FOUND) { + sieve_argument_validate_error(valdtr, arg, + "include: %s location for included script `%s' is unavailable " + "(contact system administrator for more information)", + ext_include_script_location_name(ctx_data->location), + str_sanitize(script_name, 80)); + } else { + sieve_argument_validate_error(valdtr, arg, + "include: failed to access %s location for included script `%s' " + "(contact system administrator for more information)", + ext_include_script_location_name(ctx_data->location), + str_sanitize(script_name, 80)); + } + return FALSE; + } + + /* Create script object */ + script = sieve_storage_get_script + (storage, script_name, &error); + if ( script == NULL ) + return FALSE; + + ret = sieve_script_open(script, &error); + if ( ret < 0 ) { + if ( error != SIEVE_ERROR_NOT_FOUND ) { + sieve_argument_validate_error(valdtr, arg, + "failed to access included %s script '%s': %s", + ext_include_script_location_name(ctx_data->location), + str_sanitize(script_name, 80), + sieve_script_get_last_error_lcase(script)); + sieve_script_unref(&script); + return FALSE; + + /* Not found */ + } else { + enum sieve_compile_flags cpflags = + sieve_validator_compile_flags(valdtr); + + if ( (ctx_data->flags & EXT_INCLUDE_FLAG_OPTIONAL) != 0 ) { + /* :optional */ + + } else if ( (cpflags & SIEVE_COMPILE_FLAG_UPLOADED) != 0 ) { + /* Script is being uploaded */ + sieve_argument_validate_warning(valdtr, arg, + "included %s script '%s' does not exist (ignored during upload)", + ext_include_script_location_name(ctx_data->location), + str_sanitize(script_name, 80)); + ctx_data->flags |= EXT_INCLUDE_FLAG_MISSING_AT_UPLOAD; + + } else { + /* Should have existed */ + sieve_argument_validate_error(valdtr, arg, + "included %s script '%s' does not exist", + ext_include_script_location_name(ctx_data->location), + str_sanitize(script_name, 80)); + sieve_script_unref(&script); + return FALSE; + } + } + } + + ext_include_ast_link_included_script(cmd->ext, cmd->ast_node->ast, script); + ctx_data->script = script; + + (void)sieve_ast_arguments_detach(arg, 1); + return TRUE; +} + +/* + * Code Generation + */ + +static bool cmd_include_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + struct cmd_include_context_data *ctx_data = + (struct cmd_include_context_data *) cmd->data; + const struct ext_include_script_info *included; + int ret; + + /* Compile (if necessary) and include the script into the binary. + * This yields the id of the binary block containing the compiled byte code. + */ + if ( (ret=ext_include_generate_include + (cgenv, cmd, ctx_data->location, ctx_data->flags, ctx_data->script, + &included)) < 0 ) + return FALSE; + + if ( ret > 0 ) { + (void)sieve_operation_emit(cgenv->sblock, cmd->ext, &include_operation); + (void)sieve_binary_emit_unsigned(cgenv->sblock, included->id); + (void)sieve_binary_emit_byte(cgenv->sblock, ctx_data->flags); + } + + return TRUE; +} + +/* + * Code dump + */ + +static bool opc_include_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + const struct ext_include_script_info *included; + struct ext_include_binary_context *binctx; + unsigned int include_id, flags; + + sieve_code_dumpf(denv, "INCLUDE:"); + + sieve_code_mark(denv); + if ( !sieve_binary_read_unsigned(denv->sblock, address, &include_id) ) + return FALSE; + + if ( !sieve_binary_read_byte(denv->sblock, address, &flags) ) + return FALSE; + + binctx = ext_include_binary_get_context(denv->oprtn->ext, denv->sbin); + included = ext_include_binary_script_get_included(binctx, include_id); + if ( included == NULL ) + return FALSE; + + sieve_code_descend(denv); + sieve_code_dumpf(denv, "script: `%s' from %s %s%s[ID: %d, BLOCK: %d]", + sieve_script_name(included->script), sieve_script_location(included->script), + ((flags & EXT_INCLUDE_FLAG_ONCE) != 0 ? "(once) " : ""), + ((flags & EXT_INCLUDE_FLAG_OPTIONAL) != 0 ? "(optional) " : ""), + include_id, sieve_binary_block_get_id(included->block)); + + return TRUE; +} + +/* + * Execution + */ + +static int opc_include_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + unsigned int include_id, flags; + + if ( !sieve_binary_read_unsigned(renv->sblock, address, &include_id) ) { + sieve_runtime_trace_error(renv, "invalid include-id operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if ( !sieve_binary_read_unsigned(renv->sblock, address, &flags) ) { + sieve_runtime_trace_error(renv, "invalid flags operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + return ext_include_execute_include + (renv, include_id, (enum ext_include_flags)flags); +} + + + + + diff --git a/pigeonhole/src/lib-sieve/plugins/include/cmd-return.c b/pigeonhole/src/lib-sieve/plugins/include/cmd-return.c new file mode 100644 index 0000000..65a156d --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/include/cmd-return.c @@ -0,0 +1,71 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" + +#include "sieve-code.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" + +#include "ext-include-common.h" + +/* + * Return command + * + * Syntax + * return + */ + +static bool cmd_return_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd); + +const struct sieve_command_def cmd_return = { + .identifier = "return", + .type = SCT_COMMAND, + .positional_args = 0, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .generate = cmd_return_generate +}; + +/* + * Return operation + */ + +static int opc_return_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def return_operation = { + .mnemonic = "RETURN", + .ext_def = &include_extension, + .code = EXT_INCLUDE_OPERATION_RETURN, + .execute = opc_return_execute +}; + +/* + * Code generation + */ + +static bool cmd_return_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + sieve_operation_emit(cgenv->sblock, cmd->ext, &return_operation); + + return TRUE; +} + +/* + * Execution + */ + +static int opc_return_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address ATTR_UNUSED) +{ + ext_include_execute_return(renv); + return SIEVE_EXEC_OK; +} + + diff --git a/pigeonhole/src/lib-sieve/plugins/include/ext-include-binary.c b/pigeonhole/src/lib-sieve/plugins/include/ext-include-binary.c new file mode 100644 index 0000000..3711c30 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/include/ext-include-binary.c @@ -0,0 +1,492 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-script.h" +#include "sieve-storage.h" +#include "sieve-binary.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "sieve-ext-variables.h" + +#include "ext-include-common.h" +#include "ext-include-limits.h" +#include "ext-include-variables.h" +#include "ext-include-binary.h" + +/* + * Forward declarations + */ + +static bool ext_include_binary_pre_save + (const struct sieve_extension *ext, struct sieve_binary *sbin, + void *context, enum sieve_error *error_r); +static bool ext_include_binary_open + (const struct sieve_extension *ext, struct sieve_binary *sbin, + void *context); +static bool ext_include_binary_up_to_date + (const struct sieve_extension *ext, struct sieve_binary *sbin, + void *context, enum sieve_compile_flags cpflags); +static void ext_include_binary_free + (const struct sieve_extension *ext, struct sieve_binary *sbin, + void *context); + +/* + * Binary include extension + */ + +const struct sieve_binary_extension include_binary_ext = { + .extension = &include_extension, + .binary_pre_save = ext_include_binary_pre_save, + .binary_open = ext_include_binary_open, + .binary_free = ext_include_binary_free, + .binary_up_to_date = ext_include_binary_up_to_date +}; + +/* + * Binary context management + */ + +struct ext_include_binary_context { + struct sieve_binary *binary; + struct sieve_binary_block *dependency_block; + + HASH_TABLE(struct sieve_script *, + struct ext_include_script_info *) included_scripts; + ARRAY(struct ext_include_script_info *) include_index; + + struct sieve_variable_scope_binary *global_vars; + + bool outdated:1; +}; + +static struct ext_include_binary_context *ext_include_binary_create_context +(const struct sieve_extension *this_ext, struct sieve_binary *sbin) +{ + pool_t pool = sieve_binary_pool(sbin); + + struct ext_include_binary_context *ctx = + p_new(pool, struct ext_include_binary_context, 1); + + ctx->binary = sbin; + hash_table_create(&ctx->included_scripts, pool, 0, + sieve_script_hash, sieve_script_cmp); + p_array_init(&ctx->include_index, pool, 128); + + sieve_binary_extension_set(sbin, this_ext, &include_binary_ext, ctx); + + return ctx; +} + +struct ext_include_binary_context *ext_include_binary_get_context +(const struct sieve_extension *this_ext, struct sieve_binary *sbin) +{ + struct ext_include_binary_context *ctx = (struct ext_include_binary_context *) + sieve_binary_extension_get_context(sbin, this_ext); + + if ( ctx == NULL ) + ctx = ext_include_binary_create_context(this_ext, sbin); + + return ctx; +} + +struct ext_include_binary_context *ext_include_binary_init +(const struct sieve_extension *this_ext, struct sieve_binary *sbin, + struct sieve_ast *ast) +{ + struct ext_include_ast_context *ast_ctx = + ext_include_get_ast_context(this_ext, ast); + struct ext_include_binary_context *ctx; + + /* Get/create our context from the binary we are working on */ + ctx = ext_include_binary_get_context(this_ext, sbin); + + /* Create dependency block */ + if ( ctx->dependency_block == 0 ) + ctx->dependency_block = + sieve_binary_extension_create_block(sbin, this_ext); + + if ( ctx->global_vars == NULL ) { + ctx->global_vars = + sieve_variable_scope_binary_create(ast_ctx->global_vars); + sieve_variable_scope_binary_ref(ctx->global_vars); + } + + return ctx; +} + +/* + * Script inclusion + */ + +struct ext_include_script_info *ext_include_binary_script_include +(struct ext_include_binary_context *binctx, + enum ext_include_script_location location, enum ext_include_flags flags, + struct sieve_script *script, struct sieve_binary_block *inc_block) +{ + pool_t pool = sieve_binary_pool(binctx->binary); + struct ext_include_script_info *incscript; + + incscript = p_new(pool, struct ext_include_script_info, 1); + incscript->id = array_count(&binctx->include_index)+1; + incscript->location = location; + incscript->flags = flags; + incscript->script = script; + incscript->block = inc_block; + + /* Unreferenced on binary_free */ + sieve_script_ref(script); + + hash_table_insert(binctx->included_scripts, script, incscript); + array_append(&binctx->include_index, &incscript, 1); + + return incscript; +} + +struct ext_include_script_info *ext_include_binary_script_get_include_info +(struct ext_include_binary_context *binctx, struct sieve_script *script) +{ + struct ext_include_script_info *incscript = + hash_table_lookup(binctx->included_scripts, script); + + return incscript; +} + +const struct ext_include_script_info *ext_include_binary_script_get_included +(struct ext_include_binary_context *binctx, unsigned int include_id) +{ + if ( include_id > 0 && + (include_id - 1) < array_count(&binctx->include_index) ) { + struct ext_include_script_info *const *sinfo = + array_idx(&binctx->include_index, include_id - 1); + + return *sinfo; + } + + return NULL; +} + +const struct ext_include_script_info *ext_include_binary_script_get +(struct ext_include_binary_context *binctx, struct sieve_script *script) +{ + return hash_table_lookup(binctx->included_scripts, script); +} + +unsigned int ext_include_binary_script_get_count +(struct ext_include_binary_context *binctx) +{ + return array_count(&binctx->include_index); +} + +/* + * Variables + */ + +struct sieve_variable_scope_binary *ext_include_binary_get_global_scope +(const struct sieve_extension *this_ext, struct sieve_binary *sbin) +{ + struct ext_include_binary_context *binctx = + ext_include_binary_get_context(this_ext, sbin); + + return binctx->global_vars; +} + +/* + * Binary extension + */ + +static bool ext_include_binary_pre_save +(const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_binary *sbin ATTR_UNUSED, void *context, + enum sieve_error *error_r) +{ + struct ext_include_binary_context *binctx = + (struct ext_include_binary_context *) context; + struct ext_include_script_info *const *scripts; + struct sieve_binary_block *sblock = binctx->dependency_block; + unsigned int script_count, i; + bool result = TRUE; + + sieve_binary_block_clear(sblock); + + scripts = array_get(&binctx->include_index, &script_count); + + sieve_binary_emit_unsigned(sblock, script_count); + + for ( i = 0; i < script_count; i++ ) { + struct ext_include_script_info *incscript = scripts[i]; + + if ( incscript->block != NULL ) { + sieve_binary_emit_unsigned + (sblock, sieve_binary_block_get_id(incscript->block)); + } else { + sieve_binary_emit_unsigned(sblock, 0); + } + sieve_binary_emit_byte(sblock, incscript->location); + sieve_binary_emit_cstring(sblock, sieve_script_name(incscript->script)); + sieve_binary_emit_byte(sblock, incscript->flags); + sieve_script_binary_write_metadata(incscript->script, sblock); + } + + result = ext_include_variables_save(sblock, binctx->global_vars, error_r); + + return result; +} + +static bool ext_include_binary_open +(const struct sieve_extension *ext, struct sieve_binary *sbin, void *context) +{ + struct sieve_instance *svinst = ext->svinst; + struct ext_include_context *ext_ctx = + (struct ext_include_context *)ext->context; + struct ext_include_binary_context *binctx = + (struct ext_include_binary_context *) context; + struct sieve_binary_block *sblock; + unsigned int depcount, i, block_id; + sieve_size_t offset; + + sblock = sieve_binary_extension_get_block(sbin, ext); + block_id = sieve_binary_block_get_id(sblock); + + offset = 0; + + if ( !sieve_binary_read_unsigned(sblock, &offset, &depcount) ) { + e_error(svinst->event, + "include: failed to read include count " + "for dependency block %d of binary %s", block_id, + sieve_binary_path(sbin)); + return FALSE; + } + + /* Check include limit */ + if ( depcount > ext_ctx->max_includes ) { + e_error(svinst->event, + "include: binary %s includes too many scripts (%u > %u)", + sieve_binary_path(sbin), depcount, ext_ctx->max_includes); + return FALSE; + } + + /* Read dependencies */ + for ( i = 0; i < depcount; i++ ) { + unsigned int inc_block_id; + struct sieve_binary_block *inc_block = NULL; + unsigned int location, flags; + string_t *script_name; + struct sieve_storage *storage; + struct sieve_script *script; + enum sieve_error error; + int ret; + + if ( + !sieve_binary_read_unsigned(sblock, &offset, &inc_block_id) || + !sieve_binary_read_byte(sblock, &offset, &location) || + !sieve_binary_read_string(sblock, &offset, &script_name) || + !sieve_binary_read_byte(sblock, &offset, &flags) ) { + /* Binary is corrupt, recompile */ + e_error(svinst->event, + "include: failed to read included script " + "from dependency block %d of binary %s", + block_id, sieve_binary_path(sbin)); + return FALSE; + } + + if ( inc_block_id != 0 && + (inc_block=sieve_binary_block_get(sbin, inc_block_id)) == NULL ) { + e_error(svinst->event, + "include: failed to find block %d for included script " + "from dependency block %d of binary %s", + inc_block_id, block_id, + sieve_binary_path(sbin)); + return FALSE; + } + + if ( location >= EXT_INCLUDE_LOCATION_INVALID ) { + /* Binary is corrupt, recompile */ + e_error(svinst->event, + "include: dependency block %d of binary %s " + "uses invalid script location (id %d)", + block_id, sieve_binary_path(sbin), location); + return FALSE; + } + + /* Can we find the script dependency ? */ + storage = ext_include_get_script_storage + (ext, location, str_c(script_name), &error); + if ( storage == NULL ) { + /* No, recompile */ + // FIXME: handle ':optional' in this case + return FALSE; + } + + /* Can we open the script dependency ? */ + script = sieve_storage_get_script + (storage, str_c(script_name), &error); + if ( script == NULL ) { + /* No, recompile */ + return FALSE; + } + if ( sieve_script_open(script, &error) < 0 ) { + if ( error != SIEVE_ERROR_NOT_FOUND ) { + /* No, recompile */ + return FALSE; + } + + if ( (flags & EXT_INCLUDE_FLAG_OPTIONAL) == 0 ) { + /* Not supposed to be missing, recompile */ + if ( svinst->debug ) { + e_debug(svinst->event, "include: " + "script '%s' included in binary %s is missing, " + "so recompile", + str_c(script_name), + sieve_binary_path(sbin)); + } + return FALSE; + } + + } else if (inc_block == NULL) { + /* Script exists, but it is missing from the binary, recompile no matter + * what. + */ + if ( svinst->debug ) { + e_debug(svinst->event, "include: " + "script '%s' is missing in binary %s, but is now available, " + "so recompile", str_c(script_name), sieve_binary_path(sbin)); + } + sieve_script_unref(&script); + return FALSE; + } + + /* Can we read script metadata ? */ + if ( (ret=sieve_script_binary_read_metadata + (script, sblock, &offset)) < 0 ) { + /* Binary is corrupt, recompile */ + e_error(svinst->event, "include: " + "dependency block %d of binary %s " + "contains invalid script metadata for script %s", + block_id, sieve_binary_path(sbin), + sieve_script_location(script)); + sieve_script_unref(&script); + return FALSE; + } + + if ( ret == 0 ) + binctx->outdated = TRUE; + + (void)ext_include_binary_script_include + (binctx, location, flags, script, inc_block); + + sieve_script_unref(&script); + } + + if ( !ext_include_variables_load + (ext, sblock, &offset, &binctx->global_vars) ) + return FALSE; + + return TRUE; +} + +static bool ext_include_binary_up_to_date +(const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_binary *sbin ATTR_UNUSED, void *context, + enum sieve_compile_flags cpflags ATTR_UNUSED) +{ + struct ext_include_binary_context *binctx = + (struct ext_include_binary_context *) context; + + return !binctx->outdated; +} + +static void ext_include_binary_free +(const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_binary *sbin ATTR_UNUSED, void *context) +{ + struct ext_include_binary_context *binctx = + (struct ext_include_binary_context *) context; + struct hash_iterate_context *hctx; + struct sieve_script *script; + struct ext_include_script_info *incscript; + + /* Release references to all included script objects */ + hctx = hash_table_iterate_init(binctx->included_scripts); + while ( hash_table_iterate + (hctx, binctx->included_scripts, &script, &incscript) ) + sieve_script_unref(&incscript->script); + hash_table_iterate_deinit(&hctx); + + hash_table_destroy(&binctx->included_scripts); + + if ( binctx->global_vars != NULL ) + sieve_variable_scope_binary_unref(&binctx->global_vars); +} + +/* + * Dumping the binary + */ + +bool ext_include_binary_dump +(const struct sieve_extension *ext, struct sieve_dumptime_env *denv) +{ + struct sieve_binary *sbin = denv->sbin; + struct ext_include_binary_context *binctx = + ext_include_binary_get_context(ext, sbin); + struct hash_iterate_context *hctx; + struct sieve_script *script; + struct ext_include_script_info *incscript; + + if ( !ext_include_variables_dump(denv, binctx->global_vars) ) + return FALSE; + + hctx = hash_table_iterate_init(binctx->included_scripts); + while ( hash_table_iterate + (hctx, binctx->included_scripts, &script, &incscript) ) { + + if ( incscript->block == NULL ) { + sieve_binary_dump_sectionf(denv, "Included %s script '%s' (MISSING)", + ext_include_script_location_name(incscript->location), + sieve_script_name(incscript->script)); + + } else { + unsigned int block_id = sieve_binary_block_get_id(incscript->block); + + sieve_binary_dump_sectionf(denv, "Included %s script '%s' (block: %d)", + ext_include_script_location_name(incscript->location), + sieve_script_name(incscript->script), block_id); + + denv->sblock = incscript->block; + denv->cdumper = sieve_code_dumper_create(denv); + + if ( denv->cdumper == NULL ) + return FALSE; + + sieve_code_dumper_run(denv->cdumper); + sieve_code_dumper_free(&(denv->cdumper)); + } + } + hash_table_iterate_deinit(&hctx); + + return TRUE; +} + +bool ext_include_code_dump +(const struct sieve_extension *ext, const struct sieve_dumptime_env *denv, + sieve_size_t *address ATTR_UNUSED) +{ + struct sieve_binary *sbin = denv->sbin; + struct ext_include_binary_context *binctx = + ext_include_binary_get_context(ext, sbin); + struct ext_include_context *ectx = ext_include_get_context(ext); + + sieve_ext_variables_dump_set_scope + (ectx->var_ext, denv, ext, + sieve_variable_scope_binary_get(binctx->global_vars)); + + return TRUE; +} + + diff --git a/pigeonhole/src/lib-sieve/plugins/include/ext-include-binary.h b/pigeonhole/src/lib-sieve/plugins/include/ext-include-binary.h new file mode 100644 index 0000000..066df2f --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/include/ext-include-binary.h @@ -0,0 +1,64 @@ +#ifndef EXT_INCLUDE_BINARY_H +#define EXT_INCLUDE_BINARY_H + +#include "sieve-common.h" + +/* + * Binary context management + */ + +struct ext_include_binary_context; + +struct ext_include_binary_context *ext_include_binary_init + (const struct sieve_extension *this_ext, struct sieve_binary *sbin, + struct sieve_ast *ast); +struct ext_include_binary_context *ext_include_binary_get_context + (const struct sieve_extension *this_ext, struct sieve_binary *sbin); + +/* + * Variables + */ + +struct sieve_variable_scope_binary *ext_include_binary_get_global_scope + (const struct sieve_extension *this_ext, struct sieve_binary *sbin); + +/* + * Including scripts + */ + +struct ext_include_script_info { + unsigned int id; + + struct sieve_script *script; + enum ext_include_flags flags; + enum ext_include_script_location location; + + struct sieve_binary_block *block; +}; + +struct ext_include_script_info *ext_include_binary_script_include + (struct ext_include_binary_context *binctx, + enum ext_include_script_location location, enum ext_include_flags flags, + struct sieve_script *script, struct sieve_binary_block *inc_block); +struct ext_include_script_info *ext_include_binary_script_get_include_info + (struct ext_include_binary_context *binctx, struct sieve_script *script); + +const struct ext_include_script_info *ext_include_binary_script_get_included + (struct ext_include_binary_context *binctx, unsigned int include_id); +const struct ext_include_script_info *ext_include_binary_script_get + (struct ext_include_binary_context *binctx, struct sieve_script *script); +unsigned int ext_include_binary_script_get_count + (struct ext_include_binary_context *binctx); + +/* + * Dumping the binary + */ + +bool ext_include_binary_dump + (const struct sieve_extension *ext, struct sieve_dumptime_env *denv); +bool ext_include_code_dump + (const struct sieve_extension *ext, const struct sieve_dumptime_env *denv, + sieve_size_t *address ATTR_UNUSED); + +#endif + diff --git a/pigeonhole/src/lib-sieve/plugins/include/ext-include-common.c b/pigeonhole/src/lib-sieve/plugins/include/ext-include-common.c new file mode 100644 index 0000000..7af3f00 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/include/ext-include-common.c @@ -0,0 +1,892 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" +#include "str-sanitize.h" +#include "home-expand.h" + +#include "sieve-common.h" +#include "sieve-settings.h" +#include "sieve-error.h" +#include "sieve-script.h" +#include "sieve-storage.h" +#include "sieve-ast.h" +#include "sieve-binary.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" + +#include "ext-include-common.h" +#include "ext-include-limits.h" +#include "ext-include-binary.h" +#include "ext-include-variables.h" + + +/* + * Forward declarations + */ + +/* Generator context */ + +struct ext_include_generator_context { + unsigned int nesting_depth; + struct sieve_script *script; + struct ext_include_generator_context *parent; +}; + +static inline struct ext_include_generator_context * +ext_include_get_generator_context(const struct sieve_extension *ext_this, + struct sieve_generator *gentr); + +/* Interpreter context */ + +struct ext_include_interpreter_global { + ARRAY(struct sieve_script *) included_scripts; + + struct sieve_variable_scope_binary *var_scope; + struct sieve_variable_storage *var_storage; +}; + +struct ext_include_interpreter_context { + struct ext_include_interpreter_context *parent; + struct ext_include_interpreter_global *global; + + struct sieve_interpreter *interp; + pool_t pool; + + unsigned int nesting_depth; + + struct sieve_script *script; + const struct ext_include_script_info *script_info; + + const struct ext_include_script_info *include; + bool returned; +}; + +/* + * Extension configuration + */ + +/* Extension hooks */ + +bool ext_include_load(const struct sieve_extension *ext, void **context) +{ + struct sieve_instance *svinst = ext->svinst; + struct ext_include_context *ctx; + const char *location; + unsigned long long int uint_setting; + + if (*context != NULL) + ext_include_unload(ext); + + ctx = i_new(struct ext_include_context, 1); + + /* Get location for :global scripts */ + location = sieve_setting_get(svinst, "sieve_global"); + if (location == NULL) + location = sieve_setting_get(svinst, "sieve_global_dir"); + + if (location == NULL) { + e_debug(svinst->event, "include: " + "sieve_global is not set; " + "it is currently not possible to include `:global' scripts."); + } + + ctx->global_location = i_strdup(location); + + /* Get limits */ + ctx->max_nesting_depth = EXT_INCLUDE_DEFAULT_MAX_NESTING_DEPTH; + ctx->max_includes = EXT_INCLUDE_DEFAULT_MAX_INCLUDES; + + if (sieve_setting_get_uint_value( + svinst, "sieve_include_max_nesting_depth", &uint_setting)) + ctx->max_nesting_depth = (unsigned int)uint_setting; + if (sieve_setting_get_uint_value( + svinst, "sieve_include_max_includes", &uint_setting)) + ctx->max_includes = (unsigned int)uint_setting; + + /* Extension dependencies */ + ctx->var_ext = sieve_ext_variables_get_extension(ext->svinst); + + *context = (void *)ctx; + return TRUE; +} + +void ext_include_unload(const struct sieve_extension *ext) +{ + struct ext_include_context *ctx = + (struct ext_include_context *)ext->context; + + if (ctx->global_storage != NULL) + sieve_storage_unref(&ctx->global_storage); + if (ctx->personal_storage != NULL) + sieve_storage_unref(&ctx->personal_storage); + + i_free(ctx->global_location); + i_free(ctx); +} + +/* + * Script access + */ + +struct sieve_storage * +ext_include_get_script_storage(const struct sieve_extension *ext, + enum ext_include_script_location location, + const char *script_name, + enum sieve_error *error_r) +{ + struct sieve_instance *svinst = ext->svinst; + struct ext_include_context *ctx = + (struct ext_include_context *)ext->context; + + switch (location) { + case EXT_INCLUDE_LOCATION_PERSONAL: + if (ctx->personal_storage == NULL) { + ctx->personal_storage = + sieve_storage_create_main(svinst, NULL, 0, + error_r); + } + return ctx->personal_storage; + case EXT_INCLUDE_LOCATION_GLOBAL: + if (ctx->global_location == NULL) { + e_info(svinst->event, "include: " + "sieve_global is unconfigured; " + "include of `:global' script `%s' is therefore not possible", + str_sanitize(script_name, 80)); + if (error_r != NULL) + *error_r = SIEVE_ERROR_NOT_FOUND; + return NULL; + } + if (ctx->global_storage == NULL) { + ctx->global_storage = sieve_storage_create( + svinst, ctx->global_location, 0, error_r); + } + return ctx->global_storage; + default: + break; + } + + i_unreached(); + return NULL; +} + +/* + * AST context management + */ + +static void +ext_include_ast_free(const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_ast *ast ATTR_UNUSED, void *context) +{ + struct ext_include_ast_context *actx = + (struct ext_include_ast_context *)context; + struct sieve_script **scripts; + unsigned int count, i; + + /* Unreference included scripts */ + scripts = array_get_modifiable(&actx->included_scripts, &count); + for (i = 0; i < count; i++) { + sieve_script_unref(&scripts[i]); + } + + /* Unreference variable scopes */ + if (actx->global_vars != NULL) + sieve_variable_scope_unref(&actx->global_vars); +} + +static const struct sieve_ast_extension include_ast_extension = { + &include_extension, + ext_include_ast_free +}; + +struct ext_include_ast_context * +ext_include_create_ast_context(const struct sieve_extension *this_ext, + struct sieve_ast *ast, struct sieve_ast *parent) +{ + struct ext_include_ast_context *actx; + + pool_t pool = sieve_ast_pool(ast); + actx = p_new(pool, struct ext_include_ast_context, 1); + p_array_init(&actx->included_scripts, pool, 32); + + if (parent != NULL) { + struct ext_include_ast_context *parent_ctx = + (struct ext_include_ast_context *) + sieve_ast_extension_get_context(parent, this_ext); + + actx->global_vars = parent_ctx->global_vars; + i_assert(actx->global_vars != NULL); + + sieve_variable_scope_ref(actx->global_vars); + } else { + struct ext_include_context *ectx = + ext_include_get_context(this_ext); + + actx->global_vars = sieve_variable_scope_create( + this_ext->svinst, ectx->var_ext, this_ext); + } + + sieve_ast_extension_register(ast, this_ext, &include_ast_extension, + (void *)actx); + return actx; +} + +struct ext_include_ast_context * +ext_include_get_ast_context(const struct sieve_extension *this_ext, + struct sieve_ast *ast) +{ + struct ext_include_ast_context *actx = + (struct ext_include_ast_context *) + sieve_ast_extension_get_context(ast, this_ext); + + if (actx != NULL) + return actx; + return ext_include_create_ast_context(this_ext, ast, NULL); +} + +void ext_include_ast_link_included_script( + const struct sieve_extension *this_ext, struct sieve_ast *ast, + struct sieve_script *script) +{ + struct ext_include_ast_context *actx = + ext_include_get_ast_context(this_ext, ast); + + array_append(&actx->included_scripts, &script, 1); +} + +bool ext_include_validator_have_variables( + const struct sieve_extension *this_ext, struct sieve_validator *valdtr) +{ + struct ext_include_context *ectx = ext_include_get_context(this_ext); + + return sieve_ext_variables_is_active(ectx->var_ext, valdtr); +} + +/* + * Generator context management + */ + +static struct ext_include_generator_context * +ext_include_create_generator_context( + struct sieve_generator *gentr, + struct ext_include_generator_context *parent, + struct sieve_script *script) +{ + struct ext_include_generator_context *ctx; + + pool_t pool = sieve_generator_pool(gentr); + ctx = p_new(pool, struct ext_include_generator_context, 1); + ctx->parent = parent; + ctx->script = script; + if (parent == NULL) + ctx->nesting_depth = 0; + else + ctx->nesting_depth = parent->nesting_depth + 1; + + return ctx; +} + +static inline struct ext_include_generator_context * +ext_include_get_generator_context(const struct sieve_extension *this_ext, + struct sieve_generator *gentr) +{ + return (struct ext_include_generator_context *) + sieve_generator_extension_get_context(gentr, this_ext); +} + +static inline void +ext_include_initialize_generator_context( + const struct sieve_extension *this_ext, struct sieve_generator *gentr, + struct ext_include_generator_context *parent, + struct sieve_script *script) +{ + sieve_generator_extension_set_context( + gentr, this_ext, + ext_include_create_generator_context(gentr, parent, script)); +} + +void ext_include_register_generator_context( + const struct sieve_extension *this_ext, + const struct sieve_codegen_env *cgenv) +{ + struct ext_include_generator_context *ctx = + ext_include_get_generator_context(this_ext, cgenv->gentr); + + /* Initialize generator context if necessary */ + if (ctx == NULL) { + ctx = ext_include_create_generator_context( + cgenv->gentr, NULL, cgenv->script); + + sieve_generator_extension_set_context( + cgenv->gentr, this_ext, (void *)ctx); + } + + /* Initialize ast context if necessary */ + (void)ext_include_get_ast_context(this_ext, cgenv->ast); + (void)ext_include_binary_init(this_ext, cgenv->sbin, cgenv->ast); +} + +/* + * Runtime initialization + */ + +static int +ext_include_runtime_init(const struct sieve_extension *this_ext, + const struct sieve_runtime_env *renv, + void *context, bool deferred ATTR_UNUSED) +{ + struct ext_include_interpreter_context *ctx = + (struct ext_include_interpreter_context *)context; + struct ext_include_context *ectx = ext_include_get_context(this_ext); + + if (ctx->parent == NULL) { + ctx->global = p_new(ctx->pool, + struct ext_include_interpreter_global, 1); + p_array_init(&ctx->global->included_scripts, ctx->pool, 10); + + ctx->global->var_scope = + ext_include_binary_get_global_scope( + this_ext, renv->sbin); + ctx->global->var_storage = + sieve_variable_storage_create(ectx->var_ext, ctx->pool, + ctx->global->var_scope); + } else { + ctx->global = ctx->parent->global; + } + + sieve_ext_variables_runtime_set_storage(ectx->var_ext, renv, this_ext, + ctx->global->var_storage); + return SIEVE_EXEC_OK; +} + +static struct sieve_interpreter_extension include_interpreter_extension = { + .ext_def = &include_extension, + .run = ext_include_runtime_init +}; + +/* + * Interpreter context management + */ + +static struct ext_include_interpreter_context * +ext_include_interpreter_context_create( + struct sieve_interpreter *interp, + struct ext_include_interpreter_context *parent, + struct sieve_script *script, + const struct ext_include_script_info *sinfo) +{ + struct ext_include_interpreter_context *ctx; + + pool_t pool = sieve_interpreter_pool(interp); + ctx = p_new(pool, struct ext_include_interpreter_context, 1); + ctx->pool = pool; + ctx->parent = parent; + ctx->interp = interp; + ctx->script = script; + ctx->script_info = sinfo; + + if (parent == NULL) + ctx->nesting_depth = 0; + else + ctx->nesting_depth = parent->nesting_depth + 1; + return ctx; +} + +static inline struct ext_include_interpreter_context * +ext_include_get_interpreter_context(const struct sieve_extension *this_ext, + struct sieve_interpreter *interp) +{ + return (struct ext_include_interpreter_context *) + sieve_interpreter_extension_get_context(interp, this_ext); +} + +static inline struct ext_include_interpreter_context * +ext_include_interpreter_context_init_child( + const struct sieve_extension *this_ext, + struct sieve_interpreter *interp, + struct ext_include_interpreter_context *parent, + struct sieve_script *script, + const struct ext_include_script_info *sinfo) +{ + struct ext_include_interpreter_context *ctx = + ext_include_interpreter_context_create(interp, parent, + script, sinfo); + + sieve_interpreter_extension_register(interp, this_ext, + &include_interpreter_extension, + ctx); + return ctx; +} + +void ext_include_interpreter_context_init( + const struct sieve_extension *this_ext, + struct sieve_interpreter *interp) +{ + struct ext_include_interpreter_context *ctx = + ext_include_get_interpreter_context(this_ext, interp); + + /* Is this is the top-level interpreter ? */ + if (ctx == NULL) { + struct sieve_script *script; + + /* Initialize top context */ + script = sieve_interpreter_script(interp); + ctx = ext_include_interpreter_context_create(interp, NULL, + script, NULL); + + sieve_interpreter_extension_register( + interp, this_ext, &include_interpreter_extension, + (void *)ctx); + } +} + +struct sieve_variable_storage * +ext_include_interpreter_get_global_variables( + const struct sieve_extension *this_ext, + struct sieve_interpreter *interp) +{ + struct ext_include_interpreter_context *ctx = + ext_include_get_interpreter_context(this_ext, interp); + + return ctx->global->var_storage; +} + +/* + * Including a script during code generation + */ + +int ext_include_generate_include( + const struct sieve_codegen_env *cgenv, struct sieve_command *cmd, + enum ext_include_script_location location, enum ext_include_flags flags, + struct sieve_script *script, + const struct ext_include_script_info **included_r) +{ + const struct sieve_extension *this_ext = cmd->ext; + struct ext_include_context *ext_ctx = + (struct ext_include_context *)this_ext->context; + int result = 1; + struct sieve_ast *ast; + struct sieve_binary *sbin = cgenv->sbin; + struct sieve_generator *gentr = cgenv->gentr; + struct ext_include_binary_context *binctx; + struct sieve_generator *subgentr; + struct ext_include_generator_context *ctx = + ext_include_get_generator_context(this_ext, gentr); + struct ext_include_generator_context *pctx; + struct sieve_error_handler *ehandler = + sieve_generator_error_handler(gentr); + struct ext_include_script_info *included; + + *included_r = NULL; + + /* Just to be sure: do not include more scripts when errors have occured + already. + */ + if (sieve_get_errors(ehandler) > 0) + return -1; + + /* Limit nesting level */ + if (ctx->nesting_depth >= ext_ctx->max_nesting_depth) { + sieve_command_generate_error( + gentr, cmd, + "cannot nest includes deeper than %d levels", + ext_ctx->max_nesting_depth); + return -1; + } + + /* Check for circular include */ + if ((flags & EXT_INCLUDE_FLAG_ONCE) == 0) { + pctx = ctx; + while (pctx != NULL) { + if (sieve_script_equals(pctx->script, script)) { + /* Just drop circular include when uploading + inactive script; not an error + */ + if ((cgenv->flags & SIEVE_COMPILE_FLAG_UPLOADED) != 0 && + (cgenv->flags & SIEVE_COMPILE_FLAG_ACTIVATED) == 0) { + sieve_command_generate_warning( + gentr, cmd, + "circular include (ignored during upload)"); + return 0; + } + + sieve_command_generate_error(gentr, cmd, + "circular include"); + return -1; + } + + pctx = pctx->parent; + } + } + + /* Get binary context */ + binctx = ext_include_binary_init(this_ext, sbin, cgenv->ast); + + /* Is the script already compiled into the current binary? */ + included = ext_include_binary_script_get_include_info(binctx, script); + if (included != NULL) { + /* Yes, only update flags */ + if ((flags & EXT_INCLUDE_FLAG_OPTIONAL) == 0) + included->flags &= ENUM_NEGATE(EXT_INCLUDE_FLAG_OPTIONAL); + if ((flags & EXT_INCLUDE_FLAG_ONCE) == 0) + included->flags &= ENUM_NEGATE(EXT_INCLUDE_FLAG_ONCE); + } else { + const char *script_name = sieve_script_name(script); + enum sieve_compile_flags cpflags = cgenv->flags; + + /* No, include new script */ + + /* Check whether include limit is exceeded */ + if (ext_include_binary_script_get_count(binctx) >= + ext_ctx->max_includes) { + sieve_command_generate_error( + gentr, cmd, "failed to include script '%s': " + "no more than %u includes allowed", + str_sanitize(script_name, 80), + ext_ctx->max_includes); + return -1; + } + + /* Allocate a new block in the binary and mark the script as + included. */ + if (!sieve_script_is_open(script)) { + /* Just making an empty entry to mark a missing script + */ + i_assert((flags & EXT_INCLUDE_FLAG_MISSING_AT_UPLOAD) != 0 || + (flags & EXT_INCLUDE_FLAG_OPTIONAL) != 0); + included = ext_include_binary_script_include( + binctx, location, flags, script, NULL); + result = 0; + + } else { + struct sieve_binary_block *inc_block = + sieve_binary_block_create(sbin); + + /* Real include */ + included = ext_include_binary_script_include( + binctx, location, flags, script, inc_block); + + /* Parse */ + if ((ast = sieve_parse(script, ehandler, + NULL)) == NULL) { + sieve_command_generate_error( + gentr, cmd, + "failed to parse included script '%s'", + str_sanitize(script_name, 80)); + return -1; + } + + /* Included scripts inherit global variable scope */ + (void)ext_include_create_ast_context( + this_ext, ast, cmd->ast_node->ast); + + if (location == EXT_INCLUDE_LOCATION_GLOBAL) { + cpflags &= + ENUM_NEGATE(SIEVE_EXECUTE_FLAG_NOGLOBAL); + } else { + cpflags |= SIEVE_EXECUTE_FLAG_NOGLOBAL; + } + + /* Validate */ + if (!sieve_validate(ast, ehandler, cpflags, NULL)) { + sieve_command_generate_error( + gentr, cmd, + "failed to validate included script '%s'", + str_sanitize(script_name, 80)); + sieve_ast_unref(&ast); + return -1; + } + + /* Generate + + FIXME: It might not be a good idea to recurse code + generation for included scripts. + */ + subgentr = sieve_generator_create(ast, ehandler, cpflags); + ext_include_initialize_generator_context( + cmd->ext, subgentr, ctx, script); + + if (sieve_generator_run(subgentr, &inc_block) == NULL) { + sieve_command_generate_error( + gentr, cmd, + "failed to generate code for included script '%s'", + str_sanitize(script_name, 80)); + result = -1; + } + + sieve_generator_free(&subgentr); + + /* Cleanup */ + sieve_ast_unref(&ast); + } + } + + if (result > 0) + *included_r = included; + return result; +} + +/* + * Executing an included script during interpretation + */ + +static bool +ext_include_runtime_check_circular( + struct ext_include_interpreter_context *ctx, + const struct ext_include_script_info *include) +{ + struct ext_include_interpreter_context *pctx; + + pctx = ctx; + while (pctx != NULL) { + + if (sieve_script_equals(include->script, pctx->script)) + return TRUE; + + pctx = pctx->parent; + } + + return FALSE; +} + +static bool +ext_include_runtime_include_mark(struct ext_include_interpreter_context *ctx, + const struct ext_include_script_info *include, + bool once) +{ + struct sieve_script *const *includes; + unsigned int count, i; + + includes = array_get(&ctx->global->included_scripts, &count); + for (i = 0; i < count; i++) { + if (sieve_script_equals(include->script, includes[i])) + return (!once); + } + + array_append(&ctx->global->included_scripts, &include->script, 1); + return TRUE; +} + +int ext_include_execute_include(const struct sieve_runtime_env *renv, + unsigned int include_id, + enum ext_include_flags flags) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + const struct sieve_extension *this_ext = renv->oprtn->ext; + int result = SIEVE_EXEC_OK; + struct ext_include_interpreter_context *ctx; + const struct ext_include_script_info *included; + struct ext_include_binary_context *binctx = + ext_include_binary_get_context(this_ext, renv->sbin); + bool once = ((flags & EXT_INCLUDE_FLAG_ONCE) != 0); + unsigned int block_id; + + /* Check for invalid include id (== corrupt binary) */ + included = ext_include_binary_script_get_included(binctx, include_id); + if (included == NULL) { + sieve_runtime_trace_error( + renv, "include: include id %d is invalid", include_id); + return SIEVE_EXEC_BIN_CORRUPT; + } + + ctx = ext_include_get_interpreter_context(this_ext, renv->interp); + block_id = sieve_binary_block_get_id(included->block); + + /* If :once modifier is specified, check for duplicate include */ + if (ext_include_runtime_include_mark(ctx, included, once)) { + sieve_runtime_trace( + renv, SIEVE_TRLVL_NONE, + "include: start script '%s' [inc id: %d, block: %d]", + sieve_script_name(included->script), + include_id, block_id); + } else { + /* skip */ + sieve_runtime_trace( + renv, SIEVE_TRLVL_NONE, + "include: skipped include for script '%s' " + "[inc id: %d, block: %d]; already run once", + sieve_script_name(included->script), + include_id, block_id); + return result; + } + + /* Check circular include during interpretation as well. + * Let's not trust binaries. + */ + if (ext_include_runtime_check_circular(ctx, included)) { + sieve_runtime_trace_error(renv, + "include: circular include of script '%s' " + "[inc id: %d, block: %d]", + sieve_script_name(included->script), + include_id, block_id); + + /* Situation has no valid way to emerge at runtime */ + return SIEVE_EXEC_BIN_CORRUPT; + } + + if (ctx->parent == NULL) { + struct ext_include_interpreter_context *curctx = NULL; + struct sieve_error_handler *ehandler = renv->ehandler; + struct sieve_interpreter *subinterp; + bool interrupted = FALSE; + + /* We are the top-level interpreter instance */ + if (result == SIEVE_EXEC_OK) { + struct sieve_execute_env eenv_new = *eenv; + + if (included->location != EXT_INCLUDE_LOCATION_GLOBAL) + eenv_new.flags |= SIEVE_EXECUTE_FLAG_NOGLOBAL; + else { + eenv_new.flags &= + ENUM_NEGATE(SIEVE_EXECUTE_FLAG_NOGLOBAL); + } + + /* Create interpreter for top-level included script + (first sub-interpreter) + */ + subinterp = sieve_interpreter_create_for_block( + included->block, included->script, renv->interp, + &eenv_new, ehandler); + if (subinterp != NULL) { + curctx = ext_include_interpreter_context_init_child( + this_ext, subinterp, ctx, included->script, + included); + + /* Activate and start the top-level included script */ + result = sieve_interpreter_start( + subinterp, renv->result, &interrupted); + } else { + result = SIEVE_EXEC_BIN_CORRUPT; + } + } + + /* Included scripts can have includes of their own. This is not + implemented recursively. Rather, the sub-interpreter + interrupts and defers the include to the top-level + interpreter, which is here. */ + if (result == SIEVE_EXEC_OK && interrupted && + !curctx->returned) { + while (result == SIEVE_EXEC_OK) { + if (((interrupted && curctx->returned) || + (!interrupted)) && + curctx->parent != NULL) { + const struct ext_include_script_info *ended_script = + curctx->script_info; + + /* Sub-interpreter ended or executed + return */ + + /* Ascend interpreter stack */ + curctx = curctx->parent; + sieve_interpreter_free(&subinterp); + + sieve_runtime_trace(renv, SIEVE_TRLVL_NONE, + "include: script '%s' ended " + "[inc id: %d, block: %d]", + sieve_script_name(ended_script->script), + ended_script->id, + sieve_binary_block_get_id(ended_script->block)); + + /* This is the top-most sub-interpreter, + bail out */ + if (curctx->parent == NULL) + break; + + subinterp = curctx->interp; + + /* Continue parent */ + curctx->include = NULL; + curctx->returned = FALSE; + + result = sieve_interpreter_continue( + subinterp, &interrupted); + } else { + if (curctx->include != NULL) { + /* Sub-include requested */ + + if (result == SIEVE_EXEC_OK) { + struct sieve_execute_env eenv_new = *eenv; + + if (curctx->include->location != EXT_INCLUDE_LOCATION_GLOBAL) + eenv_new.flags |= SIEVE_EXECUTE_FLAG_NOGLOBAL; + else { + eenv_new.flags &= + ENUM_NEGATE(SIEVE_EXECUTE_FLAG_NOGLOBAL); + } + + /* Create sub-interpreter */ + subinterp = sieve_interpreter_create_for_block( + curctx->include->block, curctx->include->script, + curctx->interp, &eenv_new, ehandler); + if (subinterp != NULL) { + curctx = ext_include_interpreter_context_init_child( + this_ext, subinterp, curctx, + curctx->include->script, curctx->include); + + /* Start the sub-include's interpreter */ + curctx->include = NULL; + curctx->returned = FALSE; + result = sieve_interpreter_start( + subinterp, renv->result, &interrupted); + } else { + result = SIEVE_EXEC_BIN_CORRUPT; + } + } + } else { + /* Sub-interpreter was interrupted outside + this extension, probably stop command was + executed. Generate an interrupt ourselves, + ending all script execution. */ + sieve_interpreter_interrupt(renv->interp); + break; + } + } + } + } + + /* Free any sub-interpreters that might still be active */ + while (curctx != NULL && curctx->parent != NULL) { + struct ext_include_interpreter_context *nextctx = + curctx->parent; + struct sieve_interpreter *killed_interp = curctx->interp; + const struct ext_include_script_info *ended_script = + curctx->script_info; + + /* This kills curctx too */ + sieve_interpreter_free(&killed_interp); + + sieve_runtime_trace( + renv, SIEVE_TRLVL_NONE, + "include: script '%s' ended [id: %d, block: %d]", + sieve_script_name(ended_script->script), + ended_script->id, + sieve_binary_block_get_id(ended_script->block)); + + /* Luckily we recorded the parent earlier */ + curctx = nextctx; + } + + } else { + /* We are an included script already, defer inclusion to main + interpreter */ + ctx->include = included; + sieve_interpreter_interrupt(renv->interp); + } + + return result; +} + +void ext_include_execute_return(const struct sieve_runtime_env *renv) +{ + const struct sieve_extension *this_ext = renv->oprtn->ext; + struct ext_include_interpreter_context *ctx = + ext_include_get_interpreter_context(this_ext, renv->interp); + + sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, + "return: exiting included script"); + ctx->returned = TRUE; + sieve_interpreter_interrupt(renv->interp); +} diff --git a/pigeonhole/src/lib-sieve/plugins/include/ext-include-common.h b/pigeonhole/src/lib-sieve/plugins/include/ext-include-common.h new file mode 100644 index 0000000..44bfe9a --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/include/ext-include-common.h @@ -0,0 +1,170 @@ +#ifndef EXT_INCLUDE_COMMON_H +#define EXT_INCLUDE_COMMON_H + +#include "lib.h" +#include "hash.h" + +#include "sieve-common.h" +#include "sieve-extensions.h" + +/* + * Forward declarations + */ + +struct ext_include_script_info; +struct ext_include_binary_context; + +/* + * Types + */ + +enum ext_include_flags { // stored in one byte + EXT_INCLUDE_FLAG_ONCE = 0x01, + EXT_INCLUDE_FLAG_OPTIONAL = 0x02, + EXT_INCLUDE_FLAG_MISSING_AT_UPLOAD = 0x04 +}; + +enum ext_include_script_location { + EXT_INCLUDE_LOCATION_PERSONAL, + EXT_INCLUDE_LOCATION_GLOBAL, + EXT_INCLUDE_LOCATION_INVALID +}; + +static inline const char * +ext_include_script_location_name(enum ext_include_script_location location) +{ + switch (location) { + case EXT_INCLUDE_LOCATION_PERSONAL: + return "personal"; + case EXT_INCLUDE_LOCATION_GLOBAL: + return "global"; + default: + break; + } + + return "[INVALID LOCATION]"; +} + + +/* + * Extension + */ + +extern const struct sieve_extension_def include_extension; +extern const struct sieve_binary_extension include_binary_ext; + +bool ext_include_load(const struct sieve_extension *ext, void **context); +void ext_include_unload(const struct sieve_extension *ext); + +/* + * Commands + */ + +extern const struct sieve_command_def cmd_include; +extern const struct sieve_command_def cmd_return; +extern const struct sieve_command_def cmd_global; + +/* DEPRICATED */ +extern const struct sieve_command_def cmd_import; +extern const struct sieve_command_def cmd_export; + +/* + * Operations + */ + +enum ext_include_opcode { + EXT_INCLUDE_OPERATION_INCLUDE, + EXT_INCLUDE_OPERATION_RETURN, + EXT_INCLUDE_OPERATION_GLOBAL +}; + +extern const struct sieve_operation_def include_operation; +extern const struct sieve_operation_def return_operation; +extern const struct sieve_operation_def global_operation; + +/* + * Script access + */ + +struct sieve_storage * +ext_include_get_script_storage(const struct sieve_extension *ext, + enum ext_include_script_location location, + const char *script_name, + enum sieve_error *error_r); +/* + * Context + */ + +/* Extension context */ + +struct ext_include_context { + /* Extension dependencies */ + const struct sieve_extension *var_ext; + + /* Configuration */ + char *global_location; + + struct sieve_storage *global_storage; + struct sieve_storage *personal_storage; + + unsigned int max_nesting_depth; + unsigned int max_includes; +}; + +static inline struct ext_include_context * +ext_include_get_context(const struct sieve_extension *ext) +{ + return (struct ext_include_context *) ext->context; +} + +/* AST Context */ + +struct ext_include_ast_context { + struct sieve_variable_scope *global_vars; + + ARRAY(struct sieve_script *) included_scripts; +}; + +struct ext_include_ast_context * +ext_include_create_ast_context(const struct sieve_extension *this_ext, + struct sieve_ast *ast, struct sieve_ast *parent); +struct ext_include_ast_context * +ext_include_get_ast_context(const struct sieve_extension *this_ext, + struct sieve_ast *ast); + +void ext_include_ast_link_included_script( + const struct sieve_extension *this_ext, struct sieve_ast *ast, + struct sieve_script *script); + +bool ext_include_validator_have_variables( + const struct sieve_extension *this_ext, struct sieve_validator *valdtr); + +/* Generator context */ + +void ext_include_register_generator_context( + const struct sieve_extension *this_ext, + const struct sieve_codegen_env *cgenv); + +int ext_include_generate_include( + const struct sieve_codegen_env *cgenv, struct sieve_command *cmd, + enum ext_include_script_location location, enum ext_include_flags flags, + struct sieve_script *script, + const struct ext_include_script_info **included_r); + +/* Interpreter context */ + +void ext_include_interpreter_context_init( + const struct sieve_extension *this_ext, + struct sieve_interpreter *interp); + +int ext_include_execute_include(const struct sieve_runtime_env *renv, + unsigned int block_id, + enum ext_include_flags flags); +void ext_include_execute_return(const struct sieve_runtime_env *renv); + +struct sieve_variable_storage * +ext_include_interpreter_get_global_variables( + const struct sieve_extension *this_ext, + struct sieve_interpreter *interp); + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/include/ext-include-limits.h b/pigeonhole/src/lib-sieve/plugins/include/ext-include-limits.h new file mode 100644 index 0000000..37246c0 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/include/ext-include-limits.h @@ -0,0 +1,9 @@ +#ifndef EXT_INCLUDE_LIMITS_H +#define EXT_INCLUDE_LIMITS_H + +#include "sieve-common.h" + +#define EXT_INCLUDE_DEFAULT_MAX_NESTING_DEPTH 10 +#define EXT_INCLUDE_DEFAULT_MAX_INCLUDES 255 + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/include/ext-include-variables.c b/pigeonhole/src/lib-sieve/plugins/include/ext-include-variables.c new file mode 100644 index 0000000..28cd803 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/include/ext-include-variables.c @@ -0,0 +1,254 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str-sanitize.h" + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-script.h" +#include "sieve-ast.h" +#include "sieve-binary.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "sieve-ext-variables.h" + +#include "ext-include-common.h" +#include "ext-include-binary.h" +#include "ext-include-variables.h" + +/* + * Variable import-export + */ + +struct sieve_variable *ext_include_variable_import_global +(struct sieve_validator *valdtr, struct sieve_command *cmd, + const char *variable) +{ + const struct sieve_extension *this_ext = cmd->ext; + struct sieve_ast *ast = cmd->ast_node->ast; + struct ext_include_ast_context *ctx = + ext_include_get_ast_context(this_ext, ast); + struct ext_include_context *ectx = ext_include_get_context(this_ext); + struct sieve_variable_scope *local_scope; + struct sieve_variable_scope *global_scope = ctx->global_vars; + struct sieve_variable *global_var = NULL, *local_var; + + /* Sanity safeguard */ + i_assert ( ctx->global_vars != NULL ); + + if ( !sieve_variable_identifier_is_valid(variable) ) { + sieve_command_validate_error(valdtr, cmd, + "invalid variable identifier '%s'", str_sanitize(variable,80)); + return NULL; + } + + /* Get/Declare the variable in the global scope */ + global_var = sieve_variable_scope_declare(global_scope, variable); + + /* Check whether scope is over its size limit */ + if ( global_var == NULL ) { + sieve_command_validate_error(valdtr, cmd, + "declaration of new global variable '%s' exceeds the limit " + "(max variables: %u)", variable, + sieve_variables_get_max_scope_size(ectx->var_ext)); + return NULL; + } + + /* Import the global variable into the local script scope */ + local_scope = sieve_ext_variables_get_local_scope(ectx->var_ext, valdtr); + + local_var = sieve_variable_scope_get_variable(local_scope, variable); + if ( local_var != NULL && local_var->ext != this_ext ) { + /* FIXME: indicate location of conflicting set statement */ + sieve_command_validate_error(valdtr, cmd, + "declaration of new global variable '%s' conflicts with earlier local " + "use", variable); + return NULL; + } + + return sieve_variable_scope_import(local_scope, global_var); +} + +/* + * Binary symbol table + */ + +bool ext_include_variables_save +(struct sieve_binary_block *sblock, + struct sieve_variable_scope_binary *global_vars, + enum sieve_error *error_r ATTR_UNUSED) +{ + struct sieve_variable_scope *global_scope = + sieve_variable_scope_binary_get(global_vars); + unsigned int count = sieve_variable_scope_size(global_scope); + sieve_size_t jump; + + sieve_binary_emit_unsigned(sblock, count); + + jump = sieve_binary_emit_offset(sblock, 0); + + if ( count > 0 ) { + unsigned int size, i; + struct sieve_variable *const *vars = + sieve_variable_scope_get_variables(global_scope, &size); + + for ( i = 0; i < size; i++ ) { + sieve_binary_emit_cstring(sblock, vars[i]->identifier); + } + } + + sieve_binary_resolve_offset(sblock, jump); + + return TRUE; +} + +bool ext_include_variables_load +(const struct sieve_extension *this_ext, struct sieve_binary_block *sblock, + sieve_size_t *offset, struct sieve_variable_scope_binary **global_vars_r) +{ + struct ext_include_context *ectx = + ext_include_get_context(this_ext); + + /* Sanity assert */ + i_assert( *global_vars_r == NULL ); + + *global_vars_r = sieve_variable_scope_binary_read + (this_ext->svinst, ectx->var_ext, this_ext, sblock, offset); + + return ( *global_vars_r != NULL ); +} + +bool ext_include_variables_dump +(struct sieve_dumptime_env *denv, + struct sieve_variable_scope_binary *global_vars) +{ + struct sieve_variable_scope *global_scope = + sieve_variable_scope_binary_get(global_vars); + unsigned int size; + struct sieve_variable *const *vars; + + i_assert(global_scope != NULL); + + vars = sieve_variable_scope_get_variables(global_scope, &size); + + if ( size > 0 ) { + unsigned int i; + + sieve_binary_dump_sectionf(denv, "Global variables"); + + for ( i = 0; i < size; i++ ) { + sieve_binary_dumpf(denv, "%3d: '%s' \n", i, vars[i]->identifier); + } + } + + return TRUE; +} + +/* + * Global variables namespace + */ + +bool vnspc_global_variables_validate + (struct sieve_validator *valdtr, const struct sieve_variables_namespace *nspc, + struct sieve_ast_argument *arg, struct sieve_command *cmd, + ARRAY_TYPE(sieve_variable_name) *var_name, void **var_data, + bool assignment); +bool vnspc_global_variables_generate + (const struct sieve_codegen_env *cgenv, + const struct sieve_variables_namespace *nspc, + struct sieve_ast_argument *arg, struct sieve_command *cmd, void *var_data); + +static const struct sieve_variables_namespace_def +global_variables_namespace = { + SIEVE_OBJECT("global", NULL, 0), + .validate = vnspc_global_variables_validate, + .generate = vnspc_global_variables_generate +}; + +bool vnspc_global_variables_validate +(struct sieve_validator *valdtr, + const struct sieve_variables_namespace *nspc, struct sieve_ast_argument *arg, + struct sieve_command *cmd ATTR_UNUSED, + ARRAY_TYPE(sieve_variable_name) *var_name, void **var_data, + bool assignment ATTR_UNUSED) +{ + const struct sieve_extension *this_ext = SIEVE_OBJECT_EXTENSION(nspc); + struct sieve_ast *ast = arg->ast; + struct ext_include_context *ectx = + ext_include_get_context(this_ext); + struct ext_include_ast_context *ctx = + ext_include_get_ast_context(this_ext, ast); + struct sieve_variable *var = NULL; + const struct sieve_variable_name *name_element; + const char *variable; + + /* Sanity safeguard */ + i_assert ( ctx->global_vars != NULL ); + + /* Check variable name */ + + if ( array_count(var_name) != 2 ) { + sieve_argument_validate_error(valdtr, arg, + "invalid variable name within global namespace: " + "encountered sub-namespace"); + return FALSE; + } + + name_element = array_idx(var_name, 1); + if ( name_element->num_variable >= 0 ) { + sieve_argument_validate_error(valdtr, arg, + "invalid variable name within global namespace: " + "encountered numeric variable name"); + return FALSE; + } + + variable = str_c(name_element->identifier); + + /* Get/Declare the variable in the global scope */ + + var = sieve_variable_scope_declare(ctx->global_vars, variable); + + if ( var == NULL ) { + sieve_argument_validate_error(valdtr, arg, + "(implicit) declaration of new global variable '%s' exceeds the limit " + "(max variables: %u)", variable, + sieve_variables_get_max_scope_size(ectx->var_ext)); + return FALSE; + } + + *var_data = (void *) var; + + return TRUE; +} + +bool vnspc_global_variables_generate +(const struct sieve_codegen_env *cgenv, + const struct sieve_variables_namespace *nspc, + struct sieve_ast_argument *arg ATTR_UNUSED, + struct sieve_command *cmd ATTR_UNUSED, void *var_data) +{ + const struct sieve_extension *this_ext = SIEVE_OBJECT_EXTENSION(nspc); + struct ext_include_context *ectx = ext_include_get_context(this_ext); + struct sieve_variable *var = (struct sieve_variable *) var_data; + + sieve_variables_opr_variable_emit(cgenv->sblock, ectx->var_ext, var); + + return TRUE; +} + +void ext_include_variables_global_namespace_init +(const struct sieve_extension *this_ext, struct sieve_validator *valdtr) +{ + struct ext_include_context *ectx = ext_include_get_context(this_ext); + + sieve_variables_namespace_register + (ectx->var_ext, valdtr, this_ext, &global_variables_namespace); +} + + + diff --git a/pigeonhole/src/lib-sieve/plugins/include/ext-include-variables.h b/pigeonhole/src/lib-sieve/plugins/include/ext-include-variables.h new file mode 100644 index 0000000..d5927b2 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/include/ext-include-variables.h @@ -0,0 +1,41 @@ +#ifndef EXT_INCLUDE_VARIABLES_H +#define EXT_INCLUDE_VARIABLES_H + +#include "sieve-common.h" + +#include "sieve-ext-variables.h" + +#include "ext-include-common.h" + +/* + * Variable import-export + */ + +struct sieve_variable *ext_include_variable_import_global + (struct sieve_validator *valdtr, struct sieve_command *cmd, + const char *variable); + +/* + * Binary symbol table + */ + +bool ext_include_variables_save + (struct sieve_binary_block *sblock, + struct sieve_variable_scope_binary *global_vars, + enum sieve_error *error_r); +bool ext_include_variables_load + (const struct sieve_extension *this_ext, struct sieve_binary_block *sblock, + sieve_size_t *offset, struct sieve_variable_scope_binary **global_vars_r); +bool ext_include_variables_dump + (struct sieve_dumptime_env *denv, + struct sieve_variable_scope_binary *global_vars); + +/* + * Validation + */ + +void ext_include_variables_global_namespace_init + (const struct sieve_extension *this_ext, struct sieve_validator *valdtr); + +#endif + diff --git a/pigeonhole/src/lib-sieve/plugins/include/ext-include.c b/pigeonhole/src/lib-sieve/plugins/include/ext-include.c new file mode 100644 index 0000000..0a38687 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/include/ext-include.c @@ -0,0 +1,121 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension include + * ----------------- + * + * Authors: Stephan Bosch + * Specification: RFC 6609 + * Implementation: full + * Status: testing + * + */ + +/* FIXME: Current include implementation does not allow for parts of the script + * to be located in external binaries; all included scripts are recompiled and + * the resulting byte code is imported into the main binary in separate blocks. + */ + +#include "lib.h" + +#include "sieve-common.h" + +#include "sieve-extensions.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-binary.h" +#include "sieve-dump.h" + +#include "sieve-ext-variables.h" + +#include "ext-include-common.h" +#include "ext-include-binary.h" +#include "ext-include-variables.h" + +/* + * Operations + */ + +static const struct sieve_operation_def *ext_include_operations[] = { + &include_operation, + &return_operation, + &global_operation +}; + +/* + * Extension + */ + +/* Forward declaration */ + +static bool ext_include_validator_load + (const struct sieve_extension *ext, struct sieve_validator *validator); +static bool ext_include_generator_load + (const struct sieve_extension *ext, const struct sieve_codegen_env *cgenv); +static bool ext_include_interpreter_load + (const struct sieve_extension *ext, const struct sieve_runtime_env *renv, + sieve_size_t *address); +static bool ext_include_binary_load + (const struct sieve_extension *ext, struct sieve_binary *binary); + +/* Extension objects */ + +const struct sieve_extension_def include_extension = { + .name = "include", + .version = 1, + + .load = ext_include_load, + .unload = ext_include_unload, + .validator_load = ext_include_validator_load, + .generator_load = ext_include_generator_load, + .interpreter_load = ext_include_interpreter_load, + .binary_load = ext_include_binary_load, + .binary_dump = ext_include_binary_dump, + .code_dump = ext_include_code_dump, + + SIEVE_EXT_DEFINE_OPERATIONS(ext_include_operations) +}; + +static bool ext_include_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + /* Register new commands */ + sieve_validator_register_command(valdtr, ext, &cmd_include); + sieve_validator_register_command(valdtr, ext, &cmd_return); + sieve_validator_register_command(valdtr, ext, &cmd_global); + + /* DEPRICATED */ + sieve_validator_register_command(valdtr, ext, &cmd_import); + sieve_validator_register_command(valdtr, ext, &cmd_export); + + /* Initialize global variables namespace */ + ext_include_variables_global_namespace_init(ext, valdtr); + + return TRUE; +} + +static bool ext_include_generator_load +(const struct sieve_extension *ext, const struct sieve_codegen_env *cgenv) +{ + ext_include_register_generator_context(ext, cgenv); + + return TRUE; +} + +static bool ext_include_interpreter_load +(const struct sieve_extension *ext, const struct sieve_runtime_env *renv, + sieve_size_t *address ATTR_UNUSED) +{ + ext_include_interpreter_context_init(ext, renv->interp); + + return TRUE; +} + +static bool ext_include_binary_load +(const struct sieve_extension *ext, struct sieve_binary *sbin) +{ + (void)ext_include_binary_get_context(ext, sbin); + + return TRUE; +} diff --git a/pigeonhole/src/lib-sieve/plugins/index/Makefile.am b/pigeonhole/src/lib-sieve/plugins/index/Makefile.am new file mode 100644 index 0000000..434ca1c --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/index/Makefile.am @@ -0,0 +1,13 @@ +noinst_LTLIBRARIES = libsieve_ext_index.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +libsieve_ext_index_la_SOURCES = \ + ext-index-common.c \ + ext-index.c \ + tag-index.c + +noinst_HEADERS = \ + ext-index-common.h diff --git a/pigeonhole/src/lib-sieve/plugins/index/Makefile.in b/pigeonhole/src/lib-sieve/plugins/index/Makefile.in new file mode 100644 index 0000000..852316c --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/index/Makefile.in @@ -0,0 +1,688 @@ +# 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/plugins/index +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_ext_index_la_LIBADD = +am_libsieve_ext_index_la_OBJECTS = ext-index-common.lo ext-index.lo \ + tag-index.lo +libsieve_ext_index_la_OBJECTS = $(am_libsieve_ext_index_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)/ext-index-common.Plo \ + ./$(DEPDIR)/ext-index.Plo ./$(DEPDIR)/tag-index.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_ext_index_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_index_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_ext_index.la +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +libsieve_ext_index_la_SOURCES = \ + ext-index-common.c \ + ext-index.c \ + tag-index.c + +noinst_HEADERS = \ + ext-index-common.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/plugins/index/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/index/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_ext_index.la: $(libsieve_ext_index_la_OBJECTS) $(libsieve_ext_index_la_DEPENDENCIES) $(EXTRA_libsieve_ext_index_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_index_la_OBJECTS) $(libsieve_ext_index_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-index-common.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-index.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tag-index.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)/ext-index-common.Plo + -rm -f ./$(DEPDIR)/ext-index.Plo + -rm -f ./$(DEPDIR)/tag-index.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)/ext-index-common.Plo + -rm -f ./$(DEPDIR)/ext-index.Plo + -rm -f ./$(DEPDIR)/tag-index.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/plugins/index/ext-index-common.c b/pigeonhole/src/lib-sieve/plugins/index/ext-index-common.c new file mode 100644 index 0000000..26fb706 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/index/ext-index-common.c @@ -0,0 +1,15 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "utc-offset.h" +#include "str.h" + +#include "sieve-common.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-interpreter.h" +#include "sieve-message.h" + +#include "ext-index-common.h" + diff --git a/pigeonhole/src/lib-sieve/plugins/index/ext-index-common.h b/pigeonhole/src/lib-sieve/plugins/index/ext-index-common.h new file mode 100644 index 0000000..7ce3244 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/index/ext-index-common.h @@ -0,0 +1,29 @@ +#ifndef EXT_INDEX_COMMON_H +#define EXT_INDEX_COMMON_H + +#include "sieve-common.h" + +#include <time.h> + +#define SIEVE_EXT_INDEX_HDR_OVERRIDE_SEQUENCE 100 + +/* + * Tagged arguments + */ + +extern const struct sieve_argument_def index_tag; +extern const struct sieve_argument_def last_tag; + +/* + * Operands + */ + +extern const struct sieve_operand_def index_operand; + +/* + * Extension + */ + +extern const struct sieve_extension_def index_extension; + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/index/ext-index.c b/pigeonhole/src/lib-sieve/plugins/index/ext-index.c new file mode 100644 index 0000000..33e7771 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/index/ext-index.c @@ -0,0 +1,69 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension index + * ------------------ + * + * Authors: Stephan Bosch + * Specification: RFC 5260 + * Implementation: full + * Status: testing + * + */ + +#include "lib.h" +#include "array.h" + +#include "sieve-common.h" +#include "sieve-message.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" + +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-binary.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "ext-index-common.h" + +/* + * Extension + */ + +static bool ext_index_validator_load +(const struct sieve_extension *ext, struct sieve_validator *validator); + +const struct sieve_extension_def index_extension = { + .name = "index", + .validator_load = ext_index_validator_load, + SIEVE_EXT_DEFINE_OPERAND(index_operand) +}; + +static bool ext_index_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + /* Register :index and :last tags with header, address and date test commands + * and we don't care whether these command are registered or even whether + * these will be registered at all. The validator handles either situation + * gracefully. + */ + sieve_validator_register_external_tag + (valdtr, "header", ext, &index_tag, SIEVE_OPT_MESSAGE_OVERRIDE); + sieve_validator_register_external_tag + (valdtr, "header", ext, &last_tag, 0); + + sieve_validator_register_external_tag + (valdtr, "address", ext, &index_tag, SIEVE_OPT_MESSAGE_OVERRIDE); + sieve_validator_register_external_tag + (valdtr, "address", ext, &last_tag, 0); + + sieve_validator_register_external_tag + (valdtr, "date", ext, &index_tag, SIEVE_OPT_MESSAGE_OVERRIDE); + sieve_validator_register_external_tag + (valdtr, "date", ext, &last_tag, 0); + + return TRUE; +} + + diff --git a/pigeonhole/src/lib-sieve/plugins/index/tag-index.c b/pigeonhole/src/lib-sieve/plugins/index/tag-index.c new file mode 100644 index 0000000..d8938a5 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/index/tag-index.c @@ -0,0 +1,278 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "mail-storage.h" +#include "mail-namespace.h" + +#include "sieve-common.h" +#include "sieve-stringlist.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-binary.h" +#include "sieve-code.h" +#include "sieve-message.h" +#include "sieve-result.h" +#include "sieve-validator.h" +#include "sieve-generator.h" + +#include "ext-index-common.h" + +/* + * Tagged argument + */ + +static bool +tag_index_validate(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, struct sieve_command *cmd); +static bool +tag_index_generate(const struct sieve_codegen_env *cgenv, + struct sieve_ast_argument *arg, + struct sieve_command *context); + +const struct sieve_argument_def index_tag = { + .identifier = "index", + .validate = tag_index_validate, + .generate = tag_index_generate +}; + +static bool +tag_last_validate(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, struct sieve_command *cmd); + +const struct sieve_argument_def last_tag = { + .identifier = "last", + .validate = tag_last_validate, +}; + +/* + * Header override + */ + +static bool +svmo_index_dump_context(const struct sieve_message_override *svmo, + const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +svmo_index_read_context(const struct sieve_message_override *svmo, + const struct sieve_runtime_env *renv, + sieve_size_t *address, void **ho_context); +static int +svmo_index_header_override(const struct sieve_message_override *svmo, + const struct sieve_runtime_env *renv, + bool mime_decode, struct sieve_stringlist **headers); + +const struct sieve_message_override_def index_header_override = { + SIEVE_OBJECT("index", &index_operand, 0), + .sequence = SIEVE_EXT_INDEX_HDR_OVERRIDE_SEQUENCE, + .dump_context = svmo_index_dump_context, + .read_context = svmo_index_read_context, + .header_override = svmo_index_header_override +}; + +/* + * Operand + */ + +static const struct sieve_extension_objects ext_header_overrides = + SIEVE_EXT_DEFINE_MESSAGE_OVERRIDE(index_header_override); + +const struct sieve_operand_def index_operand = { + .name = "index operand", + .ext_def = &index_extension, + .class = &sieve_message_override_operand_class, + .interface = &ext_header_overrides +}; + +/* + * Tag data + */ + +struct tag_index_data { + sieve_number_t fieldno; + bool last:1; +}; + +/* + * Tag validation + */ + +static bool +tag_index_validate(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_ast_argument **arg, struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + struct tag_index_data *data; + + /* Skip the tag itself */ + *arg = sieve_ast_argument_next(*arg); + + /* Check syntax: + * ":index" <fieldno: number> + */ + if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0, + SAAT_NUMBER, FALSE)) + return FALSE; + + if (tag->argument->data == NULL) { + data = p_new(sieve_command_pool(cmd), struct tag_index_data, 1); + tag->argument->data = (void *)data; + } else { + data = (struct tag_index_data *)tag->argument->data; + } + + data->fieldno = sieve_ast_argument_number(*arg); + if (data->fieldno == 0) { + sieve_argument_validate_error(valdtr, *arg, + "the :index tag for the %s %s cannot be zero", + sieve_command_identifier(cmd), + sieve_command_type_name(cmd)); + return FALSE; + } + + /* Detach parameter */ + *arg = sieve_ast_arguments_detach(*arg,1); + return TRUE; +} + +static bool +tag_last_validate(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_ast_argument **arg, struct sieve_command *cmd) +{ + struct sieve_ast_argument *index_arg; + struct tag_index_data *data; + + index_arg = sieve_command_find_argument(cmd, &index_tag); + if (index_arg == NULL) { + sieve_argument_validate_error( + valdtr, *arg, + "the :last tag for the %s %s cannot be specified " + "without the :index tag", + sieve_command_identifier(cmd), + sieve_command_type_name(cmd)); + return FALSE; + } + + /* Set :last flag */ + if (index_arg->argument->data == NULL) { + data = p_new(sieve_command_pool(cmd), struct tag_index_data, 1); + index_arg->argument->data = (void*)data; + } else { + data = (struct tag_index_data *)index_arg->argument->data; + } + data->last = TRUE; + + /* Detach */ + *arg = sieve_ast_arguments_detach(*arg, 1); + return TRUE; +} + +/* + * Code generation + */ + +static bool +tag_index_generate(const struct sieve_codegen_env *cgenv, + struct sieve_ast_argument *arg, + struct sieve_command *cmd ATTR_UNUSED) +{ + struct tag_index_data *data = + (struct tag_index_data *)arg->argument->data; + + if (sieve_ast_argument_type(arg) != SAAT_TAG) + return FALSE; + + sieve_opr_message_override_emit(cgenv->sblock, arg->argument->ext, + &index_header_override); + + (void)sieve_binary_emit_integer (cgenv->sblock, data->fieldno); + (void)sieve_binary_emit_byte(cgenv->sblock, (data->last ? 1 : 0)); + return TRUE; +} + +/* + * Header override implementation + */ + +/* Context data */ + +struct svmo_index_context { + unsigned int fieldno; + bool last:1; +}; + +/* Context coding */ + +static bool +svmo_index_dump_context(const struct sieve_message_override *svmo ATTR_UNUSED, + const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + sieve_number_t fieldno = 0; + unsigned int last; + + if (!sieve_binary_read_integer(denv->sblock, address, &fieldno) || + fieldno == 0) + return FALSE; + + sieve_code_dumpf(denv, "fieldno: %llu", (unsigned long long) fieldno); + + if (!sieve_binary_read_byte(denv->sblock, address, &last)) + return FALSE; + + if (last > 0) + sieve_code_dumpf(denv, "last"); + return TRUE; +} + +static int +svmo_index_read_context(const struct sieve_message_override *svmo ATTR_UNUSED, + const struct sieve_runtime_env *renv, + sieve_size_t *address, void **ho_context) +{ + pool_t pool = sieve_result_pool(renv->result); + struct svmo_index_context *ctx; + sieve_number_t fieldno; + unsigned int last = 0; + + if (!sieve_binary_read_integer(renv->sblock, address, &fieldno)) { + sieve_runtime_trace_error(renv, "fieldno: invalid number"); + return SIEVE_EXEC_BIN_CORRUPT; + } + if (fieldno == 0) { + sieve_runtime_trace_error(renv, "fieldno: index is zero"); + return SIEVE_EXEC_BIN_CORRUPT; + } + if (!sieve_binary_read_byte(renv->sblock, address, &last)) { + sieve_runtime_trace_error(renv, "last: invalid byte"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + ctx = p_new(pool, struct svmo_index_context, 1); + ctx->fieldno = fieldno; + ctx->last = (last == 0 ? FALSE : TRUE); + + *ho_context = (void *) ctx; + return SIEVE_EXEC_OK; +} + +/* Override */ + +static int +svmo_index_header_override(const struct sieve_message_override *svmo, + const struct sieve_runtime_env *renv, + bool mime_decode ATTR_UNUSED, + struct sieve_stringlist **headers) +{ + struct svmo_index_context *ctx = + (struct svmo_index_context *)svmo->context; + + sieve_runtime_trace( + renv, SIEVE_TRLVL_MATCHING, + "header index override: only returning index %d%s", + ctx->fieldno, (ctx->last ? " (from last)" : "")); + + *headers = sieve_index_stringlist_create( + renv, *headers, (int)ctx->fieldno * (ctx->last ? -1 : 1)); + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/plugins/mailbox/Makefile.am b/pigeonhole/src/lib-sieve/plugins/mailbox/Makefile.am new file mode 100644 index 0000000..b627735 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/mailbox/Makefile.am @@ -0,0 +1,26 @@ +noinst_LTLIBRARIES = libsieve_ext_mailbox.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +tags = \ + tag-mailbox-create.c + +tests = \ + tst-mailboxexists.c + +libsieve_ext_mailbox_la_SOURCES = \ + $(tags) \ + $(tests) \ + ext-mailbox.c + +public_headers = \ + sieve-ext-mailbox.h + +headers = \ + ext-mailbox-common.h + +pkginc_libdir=$(dovecot_pkgincludedir)/sieve +pkginc_lib_HEADERS = $(public_headers) +noinst_HEADERS = $(headers) diff --git a/pigeonhole/src/lib-sieve/plugins/mailbox/Makefile.in b/pigeonhole/src/lib-sieve/plugins/mailbox/Makefile.in new file mode 100644 index 0000000..6d5ed09 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/mailbox/Makefile.in @@ -0,0 +1,757 @@ +# 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/plugins/mailbox +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) \ + $(pkginc_lib_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_ext_mailbox_la_LIBADD = +am__objects_1 = tag-mailbox-create.lo +am__objects_2 = tst-mailboxexists.lo +am_libsieve_ext_mailbox_la_OBJECTS = $(am__objects_1) $(am__objects_2) \ + ext-mailbox.lo +libsieve_ext_mailbox_la_OBJECTS = \ + $(am_libsieve_ext_mailbox_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)/ext-mailbox.Plo \ + ./$(DEPDIR)/tag-mailbox-create.Plo \ + ./$(DEPDIR)/tst-mailboxexists.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_ext_mailbox_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_mailbox_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(pkginc_libdir)" +HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +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_ext_mailbox.la +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +tags = \ + tag-mailbox-create.c + +tests = \ + tst-mailboxexists.c + +libsieve_ext_mailbox_la_SOURCES = \ + $(tags) \ + $(tests) \ + ext-mailbox.c + +public_headers = \ + sieve-ext-mailbox.h + +headers = \ + ext-mailbox-common.h + +pkginc_libdir = $(dovecot_pkgincludedir)/sieve +pkginc_lib_HEADERS = $(public_headers) +noinst_HEADERS = $(headers) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/mailbox/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/mailbox/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_ext_mailbox.la: $(libsieve_ext_mailbox_la_OBJECTS) $(libsieve_ext_mailbox_la_DEPENDENCIES) $(EXTRA_libsieve_ext_mailbox_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_mailbox_la_OBJECTS) $(libsieve_ext_mailbox_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-mailbox.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tag-mailbox-create.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-mailboxexists.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) + @$(NORMAL_INSTALL) + @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ + done + +uninstall-pkginc_libHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/ext-mailbox.Plo + -rm -f ./$(DEPDIR)/tag-mailbox-create.Plo + -rm -f ./$(DEPDIR)/tst-mailboxexists.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pkginc_libHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/ext-mailbox.Plo + -rm -f ./$(DEPDIR)/tag-mailbox-create.Plo + -rm -f ./$(DEPDIR)/tst-mailboxexists.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pkginc_libHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstLTLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-pkginc_libHEADERS install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-pkginc_libHEADERS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/pigeonhole/src/lib-sieve/plugins/mailbox/ext-mailbox-common.h b/pigeonhole/src/lib-sieve/plugins/mailbox/ext-mailbox-common.h new file mode 100644 index 0000000..96cff39 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/mailbox/ext-mailbox-common.h @@ -0,0 +1,39 @@ +#ifndef EXT_MAILBOX_COMMON_H +#define EXT_MAILBOX_COMMON_H + +#include "sieve-common.h" + +#include "sieve-ext-mailbox.h" + +/* + * Tagged arguments + */ + +extern const struct sieve_argument_def mailbox_create_tag; + +/* + * Commands + */ + +extern const struct sieve_command_def mailboxexists_test; + +/* + * Operands + */ + +extern const struct sieve_operand_def mailbox_create_operand; + +/* + * Operations + */ + +extern const struct sieve_operation_def mailboxexists_operation; + +/* + * Extension + */ + +extern const struct sieve_extension_def mailbox_extension; + +#endif + diff --git a/pigeonhole/src/lib-sieve/plugins/mailbox/ext-mailbox.c b/pigeonhole/src/lib-sieve/plugins/mailbox/ext-mailbox.c new file mode 100644 index 0000000..a2dcd88 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/mailbox/ext-mailbox.c @@ -0,0 +1,72 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension mailbox + * ------------------ + * + * Authors: Stephan Bosch + * Specification: RFC 5490 + * Implementation: full + * Status: testing + * + */ + +#include "sieve-common.h" + +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-actions.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-result.h" + +#include "ext-mailbox-common.h" + +/* + * Tag registration + */ + +void sieve_ext_mailbox_register_create_tag +(struct sieve_validator *valdtr, const struct sieve_extension *mailbox_ext, + const char *command) +{ + if ( sieve_validator_extension_loaded(valdtr, mailbox_ext) ) { + sieve_validator_register_external_tag(valdtr, command, + mailbox_ext, &mailbox_create_tag, SIEVE_OPT_SIDE_EFFECT); + } +} + + +/* + * Extension + */ + +static bool ext_mailbox_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr); + +const struct sieve_extension_def mailbox_extension = { + .name = "mailbox", + .validator_load = ext_mailbox_validator_load, + SIEVE_EXT_DEFINE_OPERATION(mailboxexists_operation), + SIEVE_EXT_DEFINE_OPERAND(mailbox_create_operand) +}; + +static bool ext_mailbox_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + /* Register :create tag with fileinto command and we don't care whether this + * command is registered or even whether it will be registered at all. The + * validator handles either situation gracefully + */ + sieve_validator_register_external_tag + (valdtr, "fileinto", ext, &mailbox_create_tag, SIEVE_OPT_SIDE_EFFECT); + + /* Register new test */ + sieve_validator_register_command(valdtr, ext, &mailboxexists_test); + + return TRUE; +} + + diff --git a/pigeonhole/src/lib-sieve/plugins/mailbox/sieve-ext-mailbox.h b/pigeonhole/src/lib-sieve/plugins/mailbox/sieve-ext-mailbox.h new file mode 100644 index 0000000..f7fa4cf --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/mailbox/sieve-ext-mailbox.h @@ -0,0 +1,21 @@ +#ifndef SIEVE_EXT_MAILBOX_H +#define SIEVE_EXT_MAILBOX_H + +/* sieve_ext_mailbox_get_extension(): + * Get the extension struct for the mailbox extension. + */ +static inline const struct sieve_extension *sieve_ext_mailbox_get_extension +(struct sieve_instance *svinst) +{ + return sieve_extension_get_by_name(svinst, "mailbox"); +} + +/* sieve_ext_mailbox_register_create_tag(): + * Register the :create tagged argument for a command other than fileinto and + * redirect. + */ +void sieve_ext_mailbox_register_create_tag + (struct sieve_validator *valdtr, const struct sieve_extension *mailbox_ext, + const char *command); + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c b/pigeonhole/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c new file mode 100644 index 0000000..f3c64cf --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c @@ -0,0 +1,186 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "mail-storage.h" +#include "mail-namespace.h" + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-actions.h" +#include "sieve-code.h" +#include "sieve-actions.h" +#include "sieve-result.h" +#include "sieve-generator.h" + +#include "ext-mailbox-common.h" + +/* + * Tagged argument + */ + +static bool +tag_mailbox_create_validate(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd); +static bool +tag_mailbox_create_generate(const struct sieve_codegen_env *cgenv, + struct sieve_ast_argument *arg, + struct sieve_command *context); + +const struct sieve_argument_def mailbox_create_tag = { + .identifier = "create", + .validate = tag_mailbox_create_validate, + .generate = tag_mailbox_create_generate +}; + +/* + * Side effect + */ + +static void +seff_mailbox_create_print(const struct sieve_side_effect *seffect, + const struct sieve_action *action, + const struct sieve_result_print_env *rpenv, + bool *keep); +static int +seff_mailbox_create_pre_execute(const struct sieve_side_effect *seffect, + const struct sieve_action_exec_env *aenv, + void *tr_context, + void **se_tr_context ATTR_UNUSED); + +const struct sieve_side_effect_def mailbox_create_side_effect = { + SIEVE_OBJECT("create", &mailbox_create_operand, 0), + .precedence = 100, + .to_action = &act_store, + .print = seff_mailbox_create_print, + .pre_execute = seff_mailbox_create_pre_execute +}; + +/* + * Operand + */ + +static const struct sieve_extension_objects ext_side_effects = + SIEVE_EXT_DEFINE_SIDE_EFFECT(mailbox_create_side_effect); + +const struct sieve_operand_def mailbox_create_operand = { + .name = "create operand", + .ext_def = &mailbox_extension, + .class = &sieve_side_effect_operand_class, + .interface = &ext_side_effects +}; + +/* + * Tag validation + */ + +static bool +tag_mailbox_create_validate(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_ast_argument **arg, + struct sieve_command *cmd ATTR_UNUSED) +{ + *arg = sieve_ast_argument_next(*arg); + + return TRUE; +} + +/* + * Code generation + */ + +static bool +tag_mailbox_create_generate(const struct sieve_codegen_env *cgenv, + struct sieve_ast_argument *arg, + struct sieve_command *context ATTR_UNUSED) +{ + if (sieve_ast_argument_type(arg) != SAAT_TAG) + return FALSE; + + sieve_opr_side_effect_emit(cgenv->sblock, arg->argument->ext, + &mailbox_create_side_effect); + return TRUE; +} + +/* + * Side effect implementation + */ + +static void +seff_mailbox_create_print(const struct sieve_side_effect *seffect ATTR_UNUSED, + const struct sieve_action *action ATTR_UNUSED, + const struct sieve_result_print_env *rpenv, + bool *keep ATTR_UNUSED) +{ + sieve_result_seffect_printf( + rpenv, "create mailbox if it does not exist"); +} + +static int +seff_mailbox_create_pre_execute( + const struct sieve_side_effect *seffect ATTR_UNUSED, + const struct sieve_action_exec_env *aenv, void *tr_context, + void **se_tr_context ATTR_UNUSED) +{ + struct act_store_transaction *trans = tr_context; + const struct sieve_execute_env *eenv = aenv->exec_env; + struct mailbox *box = trans->box; + + /* Check whether creation is necessary */ + if (box == NULL || trans->disabled) + return SIEVE_EXEC_OK; + + eenv->exec_status->last_storage = mailbox_get_storage(box); + + /* Open the mailbox (may already be open) */ + if (trans->error_code == MAIL_ERROR_NONE) { + if (mailbox_open(box) < 0) + sieve_act_store_get_storage_error(aenv, trans); + } + + /* Check whether creation has a chance of working */ + switch (trans->error_code) { + case MAIL_ERROR_NONE: + return SIEVE_EXEC_OK; + case MAIL_ERROR_NOTFOUND: + break; + case MAIL_ERROR_TEMP: + return SIEVE_EXEC_TEMP_FAILURE; + default: + return SIEVE_EXEC_FAILURE; + } + + trans->error = NULL; + trans->error_code = MAIL_ERROR_NONE; + + /* Create mailbox */ + if (mailbox_create(box, NULL, FALSE) < 0) { + sieve_act_store_get_storage_error(aenv, trans); + if (trans->error_code == MAIL_ERROR_EXISTS) { + trans->error = NULL; + trans->error_code = MAIL_ERROR_NONE; + } else { + return (trans->error_code == MAIL_ERROR_TEMP ? + SIEVE_EXEC_TEMP_FAILURE : SIEVE_EXEC_FAILURE); + } + } + + /* Subscribe to it if necessary */ + if (eenv->scriptenv->mailbox_autosubscribe) { + (void)mailbox_list_set_subscribed( + mailbox_get_namespace(box)->list, + mailbox_get_name(box), TRUE); + } + + /* Try opening again */ + if (mailbox_open(box) < 0) { + /* Failed definitively */ + sieve_act_store_get_storage_error(aenv, trans); + return (trans->error_code == MAIL_ERROR_TEMP ? + SIEVE_EXEC_TEMP_FAILURE : SIEVE_EXEC_FAILURE); + } + return SIEVE_EXEC_OK; +} + + + diff --git a/pigeonhole/src/lib-sieve/plugins/mailbox/tst-mailboxexists.c b/pigeonhole/src/lib-sieve/plugins/mailbox/tst-mailboxexists.c new file mode 100644 index 0000000..e0fd33d --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/mailbox/tst-mailboxexists.c @@ -0,0 +1,284 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str-sanitize.h" +#include "mail-storage.h" +#include "mail-namespace.h" + +#include "sieve-common.h" +#include "sieve-actions.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "ext-mailbox-common.h" + +/* + * Mailboxexists command + * + * Syntax: + * mailboxexists <mailbox-names: string-list> + */ + +static bool +tst_mailboxexists_validate(struct sieve_validator *valdtr, + struct sieve_command *tst); +static bool +tst_mailboxexists_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +const struct sieve_command_def mailboxexists_test = { + .identifier = "mailboxexists", + .type = SCT_TEST, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = tst_mailboxexists_validate, + .generate = tst_mailboxexists_generate, +}; + +/* + * Mailboxexists operation + */ + +static bool +tst_mailboxexists_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +tst_mailboxexists_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def mailboxexists_operation = { + .mnemonic = "MAILBOXEXISTS", + .ext_def = &mailbox_extension, + .dump = tst_mailboxexists_operation_dump, + .execute = tst_mailboxexists_operation_execute, +}; + +/* + * Test validation + */ + +struct _validate_context { + struct sieve_validator *valdtr; + struct sieve_command *tst; +}; + +static int +tst_mailboxexists_mailbox_validate(void *context, + struct sieve_ast_argument *arg) +{ + struct _validate_context *valctx = + (struct _validate_context *)context; + + if (sieve_argument_is_string_literal(arg)) { + const char *mailbox = sieve_ast_argument_strc(arg), *error; + + if (!sieve_mailbox_check_name(mailbox, &error)) { + sieve_argument_validate_warning( + valctx->valdtr, arg, "%s test: " + "invalid mailbox name `%s' specified: %s", + sieve_command_identifier(valctx->tst), + str_sanitize(mailbox, 256), error); + } + } + + return 1; +} + +static bool +tst_mailboxexists_validate(struct sieve_validator *valdtr, + struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + struct sieve_ast_argument *aarg; + struct _validate_context valctx; + + if (!sieve_validate_positional_argument( + valdtr, tst, arg, "mailbox-names", 1, SAAT_STRING_LIST)) + return FALSE; + + if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE)) + return FALSE; + + aarg = arg; + i_zero(&valctx); + valctx.valdtr = valdtr; + valctx.tst = tst; + + return (sieve_ast_stringlist_map( + &aarg, (void*)&valctx, + tst_mailboxexists_mailbox_validate) >= 0); +} + +/* + * Test generation + */ + +static bool +tst_mailboxexists_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *tst) +{ + sieve_operation_emit(cgenv->sblock, tst->ext, &mailboxexists_operation); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, tst, NULL); +} + +/* + * Code dump + */ + +static bool +tst_mailboxexists_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + sieve_code_dumpf(denv, "MAILBOXEXISTS"); + sieve_code_descend(denv); + + return sieve_opr_stringlist_dump(denv, address, "mailbox-names"); +} + +/* + * Code execution + */ + +static int +tst_mailboxexists_test_mailbox(const struct sieve_runtime_env *renv, + const char *mailbox, bool trace, + bool *all_exist_r) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + struct mailbox *box; + const char *error; + + /* Check validity of mailbox name */ + if (!sieve_mailbox_check_name(mailbox, &error)) { + sieve_runtime_warning( + renv, NULL, "mailboxexists test: " + "invalid mailbox name `%s' specified: %s", + str_sanitize(mailbox, 256), error); + *all_exist_r = FALSE; + return SIEVE_EXEC_OK; + } + + /* Open the box */ + box = mailbox_alloc_for_user(eenv->scriptenv->user, + mailbox, + MAILBOX_FLAG_POST_SESSION); + + if (mailbox_open(box) < 0) { + if (trace) { + sieve_runtime_trace( + renv, 0, + "mailbox `%s' cannot be opened", + str_sanitize(mailbox, 80)); + } + mailbox_free(&box); + *all_exist_r = FALSE; + return SIEVE_EXEC_OK; + } + + /* Also fail when it is readonly */ + if (mailbox_is_readonly(box)) { + if (trace) { + sieve_runtime_trace( + renv, 0, + "mailbox `%s' is read-only", + str_sanitize(mailbox, 80)); + } + mailbox_free(&box); + *all_exist_r = FALSE; + return SIEVE_EXEC_OK; + } + + /* FIXME: check acl for 'p' or 'i' ACL permissions as + required by RFC */ + + if (trace) { + sieve_runtime_trace( + renv, 0, "mailbox `%s' exists", + str_sanitize(mailbox, 80)); + } + + /* Close mailbox */ + mailbox_free(&box); + return SIEVE_EXEC_OK; +} + +static int +tst_mailboxexists_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + struct sieve_stringlist *mailbox_names; + string_t *mailbox_item; + bool trace = FALSE; + bool all_exist = TRUE; + int ret; + + /* + * Read operands + */ + + /* Read notify uris */ + ret = sieve_opr_stringlist_read(renv, address, "mailbox-names", + &mailbox_names); + if (ret <= 0) + return ret; + + /* + * Perform operation + */ + + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_TESTS)) { + sieve_runtime_trace(renv, 0, "mailboxexists test"); + sieve_runtime_trace_descend(renv); + + trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING); + } + + if (eenv->scriptenv->user == NULL) { + sieve_runtime_trace(renv, 0, "no mail user; yield true"); + sieve_interpreter_set_test_result(renv->interp, TRUE); + return SIEVE_EXEC_OK; + } + + mailbox_item = NULL; + while (all_exist && + (ret = sieve_stringlist_next_item(mailbox_names, + &mailbox_item)) > 0) { + const char *mailbox = str_c(mailbox_item); + + ret = tst_mailboxexists_test_mailbox(renv, mailbox, + trace, &all_exist); + if (ret <= 0) + return ret; + } + + if (ret < 0) { + sieve_runtime_trace_error( + renv, "invalid mailbox name item"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if (trace) { + if (all_exist) { + sieve_runtime_trace(renv, 0, + "all mailboxes are available"); + } else { + sieve_runtime_trace(renv, 0, + "some mailboxes are unavailable"); + } + } + + sieve_interpreter_set_test_result(renv->interp, all_exist); + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/plugins/metadata/Makefile.am b/pigeonhole/src/lib-sieve/plugins/metadata/Makefile.am new file mode 100644 index 0000000..6d69ba8 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/metadata/Makefile.am @@ -0,0 +1,24 @@ +noinst_LTLIBRARIES = libsieve_ext_metadata.la + +libsieve_ext_metadata_la_LDFLAGS = -module -avoid-version + +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + -I$(srcdir)/../variables \ + $(LIBDOVECOT_INCLUDE) \ + $(LIBDOVECOT_STORAGE_INCLUDE) + +tests = \ + tst-metadata.c \ + tst-metadataexists.c + +extensions = \ + ext-metadata.c + +libsieve_ext_metadata_la_SOURCES = \ + $(tests) \ + $(extensions) + +noinst_HEADERS = \ + ext-metadata-common.h + diff --git a/pigeonhole/src/lib-sieve/plugins/metadata/Makefile.in b/pigeonhole/src/lib-sieve/plugins/metadata/Makefile.in new file mode 100644 index 0000000..0b95819 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/metadata/Makefile.in @@ -0,0 +1,705 @@ +# 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/plugins/metadata +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_ext_metadata_la_LIBADD = +am__objects_1 = tst-metadata.lo tst-metadataexists.lo +am__objects_2 = ext-metadata.lo +am_libsieve_ext_metadata_la_OBJECTS = $(am__objects_1) \ + $(am__objects_2) +libsieve_ext_metadata_la_OBJECTS = \ + $(am_libsieve_ext_metadata_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 = +libsieve_ext_metadata_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(libsieve_ext_metadata_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +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)/ext-metadata.Plo \ + ./$(DEPDIR)/tst-metadata.Plo \ + ./$(DEPDIR)/tst-metadataexists.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_ext_metadata_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_metadata_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_ext_metadata.la +libsieve_ext_metadata_la_LDFLAGS = -module -avoid-version +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + -I$(srcdir)/../variables \ + $(LIBDOVECOT_INCLUDE) \ + $(LIBDOVECOT_STORAGE_INCLUDE) + +tests = \ + tst-metadata.c \ + tst-metadataexists.c + +extensions = \ + ext-metadata.c + +libsieve_ext_metadata_la_SOURCES = \ + $(tests) \ + $(extensions) + +noinst_HEADERS = \ + ext-metadata-common.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/plugins/metadata/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/metadata/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_ext_metadata.la: $(libsieve_ext_metadata_la_OBJECTS) $(libsieve_ext_metadata_la_DEPENDENCIES) $(EXTRA_libsieve_ext_metadata_la_DEPENDENCIES) + $(AM_V_CCLD)$(libsieve_ext_metadata_la_LINK) $(libsieve_ext_metadata_la_OBJECTS) $(libsieve_ext_metadata_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-metadata.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-metadata.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-metadataexists.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)/ext-metadata.Plo + -rm -f ./$(DEPDIR)/tst-metadata.Plo + -rm -f ./$(DEPDIR)/tst-metadataexists.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)/ext-metadata.Plo + -rm -f ./$(DEPDIR)/tst-metadata.Plo + -rm -f ./$(DEPDIR)/tst-metadataexists.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/plugins/metadata/ext-metadata-common.h b/pigeonhole/src/lib-sieve/plugins/metadata/ext-metadata-common.h new file mode 100644 index 0000000..a4b25a0 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/metadata/ext-metadata-common.h @@ -0,0 +1,40 @@ +#ifndef EXT_METADATA_COMMON_H +#define EXT_METADATA_COMMON_H + +#include "lib.h" +#include "mail-storage.h" +#include "imap-metadata.h" + +#include "sieve-common.h" + +/* + * Extension + */ + +extern const struct sieve_extension_def mboxmetadata_extension; +extern const struct sieve_extension_def servermetadata_extension; + +/* + * Commands + */ + +extern const struct sieve_command_def metadata_test; +extern const struct sieve_command_def servermetadata_test; +extern const struct sieve_command_def metadataexists_test; +extern const struct sieve_command_def servermetadataexists_test; + +/* + * Operations + */ + +enum ext_metadata_opcode { + EXT_METADATA_OPERATION_METADATA, + EXT_METADATA_OPERATION_METADATAEXISTS +}; + +extern const struct sieve_operation_def metadata_operation; +extern const struct sieve_operation_def servermetadata_operation; +extern const struct sieve_operation_def metadataexists_operation; +extern const struct sieve_operation_def servermetadataexists_operation; + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/metadata/ext-metadata.c b/pigeonhole/src/lib-sieve/plugins/metadata/ext-metadata.c new file mode 100644 index 0000000..882116b --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/metadata/ext-metadata.c @@ -0,0 +1,83 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-binary.h" + +#include "sieve-validator.h" +#include "sieve-interpreter.h" + +#include "ext-metadata-common.h" + +/* + * Extension mboxmetadata + * ----------------------------- + * + * Authors: Stephan Bosch + * Specification: RFC 5490; Section 3 + * Implementation: skeleton + * Status: development + * + */ + +const struct sieve_operation_def *mboxmetadata_operations[] = { + &metadata_operation, + &metadataexists_operation, +}; + +static bool ext_mboxmetadata_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); + +const struct sieve_extension_def mboxmetadata_extension = { + .name = "mboxmetadata", + .validator_load = ext_mboxmetadata_validator_load, + SIEVE_EXT_DEFINE_OPERATIONS(mboxmetadata_operations) +}; + +static bool ext_mboxmetadata_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + sieve_validator_register_command(valdtr, ext, &metadata_test); + sieve_validator_register_command(valdtr, ext, &metadataexists_test); + + return TRUE; +} + +/* + * Extension servermetadata + * ----------------------------- + * + * Authors: Stephan Bosch + * Specification: RFC 5490; Section 4 + * Implementation: skeleton + * Status: development + * + */ + +const struct sieve_operation_def *servermetadata_operations[] = { + &servermetadata_operation, + &servermetadataexists_operation, +}; + +static bool ext_servermetadata_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); + +const struct sieve_extension_def servermetadata_extension = { + .name = "servermetadata", + .validator_load = ext_servermetadata_validator_load, + SIEVE_EXT_DEFINE_OPERATIONS(servermetadata_operations) +}; + +static bool ext_servermetadata_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + sieve_validator_register_command(valdtr, ext, &servermetadata_test); + sieve_validator_register_command(valdtr, ext, &servermetadataexists_test); + + return TRUE; +} + + diff --git a/pigeonhole/src/lib-sieve/plugins/metadata/tst-metadata.c b/pigeonhole/src/lib-sieve/plugins/metadata/tst-metadata.c new file mode 100644 index 0000000..2e14535 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/metadata/tst-metadata.c @@ -0,0 +1,433 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str-sanitize.h" +#include "istream.h" + +#include "sieve-common.h" +#include "sieve-limits.h" +#include "sieve-commands.h" +#include "sieve-actions.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-match.h" + +#include "ext-metadata-common.h" + +#define TST_METADATA_MAX_MATCH_SIZE SIEVE_MAX_STRING_LEN + +/* + * Test definitions + */ + +/* Forward declarations */ + +static bool +tst_metadata_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool +tst_metadata_validate(struct sieve_validator *valdtr, + struct sieve_command *tst); +static bool +tst_metadata_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd); + +/* Metadata test + * + * Syntax: + * metadata [MATCH-TYPE] [COMPARATOR] + * <mailbox: string> + * <annotation-name: string> <key-list: string-list> + */ + +const struct sieve_command_def metadata_test = { + .identifier = "metadata", + .type = SCT_TEST, + .positional_args = 3, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = tst_metadata_registered, + .validate = tst_metadata_validate, + .generate = tst_metadata_generate, +}; + +/* Servermetadata test + * + * Syntax: + * servermetadata [MATCH-TYPE] [COMPARATOR] + * <annotation-name: string> <key-list: string-list> + */ + +const struct sieve_command_def servermetadata_test = { + .identifier = "servermetadata", + .type = SCT_TEST, + .positional_args = 2, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = tst_metadata_registered, + .validate = tst_metadata_validate, + .generate = tst_metadata_generate, +}; + +/* + * Opcode definitions + */ + +static bool +tst_metadata_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +tst_metadata_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +/* Metadata operation */ + +const struct sieve_operation_def metadata_operation = { + .mnemonic = "METADATA", + .ext_def = &mboxmetadata_extension, + .code = EXT_METADATA_OPERATION_METADATA, + .dump = tst_metadata_operation_dump, + .execute = tst_metadata_operation_execute, +}; + +/* Servermetadata operation */ + +const struct sieve_operation_def servermetadata_operation = { + .mnemonic = "SERVERMETADATA", + .ext_def = &servermetadata_extension, + .code = EXT_METADATA_OPERATION_METADATA, + .dump = tst_metadata_operation_dump, + .execute = tst_metadata_operation_execute, +}; + +/* + * Test registration + */ + +static bool +tst_metadata_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_command_registration *cmd_reg) +{ + /* The order of these is not significant */ + sieve_comparators_link_tag(valdtr, cmd_reg, + SIEVE_MATCH_OPT_COMPARATOR); + sieve_match_types_link_tags(valdtr, cmd_reg, + SIEVE_MATCH_OPT_MATCH_TYPE); + return TRUE; +} + +/* + * Test validation + */ + +static bool +tst_metadata_validate(struct sieve_validator *valdtr, struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + const struct sieve_match_type mcht_default = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + const struct sieve_comparator cmp_default = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + unsigned int arg_index = 1; + const char *error; + + /* mailbox */ + if (sieve_command_is(tst, metadata_test)) { + if (!sieve_validate_positional_argument( + valdtr, tst, arg, "mailbox", arg_index++, + SAAT_STRING)) + return FALSE; + + if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE)) + return FALSE; + + /* Check name validity when mailbox argument is not a variable + */ + if (sieve_argument_is_string_literal(arg)) { + const char *mailbox = sieve_ast_argument_strc(arg); + const char *error; + + if (!sieve_mailbox_check_name(mailbox, &error)) { + sieve_argument_validate_warning( + valdtr, arg, "%s test: " + "invalid mailbox name `%s' specified: %s", + sieve_command_identifier(tst), + str_sanitize(mailbox, 256), error); + } + } + arg = sieve_ast_argument_next(arg); + } + + /* annotation-name */ + if (!sieve_validate_positional_argument(valdtr, tst, arg, + "annotation-name", arg_index++, + SAAT_STRING)) + return FALSE; + + if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE)) + return FALSE; + + if (sieve_argument_is_string_literal(arg)) { + string_t *aname = sieve_ast_argument_str(arg); + + if (!imap_metadata_verify_entry_name(str_c(aname), &error)) { + sieve_argument_validate_warning( + valdtr, arg, "%s test: " + "specified annotation name `%s' is invalid: %s", + sieve_command_identifier(tst), + str_sanitize(str_c(aname), 256), + sieve_error_from_external(error)); + } + } + + arg = sieve_ast_argument_next(arg); + + /* key-list */ + if (!sieve_validate_positional_argument(valdtr, tst, arg, + "key-list", arg_index++, + SAAT_STRING_LIST)) + return FALSE; + + if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE)) + return FALSE; + + /* Validate the key argument to a specified match type */ + return sieve_match_type_validate(valdtr, tst, arg, + &mcht_default, &cmp_default); +} + +/* + * Test generation + */ + +static bool +tst_metadata_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *tst) +{ + if (sieve_command_is(tst, metadata_test)) { + sieve_operation_emit(cgenv->sblock, tst->ext, + &metadata_operation); + } else if (sieve_command_is(tst, servermetadata_test)) { + sieve_operation_emit(cgenv->sblock, tst->ext, + &servermetadata_operation); + } else { + i_unreached(); + } + + /* Generate arguments */ + if (!sieve_generate_arguments(cgenv, tst, NULL)) + return FALSE; + return TRUE; +} + +/* + * Code dump + */ + +static bool +tst_metadata_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + bool metadata = sieve_operation_is(denv->oprtn, metadata_operation); + + if (metadata) + sieve_code_dumpf(denv, "METADATA"); + else + sieve_code_dumpf(denv, "SERVERMETADATA"); + + sieve_code_descend(denv); + + /* Handle any optional arguments */ + if (sieve_match_opr_optional_dump(denv, address, NULL) != 0) + return FALSE; + if (metadata && !sieve_opr_string_dump(denv, address, "mailbox")) + return FALSE; + + return (sieve_opr_string_dump(denv, address, "annotation-name") && + sieve_opr_stringlist_dump(denv, address, "key list")); +} + +/* + * Code execution + */ + +static int +tst_metadata_get_annotation(const struct sieve_runtime_env *renv, + const char *mailbox, const char *aname, + const char **annotation_r) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + struct mail_user *user = eenv->scriptenv->user; + struct mailbox *box; + struct imap_metadata_transaction *imtrans; + struct mail_attribute_value avalue; + int status, ret; + + *annotation_r = NULL; + + if (user == NULL) + return SIEVE_EXEC_OK; + + if (mailbox != NULL) { + struct mail_namespace *ns; + ns = mail_namespace_find(user->namespaces, mailbox); + box = mailbox_alloc(ns->list, mailbox, 0); + imtrans = imap_metadata_transaction_begin(box); + } else { + box = NULL; + imtrans = imap_metadata_transaction_begin_server(user); + } + + status = SIEVE_EXEC_OK; + ret = imap_metadata_get(imtrans, aname, &avalue); + if (ret < 0) { + enum mail_error error_code; + const char *error; + + error = imap_metadata_transaction_get_last_error( + imtrans, &error_code); + + sieve_runtime_error( + renv, NULL, "%s test: " + "failed to retrieve annotation `%s': %s%s", + (mailbox != NULL ? "metadata" : "servermetadata"), + str_sanitize(aname, 256), + sieve_error_from_external(error), + (error_code == MAIL_ERROR_TEMP ? + " (temporary failure)" : "")); + + status = (error_code == MAIL_ERROR_TEMP ? + SIEVE_EXEC_TEMP_FAILURE : SIEVE_EXEC_FAILURE); + + } else if (avalue.value != NULL) { + *annotation_r = avalue.value; + } + (void)imap_metadata_transaction_commit(&imtrans, NULL, NULL); + if (box != NULL) + mailbox_free(&box); + return status; +} + +static int +tst_metadata_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + bool metadata = sieve_operation_is(renv->oprtn, metadata_operation); + struct sieve_match_type mcht = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + struct sieve_comparator cmp = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + string_t *mailbox, *aname; + struct sieve_stringlist *value_list, *key_list; + const char *annotation = NULL, *error; + int match, ret; + + /* + * Read operands + */ + + /* Handle match-type and comparator operands */ + if (sieve_match_opr_optional_read(renv, address, NULL, + &ret, &cmp, &mcht) < 0) + return ret; + + /* Read mailbox */ + if (metadata) { + ret = sieve_opr_string_read(renv, address, "mailbox", &mailbox); + if (ret <= 0) + return ret; + } + + /* Read annotation-name */ + ret = sieve_opr_string_read(renv, address, "annotation-name", &aname); + if (ret <= 0) + return ret; + + /* Read key-list */ + ret = sieve_opr_stringlist_read(renv, address, "key-list", &key_list); + if (ret <= 0) + return ret; + + /* + * Perform operation + */ + + if (metadata) { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "metadata test"); + } else { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "servermetadata test"); + } + sieve_runtime_trace_descend(renv); + + if (!imap_metadata_verify_entry_name(str_c(aname), &error)) { + sieve_runtime_warning( + renv, NULL, "%s test: " + "specified annotation name `%s' is invalid: %s", + (metadata ? "metadata" : "servermetadata"), + str_sanitize(str_c(aname), 256), + sieve_error_from_external(error)); + sieve_interpreter_set_test_result(renv->interp, FALSE); + return SIEVE_EXEC_OK; + } + + if (metadata) { + if (!sieve_mailbox_check_name(str_c(mailbox), &error)) { + sieve_runtime_warning( + renv, NULL, "metadata test: " + "invalid mailbox name `%s' specified: %s", + str_sanitize(str_c(mailbox), 256), error); + sieve_interpreter_set_test_result(renv->interp, FALSE); + return SIEVE_EXEC_OK; + } + + sieve_runtime_trace( + renv, SIEVE_TRLVL_TESTS, + "retrieving annotation `%s' from mailbox `%s'", + str_sanitize(str_c(aname), 256), + str_sanitize(str_c(mailbox), 80)); + } else { + sieve_runtime_trace( + renv, SIEVE_TRLVL_TESTS, + "retrieving server annotation `%s'", + str_sanitize(str_c(aname), 256)); + } + + /* Get annotation */ + ret = tst_metadata_get_annotation(renv, + (metadata ? str_c(mailbox) : NULL), + str_c(aname), &annotation); + if (ret == SIEVE_EXEC_OK) { + /* Perform match */ + if (annotation != NULL) { + /* Create value stringlist */ + value_list = sieve_single_stringlist_create_cstr( + renv, annotation, FALSE); + + /* Perform match */ + match = sieve_match(renv, &mcht, &cmp, + value_list, key_list, &ret); + if (ret < 0) + return ret; + } else { + match = 0; + } + } + + /* Set test result for subsequent conditional jump */ + if (ret == SIEVE_EXEC_OK) + sieve_interpreter_set_test_result(renv->interp, match > 0); + return ret; +} diff --git a/pigeonhole/src/lib-sieve/plugins/metadata/tst-metadataexists.c b/pigeonhole/src/lib-sieve/plugins/metadata/tst-metadataexists.c new file mode 100644 index 0000000..e11d692 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/metadata/tst-metadataexists.c @@ -0,0 +1,431 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str-sanitize.h" +#include "mail-storage.h" +#include "mail-namespace.h" + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-actions.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "ext-metadata-common.h" + +/* + * Command definitions + */ + +/* Forward declarations */ + +static bool +tst_metadataexists_validate(struct sieve_validator *valdtr, + struct sieve_command *tst); +static bool +tst_metadataexists_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +/* Metadataexists command + * + * Syntax: + * metadataexists <mailbox: string> <annotation-names: string-list> + */ + +static bool +tst_metadataexists_validate(struct sieve_validator *valdtr, + struct sieve_command *tst); +static bool +tst_metadataexists_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +const struct sieve_command_def metadataexists_test = { + .identifier = "metadataexists", + .type = SCT_TEST, + .positional_args = 2, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = tst_metadataexists_validate, + .generate = tst_metadataexists_generate, +}; + +/* Servermetadataexists command + * + * Syntax: + * servermetadataexists <annotation-names: string-list> + */ + +const struct sieve_command_def servermetadataexists_test = { + .identifier = "servermetadataexists", + .type = SCT_TEST, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = tst_metadataexists_validate, + .generate = tst_metadataexists_generate, +}; + +/* + * Opcode definitions + */ + +static bool +tst_metadataexists_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +tst_metadataexists_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +/* Metadata operation */ + +const struct sieve_operation_def metadataexists_operation = { + .mnemonic = "METADATAEXISTS", + .ext_def = &mboxmetadata_extension, + .code = EXT_METADATA_OPERATION_METADATAEXISTS, + .dump = tst_metadataexists_operation_dump, + .execute = tst_metadataexists_operation_execute, +}; + +/* Mailboxexists operation */ + +const struct sieve_operation_def servermetadataexists_operation = { + .mnemonic = "SERVERMETADATAEXISTS", + .ext_def = &servermetadata_extension, + .code = EXT_METADATA_OPERATION_METADATAEXISTS, + .dump = tst_metadataexists_operation_dump, + .execute = tst_metadataexists_operation_execute, +}; + +/* + * Test validation + */ + +struct _validate_context { + struct sieve_validator *valdtr; + struct sieve_command *tst; +}; + +static int +tst_metadataexists_annotation_validate(void *context, + struct sieve_ast_argument *arg) +{ + struct _validate_context *valctx = + (struct _validate_context *)context; + + if (sieve_argument_is_string_literal(arg)) { + const char *aname = sieve_ast_strlist_strc(arg); + const char *error; + + if (!imap_metadata_verify_entry_name(aname, &error)) { + sieve_argument_validate_warning( + valctx->valdtr, arg, "%s test: " + "specified annotation name `%s' is invalid: %s", + sieve_command_identifier(valctx->tst), + str_sanitize(aname, 256), + sieve_error_from_external(error)); + } + } + return 1; /* Can't check at compile time */ +} + +static bool +tst_metadataexists_validate(struct sieve_validator *valdtr, + struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + struct sieve_ast_argument *aarg; + struct _validate_context valctx; + unsigned int arg_index = 1; + + if (sieve_command_is(tst, metadataexists_test)) { + if (!sieve_validate_positional_argument(valdtr, tst, arg, + "mailbox", arg_index++, + SAAT_STRING)) + return FALSE; + + if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE)) + return FALSE; + + /* Check name validity when mailbox argument is not a variable */ + if (sieve_argument_is_string_literal(arg)) { + const char *mailbox = sieve_ast_argument_strc(arg); + const char *error; + + if (!sieve_mailbox_check_name(mailbox, &error)) { + sieve_argument_validate_warning( + valdtr, arg, "%s test: " + "invalid mailbox name `%s' specified: %s", + sieve_command_identifier(tst), + str_sanitize(mailbox, 256), error); + } + } + arg = sieve_ast_argument_next(arg); + } + + if (!sieve_validate_positional_argument(valdtr, tst, arg, + "annotation-names", arg_index++, + SAAT_STRING_LIST)) + return FALSE; + if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE)) + return FALSE; + + aarg = arg; + i_zero(&valctx); + valctx.valdtr = valdtr; + valctx.tst = tst; + + return (sieve_ast_stringlist_map( + &aarg, (void*)&valctx, + tst_metadataexists_annotation_validate) >= 0); +} + +/* + * Test generation + */ + +static bool +tst_metadataexists_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *tst) +{ + if (sieve_command_is(tst, metadataexists_test)) { + sieve_operation_emit(cgenv->sblock, tst->ext, + &metadataexists_operation); + } else if (sieve_command_is(tst, servermetadataexists_test)) { + sieve_operation_emit(cgenv->sblock, tst->ext, + &servermetadataexists_operation); + } else { + i_unreached(); + } + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, tst, NULL); +} + +/* + * Code dump + */ + +static bool +tst_metadataexists_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + bool metadata = sieve_operation_is(denv->oprtn, + metadataexists_operation); + + if (metadata) + sieve_code_dumpf(denv, "METADATAEXISTS"); + else + sieve_code_dumpf(denv, "SERVERMETADATAEXISTS"); + + sieve_code_descend(denv); + + if (metadata && !sieve_opr_string_dump(denv, address, "mailbox")) + return FALSE; + + return sieve_opr_stringlist_dump(denv, address, "annotation-names"); +} + +/* + * Code execution + */ + +static int +tst_metadataexists_check_annotation(const struct sieve_runtime_env *renv, + struct imap_metadata_transaction *imtrans, + const char *mailbox, const char *aname, + bool *all_exist_r) +{ + struct mail_attribute_value avalue; + const char *error; + int ret; + + if (!imap_metadata_verify_entry_name(aname, &error)) { + sieve_runtime_warning( + renv, NULL, "%s test: " + "specified annotation name `%s' is invalid: %s", + (mailbox != NULL ? + "metadataexists" : "servermetadataexists"), + str_sanitize(aname, 256), + sieve_error_from_external(error)); + *all_exist_r = FALSE; + return SIEVE_EXEC_OK; + } + + ret = imap_metadata_get(imtrans, aname, &avalue); + if (ret < 0) { + enum mail_error error_code; + const char *error; + + error = imap_metadata_transaction_get_last_error( + imtrans, &error_code); + sieve_runtime_error( + renv, NULL, "%s test: " + "failed to retrieve annotation `%s': %s%s", + (mailbox != NULL ? + "metadataexists" : "servermetadataexists"), + str_sanitize(aname, 256), + sieve_error_from_external(error), + (error_code == MAIL_ERROR_TEMP ? + " (temporary failure)" : "")); + + *all_exist_r = FALSE; + return (error_code == MAIL_ERROR_TEMP ? + SIEVE_EXEC_TEMP_FAILURE : SIEVE_EXEC_FAILURE); + } + if (avalue.value == NULL && avalue.value_stream == NULL) { + sieve_runtime_trace(renv, 0, + "annotation `%s': not found", aname); + *all_exist_r = FALSE; + } + + sieve_runtime_trace(renv, 0, "annotation `%s': found", aname); + return SIEVE_EXEC_OK; +} + +static int +tst_metadataexists_check_annotations(const struct sieve_runtime_env *renv, + const char *mailbox, + struct sieve_stringlist *anames, + bool *all_exist_r) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + struct mail_user *user = eenv->scriptenv->user; + struct mailbox *box = NULL; + struct imap_metadata_transaction *imtrans; + string_t *aname; + bool all_exist = TRUE; + int ret, sret, status; + + *all_exist_r = FALSE; + + if (user == NULL) + return SIEVE_EXEC_OK; + + if (mailbox != NULL) { + struct mail_namespace *ns; + ns = mail_namespace_find(user->namespaces, mailbox); + box = mailbox_alloc(ns->list, mailbox, 0); + imtrans = imap_metadata_transaction_begin(box); + } else { + imtrans = imap_metadata_transaction_begin_server(user); + } + + if (mailbox != NULL) { + sieve_runtime_trace( + renv, SIEVE_TRLVL_TESTS, + "checking annotations of mailbox `%s':", + str_sanitize(mailbox, 80)); + } else { + sieve_runtime_trace( + renv, SIEVE_TRLVL_TESTS, + "checking server annotations"); + } + + aname = NULL; + status = SIEVE_EXEC_OK; + while (all_exist && + (sret = sieve_stringlist_next_item(anames, &aname)) > 0) { + ret = tst_metadataexists_check_annotation( + renv, imtrans, mailbox, str_c(aname), &all_exist); + if (ret <= 0) { + status = ret; + break; + } + } + + if (sret < 0) { + sieve_runtime_trace_error( + renv, "invalid annotation name stringlist item"); + status = SIEVE_EXEC_BIN_CORRUPT; + } + + (void)imap_metadata_transaction_commit(&imtrans, NULL, NULL); + if (box != NULL) + mailbox_free(&box); + + *all_exist_r = all_exist; + return status; +} + +static int +tst_metadataexists_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + bool metadata = sieve_operation_is(renv->oprtn, + metadataexists_operation); + struct sieve_stringlist *anames; + string_t *mailbox; + bool trace = FALSE, all_exist = TRUE; + const char *error; + int ret; + + /* + * Read operands + */ + + /* Read mailbox */ + if (metadata) { + ret = sieve_opr_string_read(renv, address, "mailbox", &mailbox); + if (ret <= 0) + return ret; + } + + /* Read annotation names */ + ret = sieve_opr_stringlist_read(renv, address, "annotation-names", + &anames); + if (ret <= 0) + return ret; + + /* + * Perform operation + */ + + if (metadata && + !sieve_mailbox_check_name(str_c(mailbox), &error)) { + sieve_runtime_warning( + renv, NULL, "metadataexists test: " + "invalid mailbox name `%s' specified: %s", + str_sanitize(str_c(mailbox), 256), error); + sieve_interpreter_set_test_result(renv->interp, FALSE); + return SIEVE_EXEC_OK; + } + + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_TESTS)) { + if (metadata) { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "metadataexists test"); + } else { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "servermetadataexists test"); + } + + sieve_runtime_trace_descend(renv); + + trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING); + } + + ret = tst_metadataexists_check_annotations( + renv, (metadata ? str_c(mailbox) : NULL), anames, &all_exist); + if (ret <= 0) + return ret; + + if (trace) { + if (all_exist) { + sieve_runtime_trace(renv, 0, + "all annotations exist"); + } else { + sieve_runtime_trace(renv, 0, + "some annotations do not exist"); + } + } + + sieve_interpreter_set_test_result(renv->interp, all_exist); + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/plugins/mime/Makefile.am b/pigeonhole/src/lib-sieve/plugins/mime/Makefile.am new file mode 100644 index 0000000..8ded659 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/mime/Makefile.am @@ -0,0 +1,30 @@ +noinst_LTLIBRARIES = libsieve_ext_mime.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + -I$(srcdir)/../../util \ + -I$(srcdir)/../variables \ + $(LIBDOVECOT_INCLUDE) + +commands = \ + cmd-foreverypart.c \ + cmd-break.c \ + cmd-extracttext.c + +tags = \ + tag-mime.c + +extensions = \ + ext-mime.c \ + ext-foreverypart.c \ + ext-extracttext.c + +libsieve_ext_mime_la_SOURCES = \ + ext-mime-common.c \ + $(commands) \ + $(tags) \ + $(extensions) + +noinst_HEADERS = \ + ext-mime-common.h + diff --git a/pigeonhole/src/lib-sieve/plugins/mime/Makefile.in b/pigeonhole/src/lib-sieve/plugins/mime/Makefile.in new file mode 100644 index 0000000..cfe25ff --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/mime/Makefile.in @@ -0,0 +1,727 @@ +# 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/plugins/mime +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_ext_mime_la_LIBADD = +am__objects_1 = cmd-foreverypart.lo cmd-break.lo cmd-extracttext.lo +am__objects_2 = tag-mime.lo +am__objects_3 = ext-mime.lo ext-foreverypart.lo ext-extracttext.lo +am_libsieve_ext_mime_la_OBJECTS = ext-mime-common.lo $(am__objects_1) \ + $(am__objects_2) $(am__objects_3) +libsieve_ext_mime_la_OBJECTS = $(am_libsieve_ext_mime_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)/cmd-break.Plo \ + ./$(DEPDIR)/cmd-extracttext.Plo \ + ./$(DEPDIR)/cmd-foreverypart.Plo \ + ./$(DEPDIR)/ext-extracttext.Plo \ + ./$(DEPDIR)/ext-foreverypart.Plo \ + ./$(DEPDIR)/ext-mime-common.Plo ./$(DEPDIR)/ext-mime.Plo \ + ./$(DEPDIR)/tag-mime.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_ext_mime_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_mime_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_ext_mime.la +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + -I$(srcdir)/../../util \ + -I$(srcdir)/../variables \ + $(LIBDOVECOT_INCLUDE) + +commands = \ + cmd-foreverypart.c \ + cmd-break.c \ + cmd-extracttext.c + +tags = \ + tag-mime.c + +extensions = \ + ext-mime.c \ + ext-foreverypart.c \ + ext-extracttext.c + +libsieve_ext_mime_la_SOURCES = \ + ext-mime-common.c \ + $(commands) \ + $(tags) \ + $(extensions) + +noinst_HEADERS = \ + ext-mime-common.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/plugins/mime/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/mime/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_ext_mime.la: $(libsieve_ext_mime_la_OBJECTS) $(libsieve_ext_mime_la_DEPENDENCIES) $(EXTRA_libsieve_ext_mime_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_mime_la_OBJECTS) $(libsieve_ext_mime_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-break.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-extracttext.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-foreverypart.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-extracttext.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-foreverypart.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-mime-common.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-mime.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tag-mime.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)/cmd-break.Plo + -rm -f ./$(DEPDIR)/cmd-extracttext.Plo + -rm -f ./$(DEPDIR)/cmd-foreverypart.Plo + -rm -f ./$(DEPDIR)/ext-extracttext.Plo + -rm -f ./$(DEPDIR)/ext-foreverypart.Plo + -rm -f ./$(DEPDIR)/ext-mime-common.Plo + -rm -f ./$(DEPDIR)/ext-mime.Plo + -rm -f ./$(DEPDIR)/tag-mime.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)/cmd-break.Plo + -rm -f ./$(DEPDIR)/cmd-extracttext.Plo + -rm -f ./$(DEPDIR)/cmd-foreverypart.Plo + -rm -f ./$(DEPDIR)/ext-extracttext.Plo + -rm -f ./$(DEPDIR)/ext-foreverypart.Plo + -rm -f ./$(DEPDIR)/ext-mime-common.Plo + -rm -f ./$(DEPDIR)/ext-mime.Plo + -rm -f ./$(DEPDIR)/tag-mime.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/plugins/mime/cmd-break.c b/pigeonhole/src/lib-sieve/plugins/mime/cmd-break.c new file mode 100644 index 0000000..e6ade4c --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/mime/cmd-break.c @@ -0,0 +1,273 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-binary.h" +#include "sieve-dump.h" + +#include "ext-mime-common.h" + +#include <ctype.h> + +/* break + * + * Syntax: + * break [":name" <name: string>] + * + */ + +static bool cmd_break_registered + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool cmd_break_pre_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); +static bool cmd_break_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); +static bool cmd_break_generate + (const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +const struct sieve_command_def cmd_break = { + .identifier = "break", + .type = SCT_COMMAND, + .positional_args = 0, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = cmd_break_registered, + .pre_validate = cmd_break_pre_validate, + .validate = cmd_break_validate, + .generate = cmd_break_generate, +}; + +/* + * Tagged arguments + */ + +/* Forward declarations */ + +static bool cmd_break_validate_name_tag + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd); + +/* Argument objects */ + +static const struct sieve_argument_def break_name_tag = { + .identifier = "name", + .validate = cmd_break_validate_name_tag +}; + +/* + * Break operation + */ + +static bool cmd_break_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int cmd_break_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def break_operation = { + .mnemonic = "BREAK", + .ext_def = &foreverypart_extension, + .code = EXT_FOREVERYPART_OPERATION_BREAK, + .dump = cmd_break_operation_dump, + .execute = cmd_break_operation_execute +}; + +/* + * Validation data + */ + +struct cmd_break_data { + struct sieve_ast_argument *name; + struct sieve_command *loop_cmd; +}; + +/* + * Tag validation + */ + +static bool cmd_break_validate_name_tag +(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct cmd_break_data *data = + (struct cmd_break_data *)cmd->data; + struct sieve_ast_argument *tag = *arg; + + /* Detach the tag itself */ + *arg = sieve_ast_arguments_detach(*arg, 1); + + /* Check syntax: + * :name <string> + */ + if ( !sieve_validate_tag_parameter + (valdtr, cmd, tag, *arg, NULL, 0, SAAT_STRING, TRUE) ) + return FALSE; + data->name = *arg; + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + return TRUE; +} + +/* + * Command registration + */ + +static bool cmd_break_registered +(struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + sieve_validator_register_tag + (valdtr, cmd_reg, ext, &break_name_tag, 0); + + return TRUE; +} + +/* + * Command validation + */ + +static bool cmd_break_pre_validate +(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *cmd) +{ + struct cmd_break_data *data; + pool_t pool = sieve_command_pool(cmd); + + data = p_new(pool, struct cmd_break_data, 1); + cmd->data = data; + return TRUE; +} + +static bool cmd_break_validate +(struct sieve_validator *valdtr, struct sieve_command *cmd) +{ + struct cmd_break_data *data = + (struct cmd_break_data *)cmd->data; + struct sieve_ast_node *node = cmd->ast_node; + const char *name = ( data->name == NULL ? + NULL : sieve_ast_argument_strc(data->name) ); + + i_assert(node != NULL); + while ( node != NULL && node->command != NULL ) { + if ( sieve_command_is(node->command, cmd_foreverypart) ) { + struct ext_foreverypart_loop *loop = + (struct ext_foreverypart_loop *)node->command->data; + if ( name == NULL || + (name != NULL && loop->name != NULL && + strcmp(name, loop->name) == 0) ) { + data->loop_cmd = node->command; + break; + } + } + node = sieve_ast_node_parent(node); + } + + if ( data->loop_cmd == NULL ) { + if ( name == NULL ) { + sieve_command_validate_error(valdtr, cmd, + "the break command is not placed inside " + "a foreverypart loop"); + } else { + sieve_command_validate_error(valdtr, cmd, + "the break command is not placed inside " + "a foreverypart loop named `%s'", + name); + } + return FALSE; + } + + sieve_command_exit_block_unconditionally(cmd); + return TRUE; +} + +/* + * Code generation + */ + +static bool cmd_break_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + struct cmd_break_data *data = + (struct cmd_break_data *)cmd->data; + struct ext_foreverypart_loop *loop; + + i_assert( data->loop_cmd != NULL ); + loop = (struct ext_foreverypart_loop *)data->loop_cmd->data; + + sieve_operation_emit(cgenv->sblock, cmd->ext, &break_operation); + sieve_jumplist_add(loop->exit_jumps, + sieve_binary_emit_offset(cgenv->sblock, 0)); + return TRUE; +} + +/* + * Code dump + */ + +static bool cmd_break_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + unsigned int pc = *address; + sieve_offset_t offset; + + sieve_code_dumpf(denv, "BREAK"); + sieve_code_descend(denv); + + if ( !sieve_binary_read_offset(denv->sblock, address, &offset) ) + return FALSE; + + sieve_code_dumpf(denv, "END: %d [%08x]", offset, pc + offset); + return TRUE; +} + +/* + * Code execution + */ + +static int cmd_break_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + struct sieve_interpreter_loop *loop; + unsigned int pc = *address; + sieve_offset_t offset; + sieve_size_t loop_end; + + /* + * Read operands + */ + + if ( !sieve_binary_read_offset(renv->sblock, address, &offset) ) + { + sieve_runtime_trace_error(renv, "invalid loop end offset"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + loop_end = pc + offset; + + /* + * Perform operation + */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "break command"); + sieve_runtime_trace_descend(renv); + + loop = sieve_interpreter_loop_get + (renv->interp, loop_end, &foreverypart_extension); + if ( loop == NULL ) { + sieve_runtime_trace_error(renv, "no matching loop found"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + sieve_interpreter_loop_break(renv->interp, loop); + return SIEVE_EXEC_OK; +} + + diff --git a/pigeonhole/src/lib-sieve/plugins/mime/cmd-extracttext.c b/pigeonhole/src/lib-sieve/plugins/mime/cmd-extracttext.c new file mode 100644 index 0000000..d9b31a2 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/mime/cmd-extracttext.c @@ -0,0 +1,370 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "array.h" + +#include "sieve-common.h" +#include "sieve-extensions.h" +#include "sieve-code.h" +#include "sieve-ast.h" +#include "sieve-commands.h" +#include "sieve-binary.h" +#include "sieve-message.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "sieve-ext-variables.h" + +#include "ext-mime-common.h" + +/* + * Extracttext command + * + * Syntax: + * extracttext [MODIFIER] [":first" number] <varname: string> + */ + +static bool cmd_extracttext_registered + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool cmd_extracttext_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); +static bool cmd_extracttext_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx); + +const struct sieve_command_def cmd_extracttext = { + .identifier = "extracttext", + .type = SCT_COMMAND, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = cmd_extracttext_registered, + .validate = cmd_extracttext_validate, + .generate = cmd_extracttext_generate +}; + +/* + * Extracttext command tags + */ + +enum cmd_extracttext_optional { + CMD_EXTRACTTEXT_OPT_END, + CMD_EXTRACTTEXT_OPT_FIRST +}; + +static bool cmd_extracttext_validate_first_tag + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd); + +static const struct sieve_argument_def extracttext_from_tag = { + .identifier = "first", + .validate = cmd_extracttext_validate_first_tag +}; + +/* + * Extracttext operation + */ + +static bool cmd_extracttext_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int cmd_extracttext_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def extracttext_operation = { + .mnemonic = "EXTRACTTEXT", + .ext_def = &extracttext_extension, + .dump = cmd_extracttext_operation_dump, + .execute = cmd_extracttext_operation_execute +}; + +/* + * Compiler context + */ + +struct cmd_extracttext_context { + ARRAY_TYPE(sieve_variables_modifier) modifiers; +}; + +/* + * Tag validation + */ + +static bool cmd_extracttext_validate_first_tag +(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + + /* Detach the tag itself */ + *arg = sieve_ast_arguments_detach(*arg,1); + + /* Check syntax: + * :first <number> + */ + if ( !sieve_validate_tag_parameter + (valdtr, cmd, tag, *arg, NULL, 0, SAAT_NUMBER, FALSE) ) + return FALSE; + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + + return TRUE; +} + +/* Command registration */ + +static bool cmd_extracttext_registered +(struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + struct ext_extracttext_context *ectx = + (struct ext_extracttext_context *)ext->context; + + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &extracttext_from_tag, CMD_EXTRACTTEXT_OPT_FIRST); + sieve_variables_modifiers_link_tag + (valdtr, ectx->var_ext, cmd_reg); + return TRUE; +} + +/* + * Command validation + */ + +static bool cmd_extracttext_validate(struct sieve_validator *valdtr, + struct sieve_command *cmd) +{ + const struct sieve_extension *this_ext = cmd->ext; + struct ext_extracttext_context *ectx = + (struct ext_extracttext_context *)this_ext->context; + struct sieve_ast_node *node = cmd->ast_node; + struct sieve_ast_argument *arg = cmd->first_positional; + pool_t pool = sieve_command_pool(cmd); + struct cmd_extracttext_context *sctx; + + /* Create command context */ + sctx = p_new(pool, struct cmd_extracttext_context, 1); + p_array_init(&sctx->modifiers, pool, 4); + cmd->data = (void *) sctx; + + /* Validate modifiers */ + if ( !sieve_variables_modifiers_validate + (valdtr, cmd, &sctx->modifiers) ) + return FALSE; + + /* Validate varname argument */ + if ( !sieve_validate_positional_argument + (valdtr, cmd, arg, "varname", 1, SAAT_STRING) ) { + return FALSE; + } + if ( !sieve_variable_argument_activate + (ectx->var_ext, ectx->var_ext, valdtr, cmd, arg, TRUE) ) + return FALSE; + + /* Check foreverypart context */ + i_assert(node != NULL); + while ( node != NULL ) { + if ( node->command != NULL && + sieve_command_is(node->command, cmd_foreverypart) ) + break; + node = sieve_ast_node_parent(node); + } + + if ( node == NULL ) { + sieve_command_validate_error(valdtr, cmd, + "the extracttext command is not placed inside " + "a foreverypart loop"); + return FALSE; + } + return TRUE; +} + +/* + * Code generation + */ + +static bool cmd_extracttext_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + const struct sieve_extension *this_ext = cmd->ext; + struct sieve_binary_block *sblock = cgenv->sblock; + struct cmd_extracttext_context *sctx = + (struct cmd_extracttext_context *) cmd->data; + + sieve_operation_emit(sblock, this_ext, &extracttext_operation); + + /* Generate arguments */ + if ( !sieve_generate_arguments(cgenv, cmd, NULL) ) + return FALSE; + + /* Generate modifiers */ + if ( !sieve_variables_modifiers_generate + (cgenv, &sctx->modifiers) ) + return FALSE; + + return TRUE; +} + +/* + * Code dump + */ + +static bool cmd_extracttext_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + int opt_code = 0; + + sieve_code_dumpf(denv, "EXTRACTTEXT"); + sieve_code_descend(denv); + + /* Dump optional operands */ + + for (;;) { + int opt; + bool opok = TRUE; + + if ( (opt=sieve_opr_optional_dump(denv, address, &opt_code)) < 0 ) + return FALSE; + if ( opt == 0 ) break; + + switch ( opt_code ) { + case CMD_EXTRACTTEXT_OPT_FIRST: + opok = sieve_opr_number_dump(denv, address, "first"); + break; + default: + return FALSE; + } + if ( !opok ) return FALSE; + } + + /* Print both variable name and string value */ + if ( !sieve_opr_string_dump(denv, address, "varname") ) + return FALSE; + + return sieve_variables_modifiers_code_dump(denv, address); +} + +/* + * Code execution + */ + +static int cmd_extracttext_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + const struct sieve_extension *this_ext = renv->oprtn->ext; + struct ext_extracttext_context *ectx = + (struct ext_extracttext_context *)this_ext->context; + struct sieve_variable_storage *storage; + ARRAY_TYPE(sieve_variables_modifier) modifiers; + struct ext_foreverypart_runtime_loop *sfploop; + struct sieve_message_part *mpart; + struct sieve_message_part_data mpart_data; + int opt_code = 0; + sieve_number_t first = 0; + string_t *value; + unsigned int var_index; + bool have_first = FALSE; + int ret = SIEVE_EXEC_OK; + + /* + * Read the normal operands + */ + + /* Optional operands */ + + for (;;) { + int opt; + + if ( (opt=sieve_opr_optional_read + (renv, address, &opt_code)) < 0 ) + return SIEVE_EXEC_BIN_CORRUPT; + if ( opt == 0 ) break; + + switch ( opt_code ) { + case CMD_EXTRACTTEXT_OPT_FIRST: + ret = sieve_opr_number_read + (renv, address, "first", &first); + have_first = TRUE; + break; + default: + sieve_runtime_trace_error(renv, "unknown optional operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + if ( ret <= 0 ) return ret; + } + + /* Varname operand */ + + if ( (ret=sieve_variable_operand_read + (renv, address, "varname", &storage, &var_index)) <= 0 ) + return ret; + + /* Modifiers */ + + if ( (ret=sieve_variables_modifiers_code_read + (renv, ectx->var_ext, address, &modifiers)) <= 0 ) + return ret; + + /* + * Determine and assign the value + */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "extracttext command"); + sieve_runtime_trace_descend(renv); + + sfploop = ext_foreverypart_runtime_loop_get_current(renv); + if ( sfploop == NULL ) { + sieve_runtime_trace_error(renv, + "outside foreverypart context"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + /* Get current message part */ + mpart = sieve_message_part_iter_current(&sfploop->part_iter); + i_assert( mpart != NULL ); + + /* Get message part content */ + sieve_message_part_get_data(mpart, &mpart_data, TRUE); + + /* Apply ":first" limit, if any */ + if ( !have_first || (size_t)first > mpart_data.size ) { + value = t_str_new_const(mpart_data.content, mpart_data.size); + } else { + value = t_str_new((size_t)first); + str_append_data(value, mpart_data.content, (size_t)first); + } + + /* Apply modifiers */ + if ( (ret=sieve_variables_modifiers_apply + (renv, ectx->var_ext, &modifiers, &value)) <= 0 ) + return ret; + + /* Actually assign the value if all is well */ + i_assert ( value != NULL ); + if ( !sieve_variable_assign(storage, var_index, value) ) + return SIEVE_EXEC_BIN_CORRUPT; + + /* Trace */ + if ( sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS) ) { + const char *var_name, *var_id; + + (void)sieve_variable_get_identifier(storage, var_index, &var_name); + var_id = sieve_variable_get_varid(storage, var_index); + + sieve_runtime_trace_here(renv, 0, "assign `%s' [%s] = \"%s\"", + var_name, var_id, str_c(value)); + } + + return SIEVE_EXEC_OK; +} + + + + + + diff --git a/pigeonhole/src/lib-sieve/plugins/mime/cmd-foreverypart.c b/pigeonhole/src/lib-sieve/plugins/mime/cmd-foreverypart.c new file mode 100644 index 0000000..435cb5c --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/mime/cmd-foreverypart.c @@ -0,0 +1,377 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-limits.h" +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-binary.h" +#include "sieve-dump.h" +#include "sieve-message.h" + +#include "ext-mime-common.h" + +#include <ctype.h> + +/* Foreverypart + * + * Syntax: + * foreverypart [":name" <name: string>] <block> + * + */ + +static bool cmd_foreverypart_registered + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool cmd_foreverypart_pre_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); +static bool cmd_foreverypart_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); +static bool cmd_foreverypart_generate + (const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +const struct sieve_command_def cmd_foreverypart = { + .identifier = "foreverypart", + .type = SCT_COMMAND, + .positional_args = 0, + .subtests = 0, + .block_allowed = TRUE, + .block_required = TRUE, + .registered = cmd_foreverypart_registered, + .pre_validate = cmd_foreverypart_pre_validate, + .validate = cmd_foreverypart_validate, + .generate = cmd_foreverypart_generate +}; + +/* + * Tagged arguments + */ + +/* Forward declarations */ + +static bool cmd_foreverypart_validate_name_tag + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd); + +/* Argument objects */ + +static const struct sieve_argument_def foreverypart_name_tag = { + .identifier = "name", + .validate = cmd_foreverypart_validate_name_tag, +}; + +/* + * foreverypart operation + */ + +static bool cmd_foreverypart_begin_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int cmd_foreverypart_begin_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def foreverypart_begin_operation = { + .mnemonic = "FOREVERYPART_BEGIN", + .ext_def = &foreverypart_extension, + .code = EXT_FOREVERYPART_OPERATION_FOREVERYPART_BEGIN, + .dump = cmd_foreverypart_begin_operation_dump, + .execute = cmd_foreverypart_begin_operation_execute +}; + +static bool cmd_foreverypart_end_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int cmd_foreverypart_end_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def foreverypart_end_operation = { + .mnemonic = "FOREVERYPART_END", + .ext_def = &foreverypart_extension, + .code = EXT_FOREVERYPART_OPERATION_FOREVERYPART_END, + .dump = cmd_foreverypart_end_operation_dump, + .execute = cmd_foreverypart_end_operation_execute +}; + +/* + * Tag validation + */ + +static bool cmd_foreverypart_validate_name_tag +(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct ext_foreverypart_loop *loop = + (struct ext_foreverypart_loop *)cmd->data; + struct sieve_ast_argument *tag = *arg; + + /* Detach the tag itself */ + *arg = sieve_ast_arguments_detach(*arg, 1); + + /* Check syntax: + * :name <string> + */ + if ( !sieve_validate_tag_parameter + (valdtr, cmd, tag, *arg, NULL, 0, SAAT_STRING, TRUE) ) + return FALSE; + loop->name = sieve_ast_argument_strc(*arg); + + /* Detach parameter */ + *arg = sieve_ast_arguments_detach(*arg, 1); + return TRUE; +} + +/* + * Command registration + */ + +static bool cmd_foreverypart_registered +(struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + sieve_validator_register_tag + (valdtr, cmd_reg, ext, &foreverypart_name_tag, 0); + return TRUE; +} + +/* + * Command validation + */ + +static bool cmd_foreverypart_pre_validate +(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *cmd) +{ + struct ext_foreverypart_loop *loop; + pool_t pool = sieve_command_pool(cmd); + + loop = p_new(pool, struct ext_foreverypart_loop, 1); + cmd->data = loop; + + return TRUE; +} + +static bool cmd_foreverypart_validate +(struct sieve_validator *valdtr, struct sieve_command *cmd) +{ + struct sieve_ast_node *node = cmd->ast_node; + unsigned int nesting = 0; + + /* Determine nesting depth of foreverypart commands at this point. */ + i_assert(node != NULL); + node = sieve_ast_node_parent(node); + while ( node != NULL && node->command != NULL ) { + if ( sieve_command_is(node->command, cmd_foreverypart) ) + nesting++; + node = sieve_ast_node_parent(node); + } + + /* Enforce nesting limit + NOTE: this only recognizes the foreverypart command as a loop; if + new loop commands are introduced in the future, these must be + recognized somehow. */ + if ( nesting + 1 > SIEVE_MAX_LOOP_DEPTH ) { + sieve_command_validate_error(valdtr, cmd, + "the nested foreverypart loop exceeds " + "the nesting limit (<= %u levels)", + SIEVE_MAX_LOOP_DEPTH); + return FALSE; + } + + return TRUE; +} + +/* + * Code generation + */ + +static bool cmd_foreverypart_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + struct ext_foreverypart_loop *loop = + (struct ext_foreverypart_loop *)cmd->data; + sieve_size_t block_begin, loop_jump; + + /* Emit FOREVERYPART_BEGIN operation */ + sieve_operation_emit(cgenv->sblock, + cmd->ext, &foreverypart_begin_operation); + + /* Emit exit address */ + loop->exit_jumps = sieve_jumplist_create + (sieve_command_pool(cmd), cgenv->sblock); + sieve_jumplist_add(loop->exit_jumps, + sieve_binary_emit_offset(cgenv->sblock, 0)); + block_begin = sieve_binary_block_get_size(cgenv->sblock); + + /* Generate loop block */ + if ( !sieve_generate_block(cgenv, cmd->ast_node) ) + return FALSE; + + /* Emit FOREVERYPART_END operation */ + sieve_operation_emit(cgenv->sblock, + cmd->ext, &foreverypart_end_operation); + loop_jump = sieve_binary_block_get_size(cgenv->sblock); + i_assert(loop_jump > block_begin); + (void)sieve_binary_emit_offset + (cgenv->sblock, (loop_jump - block_begin)); + + /* Resolve exit address */ + sieve_jumplist_resolve(loop->exit_jumps); + + return TRUE; +} + +/* + * Code dump + */ + +static bool cmd_foreverypart_begin_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + unsigned int pc = *address; + sieve_offset_t offset; + + sieve_code_dumpf(denv, "FOREVERYPART_BEGIN"); + sieve_code_descend(denv); + + if ( !sieve_binary_read_offset(denv->sblock, address, &offset) ) + return FALSE; + + sieve_code_dumpf(denv, "END: %d [%08x]", offset, pc + offset); + return TRUE; +} + +static bool cmd_foreverypart_end_operation_dump +(const struct sieve_dumptime_env *denv, + sieve_size_t *address ATTR_UNUSED) +{ + unsigned int pc = *address; + sieve_offset_t offset; + + sieve_code_dumpf(denv, "FOREVERYPART_END"); + sieve_code_descend(denv); + + if ( !sieve_binary_read_offset(denv->sblock, address, &offset) ) + return FALSE; + + sieve_code_dumpf(denv, "BEGIN: -%d [%08x]", offset, pc - offset); + return TRUE; +} + +/* + * Code execution + */ + +static int cmd_foreverypart_begin_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + struct sieve_interpreter_loop *loop; + struct ext_foreverypart_runtime_loop *fploop, *sfploop; + unsigned int pc = *address; + sieve_offset_t offset; + sieve_size_t loop_end; + pool_t pool; + int ret; + + /* + * Read operands + */ + + if ( !sieve_binary_read_offset(renv->sblock, address, &offset) ) + { + sieve_runtime_trace_error(renv, "invalid loop end offset"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + loop_end = pc + offset; + + /* + * Perform operation + */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, + "foreverypart loop begin"); + sieve_runtime_trace_descend(renv); + + sfploop = ext_foreverypart_runtime_loop_get_current(renv); + + if ( (ret=sieve_interpreter_loop_start(renv->interp, + loop_end, &foreverypart_extension, &loop)) <= 0 ) + return ret; + + pool = sieve_interpreter_loop_get_pool(loop); + fploop = p_new(pool, struct ext_foreverypart_runtime_loop, 1); + + if ( sfploop == NULL ) { + if ( (ret=sieve_message_part_iter_init + (&fploop->part_iter, renv)) <= 0 ) + return ret; + } else { + sieve_message_part_iter_children(&sfploop->part_iter, + &fploop->part_iter); + } + fploop->part = sieve_message_part_iter_current(&fploop->part_iter); + if (fploop->part != NULL) { + sieve_interpreter_loop_set_context(loop, (void*)fploop); + } else { + /* No children parts to iterate */ + sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, + "no children at this level"); + sieve_interpreter_loop_break(renv->interp, loop); + } + return SIEVE_EXEC_OK; +} + +static int cmd_foreverypart_end_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + struct sieve_interpreter_loop *loop; + struct ext_foreverypart_runtime_loop *fploop; + unsigned int pc = *address; + sieve_offset_t offset; + sieve_size_t loop_begin; + + /* + * Read operands + */ + + if ( !sieve_binary_read_offset(renv->sblock, address, &offset) ) + { + sieve_runtime_trace_error(renv, "invalid loop begin offset"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + loop_begin = pc - offset; + + /* + * Perform operation + */ + + sieve_runtime_trace(renv, + SIEVE_TRLVL_COMMANDS, "foreverypart loop end"); + sieve_runtime_trace_descend(renv); + + loop = sieve_interpreter_loop_get + (renv->interp, *address, &foreverypart_extension); + if ( loop == NULL ) { + sieve_runtime_trace_error(renv, "no matching loop found"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + fploop = (struct ext_foreverypart_runtime_loop *) + sieve_interpreter_loop_get_context(loop); + i_assert(fploop->part != NULL); + fploop->part = sieve_message_part_iter_next(&fploop->part_iter); + if ( fploop->part == NULL ) { + sieve_runtime_trace(renv, + SIEVE_TRLVL_COMMANDS, "no more message parts"); + return sieve_interpreter_loop_break(renv->interp, loop); + } + + sieve_runtime_trace(renv, + SIEVE_TRLVL_COMMANDS, "switched to next message part"); + return sieve_interpreter_loop_next(renv->interp, loop, loop_begin); +} + + diff --git a/pigeonhole/src/lib-sieve/plugins/mime/ext-extracttext.c b/pigeonhole/src/lib-sieve/plugins/mime/ext-extracttext.c new file mode 100644 index 0000000..4eab76b --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/mime/ext-extracttext.c @@ -0,0 +1,130 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension extracttext + * --------------------- + * + * Authors: Stephan Bosch + * Specification: RFC 5703, Section 7 + * Implementation: full + * Status: experimental + * + */ + +#include "sieve-common.h" + +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-actions.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-result.h" + +#include "sieve-ext-variables.h" + +#include "ext-mime-common.h" + +/* + * Extension + */ + +static bool ext_extracttext_load + (const struct sieve_extension *ext, void **context); +static void ext_extracttext_unload + (const struct sieve_extension *ext); +static bool ext_extracttext_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); + +const struct sieve_extension_def extracttext_extension = { + .name = "extracttext", + .load = ext_extracttext_load, + .unload = ext_extracttext_unload, + .validator_load = ext_extracttext_validator_load, + SIEVE_EXT_DEFINE_OPERATION(extracttext_operation) +}; + +static bool ext_extracttext_load +(const struct sieve_extension *ext, void **context) +{ + struct sieve_instance *svinst = ext->svinst; + struct ext_extracttext_context *ectx; + + if ( *context != NULL ) + ext_extracttext_unload(ext); + + ectx = i_new(struct ext_extracttext_context, 1); + ectx->var_ext = sieve_ext_variables_get_extension(ext->svinst); + ectx->fep_ext = sieve_extension_register + (svinst, &foreverypart_extension, FALSE); + *context = (void *)ectx; + return TRUE; +} + +static void ext_extracttext_unload +(const struct sieve_extension *ext) +{ + struct ext_extracttext_context *ctx = + (struct ext_extracttext_context *) ext->context; + + i_free(ctx); +} + +/* + * Extension validation + */ + +static bool ext_extracttext_validator_validate + (const struct sieve_extension *ext, + struct sieve_validator *valdtr, void *context, + struct sieve_ast_argument *require_arg, + bool required); + +const struct sieve_validator_extension +extracttext_validator_extension = { + .ext = &extracttext_extension, + .validate = ext_extracttext_validator_validate +}; + +static bool ext_extracttext_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + /* Register validator extension to check for conflict with eextracttext */ + sieve_validator_extension_register + (valdtr, ext, &extracttext_validator_extension, NULL); + + /* Register new commands */ + sieve_validator_register_command(valdtr, ext, &cmd_extracttext); + + return TRUE; +} + +static bool ext_extracttext_validator_validate +(const struct sieve_extension *ext, + struct sieve_validator *valdtr, void *context ATTR_UNUSED, + struct sieve_ast_argument *require_arg, + bool required ATTR_UNUSED) +{ + struct ext_extracttext_context *ectx = + (struct ext_extracttext_context *)ext->context; + + if ( ectx->var_ext == NULL || + !sieve_ext_variables_is_active + (ectx->var_ext, valdtr) ) { + sieve_argument_validate_error(valdtr, require_arg, + "extracttext extension cannot be used " + "without variables extension"); + return FALSE; + } + if ( ectx->fep_ext == NULL || + !sieve_validator_extension_loaded + (valdtr, ectx->fep_ext) ) { + sieve_argument_validate_error(valdtr, require_arg, + "extracttext extension cannot be used " + "without foreverypart extension"); + return FALSE; + } + + return TRUE; +} diff --git a/pigeonhole/src/lib-sieve/plugins/mime/ext-foreverypart.c b/pigeonhole/src/lib-sieve/plugins/mime/ext-foreverypart.c new file mode 100644 index 0000000..6bca199 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/mime/ext-foreverypart.c @@ -0,0 +1,62 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension foreverypart + * ---------------------- + * + * Authors: Stephan Bosch + * Specification: RFC 5703, Section 3 + * Implementation: full + * Status: experimental + * + */ + +#include "sieve-common.h" + +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-actions.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-result.h" + +#include "ext-mime-common.h" + +/* + * Operations + */ + +const struct sieve_operation_def *ext_foreverypart_operations[] = { + &foreverypart_begin_operation, + &foreverypart_end_operation, + &break_operation +}; + +/* + * Extension + */ + +static bool ext_foreverypart_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); + +const struct sieve_extension_def foreverypart_extension = { + .name = "foreverypart", + .validator_load = ext_foreverypart_validator_load, + SIEVE_EXT_DEFINE_OPERATIONS(ext_foreverypart_operations) +}; + +/* + * Extension validation + */ + +static bool ext_foreverypart_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + /* Register new commands */ + sieve_validator_register_command(valdtr, ext, &cmd_foreverypart); + sieve_validator_register_command(valdtr, ext, &cmd_break); + + return TRUE; +} diff --git a/pigeonhole/src/lib-sieve/plugins/mime/ext-mime-common.c b/pigeonhole/src/lib-sieve/plugins/mime/ext-mime-common.c new file mode 100644 index 0000000..5b38bcb --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/mime/ext-mime-common.c @@ -0,0 +1,27 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-interpreter.h" + +#include "ext-mime-common.h" + +struct ext_foreverypart_runtime_loop * +ext_foreverypart_runtime_loop_get_current +(const struct sieve_runtime_env *renv) +{ + struct sieve_interpreter_loop *loop; + struct ext_foreverypart_runtime_loop *fploop; + + loop = sieve_interpreter_loop_get_global + (renv->interp, NULL, &foreverypart_extension); + if ( loop == NULL ) { + fploop = NULL; + } else { + fploop = (struct ext_foreverypart_runtime_loop *) + sieve_interpreter_loop_get_context(loop); + i_assert(fploop->part != NULL); + } + + return fploop; +} diff --git a/pigeonhole/src/lib-sieve/plugins/mime/ext-mime-common.h b/pigeonhole/src/lib-sieve/plugins/mime/ext-mime-common.h new file mode 100644 index 0000000..8b0054d --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/mime/ext-mime-common.h @@ -0,0 +1,85 @@ +#ifndef EXT_FOREVERYPART_COMMON_H +#define EXT_FOREVERYPART_COMMON_H + +#include "sieve-message.h" + +/* + * Extension + */ + +struct ext_extracttext_context { + const struct sieve_extension *var_ext; + const struct sieve_extension *fep_ext; +}; + +extern const struct sieve_extension_def foreverypart_extension; +extern const struct sieve_extension_def mime_extension; +extern const struct sieve_extension_def extracttext_extension; + +/* + * Tagged arguments + */ + +extern const struct sieve_argument_def mime_tag; +extern const struct sieve_argument_def mime_anychild_tag; +extern const struct sieve_argument_def mime_type_tag; +extern const struct sieve_argument_def mime_subtype_tag; +extern const struct sieve_argument_def mime_contenttype_tag; +extern const struct sieve_argument_def mime_param_tag; + +/* + * Commands + */ + +struct ext_foreverypart_loop { + const char *name; + struct sieve_jumplist *exit_jumps; +}; + +extern const struct sieve_command_def cmd_foreverypart; +extern const struct sieve_command_def cmd_break; +extern const struct sieve_command_def cmd_extracttext; + +/* + * Operations + */ + +extern const struct sieve_operation_def foreverypart_begin_operation; +extern const struct sieve_operation_def foreverypart_end_operation; +extern const struct sieve_operation_def break_operation; +extern const struct sieve_operation_def extracttext_operation; + +enum ext_foreverypart_opcode { + EXT_FOREVERYPART_OPERATION_FOREVERYPART_BEGIN, + EXT_FOREVERYPART_OPERATION_FOREVERYPART_END, + EXT_FOREVERYPART_OPERATION_BREAK, +}; + +/* + * Operands + */ + +enum ext_mime_option { + EXT_MIME_OPTION_NONE = 0, + EXT_MIME_OPTION_TYPE, + EXT_MIME_OPTION_SUBTYPE, + EXT_MIME_OPTION_CONTENTTYPE, + EXT_MIME_OPTION_PARAM +}; + +extern const struct sieve_operand_def mime_operand; + +/* + * Foreverypart loop + */ + +struct ext_foreverypart_runtime_loop { + struct sieve_message_part_iter part_iter; + struct sieve_message_part *part; +}; + +struct ext_foreverypart_runtime_loop * +ext_foreverypart_runtime_loop_get_current +(const struct sieve_runtime_env *renv); + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/mime/ext-mime.c b/pigeonhole/src/lib-sieve/plugins/mime/ext-mime.c new file mode 100644 index 0000000..da9963f --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/mime/ext-mime.c @@ -0,0 +1,77 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension mime + * -------------- + * + * Authors: Stephan Bosch + * Specification: RFC 5703, Section 4 + * Implementation: full + * Status: experimental + * + */ + +#include "sieve-common.h" + +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-actions.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-message.h" +#include "sieve-result.h" + +#include "ext-mime-common.h" + +/* + * Extension + */ + +static bool ext_mime_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); + +const struct sieve_extension_def mime_extension = { + .name = "mime", + .validator_load = ext_mime_validator_load, + SIEVE_EXT_DEFINE_OPERAND(mime_operand) +}; + +/* + * Extension validation + */ + +static bool ext_mime_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + /* Register :mime tag and friends with header, address and exists test + * commands and we don't care whether these command are registered or + * even whether these will be registered at all. The validator handles + * either situation gracefully. + */ + sieve_validator_register_external_tag + (valdtr, "header", ext, &mime_tag, SIEVE_OPT_MESSAGE_OVERRIDE); + sieve_validator_register_external_tag + (valdtr, "header", ext, &mime_anychild_tag, 0); + sieve_validator_register_external_tag + (valdtr, "header", ext, &mime_type_tag, 0); + sieve_validator_register_external_tag + (valdtr, "header", ext, &mime_subtype_tag, 0); + sieve_validator_register_external_tag + (valdtr, "header", ext, &mime_contenttype_tag, 0); + sieve_validator_register_external_tag + (valdtr, "header", ext, &mime_param_tag, 0); + + sieve_validator_register_external_tag + (valdtr, "address", ext, &mime_tag, SIEVE_OPT_MESSAGE_OVERRIDE); + sieve_validator_register_external_tag + (valdtr, "address", ext, &mime_anychild_tag, 0); + + sieve_validator_register_external_tag + (valdtr, "exists", ext, &mime_tag, SIEVE_OPT_MESSAGE_OVERRIDE); + sieve_validator_register_external_tag + (valdtr, "exists", ext, &mime_anychild_tag, 0); + + return TRUE; +} diff --git a/pigeonhole/src/lib-sieve/plugins/mime/tag-mime.c b/pigeonhole/src/lib-sieve/plugins/mime/tag-mime.c new file mode 100644 index 0000000..379365e --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/mime/tag-mime.c @@ -0,0 +1,757 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "rfc822-parser.h" +#include "rfc2231-parser.h" +#include "mail-storage.h" + +#include "sieve-common.h" +#include "sieve-stringlist.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-binary.h" +#include "sieve-code.h" +#include "sieve-message.h" +#include "sieve-result.h" +#include "sieve-validator.h" +#include "sieve-generator.h" + +#include "ext-mime-common.h" + +/* + * Tagged argument + */ + +static bool tag_mime_validate + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd); +static bool tag_mime_generate + (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *context); + +const struct sieve_argument_def mime_tag = { + .identifier = "mime", + .validate = tag_mime_validate, + .generate = tag_mime_generate +}; + +static bool tag_mime_option_validate + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd); + +const struct sieve_argument_def mime_anychild_tag = { + .identifier = "anychild", + .validate = tag_mime_option_validate +}; + +const struct sieve_argument_def mime_type_tag = { + .identifier = "type", + .validate = tag_mime_option_validate +}; + +const struct sieve_argument_def mime_subtype_tag = { + .identifier = "subtype", + .validate = tag_mime_option_validate +}; + +const struct sieve_argument_def mime_contenttype_tag = { + .identifier = "contenttype", + .validate = tag_mime_option_validate +}; + +const struct sieve_argument_def mime_param_tag = { + .identifier = "param", + .validate = tag_mime_option_validate +}; + +/* + * Header override + */ + +static bool svmo_mime_dump_context + (const struct sieve_message_override *svmo, + const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int svmo_mime_read_context + (const struct sieve_message_override *svmo, + const struct sieve_runtime_env *renv, sieve_size_t *address, + void **ho_context); +static int svmo_mime_header_override + (const struct sieve_message_override *svmo, + const struct sieve_runtime_env *renv, + bool mime_decode, struct sieve_stringlist **headers); + +const struct sieve_message_override_def mime_header_override = { + SIEVE_OBJECT("mime", &mime_operand, 0), + .sequence = 0, /* Completely replace header source */ + .dump_context = svmo_mime_dump_context, + .read_context = svmo_mime_read_context, + .header_override = svmo_mime_header_override +}; + +/* + * Operand + */ + +static const struct sieve_extension_objects ext_header_overrides = + SIEVE_EXT_DEFINE_MESSAGE_OVERRIDE(mime_header_override); + +const struct sieve_operand_def mime_operand = { + .name = "mime operand", + .ext_def = &mime_extension, + .class = &sieve_message_override_operand_class, + .interface = &ext_header_overrides +}; + +/* + * Tag data + */ + +struct tag_mime_data { + enum ext_mime_option mimeopt; + struct sieve_ast_argument *param_arg; + bool anychild:1; +}; + +/* + * Tag validation + */ + +static struct tag_mime_data * +tag_mime_get_data(struct sieve_command *cmd, + struct sieve_ast_argument *tag) +{ + struct tag_mime_data *data; + + if (tag->argument->data == NULL) { + data = p_new(sieve_command_pool(cmd), struct tag_mime_data, 1); + tag->argument->data = (void *)data; + } else { + data = (struct tag_mime_data *)tag->argument->data; + } + + return data; +} + +static bool tag_mime_validate +(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_ast_argument **arg, struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + + /* Skip the tag itself */ + *arg = sieve_ast_argument_next(*arg); + + (void)tag_mime_get_data(cmd, tag); + return TRUE; +} + +static bool tag_mime_option_validate +(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_ast_argument **arg, struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + struct sieve_ast_argument *mime_arg; + struct tag_mime_data *data; + + i_assert(tag != NULL); + + /* Detach tag itself */ + *arg = sieve_ast_arguments_detach(*arg,1); + + /* Find required ":mime" tag */ + mime_arg = sieve_command_find_argument(cmd, &mime_tag); + if ( mime_arg == NULL ) { + sieve_argument_validate_error(valdtr, tag, + "the :%s tag for the %s %s cannot be specified " + "without the :mime tag", sieve_ast_argument_tag(tag), + sieve_command_identifier(cmd), sieve_command_type_name(cmd)); + return FALSE; + } + + /* Annotate ":mime" tag with the data provided by this option tag */ + data = tag_mime_get_data(cmd, mime_arg); + if ( sieve_argument_is(tag, mime_anychild_tag) ) + data->anychild = TRUE; + else { + if ( data->mimeopt != EXT_MIME_OPTION_NONE ) { + sieve_argument_validate_error(valdtr, *arg, + "the :type, :subtype, :contenttype, and :param " + "arguments for the %s test are mutually exclusive, " + "but more than one was specified", + sieve_command_identifier(cmd)); + return FALSE; + } + if ( sieve_argument_is(tag, mime_type_tag) ) + data->mimeopt = EXT_MIME_OPTION_TYPE; + else if ( sieve_argument_is(tag, mime_subtype_tag) ) + data->mimeopt = EXT_MIME_OPTION_SUBTYPE; + else if ( sieve_argument_is(tag, mime_contenttype_tag) ) + data->mimeopt = EXT_MIME_OPTION_CONTENTTYPE; + else if ( sieve_argument_is(tag, mime_param_tag) ) { + /* Check syntax: + * ":param" <param-list: string-list> + */ + if ( !sieve_validate_tag_parameter + (valdtr, cmd, tag, *arg, NULL, 0, SAAT_STRING_LIST, FALSE) ) { + return FALSE; + } + + data->mimeopt = EXT_MIME_OPTION_PARAM; + data->param_arg = *arg; + + /* Detach parameter */ + *arg = sieve_ast_arguments_detach(*arg,1); + } else + i_unreached(); + } + return TRUE; +} + +/* + * Code generation + */ + +static bool tag_mime_generate +(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *cmd) +{ + struct tag_mime_data *data = + (struct tag_mime_data *)arg->argument->data; + + if ( sieve_ast_argument_type(arg) != SAAT_TAG ) + return FALSE; + + sieve_opr_message_override_emit + (cgenv->sblock, arg->argument->ext, &mime_header_override); + + (void)sieve_binary_emit_byte + (cgenv->sblock, ( data->anychild ? 1 : 0 )); + (void)sieve_binary_emit_byte + (cgenv->sblock, data->mimeopt); + if ( data->mimeopt == EXT_MIME_OPTION_PARAM && + !sieve_generate_argument(cgenv, data->param_arg, cmd) ) + return FALSE; + return TRUE; +} + +/* + * Content-type stringlist + */ + +enum content_type_part { + CONTENT_TYPE_PART_NONE = 0, + CONTENT_TYPE_PART_TYPE, + CONTENT_TYPE_PART_SUBTYPE, + CONTENT_TYPE_PART_CONTENTTYPE, +}; + +/* Object */ + +static int content_header_stringlist_next_item + (struct sieve_stringlist *_strlist, string_t **str_r); +static void content_header_stringlist_reset + (struct sieve_stringlist *_strlist); +static int content_header_stringlist_get_length + (struct sieve_stringlist *_strlist); +static void content_header_stringlist_set_trace + (struct sieve_stringlist *strlist, bool trace); + +struct content_header_stringlist { + struct sieve_stringlist strlist; + + struct sieve_header_list *source; + + enum ext_mime_option option; + const char *const *params; + + const char *const *param_values; +}; + +static struct sieve_stringlist *content_header_stringlist_create +(const struct sieve_runtime_env *renv, + struct sieve_header_list *source, + enum ext_mime_option option, const char *const *params) +{ + struct content_header_stringlist *strlist; + + strlist = t_new(struct content_header_stringlist, 1); + strlist->strlist.runenv = renv; + strlist->strlist.exec_status = SIEVE_EXEC_OK; + strlist->strlist.next_item = content_header_stringlist_next_item; + strlist->strlist.reset = content_header_stringlist_reset; + strlist->strlist.set_trace = content_header_stringlist_set_trace; + strlist->source = source; + strlist->option = option; + strlist->params = params; + + if ( option != EXT_MIME_OPTION_PARAM ) { + /* One header can have multiple parameters, so we cannot rely + on the source length for the :param option. */ + strlist->strlist.get_length = content_header_stringlist_get_length; + } + + return &strlist->strlist; +} + +/* Implementation */ + +static inline int _decode_hex_digit(const unsigned char digit) +{ + switch ( digit ) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return digit - '0'; + + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + return digit - 'a' + 0x0a; + + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + return digit - 'A' + 0x0A; + } + return -1; +} + +static string_t * +content_type_param_decode(const char *value) +{ + const unsigned char *p, *plast; + + string_t *str = t_str_new(64); + plast = p = (const unsigned char *)value; + while ( *p != '\0' ) { + unsigned char ch; + int digit; + + if ( *p == '%' ) { + if ( p - plast > 0 ) + str_append_data(str, plast, (p - plast)); + p++; + if ( *p == '\0' || (digit=_decode_hex_digit(*p)) < 0 ) + return NULL; + ch = (unsigned char)digit; + p++; + if ( *p == '\0' || (digit=_decode_hex_digit(*p)) < 0 ) + return NULL; + ch = (ch << 4) + (unsigned char)digit; + str_append_data(str, &ch, 1); + plast = p + 1; + } + p++; + } + if ( p - plast > 0 ) + str_append_data(str, plast, (p - plast)); + return str; +} + +static string_t * +content_type_param_next(struct content_header_stringlist *strlist) +{ + const struct sieve_runtime_env *renv = strlist->strlist.runenv; + const char *const *values = strlist->param_values; + bool trace = strlist->strlist.trace; + + i_assert( strlist->params != NULL ); + + /* Iterate over all parsed parameter values */ + for ( ; *values != NULL; values += 2 ) { + const char *const *params = strlist->params; + const char *name = values[0], *value = values[1]; + size_t nlen = strlen(name); + + /* Iterate over all interesting parameter names */ + for ( ; *params != NULL; params++ ) { + size_t plen = strlen(*params); + + if ( plen != nlen && + (nlen != plen + 1 || name[nlen-1] != '*') ) + continue; + + if ( plen == nlen ) { + if ( strcasecmp(name, *params) == 0 ) { + /* Return raw value */ + if ( trace ) { + sieve_runtime_trace(renv, 0, + "found mime parameter `%s' in mime header", + *params); + } + + strlist->param_values = values + 2; + return t_str_new_const(value, strlen(value)); + } + } else { + if ( trace ) { + sieve_runtime_trace(renv, 0, + "found encoded parameter `%s' in mime header", + *params); + } + + if ( strncasecmp(name, *params, plen) == 0 ) { + string_t *result = NULL; + + strlist->param_values = values + 2; + + /* Decode value first */ + // FIXME: transcode charset + value = strchr(value, '\''); + if (value != NULL) + value = strchr(value+1, '\''); + if (value != NULL) + result = content_type_param_decode(value + 1); + if (result == NULL) + strlist->param_values = NULL; + return result; + } + } + } + } + + strlist->param_values = NULL; + return NULL; +} + +// FIXME: not too happy with the use of string_t like this. +// Sieve should have a special runtime string type (TODO) +static string_t * +content_header_parse(struct content_header_stringlist *strlist, + const char *hdr_name, string_t *str) +{ + const struct sieve_runtime_env *renv = strlist->strlist.runenv; + bool trace = strlist->strlist.trace; + struct rfc822_parser_context parser; + const char *type, *p; + bool is_ctype = FALSE; + string_t *content; + + if ( strlist->option == EXT_MIME_OPTION_NONE ) + return str; + + if ( strcasecmp(hdr_name, "content-type") == 0 ) + is_ctype = TRUE; + else if ( strcasecmp(hdr_name, "content-disposition") != 0 ) { + if ( trace ) { + sieve_runtime_trace(renv, 0, + "non-mime header yields empty string"); + } + return t_str_new(0); + } + + /* Initialize parsing */ + rfc822_parser_init(&parser, str_data(str), str_len(str), NULL); + (void)rfc822_skip_lwsp(&parser); + + /* Parse content type/disposition */ + content = t_str_new(64); + if ( is_ctype ){ + if (rfc822_parse_content_type(&parser, content) < 0) { + str_truncate(content, 0); + return content; + } + } else { + if (rfc822_parse_mime_token(&parser, content) < 0) { + str_truncate(content, 0); + return content; + } + } + + /* Content-type value must end here, otherwise it is invalid after all */ + (void)rfc822_skip_lwsp(&parser); + if ( parser.data != parser.end && *parser.data != ';' ) { + str_truncate(content, 0); + return content; + } + + if ( strlist->option == EXT_MIME_OPTION_PARAM ) { + string_t *param_val; + + /* MIME parameter */ + i_assert( strlist->params != NULL ); + + // FIXME: not very nice when multiple parameters in the same header + // are queried in successive tests. + str_truncate(content, 0); + rfc2231_parse(&parser, &strlist->param_values); + + param_val = content_type_param_next(strlist); + if ( param_val != NULL ) + content = param_val; + } else { + /* Get :type/:subtype:/:contenttype value */ + type = str_c(content); + p = strchr(type, '/'); + switch ( strlist->option ) { + case EXT_MIME_OPTION_TYPE: + if ( trace ) { + sieve_runtime_trace(renv, 0, + "extracted MIME type"); + } + if ( p != NULL ) { + i_assert( is_ctype ); + str_truncate(content, (p - type)); + } + break; + case EXT_MIME_OPTION_SUBTYPE: + if ( p == NULL ) { + i_assert( !is_ctype ); + if ( trace ) { + sieve_runtime_trace(renv, 0, + "no MIME sub-type for content-disposition"); + } + str_truncate(content, 0); + break; + } + + i_assert( is_ctype ); + if ( trace ) { + sieve_runtime_trace(renv, 0, + "extracted MIME sub-type"); + } + str_delete(content, 0, (p - type) + 1); + break; + case EXT_MIME_OPTION_CONTENTTYPE: + sieve_runtime_trace(renv, 0, + "extracted full MIME contenttype"); + break; + default: + break; + } + } + + /* Success */ + return content; +} + +static int content_header_stringlist_next_item +(struct sieve_stringlist *_strlist, string_t **str_r) +{ + struct content_header_stringlist *strlist = + (struct content_header_stringlist *)_strlist; + const char *hdr_name; + int ret; + + if ( strlist->param_values != NULL ) { + string_t *param_val; + + i_assert( strlist->option == EXT_MIME_OPTION_PARAM ); + param_val = content_type_param_next(strlist); + if ( param_val != NULL ) { + *str_r = param_val; + return 1; + } + } + + if ( (ret=sieve_header_list_next_item + (strlist->source, &hdr_name, str_r)) <= 0 ) { + if (ret < 0) { + _strlist->exec_status = + strlist->source->strlist.exec_status; + } + return ret; + } + + *str_r = content_header_parse(strlist, hdr_name, *str_r); + return 1; +} + +static void content_header_stringlist_reset +(struct sieve_stringlist *_strlist) +{ + struct content_header_stringlist *strlist = + (struct content_header_stringlist *)_strlist; + sieve_header_list_reset(strlist->source); +} + +static int content_header_stringlist_get_length +(struct sieve_stringlist *_strlist) +{ + struct content_header_stringlist *strlist = + (struct content_header_stringlist *)_strlist; + return sieve_header_list_get_length(strlist->source); +} + +static void content_header_stringlist_set_trace +(struct sieve_stringlist *_strlist, bool trace) +{ + struct content_header_stringlist *strlist = + (struct content_header_stringlist *)_strlist; + sieve_header_list_set_trace(strlist->source, trace); +} + +/* + * Header override implementation + */ + +/* Context data */ + +struct svmo_mime_context { + enum ext_mime_option mimeopt; + const char *const *params; + bool anychild:1; +}; + +/* Context coding */ + +static bool svmo_mime_dump_context +(const struct sieve_message_override *svmo ATTR_UNUSED, + const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + unsigned int anychild, mimeopt; + + if ( !sieve_binary_read_byte(denv->sblock, address, &anychild) ) + return FALSE; + if ( anychild > 0 ) + sieve_code_dumpf(denv, "anychild"); + + if ( !sieve_binary_read_byte(denv->sblock, address, &mimeopt) ) + return FALSE; + + switch ( mimeopt ) { + case EXT_MIME_OPTION_NONE: + break; + case EXT_MIME_OPTION_TYPE: + sieve_code_dumpf(denv, "option: type"); + break; + case EXT_MIME_OPTION_SUBTYPE: + sieve_code_dumpf(denv, "option: subtype"); + break; + case EXT_MIME_OPTION_CONTENTTYPE: + sieve_code_dumpf(denv, "option: contenttype"); + break; + case EXT_MIME_OPTION_PARAM: + sieve_code_dumpf(denv, "option: param"); + sieve_code_descend(denv); + if ( !sieve_opr_stringlist_dump(denv, address, "param-list") ) + return FALSE; + sieve_code_ascend(denv); + break; + default: + return FALSE; + } + return TRUE; +} + +static int svmo_mime_read_context +(const struct sieve_message_override *svmo ATTR_UNUSED, + const struct sieve_runtime_env *renv, sieve_size_t *address, + void **ho_context) +{ + pool_t pool = sieve_result_pool(renv->result); // FIXME: investigate + struct svmo_mime_context *ctx; + unsigned int anychild = 0, mimeopt = EXT_MIME_OPTION_NONE; + struct sieve_stringlist *param_list = NULL; + int ret; + + if ( !sieve_binary_read_byte + (renv->sblock, address, &anychild) ) { + sieve_runtime_trace_error(renv, + "anychild: invalid byte"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if ( !sieve_binary_read_byte + (renv->sblock, address, &mimeopt) || + mimeopt > EXT_MIME_OPTION_PARAM ) { + sieve_runtime_trace_error(renv, + "option: invalid mime option code"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if ( mimeopt == EXT_MIME_OPTION_PARAM && + (ret=sieve_opr_stringlist_read + (renv, address, "param-list", ¶m_list)) <= 0 ) + return ret; + + ctx = p_new(pool, struct svmo_mime_context, 1); + ctx->anychild = (anychild == 0 ? FALSE : TRUE); + ctx->mimeopt = (enum ext_mime_option)mimeopt; + + if ( param_list != NULL && sieve_stringlist_read_all + (param_list, pool, &ctx->params) < 0 ) { + sieve_runtime_trace_error(renv, + "failed to read param-list operand"); + return param_list->exec_status; + } + + *ho_context = (void *) ctx; + return SIEVE_EXEC_OK; +} + +/* Override */ + +static int svmo_mime_header_override +(const struct sieve_message_override *svmo, + const struct sieve_runtime_env *renv, bool mime_decode, + struct sieve_stringlist **headers_r) +{ + struct svmo_mime_context *ctx = + (struct svmo_mime_context *)svmo->context; + struct ext_foreverypart_runtime_loop *sfploop; + struct sieve_header_list *headers; + struct sieve_stringlist *values; + int ret; + + sieve_runtime_trace(renv, SIEVE_TRLVL_MATCHING, + "header mime override:"); + sieve_runtime_trace_descend(renv); + + if ( ctx->anychild ) { + sieve_runtime_trace(renv, SIEVE_TRLVL_MATCHING, + "headers from current mime part and children"); + } else { + sieve_runtime_trace(renv, SIEVE_TRLVL_MATCHING, + "headers from current mime part"); + } + + sfploop = ext_foreverypart_runtime_loop_get_current(renv); + if ( sfploop != NULL ) { + headers = sieve_mime_header_list_create + (renv, *headers_r, &sfploop->part_iter, + mime_decode, ctx->anychild); + } else if ( ctx->anychild ) { + struct sieve_message_part_iter part_iter; + + if ( (ret=sieve_message_part_iter_init + (&part_iter, renv)) <= 0 ) + return ret; + + headers = sieve_mime_header_list_create + (renv, *headers_r, &part_iter, mime_decode, TRUE); + } else { + headers = sieve_message_header_list_create + (renv, *headers_r, mime_decode); + } + values = &headers->strlist; + + switch ( ctx->mimeopt ) { + case EXT_MIME_OPTION_NONE: + break; + case EXT_MIME_OPTION_TYPE: + sieve_runtime_trace(renv, SIEVE_TRLVL_MATCHING, + "extract mime type from header value"); + break; + case EXT_MIME_OPTION_SUBTYPE: + sieve_runtime_trace(renv, SIEVE_TRLVL_MATCHING, + "extract mime subtype from header value"); + break; + case EXT_MIME_OPTION_CONTENTTYPE: + sieve_runtime_trace(renv, SIEVE_TRLVL_MATCHING, + "extract mime contenttype from header value"); + break; + case EXT_MIME_OPTION_PARAM: + sieve_runtime_trace(renv, SIEVE_TRLVL_MATCHING, + "extract mime parameters from header value"); + break; + default: + i_unreached(); + } + + if ( ctx->mimeopt != EXT_MIME_OPTION_NONE ) { + values = content_header_stringlist_create + (renv, headers, ctx->mimeopt, ctx->params); + } + *headers_r = values; + + sieve_runtime_trace_ascend(renv); + return SIEVE_EXEC_OK; +} + diff --git a/pigeonhole/src/lib-sieve/plugins/notify/Makefile.am b/pigeonhole/src/lib-sieve/plugins/notify/Makefile.am new file mode 100644 index 0000000..aaa76b3 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/notify/Makefile.am @@ -0,0 +1,20 @@ +noinst_LTLIBRARIES = libsieve_ext_notify.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + -I$(srcdir)/../../util \ + $(LIBDOVECOT_INCLUDE) + +commands = \ + cmd-notify.c \ + cmd-denotify.c + +libsieve_ext_notify_la_SOURCES = \ + ext-notify.c \ + ext-notify-common.c \ + $(commands) + +noinst_HEADERS = \ + ext-notify-common.h \ + ext-notify-limits.h + diff --git a/pigeonhole/src/lib-sieve/plugins/notify/Makefile.in b/pigeonhole/src/lib-sieve/plugins/notify/Makefile.in new file mode 100644 index 0000000..910007d --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/notify/Makefile.in @@ -0,0 +1,699 @@ +# 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/plugins/notify +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_ext_notify_la_LIBADD = +am__objects_1 = cmd-notify.lo cmd-denotify.lo +am_libsieve_ext_notify_la_OBJECTS = ext-notify.lo ext-notify-common.lo \ + $(am__objects_1) +libsieve_ext_notify_la_OBJECTS = $(am_libsieve_ext_notify_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)/cmd-denotify.Plo \ + ./$(DEPDIR)/cmd-notify.Plo ./$(DEPDIR)/ext-notify-common.Plo \ + ./$(DEPDIR)/ext-notify.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_ext_notify_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_notify_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_ext_notify.la +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + -I$(srcdir)/../../util \ + $(LIBDOVECOT_INCLUDE) + +commands = \ + cmd-notify.c \ + cmd-denotify.c + +libsieve_ext_notify_la_SOURCES = \ + ext-notify.c \ + ext-notify-common.c \ + $(commands) + +noinst_HEADERS = \ + ext-notify-common.h \ + ext-notify-limits.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/plugins/notify/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/notify/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_ext_notify.la: $(libsieve_ext_notify_la_OBJECTS) $(libsieve_ext_notify_la_DEPENDENCIES) $(EXTRA_libsieve_ext_notify_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_notify_la_OBJECTS) $(libsieve_ext_notify_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-denotify.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-notify.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-notify-common.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-notify.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)/cmd-denotify.Plo + -rm -f ./$(DEPDIR)/cmd-notify.Plo + -rm -f ./$(DEPDIR)/ext-notify-common.Plo + -rm -f ./$(DEPDIR)/ext-notify.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)/cmd-denotify.Plo + -rm -f ./$(DEPDIR)/cmd-notify.Plo + -rm -f ./$(DEPDIR)/ext-notify-common.Plo + -rm -f ./$(DEPDIR)/ext-notify.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/plugins/notify/cmd-denotify.c b/pigeonhole/src/lib-sieve/plugins/notify/cmd-denotify.c new file mode 100644 index 0000000..8769808 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/notify/cmd-denotify.c @@ -0,0 +1,389 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" + +#include "sieve-common.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-ast.h" +#include "sieve-commands.h" +#include "sieve-match-types.h" +#include "sieve-comparators.h" +#include "sieve-match.h" +#include "sieve-actions.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-result.h" + +#include "ext-notify-common.h" + +/* + * Denotify command + * + * Syntax: + * denotify [MATCH-TYPE string] [<":low" / ":normal" / ":high">] + */ + +static bool cmd_denotify_registered + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool cmd_denotify_pre_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); +static bool cmd_denotify_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); +static bool cmd_denotify_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd); + +const struct sieve_command_def cmd_denotify = { + .identifier = "denotify", + .type = SCT_COMMAND, + .positional_args = 0, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = cmd_denotify_registered, + .pre_validate = cmd_denotify_pre_validate, + .validate = cmd_denotify_validate, + .generate = cmd_denotify_generate +}; + +/* + * Tagged arguments + */ + +/* Forward declarations */ + +static bool tag_match_type_is_instance_of + (struct sieve_validator *validator, struct sieve_command *cmd, + const struct sieve_extension *ext, const char *identifier, void **data); +static bool tag_match_type_validate + (struct sieve_validator *validator, struct sieve_ast_argument **arg, + struct sieve_command *cmd); + +/* Argument object */ + +const struct sieve_argument_def denotify_match_tag = { + .identifier = "MATCH-TYPE-STRING", + .is_instance_of = tag_match_type_is_instance_of, + .validate = tag_match_type_validate +}; + +/* Codes for optional operands */ + +enum cmd_denotify_optional { + OPT_END, + OPT_IMPORTANCE, + OPT_MATCH_TYPE, + OPT_MATCH_KEY +}; + +/* + * Denotify operation + */ + +static bool cmd_denotify_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int cmd_denotify_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def denotify_operation = { + .mnemonic = "DENOTIFY", + .ext_def = ¬ify_extension, + .code = EXT_NOTIFY_OPERATION_DENOTIFY, + .dump = cmd_denotify_operation_dump, + .execute = cmd_denotify_operation_execute +}; + +/* + * Command validation context + */ + +struct cmd_denotify_context_data { + struct sieve_ast_argument *match_key_arg; +}; + +/* + * Tag validation + */ + +static bool tag_match_type_is_instance_of +(struct sieve_validator *valdtr, struct sieve_command *cmd, + const struct sieve_extension *ext, const char *identifier, void **data) +{ + return match_type_tag.is_instance_of(valdtr, cmd, ext, identifier, data); +} + +static bool tag_match_type_validate +(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct cmd_denotify_context_data *cmd_data = + (struct cmd_denotify_context_data *) cmd->data; + struct sieve_ast_argument *tag = *arg; + + i_assert(tag != NULL); + + if ( !match_type_tag.validate(valdtr, arg, cmd) ) + return FALSE; + + if ( *arg == NULL ) { + sieve_argument_validate_error(valdtr, tag, + "the MATCH-TYPE argument (:%s) for the denotify command requires " + "an additional key-string parameter, but no more arguments were found", + sieve_ast_argument_tag(tag)); + return FALSE; + } + + if ( sieve_ast_argument_type(*arg) != SAAT_STRING ) + { + sieve_argument_validate_error(valdtr, *arg, + "the MATCH-TYPE argument (:%s) for the denotify command requires " + "an additional key-string parameter, but %s was found", + sieve_ast_argument_tag(tag), sieve_ast_argument_name(*arg)); + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, cmd, *arg, FALSE) ) + return FALSE; + + tag->argument->def = &match_type_tag; + tag->argument->ext = NULL; + + (*arg)->argument->id_code = OPT_MATCH_KEY; + cmd_data->match_key_arg = *arg; + + *arg = sieve_ast_argument_next(*arg); + + return TRUE; +} + +/* + * Command registration + */ + +static bool cmd_denotify_registered +(struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + sieve_validator_register_tag + (valdtr, cmd_reg, ext, &denotify_match_tag, OPT_MATCH_TYPE); + + ext_notify_register_importance_tags(valdtr, cmd_reg, ext, OPT_IMPORTANCE); + + return TRUE; +} + +/* + * Command validation + */ + +static bool cmd_denotify_pre_validate +(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_command *cmd) +{ + struct cmd_denotify_context_data *ctx_data; + + /* Assign context */ + ctx_data = p_new(sieve_command_pool(cmd), + struct cmd_denotify_context_data, 1); + cmd->data = (void *) ctx_data; + + return TRUE; +} + +static bool cmd_denotify_validate +(struct sieve_validator *valdtr, struct sieve_command *cmd) +{ + struct cmd_denotify_context_data *ctx_data = + (struct cmd_denotify_context_data *) cmd->data; + struct sieve_ast_argument *key_arg = ctx_data->match_key_arg; + const struct sieve_match_type mcht_default = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + const struct sieve_comparator cmp_default = + SIEVE_COMPARATOR_DEFAULT(i_octet_comparator); + + if ( key_arg != NULL ) { + if ( !sieve_match_type_validate + (valdtr, cmd, key_arg, &mcht_default, &cmp_default) ) + return FALSE; + } + + return TRUE; +} + +/* + * Code generation + */ + +static bool cmd_denotify_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + sieve_operation_emit(cgenv->sblock, cmd->ext, &denotify_operation); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, cmd, NULL); +} + +/* + * Code dump + */ + +static bool cmd_denotify_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + const struct sieve_operation *op = denv->oprtn; + int opt_code = 0; + + sieve_code_dumpf(denv, "%s", sieve_operation_mnemonic(op)); + sieve_code_descend(denv); + + for (;;) { + int opt; + bool opok = TRUE; + + if ( (opt=sieve_opr_optional_dump(denv, address, &opt_code)) < 0 ) + return FALSE; + + if ( opt == 0 ) break; + + switch ( opt_code ) { + case OPT_MATCH_KEY: + opok = sieve_opr_string_dump(denv, address, "key-string"); + break; + case OPT_MATCH_TYPE: + opok = sieve_opr_match_type_dump(denv, address); + break; + case OPT_IMPORTANCE: + opok = sieve_opr_number_dump(denv, address, "importance"); + break; + default: + return FALSE; + } + + if ( !opok ) return FALSE; + } + + return TRUE; +} + +/* + * Code execution + */ + +static int cmd_denotify_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + int opt_code = 0; + struct sieve_match_type mcht = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + const struct sieve_comparator cmp = + SIEVE_COMPARATOR_DEFAULT(i_octet_comparator); + struct sieve_stringlist *match_key = NULL; + sieve_number_t importance = 0; + struct sieve_match_context *mctx; + struct sieve_result_iterate_context *rictx; + const struct sieve_action *action; + int ret; + + /* + * Read operands + */ + + /* Optional operands */ + + for (;;) { + int opt; + + if ( (opt=sieve_opr_optional_read(renv, address, &opt_code)) < 0 ) + return SIEVE_EXEC_BIN_CORRUPT; + + if ( opt == 0 ) break; + + switch ( opt_code ) { + case OPT_MATCH_TYPE: + ret = sieve_opr_match_type_read(renv, address, &mcht); + break; + case OPT_MATCH_KEY: + ret = sieve_opr_stringlist_read(renv, address, "match key", &match_key); + break; + case OPT_IMPORTANCE: + ret = sieve_opr_number_read(renv, address, "importance", &importance); + break; + default: + sieve_runtime_trace_error(renv, "unknown optional operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if ( ret <= 0 ) return ret; + } + + /* + * Perform operation + */ + + /* Enforce 0 < importance < 4 (just to be sure) */ + + if ( importance < 1 ) + importance = 1; + else if ( importance > 3 ) + importance = 3; + + /* Trace */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "denotify action"); + + /* Either do string matching or just kill all notify actions */ + if ( match_key != NULL ) { + /* Initialize match */ + mctx = sieve_match_begin(renv, &mcht, &cmp); + + /* Iterate through all notify actions and delete those that match */ + rictx = sieve_result_iterate_init(renv->result); + + while ( (action=sieve_result_iterate_next(rictx, NULL)) != NULL ) { + if ( sieve_action_is(action, act_notify_old) ) { + struct ext_notify_action *nact = + (struct ext_notify_action *) action->context; + + if ( importance == 0 || nact->importance == importance ) { + int match; + + if ( (match=sieve_match_value + (mctx, nact->id, strlen(nact->id), match_key)) < 0 ) + break; + + if ( match > 0 ) + sieve_result_iterate_delete(rictx); + } + } + } + + /* Finish match */ + if ( sieve_match_end(&mctx, &ret) < 0 ) + return ret; + + } else { + /* Delete all notify actions */ + rictx = sieve_result_iterate_init(renv->result); + + while ( (action=sieve_result_iterate_next(rictx, NULL)) != NULL ) { + + if ( sieve_action_is(action, act_notify_old) ) { + struct ext_notify_action *nact = + (struct ext_notify_action *) action->context; + + if ( importance == 0 || nact->importance == importance ) + sieve_result_iterate_delete(rictx); + } + } + } + + return SIEVE_EXEC_OK; +} + + + diff --git a/pigeonhole/src/lib-sieve/plugins/notify/cmd-notify.c b/pigeonhole/src/lib-sieve/plugins/notify/cmd-notify.c new file mode 100644 index 0000000..a683c31 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/notify/cmd-notify.c @@ -0,0 +1,900 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" +#include "str.h" +#include "ioloop.h" +#include "str-sanitize.h" +#include "ostream.h" +#include "message-date.h" +#include "mail-storage.h" + +#include "rfc2822.h" + +#include "sieve-common.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-actions.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-result.h" +#include "sieve-address.h" +#include "sieve-message.h" +#include "sieve-smtp.h" + +#include "ext-notify-common.h" +#include "ext-notify-limits.h" + +#include <ctype.h> + +/* Notify command (DEPRECATED) + * + * Syntax: + * notify [":method" string] [":id" string] [":options" string-list] + * [<":low" / ":normal" / ":high">] ["message:" string] + * + */ + +static bool +cmd_notify_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool +cmd_notify_pre_validate(struct sieve_validator *valdtr, + struct sieve_command *cmd); +static bool +cmd_notify_validate(struct sieve_validator *valdtr, + struct sieve_command *cmd); +static bool +cmd_notify_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +const struct sieve_command_def cmd_notify_old = { + .identifier = "notify", + .type = SCT_COMMAND, + .positional_args = 0, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = cmd_notify_registered, + .pre_validate = cmd_notify_pre_validate, + .validate = cmd_notify_validate, + .generate = cmd_notify_generate +}; + +/* + * Tagged arguments + */ + +/* Forward declarations */ + +static bool +cmd_notify_validate_string_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd); +static bool +cmd_notify_validate_stringlist_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd); + +/* Argument objects */ + +static const struct sieve_argument_def notify_method_tag = { + .identifier = "method", + .validate = cmd_notify_validate_string_tag +}; + +static const struct sieve_argument_def notify_options_tag = { + .identifier = "options", + .validate = cmd_notify_validate_stringlist_tag +}; + +static const struct sieve_argument_def notify_id_tag = { + .identifier = "id", + .validate = cmd_notify_validate_string_tag +}; + +static const struct sieve_argument_def notify_message_tag = { + .identifier = "message", + .validate = cmd_notify_validate_string_tag +}; + +/* + * Notify operation + */ + +static bool +cmd_notify_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +cmd_notify_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def notify_old_operation = { + .mnemonic = "NOTIFY", + .ext_def = ¬ify_extension, + .code = EXT_NOTIFY_OPERATION_NOTIFY, + .dump = cmd_notify_operation_dump, + .execute = cmd_notify_operation_execute +}; + +/* Codes for optional operands */ + +enum cmd_notify_optional { + OPT_END, + OPT_MESSAGE, + OPT_IMPORTANCE, + OPT_OPTIONS, + OPT_ID +}; + +/* + * Notify action + */ + +/* Forward declarations */ + +static int +act_notify_check_duplicate(const struct sieve_runtime_env *renv, + const struct sieve_action *act, + const struct sieve_action *act_other); +static void +act_notify_print(const struct sieve_action *action, + const struct sieve_result_print_env *rpenv, bool *keep); +static int +act_notify_commit(const struct sieve_action_exec_env *aenv, + void *tr_context); + +/* Action object */ + +const struct sieve_action_def act_notify_old = { + .name = "notify", + .check_duplicate = act_notify_check_duplicate, + .print = act_notify_print, + .commit = act_notify_commit +}; + +/* + * Command validation context + */ + +struct cmd_notify_context_data { + struct sieve_ast_argument *id; + struct sieve_ast_argument *method; + struct sieve_ast_argument *options; + struct sieve_ast_argument *message; +}; + +/* + * Tag validation + */ + +static bool +cmd_notify_validate_string_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + struct cmd_notify_context_data *ctx_data = + (struct cmd_notify_context_data *)cmd->data; + + /* Detach the tag itself */ + *arg = sieve_ast_arguments_detach(*arg, 1); + + /* Check syntax: + * :id <string> + * :method <string> + * :message <string> + */ + if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0, + SAAT_STRING, FALSE)) + return FALSE; + + if (sieve_argument_is(tag, notify_method_tag)) { + ctx_data->method = *arg; + + /* Removed */ + *arg = sieve_ast_arguments_detach(*arg, 1); + } else if (sieve_argument_is(tag, notify_id_tag)) { + ctx_data->id = *arg; + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + } else if (sieve_argument_is(tag, notify_message_tag)) { + ctx_data->message = *arg; + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + } + return TRUE; +} + +static bool +cmd_notify_validate_stringlist_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + struct cmd_notify_context_data *ctx_data = + (struct cmd_notify_context_data *)cmd->data; + + /* Detach the tag itself */ + *arg = sieve_ast_arguments_detach(*arg,1); + + /* Check syntax: + * :options string-list + */ + if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0, + SAAT_STRING_LIST, FALSE)) + return FALSE; + + /* Assign context */ + ctx_data->options = *arg; + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + + return TRUE; +} + +/* + * Command registration + */ + +static bool +cmd_notify_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + sieve_validator_register_tag(valdtr, cmd_reg, ext, + ¬ify_method_tag, 0); + sieve_validator_register_tag(valdtr, cmd_reg, ext, + ¬ify_id_tag, OPT_ID); + sieve_validator_register_tag(valdtr, cmd_reg, ext, + ¬ify_message_tag, OPT_MESSAGE); + sieve_validator_register_tag(valdtr, cmd_reg, ext, + ¬ify_options_tag, OPT_OPTIONS); + + ext_notify_register_importance_tags(valdtr, cmd_reg, ext, + OPT_IMPORTANCE); + + return TRUE; +} + +/* + * Command validation + */ + +static bool +cmd_notify_pre_validate(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_command *cmd) +{ + struct cmd_notify_context_data *ctx_data; + + /* Create context */ + ctx_data = p_new(sieve_command_pool(cmd), + struct cmd_notify_context_data, 1); + cmd->data = ctx_data; + + return TRUE; +} + +static int +cmd_notify_address_validate(void *context, struct sieve_ast_argument *arg) +{ + struct sieve_validator *valdtr = (struct sieve_validator *)context; + + if (sieve_argument_is_string_literal(arg)) { + string_t *address = sieve_ast_argument_str(arg); + const char *error; + int result; + + T_BEGIN { + result = (sieve_address_validate_str(address, &error) ? + 1 : -1); + + if (result <= 0) { + sieve_argument_validate_error( + valdtr, arg, + "specified :options address '%s' is invalid for " + "the mailto notify method: %s", + str_sanitize(str_c(address), 128), + error); + } + } T_END; + + return result; + } + + return 1; +} + +static bool +cmd_notify_validate(struct sieve_validator *valdtr, struct sieve_command *cmd) +{ + struct cmd_notify_context_data *ctx_data = + (struct cmd_notify_context_data *)cmd->data; + + /* Check :method argument */ + if (ctx_data->method != NULL) { + const char *method = sieve_ast_argument_strc(ctx_data->method); + + if (strcasecmp(method, "mailto") != 0) { + sieve_command_validate_error( + valdtr, cmd, + "the notify command of the deprecated notify extension " + "only supports the 'mailto' notification method"); + return FALSE; + } + } + + /* Check :options argument */ + if (ctx_data->options != NULL) { + struct sieve_ast_argument *option = ctx_data->options; + + /* Parse and check options */ + if (sieve_ast_stringlist_map( + &option, (void *)valdtr, + cmd_notify_address_validate) <= 0) { + return FALSE; + } + } else { + sieve_command_validate_warning( + valdtr, cmd, + "no :options (and hence recipients) specified for the notify command"); + } + + return TRUE; +} + +/* + * Code generation + */ + +static bool +cmd_notify_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd) +{ + sieve_operation_emit(cgenv->sblock, cmd->ext, ¬ify_old_operation); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, cmd, NULL); +} + +/* + * Code dump + */ + +static bool +cmd_notify_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + int opt_code = 0; + + sieve_code_dumpf(denv, "NOTIFY"); + sieve_code_descend(denv); + + /* Dump optional operands */ + for (;;) { + int opt; + bool opok = TRUE; + + if ((opt = sieve_opr_optional_dump(denv, address, + &opt_code)) < 0) + return FALSE; + + if (opt == 0) + break; + + switch (opt_code) { + case OPT_IMPORTANCE: + opok = sieve_opr_number_dump(denv, address, + "importance"); + break; + case OPT_ID: + opok = sieve_opr_string_dump(denv, address, + "id"); + break; + case OPT_OPTIONS: + opok = sieve_opr_stringlist_dump(denv, address, + "options"); + break; + case OPT_MESSAGE: + opok = sieve_opr_string_dump(denv, address, + "message"); + break; + default: + return FALSE; + } + + if (!opok) + return FALSE; + } + + return TRUE; +} + +/* + * Code execution + */ + + +static int +cmd_notify_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + const struct sieve_extension *this_ext = renv->oprtn->ext; + struct ext_notify_action *act; + pool_t pool; + int opt_code = 0; + sieve_number_t importance = 1; + struct sieve_stringlist *options = NULL; + string_t *message = NULL, *id = NULL; + int ret = 0; + + /* + * Read operands + */ + + /* Optional operands */ + + for (;;) { + int opt; + + if ((opt = sieve_opr_optional_read(renv, address, + &opt_code)) < 0) + return SIEVE_EXEC_BIN_CORRUPT; + + if (opt == 0) + break; + + switch (opt_code) { + case OPT_IMPORTANCE: + ret = sieve_opr_number_read(renv, address, "importance", + &importance); + break; + case OPT_ID: + ret = sieve_opr_string_read(renv, address, "id", &id); + break; + case OPT_MESSAGE: + ret = sieve_opr_string_read(renv, address, "from", + &message); + break; + case OPT_OPTIONS: + ret = sieve_opr_stringlist_read(renv, address, + "options", &options); + break; + default: + sieve_runtime_trace_error( + renv, "unknown optional operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if (ret <= 0) return ret; + } + + /* + * Perform operation + */ + + /* Enforce 0 < importance < 4 (just to be sure) */ + + if (importance < 1) + importance = 1; + else if (importance > 3) + importance = 3; + + /* Trace */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "notify action"); + + /* Compose action */ + if (options != NULL) { + string_t *raw_address; + string_t *out_message; + + pool = sieve_result_pool(renv->result); + act = p_new(pool, struct ext_notify_action, 1); + if (id != NULL) + act->id = p_strdup(pool, str_c(id)); + act->importance = importance; + + /* Process message */ + + out_message = t_str_new(1024); + if ((ret = ext_notify_construct_message( + renv, (message == NULL ? NULL : str_c(message)), + out_message)) <= 0) + return ret; + act->message = p_strdup(pool, str_c(out_message)); + + /* Normalize and verify all :options addresses */ + + sieve_stringlist_reset(options); + + p_array_init(&act->recipients, pool, 4); + + raw_address = NULL; + while ((ret = sieve_stringlist_next_item( + options, &raw_address)) > 0) { + const char *error = NULL; + const struct smtp_address *address; + + /* Add if valid address */ + address = sieve_address_parse_str(raw_address, &error); + if (address != NULL) { + const struct ext_notify_recipient *rcpts; + unsigned int rcpt_count, i; + + /* Prevent duplicates */ + rcpts = array_get(&act->recipients, &rcpt_count); + + for (i = 0; i < rcpt_count; i++) { + if (smtp_address_equals(rcpts[i].address, + address)) + break; + } + + /* Add only if unique */ + if (i != rcpt_count) { + sieve_runtime_warning( + renv, NULL, + "duplicate recipient '%s' specified in the :options argument of " + "the deprecated notify command", + str_sanitize(str_c(raw_address), 128)); + + } else if (array_count(&act->recipients) >= + EXT_NOTIFY_MAX_RECIPIENTS) { + sieve_runtime_warning(renv, NULL, + "more than the maximum %u recipients are specified " + "for the deprecated notify command; " + "the rest is discarded", + EXT_NOTIFY_MAX_RECIPIENTS); + break; + + } else { + struct ext_notify_recipient recipient; + + recipient.full = + p_strdup(pool, str_c(raw_address)); + recipient.address = + smtp_address_clone(pool, address); + + array_append(&act->recipients, &recipient, 1); + } + } else { + sieve_runtime_error( + renv, NULL, + "specified :options address '%s' is invalid for " + "the deprecated notify command: %s", + str_sanitize(str_c(raw_address), 128), error); + return SIEVE_EXEC_FAILURE; + } + } + + if (ret < 0) { + sieve_runtime_trace_error( + renv, "invalid options stringlist"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if (sieve_result_add_action(renv, this_ext, "notify", + &act_notify_old, NULL, (void *)act, + 0, FALSE) < 0) + return SIEVE_EXEC_FAILURE; + } + + return SIEVE_EXEC_OK; +} + +/* + * Action + */ + +/* Runtime verification */ + +static int +act_notify_check_duplicate(const struct sieve_runtime_env *renv ATTR_UNUSED, + const struct sieve_action *act ATTR_UNUSED, + const struct sieve_action *act_other ATTR_UNUSED) +{ + struct ext_notify_action *new_nact, *old_nact; + const struct ext_notify_recipient *new_rcpts; + const struct ext_notify_recipient *old_rcpts; + unsigned int new_count, old_count, i, j; + unsigned int del_start = 0, del_len = 0; + + if (act->context == NULL || act_other->context == NULL) + return 0; + + new_nact = (struct ext_notify_action *)act->context; + old_nact = (struct ext_notify_action *)act_other->context; + + new_rcpts = array_get(&new_nact->recipients, &new_count); + old_rcpts = array_get(&old_nact->recipients, &old_count); + + for (i = 0; i < new_count; i++) { + for (j = 0; j < old_count; j++) { + if (smtp_address_equals(new_rcpts[i].address, + old_rcpts[j].address)) + break; + } + + if (j == old_count) { + /* Not duplicate */ + if (del_len > 0) { + /* Perform pending deletion */ + array_delete(&new_nact->recipients, + del_start, del_len); + + /* Make sure the loop integrity is maintained */ + i -= del_len; + new_rcpts = array_get(&new_nact->recipients, + &new_count); + } + + del_len = 0; + } else { + /* Mark deletion */ + if (del_len == 0) + del_start = i; + del_len++; + } + } + + /* Perform pending deletion */ + if (del_len > 0) + array_delete(&new_nact->recipients, del_start, del_len); + + return (array_count(&new_nact->recipients) > 0 ? 0 : 1); +} + +/* Result printing */ + +static void +act_notify_print(const struct sieve_action *action, + const struct sieve_result_print_env *rpenv, + bool *keep ATTR_UNUSED) +{ + const struct ext_notify_action *act = + (const struct ext_notify_action *)action->context; + const struct ext_notify_recipient *recipients; + unsigned int count, i; + + sieve_result_action_printf( + rpenv, "send (deprecated) notification with method 'mailto':"); + + /* Print main method parameters */ + + sieve_result_printf(rpenv, " => importance : %llu\n", + (unsigned long long)act->importance); + + if (act->message != NULL) { + sieve_result_printf( + rpenv, " => message : %s\n", act->message); + } + if (act->id != NULL) { + sieve_result_printf( + rpenv, " => id : %s \n", act->id); + } + + /* Print mailto: recipients */ + + sieve_result_printf(rpenv, " => recipients :\n"); + + recipients = array_get(&act->recipients, &count); + if (count == 0) { + sieve_result_printf( + rpenv, " NONE, action has no effect\n"); + } else { + for (i = 0; i < count; i++) { + sieve_result_printf( + rpenv, " + To: %s\n", recipients[i].full); + } + } + + /* Finish output with an empty line */ + + sieve_result_printf(rpenv, "\n"); +} + +/* Result execution */ + +static bool contains_8bit(const char *msg) +{ + const unsigned char *s = (const unsigned char *)msg; + + for (; *s != '\0'; s++) { + if ((*s & 0x80) != 0) + return TRUE; + } + return FALSE; +} + +static bool +act_notify_send(const struct sieve_action_exec_env *aenv, + const struct ext_notify_action *act) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + const struct sieve_script_env *senv = eenv->scriptenv; + const struct ext_notify_recipient *recipients; + struct sieve_smtp_context *sctx; + unsigned int count, i; + struct ostream *output; + string_t *msg, *to, *all; + const char *outmsgid, *error; + int ret; + + /* Get recipients */ + recipients = array_get(&act->recipients, &count); + if (count == 0) { + sieve_result_warning( + aenv, "notify action specifies no recipients; " + "action has no effect"); + return TRUE; + } + + /* Just to be sure */ + if (!sieve_smtp_available(senv)) { + sieve_result_global_warning( + aenv, "notify action has no means to send mail"); + return TRUE; + } + + /* Compose common headers */ + msg = t_str_new(512); + rfc2822_header_write(msg, "X-Sieve", SIEVE_IMPLEMENTATION); + rfc2822_header_write(msg, "Date", message_date_create(ioloop_time)); + + /* Set importance */ + switch (act->importance) { + case 1: + rfc2822_header_write(msg, "X-Priority", "1 (Highest)"); + rfc2822_header_write(msg, "Importance", "High"); + break; + case 3: + rfc2822_header_write(msg, "X-Priority", "5 (Lowest)"); + rfc2822_header_write(msg, "Importance", "Low"); + break; + case 2: + default: + rfc2822_header_write(msg, "X-Priority", "3 (Normal)"); + rfc2822_header_write(msg, "Importance", "Normal"); + break; + } + + rfc2822_header_write(msg, "From", sieve_get_postmaster_address(senv)); + + rfc2822_header_write(msg, "Subject", "[SIEVE] New mail notification"); + + rfc2822_header_write(msg, "Auto-Submitted", "auto-generated (notify)"); + rfc2822_header_write(msg, "Precedence", "bulk"); + + rfc2822_header_write(msg, "MIME-Version", "1.0"); + if (contains_8bit(act->message)) { + rfc2822_header_write(msg, "Content-Type", + "text/plain; charset=utf-8"); + rfc2822_header_write(msg, "Content-Transfer-Encoding", "8bit"); + } else { + rfc2822_header_write(msg, "Content-Type", + "text/plain; charset=us-ascii"); + rfc2822_header_write(msg, "Content-Transfer-Encoding", "7bit"); + } + + outmsgid = sieve_message_get_new_id(eenv->svinst); + rfc2822_header_write(msg, "Message-ID", outmsgid); + + if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0 && + sieve_message_get_sender(aenv->msgctx) != NULL) { + sctx = sieve_smtp_start(senv, sieve_get_postmaster_smtp(senv)); + } else { + sctx = sieve_smtp_start(senv, NULL); + } + + /* Add all recipients (and compose To header field) */ + to = t_str_new(128); + all = t_str_new(256); + for (i = 0; i < count; i++) { + sieve_smtp_add_rcpt(sctx, recipients[i].address); + if (i > 0) + str_append(to, ", "); + str_append(to, recipients[i].full); + if (i < 3) { + if (i > 0) + str_append(all, ", "); + str_append(all, smtp_address_encode_path( + recipients[i].address)); + } else if (i == 3) { + str_printfa(all, ", ... (%u total)", count); + } + } + + rfc2822_header_write_address(msg, "To", str_c(to)); + + /* Generate message body */ + str_printfa(msg, "\r\n%s\r\n", act->message); + + output = sieve_smtp_send(sctx); + o_stream_nsend(output, str_data(msg), str_len(msg)); + + if ((ret = sieve_smtp_finish(sctx, &error)) <= 0) { + if (ret < 0) { + sieve_result_global_error( + aenv, "failed to send mail notification to %s: " + "%s (temporary failure)", str_c(all), + str_sanitize(error, 512)); + } else { + sieve_result_global_log_error( + aenv, "failed to send mail notification to %s: " + "%s (permanent failure)", str_c(all), + str_sanitize(error, 512)); + } + } else { + struct event_passthrough *e = + sieve_action_create_finish_event(aenv)-> + add_str("notify_target", str_c(all)); + + sieve_result_event_log(aenv, e->event(), + "sent mail notification to %s", + str_c(all)); + } + + return TRUE; +} + +static int +act_notify_commit(const struct sieve_action_exec_env *aenv, + void *tr_context ATTR_UNUSED) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + struct mail *mail = eenv->msgdata->mail; + const struct ext_notify_action *act = + (const struct ext_notify_action *)aenv->action->context; + const char *const *hdsp; + bool result; + int ret; + + /* Is the message an automatic reply ? */ + if ((ret = mail_get_headers(mail, "auto-submitted", &hdsp)) < 0) { + return sieve_result_mail_error( + aenv, mail, + "failed to read `auto-submitted' header field"); + } + + /* Theoretically multiple headers could exist, so lets make sure */ + if (ret > 0) { + while (*hdsp != NULL) { + if (strcasecmp(*hdsp, "no") != 0) { + const struct smtp_address *sender = NULL; + const char *from; + + if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0) + sender = sieve_message_get_sender(aenv->msgctx); + from = (sender == NULL ? "" : + t_strdup_printf(" from <%s>", + smtp_address_encode(sender))); + + sieve_result_global_log( + aenv, + "not sending notification for auto-submitted message%s", + from); + return SIEVE_EXEC_OK; + } + hdsp++; + } + } + + T_BEGIN { + result = act_notify_send(aenv, act); + } T_END; + + if (!result) + return SIEVE_EXEC_FAILURE; + eenv->exec_status->significant_action_executed = TRUE; + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/plugins/notify/ext-notify-common.c b/pigeonhole/src/lib-sieve/plugins/notify/ext-notify-common.c new file mode 100644 index 0000000..589b21b --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/notify/ext-notify-common.c @@ -0,0 +1,361 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "istream.h" +#include "rfc822-parser.h" +#include "message-parser.h" +#include "message-decoder.h" +#include "mail-storage.h" + +#include "sieve-common.h" +#include "sieve-code.h" +#include "sieve-message.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-actions.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-result.h" + +#include "ext-notify-common.h" + +#include <ctype.h> + +/* + * Importance argument + */ + +static bool +tag_importance_validate(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd); + +static const struct sieve_argument_def importance_low_tag = { + .identifier = "low", + .validate = tag_importance_validate, +}; + +static const struct sieve_argument_def importance_normal_tag = { + .identifier = "normal", + .validate = tag_importance_validate, +}; + +static const struct sieve_argument_def importance_high_tag = { + .identifier = "high", + .validate = tag_importance_validate, +}; + +static bool +tag_importance_validate(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_ast_argument **arg, + struct sieve_command *cmd ATTR_UNUSED) +{ + struct sieve_ast_argument *tag = *arg; + + if (sieve_argument_is(tag, importance_low_tag)) + sieve_ast_argument_number_substitute(tag, 3); + else if (sieve_argument_is(tag, importance_normal_tag)) + sieve_ast_argument_number_substitute(tag, 2); + else + sieve_ast_argument_number_substitute(tag, 1); + + tag->argument = sieve_argument_create(tag->ast, &number_argument, + tag->argument->ext, + tag->argument->id_code); + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + + return TRUE; +} + +void ext_notify_register_importance_tags( + struct sieve_validator *valdtr, + struct sieve_command_registration *cmd_reg, + const struct sieve_extension *ext, unsigned int id_code) +{ + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &importance_low_tag, id_code); + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &importance_normal_tag, id_code); + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &importance_high_tag, id_code); +} + +/* + * Body extraction + */ + +/* FIXME: overlaps somewhat with body extension */ + +struct ext_notify_message_context { + pool_t pool; + buffer_t *body_text; +}; + +static struct ext_notify_message_context * +ext_notify_get_message_context(const struct sieve_extension *this_ext, + struct sieve_message_context *msgctx) +{ + struct ext_notify_message_context *ctx; + + /* Get message context (contains cached message body information) */ + ctx = (struct ext_notify_message_context *) + sieve_message_context_extension_get(msgctx, this_ext); + + /* Create it if it does not exist already */ + if (ctx == NULL) { + pool_t pool = sieve_message_context_pool(msgctx); + ctx = p_new(pool, struct ext_notify_message_context, 1); + ctx->pool = pool; + ctx->body_text = NULL; + + /* Register context */ + sieve_message_context_extension_set(msgctx, this_ext, + (void *)ctx); + } + + return ctx; +} + +static bool _is_text_content(const struct message_header_line *hdr) +{ + struct rfc822_parser_context parser; + string_t *content_type; + const char *data; + + /* Initialize parsing */ + rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL); + (void)rfc822_skip_lwsp(&parser); + + /* Parse content type */ + content_type = t_str_new(64); + if (rfc822_parse_content_type(&parser, content_type) < 0) + return FALSE; + + /* Content-type value must end here, otherwise it is invalid after all + */ + (void)rfc822_skip_lwsp(&parser); + if (parser.data != parser.end && *parser.data != ';') + return FALSE; + + /* Success */ + data = str_c(content_type); + if (str_begins(data, "text/")) + return TRUE; + return FALSE; +} + +static int +cmd_notify_extract_body_text(const struct sieve_runtime_env *renv, + const char **body_text_r, size_t *body_size_r) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + const struct sieve_extension *this_ext = renv->oprtn->ext; + struct ext_notify_message_context *mctx; + struct mail *mail = eenv->msgdata->mail; + struct message_parser_ctx *parser; + struct message_decoder_context *decoder; + struct message_part *parts; + struct message_block block, decoded; + struct istream *input; + bool is_text, save_body; + int ret = 1; + + *body_text_r = NULL; + *body_size_r = 0; + + /* Return cached result if available */ + mctx = ext_notify_get_message_context(this_ext, renv->msgctx); + if (mctx->body_text != NULL) { + *body_text_r = (const char *) + buffer_get_data(mctx->body_text, body_size_r); + return SIEVE_EXEC_OK; + } + + /* Create buffer */ + mctx->body_text = buffer_create_dynamic(mctx->pool, 1024*64); + + /* Get the message stream */ + if (mail_get_stream(mail, NULL, NULL, &input) < 0) { + return sieve_runtime_mail_error(renv, mail, "notify action: " + "failed to read input message"); + } + + /* Initialize body decoder */ + decoder = message_decoder_init(NULL, 0); + + struct message_parser_settings mparser_set = { + .hdr_flags = 0, + .flags = 0, + }; + parser = message_parser_init(mctx->pool, input, &mparser_set); + is_text = TRUE; + save_body = FALSE; + while ((ret = message_parser_parse_next_block(parser, &block)) > 0) { + if (block.hdr != NULL || block.size == 0) { + /* Decode block */ + (void)message_decoder_decode_next_block(decoder, &block, + &decoded); + + /* Check for end of headers */ + if (block.hdr == NULL) { + save_body = is_text; + continue; + } + + /* We're interested of only Content-Type: header */ + if (strcasecmp(block.hdr->name, "Content-Type") != 0) + continue; + + /* Header can have folding whitespace. Acquire the full + value before continuing */ + if (block.hdr->continues) { + block.hdr->use_full_value = TRUE; + continue; + } + + /* Is it a text part? */ + T_BEGIN { + is_text = _is_text_content(block.hdr); + } T_END; + + continue; + } + + /* Read text body */ + if (save_body) { + (void)message_decoder_decode_next_block(decoder, &block, + &decoded); + buffer_append(mctx->body_text, decoded.data, + decoded.size); + is_text = TRUE; + } + } + + /* Cleanup */ + (void)message_parser_deinit(&parser, &parts); + message_decoder_deinit(&decoder); + + if (ret < 0 && input->stream_errno != 0) { + sieve_runtime_critical(renv, NULL, "notify action: " + "failed to read input message", + "notify action: read(%s) failed: %s", + i_stream_get_name(input), + i_stream_get_error(input)); + return SIEVE_EXEC_TEMP_FAILURE; + } + + /* Return status */ + *body_text_r = (const char *)buffer_get_data(mctx->body_text, + body_size_r); + return SIEVE_EXEC_OK; +} + +int ext_notify_construct_message(const struct sieve_runtime_env *renv, + const char *msg_format, + string_t *out_msg) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + const struct sieve_message_data *msgdata = eenv->msgdata; + struct sieve_message_context *msgctx = renv->msgctx; + const struct smtp_address *return_path = + sieve_message_get_sender(msgctx); + const char *p; + int ret; + + if (msg_format == NULL) + msg_format = "$from$: $subject$"; + + /* Scan message for substitutions */ + p = msg_format; + while (*p != '\0') { + const char *header; + + if (strncasecmp(p, "$from$", 6) == 0) { + p += 6; + + /* Fetch sender from original message */ + if ((ret = mail_get_first_header_utf8( + msgdata->mail, "from", &header)) < 0) { + return sieve_runtime_mail_error( + renv, msgdata->mail, + "failed to read header field `from'"); + } + if (ret > 0) + str_append(out_msg, header); + } else if (strncasecmp(p, "$env-from$", 10) == 0) { + p += 10; + + if (return_path != NULL) + smtp_address_write(out_msg, return_path); + } else if (strncasecmp(p, "$subject$", 9) == 0) { + p += 9; + + /* Fetch sender from oriinal message */ + if ((ret = mail_get_first_header_utf8( + msgdata->mail, "subject", &header)) < 0) { + return sieve_runtime_mail_error( + renv, msgdata->mail, + "failed to read header field `subject'"); + } + if (ret > 0) + str_append(out_msg, header); + } else if (strncasecmp(p, "$text", 5) == 0 && + (p[5] == '[' || p[5] == '$')) { + size_t num = 0; + const char *begin = p; + bool valid = TRUE; + + p += 5; + if (*p == '[') { + p += 1; + + while (i_isdigit(*p)) { + num = num * 10 + (*p - '0'); + p++; + } + + if (*p++ != ']' || *p++ != '$') { + str_append_data(out_msg, begin, + p-begin); + valid = FALSE; + } + } else { + p += 1; + } + + if (valid) { + size_t body_size; + const char *body_text; + + if ((ret = cmd_notify_extract_body_text( + renv, &body_text, &body_size)) <= 0) + return ret; + + if (num > 0 && num < body_size) { + str_append_data(out_msg, body_text, + num); + } else { + str_append_data(out_msg, body_text, + body_size); + } + } + } else { + size_t len; + + /* Find next substitution */ + len = strcspn(p + 1, "$") + 1; + + /* Copy normal text */ + str_append_data(out_msg, p, len); + p += len; + } + } + + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/plugins/notify/ext-notify-common.h b/pigeonhole/src/lib-sieve/plugins/notify/ext-notify-common.h new file mode 100644 index 0000000..09db2cb --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/notify/ext-notify-common.h @@ -0,0 +1,66 @@ +#ifndef EXT_NOTIFY_COMMON_H +#define EXT_NOTIFY_COMMON_H + +/* + * Extension + */ + +extern const struct sieve_extension_def notify_extension; + +/* + * Commands + */ + +extern const struct sieve_command_def cmd_notify_old; +extern const struct sieve_command_def cmd_denotify; + +/* + * Arguments + */ + +void ext_notify_register_importance_tags( + struct sieve_validator *valdtr, + struct sieve_command_registration *cmd_reg, + const struct sieve_extension *this_ext, unsigned int id_code); + +/* + * Operations + */ + +extern const struct sieve_operation_def notify_old_operation; +extern const struct sieve_operation_def denotify_operation; + +enum ext_notify_opcode { + EXT_NOTIFY_OPERATION_NOTIFY, + EXT_NOTIFY_OPERATION_DENOTIFY, +}; + +/* + * Actions + */ + +extern const struct sieve_action_def act_notify_old; + +struct ext_notify_recipient { + const char *full; + const struct smtp_address *address; +}; + +ARRAY_DEFINE_TYPE(recipients, struct ext_notify_recipient); + +struct ext_notify_action { + const char *id; + const char *message; + sieve_number_t importance; + + ARRAY_TYPE(recipients) recipients; +}; + +/* + * Message construct + */ + +int ext_notify_construct_message(const struct sieve_runtime_env *renv, + const char *msg_format, string_t *out_msg); + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/notify/ext-notify-limits.h b/pigeonhole/src/lib-sieve/plugins/notify/ext-notify-limits.h new file mode 100644 index 0000000..5fab7cb --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/notify/ext-notify-limits.h @@ -0,0 +1,7 @@ +#ifndef EXT_NOTIFY_LIMITS_H +#define EXT_NOTIFY_LIMITS_H + +#define EXT_NOTIFY_MAX_RECIPIENTS 8 +#define EXT_NOTIFY_MAX_MESSAGE 256 + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/notify/ext-notify.c b/pigeonhole/src/lib-sieve/plugins/notify/ext-notify.c new file mode 100644 index 0000000..e79e049 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/notify/ext-notify.c @@ -0,0 +1,108 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension notify + * ---------------- + * + * Authors: Stephan Bosch + * Specification: draft-ietf-sieve-notify-00.txt + * Implementation: full, but deprecated; provided for backwards compatibility + * Status: testing + * + */ + +#include "sieve-common.h" + +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-actions.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-result.h" + +#include "ext-notify-common.h" + +/* + * Operations + */ + +const struct sieve_operation_def *ext_notify_operations[] = { + ¬ify_old_operation, + &denotify_operation +}; + +/* + * Extension + */ + +static bool ext_notify_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); + +const struct sieve_extension_def notify_extension = { + .name = "notify", + .validator_load = ext_notify_validator_load, + SIEVE_EXT_DEFINE_OPERATIONS(ext_notify_operations) +}; + +/* + * Extension validation + */ + +static bool ext_notify_validator_check_conflict + (const struct sieve_extension *ext, + struct sieve_validator *valdtr, void *context, + struct sieve_ast_argument *require_arg, + const struct sieve_extension *ext_other, + bool required); +static bool ext_notify_validator_validate + (const struct sieve_extension *ext, + struct sieve_validator *valdtr, void *context, + struct sieve_ast_argument *require_arg, + bool required); + +const struct sieve_validator_extension notify_validator_extension = { + .ext = ¬ify_extension, + .check_conflict = ext_notify_validator_check_conflict, + .validate = ext_notify_validator_validate +}; + +static bool ext_notify_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + /* Register validator extension to check for conflict with enotify */ + sieve_validator_extension_register + (valdtr, ext, ¬ify_validator_extension, NULL); + return TRUE; +} + +static bool ext_notify_validator_check_conflict +(const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_validator *valdtr, void *context ATTR_UNUSED, + struct sieve_ast_argument *require_arg, + const struct sieve_extension *ext_other, + bool required ATTR_UNUSED) +{ + /* Check for conflict with enotify */ + if ( sieve_extension_name_is(ext_other, "enotify") ) { + sieve_argument_validate_error(valdtr, require_arg, + "the (deprecated) notify extension cannot be used " + "together with the enotify extension"); + return FALSE; + } + + return TRUE; +} + +static bool ext_notify_validator_validate +(const struct sieve_extension *ext, + struct sieve_validator *valdtr, void *context ATTR_UNUSED, + struct sieve_ast_argument *require_arg ATTR_UNUSED, + bool required ATTR_UNUSED) +{ + /* No conflicts: register new commands */ + sieve_validator_register_command(valdtr, ext, &cmd_notify_old); + sieve_validator_register_command(valdtr, ext, &cmd_denotify); + return TRUE; +} diff --git a/pigeonhole/src/lib-sieve/plugins/regex/Makefile.am b/pigeonhole/src/lib-sieve/plugins/regex/Makefile.am new file mode 100644 index 0000000..eeb735e --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/regex/Makefile.am @@ -0,0 +1,13 @@ +noinst_LTLIBRARIES = libsieve_ext_regex.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +libsieve_ext_regex_la_SOURCES = \ + mcht-regex.c \ + ext-regex-common.c \ + ext-regex.c + +noinst_HEADERS = \ + ext-regex-common.h diff --git a/pigeonhole/src/lib-sieve/plugins/regex/Makefile.in b/pigeonhole/src/lib-sieve/plugins/regex/Makefile.in new file mode 100644 index 0000000..379d384 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/regex/Makefile.in @@ -0,0 +1,688 @@ +# 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/plugins/regex +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_ext_regex_la_LIBADD = +am_libsieve_ext_regex_la_OBJECTS = mcht-regex.lo ext-regex-common.lo \ + ext-regex.lo +libsieve_ext_regex_la_OBJECTS = $(am_libsieve_ext_regex_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)/ext-regex-common.Plo \ + ./$(DEPDIR)/ext-regex.Plo ./$(DEPDIR)/mcht-regex.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_ext_regex_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_regex_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_ext_regex.la +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +libsieve_ext_regex_la_SOURCES = \ + mcht-regex.c \ + ext-regex-common.c \ + ext-regex.c + +noinst_HEADERS = \ + ext-regex-common.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/plugins/regex/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/regex/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_ext_regex.la: $(libsieve_ext_regex_la_OBJECTS) $(libsieve_ext_regex_la_DEPENDENCIES) $(EXTRA_libsieve_ext_regex_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_regex_la_OBJECTS) $(libsieve_ext_regex_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-regex-common.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-regex.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mcht-regex.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)/ext-regex-common.Plo + -rm -f ./$(DEPDIR)/ext-regex.Plo + -rm -f ./$(DEPDIR)/mcht-regex.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)/ext-regex-common.Plo + -rm -f ./$(DEPDIR)/ext-regex.Plo + -rm -f ./$(DEPDIR)/mcht-regex.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/plugins/regex/ext-regex-common.c b/pigeonhole/src/lib-sieve/plugins/regex/ext-regex-common.c new file mode 100644 index 0000000..975f4fb --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/regex/ext-regex-common.c @@ -0,0 +1,22 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-match-types.h" + +#include "ext-regex-common.h" + +/* + * Regex match type operand + */ + +static const struct sieve_extension_objects ext_match_types = + SIEVE_EXT_DEFINE_MATCH_TYPE(regex_match_type); + +const struct sieve_operand_def regex_match_type_operand = { + .name = "regex match", + .ext_def = ®ex_extension, + .class = &sieve_match_type_operand_class, + .interface = &ext_match_types +}; + diff --git a/pigeonhole/src/lib-sieve/plugins/regex/ext-regex-common.h b/pigeonhole/src/lib-sieve/plugins/regex/ext-regex-common.h new file mode 100644 index 0000000..e9d809f --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/regex/ext-regex-common.h @@ -0,0 +1,24 @@ +#ifndef EXT_REGEX_COMMON_H +#define EXT_REGEX_COMMON_H + +/* + * Extension + */ + +extern const struct sieve_extension_def regex_extension; + +/* + * Operand + */ + +extern const struct sieve_operand_def regex_match_type_operand; + +/* + * Match type + */ + +extern const struct sieve_match_type_def regex_match_type; + +#endif + + diff --git a/pigeonhole/src/lib-sieve/plugins/regex/ext-regex.c b/pigeonhole/src/lib-sieve/plugins/regex/ext-regex.c new file mode 100644 index 0000000..0440b30 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/regex/ext-regex.c @@ -0,0 +1,65 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension regex + * --------------- + * + * Authors: Stephan Bosch + * Specification: draft-murchison-sieve-regex-08 (not latest) + * Implementation: full + * Status: testing + * + */ + +/* FIXME: Regular expressions are compiled during compilation and + * again during interpretation. This is suboptimal and should be + * changed. This requires dumping the compiled regex to the binary. + * Most likely, this will only be possible when we implement regular + * expressions ourselves. + * + */ + +#include "lib.h" +#include "mempool.h" +#include "buffer.h" + +#include "sieve-common.h" + +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" + +#include "sieve-comparators.h" +#include "sieve-match-types.h" + +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" + +#include "ext-regex-common.h" + +#include <sys/types.h> +#include <regex.h> + +/* + * Extension + */ + +static bool ext_regex_validator_load + (const struct sieve_extension *ext, struct sieve_validator *validator); + +const struct sieve_extension_def regex_extension = { + .name = "regex", + .validator_load = ext_regex_validator_load, + SIEVE_EXT_DEFINE_OPERAND(regex_match_type_operand) +}; + +static bool ext_regex_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + sieve_match_type_register(valdtr, ext, ®ex_match_type); + + return TRUE; +} + + diff --git a/pigeonhole/src/lib-sieve/plugins/regex/mcht-regex.c b/pigeonhole/src/lib-sieve/plugins/regex/mcht-regex.c new file mode 100644 index 0000000..f0c630e --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/regex/mcht-regex.c @@ -0,0 +1,385 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Match-type ':regex' + */ + +#include "lib.h" +#include "mempool.h" +#include "buffer.h" +#include "array.h" +#include "str.h" +#include "str-sanitize.h" + +#include "sieve-common.h" +#include "sieve-limits.h" +#include "sieve-ast.h" +#include "sieve-stringlist.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-interpreter.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-match.h" + +#include "ext-regex-common.h" + +#include <sys/types.h> +#include <regex.h> +#include <ctype.h> + +/* + * Configuration + */ + +#define MCHT_REGEX_MAX_SUBSTITUTIONS SIEVE_MAX_MATCH_VALUES + +/* + * Match type + */ + +static bool mcht_regex_validate_context +(struct sieve_validator *valdtr, struct sieve_ast_argument *arg, + struct sieve_match_type_context *ctx, struct sieve_ast_argument *key_arg); + +static void mcht_regex_match_init(struct sieve_match_context *mctx); +static int mcht_regex_match_keys + (struct sieve_match_context *mctx, const char *val, size_t val_size, + struct sieve_stringlist *key_list); +static void mcht_regex_match_deinit(struct sieve_match_context *mctx); + +const struct sieve_match_type_def regex_match_type = { + SIEVE_OBJECT("regex", ®ex_match_type_operand, 0), + .validate_context = mcht_regex_validate_context, + .match_init = mcht_regex_match_init, + .match_keys = mcht_regex_match_keys, + .match_deinit = mcht_regex_match_deinit +}; + +/* + * Match type validation + */ + +/* Wrapper around the regerror function for easy access */ +static const char *_regexp_error(regex_t *regexp, int errorcode) +{ + size_t errsize = regerror(errorcode, regexp, NULL, 0); + + if ( errsize > 0 ) { + char *errbuf; + + buffer_t *error_buf = + buffer_create_dynamic(pool_datastack_create(), errsize); + errbuf = buffer_get_space_unsafe(error_buf, 0, errsize); + + errsize = regerror(errorcode, regexp, errbuf, errsize); + + /* We don't want the error to start with a capital letter */ + errbuf[0] = i_tolower(errbuf[0]); + + buffer_append_space_unsafe(error_buf, errsize); + + return str_c(error_buf); + } + + return ""; +} + +static int mcht_regex_validate_regexp +(struct sieve_validator *valdtr, + struct sieve_match_type_context *mtctx ATTR_UNUSED, + struct sieve_ast_argument *key, int cflags) +{ + int ret; + regex_t regexp; + const char *regex_str = sieve_ast_argument_strc(key); + + if ( (ret=regcomp(®exp, regex_str, cflags)) != 0 ) { + sieve_argument_validate_error(valdtr, key, + "invalid regular expression '%s' for regex match: %s", + str_sanitize(regex_str, 128), _regexp_error(®exp, ret)); + + regfree(®exp); + return -1; + } + + regfree(®exp); + return 1; +} + +struct _regex_key_context { + struct sieve_validator *valdtr; + struct sieve_match_type_context *mtctx; + int cflags; +}; + +static int mcht_regex_validate_key_argument +(void *context, struct sieve_ast_argument *key) +{ + struct _regex_key_context *keyctx = (struct _regex_key_context *) context; + + /* FIXME: We can currently only handle string literal argument, so + * variables are not allowed. + */ + if ( sieve_argument_is_string_literal(key) ) { + return mcht_regex_validate_regexp + (keyctx->valdtr, keyctx->mtctx, key, keyctx->cflags); + } + + return 1; +} + +static bool mcht_regex_validate_context +(struct sieve_validator *valdtr, struct sieve_ast_argument *arg ATTR_UNUSED, + struct sieve_match_type_context *mtctx, struct sieve_ast_argument *key_arg) +{ + const struct sieve_comparator *cmp = mtctx->comparator; + int cflags = REG_EXTENDED | REG_NOSUB; + struct _regex_key_context keyctx; + struct sieve_ast_argument *kitem; + + if ( cmp != NULL ) { + if ( sieve_comparator_is(cmp, i_ascii_casemap_comparator) ) + cflags = REG_EXTENDED | REG_NOSUB | REG_ICASE; + else if ( sieve_comparator_is(cmp, i_octet_comparator) ) + cflags = REG_EXTENDED | REG_NOSUB; + else { + sieve_argument_validate_error(valdtr, mtctx->argument, + "regex match type only supports " + "i;octet and i;ascii-casemap comparators" ); + return FALSE; + } + } + + /* Validate regular expression keys */ + + keyctx.valdtr = valdtr; + keyctx.mtctx = mtctx; + keyctx.cflags = cflags; + + kitem = key_arg; + if ( sieve_ast_stringlist_map(&kitem, (void *) &keyctx, + mcht_regex_validate_key_argument) <= 0 ) + return FALSE; + + return TRUE; +} + +/* + * Match type implementation + */ + +struct mcht_regex_key { + regex_t regexp; + int status; +}; + +struct mcht_regex_context { + ARRAY(struct mcht_regex_key) reg_expressions; + regmatch_t *pmatch; + size_t nmatch; + bool all_compiled:1; +}; + +static void mcht_regex_match_init +(struct sieve_match_context *mctx) +{ + pool_t pool = mctx->pool; + struct mcht_regex_context *ctx; + + /* Create context */ + ctx = p_new(pool, struct mcht_regex_context, 1); + + /* Create storage for match values if match values are requested */ + if ( sieve_match_values_are_enabled(mctx->runenv) ) { + ctx->pmatch = p_new(pool, regmatch_t, MCHT_REGEX_MAX_SUBSTITUTIONS); + ctx->nmatch = MCHT_REGEX_MAX_SUBSTITUTIONS; + } else { + ctx->pmatch = NULL; + ctx->nmatch = 0; + } + + /* Assign context */ + mctx->data = (void *) ctx; +} + +static int mcht_regex_match_key +(struct sieve_match_context *mctx, const char *val, + const regex_t *regexp) +{ + struct mcht_regex_context *ctx = (struct mcht_regex_context *) mctx->data; + int ret; + + /* Execute regex */ + + ret = regexec(regexp, val, ctx->nmatch, ctx->pmatch, 0); + + /* Handle match values if necessary */ + + if ( ret == 0 ) { + if ( ctx->nmatch > 0 ) { + struct sieve_match_values *mvalues; + size_t i; + int skipped = 0; + string_t *subst = t_str_new(32); + + /* Start new list of match values */ + mvalues = sieve_match_values_start(mctx->runenv); + + i_assert( mvalues != NULL ); + + /* Add match values from regular expression */ + for ( i = 0; i < ctx->nmatch; i++ ) { + str_truncate(subst, 0); + + if ( ctx->pmatch[i].rm_so != -1 ) { + if ( skipped > 0 ) { + sieve_match_values_skip(mvalues, skipped); + skipped = 0; + } + + str_append_data(subst, val + ctx->pmatch[i].rm_so, + ctx->pmatch[i].rm_eo - ctx->pmatch[i].rm_so); + sieve_match_values_add(mvalues, subst); + } else + skipped++; + } + + /* Substitute the new match values */ + sieve_match_values_commit(mctx->runenv, &mvalues); + } + + return 1; + } + + return 0; +} + +static int mcht_regex_match_keys +(struct sieve_match_context *mctx, const char *val, size_t val_size ATTR_UNUSED, + struct sieve_stringlist *key_list) +{ + const struct sieve_runtime_env *renv = mctx->runenv; + bool trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING); + struct mcht_regex_context *ctx = (struct mcht_regex_context *) mctx->data; + const struct sieve_comparator *cmp = mctx->comparator; + int match; + + if ( !ctx->all_compiled ) { + string_t *key_item = NULL; + unsigned int i; + int ret; + + /* Regular expressions still need to be compiled */ + + if ( !array_is_created(&ctx->reg_expressions) ) + p_array_init(&ctx->reg_expressions, mctx->pool, 16); + + i = 0; + match = 0; + while ( match == 0 && + (ret=sieve_stringlist_next_item(key_list, &key_item)) > 0 ) { + + T_BEGIN { + struct mcht_regex_key *rkey; + + if ( i >= array_count(&ctx->reg_expressions) ) { + int cflags; + + rkey = array_append_space(&ctx->reg_expressions); + + /* Configure case-sensitivity according to comparator */ + if ( sieve_comparator_is(cmp, i_octet_comparator) ) + cflags = REG_EXTENDED; + else if ( sieve_comparator_is(cmp, i_ascii_casemap_comparator) ) + cflags = REG_EXTENDED | REG_ICASE; + else + rkey->status = -1; /* Not supported */ + + if ( rkey->status >= 0 ) { + const char *regex_str = str_c(key_item); + int rxret; + + /* Indicate whether match values need to be produced */ + if ( ctx->nmatch == 0 ) cflags |= REG_NOSUB; + + /* Compile regular expression */ + if ( (rxret=regcomp(&rkey->regexp, regex_str, cflags)) != 0 ) { + sieve_runtime_error(renv, NULL, + "invalid regular expression '%s' for regex match: %s", + str_sanitize(regex_str, 128), + _regexp_error(&rkey->regexp, rxret)); + rkey->status = -1; + } else { + rkey->status = 1; + } + } + } else { + rkey = array_idx_modifiable(&ctx->reg_expressions, 1); + } + + if ( rkey->status > 0 ) { + match = mcht_regex_match_key(mctx, val, &rkey->regexp); + + if ( trace ) { + sieve_runtime_trace(renv, 0, + "with regex `%s' [id=%d] => %d", + str_sanitize(str_c(key_item), 80), + array_count(&ctx->reg_expressions)-1, match); + } + } + } T_END; + + i++; + } + + if ( ret == 0 ) { + ctx->all_compiled = TRUE; + } else if ( ret < 0 ) { + mctx->exec_status = key_list->exec_status; + match = -1; + } + + } else { + const struct mcht_regex_key *rkeys; + unsigned int i, count; + + /* Regular expressions are compiled */ + + rkeys = array_get(&ctx->reg_expressions, &count); + + i = 0; + match = 0; + while ( match == 0 && i < count ) { + if ( rkeys[i].status > 0 ) { + match = mcht_regex_match_key(mctx, val, &rkeys[i].regexp); + + if ( trace ) { + sieve_runtime_trace(renv, 0, + "with compiled regex [id=%d] => %d", i, match); + } + } + + i++; + } + } + + return match; +} + +void mcht_regex_match_deinit +(struct sieve_match_context *mctx) +{ + struct mcht_regex_context *ctx = (struct mcht_regex_context *) mctx->data; + struct mcht_regex_key *rkeys; + unsigned int count, i; + + /* Clean up compiled regular expressions */ + if ( array_is_created(&ctx->reg_expressions) ) { + rkeys = array_get_modifiable(&ctx->reg_expressions, &count); + for ( i = 0; i < count; i++ ) { + regfree(&rkeys[i].regexp); + } + } +} + diff --git a/pigeonhole/src/lib-sieve/plugins/relational/Makefile.am b/pigeonhole/src/lib-sieve/plugins/relational/Makefile.am new file mode 100644 index 0000000..dd28cc5 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/relational/Makefile.am @@ -0,0 +1,14 @@ +noinst_LTLIBRARIES = libsieve_ext_relational.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +libsieve_ext_relational_la_SOURCES = \ + ext-relational-common.c \ + mcht-value.c \ + mcht-count.c \ + ext-relational.c + +noinst_HEADERS = \ + ext-relational-common.h diff --git a/pigeonhole/src/lib-sieve/plugins/relational/Makefile.in b/pigeonhole/src/lib-sieve/plugins/relational/Makefile.in new file mode 100644 index 0000000..4977808 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/relational/Makefile.in @@ -0,0 +1,694 @@ +# 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/plugins/relational +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_ext_relational_la_LIBADD = +am_libsieve_ext_relational_la_OBJECTS = ext-relational-common.lo \ + mcht-value.lo mcht-count.lo ext-relational.lo +libsieve_ext_relational_la_OBJECTS = \ + $(am_libsieve_ext_relational_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)/ext-relational-common.Plo \ + ./$(DEPDIR)/ext-relational.Plo ./$(DEPDIR)/mcht-count.Plo \ + ./$(DEPDIR)/mcht-value.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_ext_relational_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_relational_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_ext_relational.la +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +libsieve_ext_relational_la_SOURCES = \ + ext-relational-common.c \ + mcht-value.c \ + mcht-count.c \ + ext-relational.c + +noinst_HEADERS = \ + ext-relational-common.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/plugins/relational/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/relational/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_ext_relational.la: $(libsieve_ext_relational_la_OBJECTS) $(libsieve_ext_relational_la_DEPENDENCIES) $(EXTRA_libsieve_ext_relational_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_relational_la_OBJECTS) $(libsieve_ext_relational_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-relational-common.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-relational.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mcht-count.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mcht-value.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)/ext-relational-common.Plo + -rm -f ./$(DEPDIR)/ext-relational.Plo + -rm -f ./$(DEPDIR)/mcht-count.Plo + -rm -f ./$(DEPDIR)/mcht-value.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)/ext-relational-common.Plo + -rm -f ./$(DEPDIR)/ext-relational.Plo + -rm -f ./$(DEPDIR)/mcht-count.Plo + -rm -f ./$(DEPDIR)/mcht-value.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/plugins/relational/ext-relational-common.c b/pigeonhole/src/lib-sieve/plugins/relational/ext-relational-common.c new file mode 100644 index 0000000..a6dd1ed --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/relational/ext-relational-common.c @@ -0,0 +1,171 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Syntax: + + MATCH-TYPE =/ COUNT / VALUE + COUNT = ":count" relational-match + VALUE = ":value" relational-match + relational-match = DQUOTE ( "gt" / "ge" / "lt" + / "le" / "eq" / "ne" ) DQUOTE + */ + +#include "lib.h" +#include "str.h" +#include "str-sanitize.h" + +#include "sieve-common.h" +#include "sieve-ast.h" +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" + +#include "ext-relational-common.h" + +/* + * Forward declarations + */ + +const struct sieve_match_type_def *rel_match_types[]; + +/* + * Validation + */ + +bool mcht_relational_validate(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_match_type_context *ctx) +{ + struct sieve_match_type *mcht; + enum relational_match rel_match = REL_MATCH_INVALID; + pool_t pool = sieve_ast_argument_pool(ctx->argument); + string_t *rel_match_ident; + + /* Check syntax: + relational-match = DQUOTE ( "gt" / "ge" / "lt" + / "le" / "eq" / "ne" ) DQUOTE + + So, actually this must be a constant string and it is implemented as + such. + */ + + /* Did we get a string in the first place? */ + if (*arg == NULL || (*arg)->type != SAAT_STRING) { + sieve_argument_validate_error( + valdtr, (*arg == NULL ? ctx->argument : *arg), + "the :%s match-type requires a constant string argument being " + "one of \"gt\", \"ge\", \"lt\", \"le\", \"eq\" or \"ne\", " + "but %s was found", + sieve_match_type_name(ctx->match_type), + (*arg == NULL ? + "none" : sieve_ast_argument_name(*arg))); + return FALSE; + } + + /* Check the relational match id */ + + rel_match_ident = sieve_ast_argument_str(*arg); + if (str_len(rel_match_ident) == 2) { + const char *rel_match_id = str_c(rel_match_ident); + + switch (rel_match_id[0]) { + /* "gt" or "ge" */ + case 'g': + switch (rel_match_id[1]) { + case 't': + rel_match = REL_MATCH_GREATER; + break; + case 'e': + rel_match = REL_MATCH_GREATER_EQUAL; + break; + default: + rel_match = REL_MATCH_INVALID; + } + break; + /* "lt" or "le" */ + case 'l': + switch (rel_match_id[1]) { + case 't': + rel_match = REL_MATCH_LESS; + break; + case 'e': + rel_match = REL_MATCH_LESS_EQUAL; + break; + default: + rel_match = REL_MATCH_INVALID; + } + break; + /* "eq" */ + case 'e': + if (rel_match_id[1] == 'q') + rel_match = REL_MATCH_EQUAL; + else + rel_match = REL_MATCH_INVALID; + break; + /* "ne" */ + case 'n': + if (rel_match_id[1] == 'e') + rel_match = REL_MATCH_NOT_EQUAL; + else + rel_match = REL_MATCH_INVALID; + break; + /* invalid */ + default: + rel_match = REL_MATCH_INVALID; + } + } + + if (rel_match >= REL_MATCH_INVALID) { + sieve_argument_validate_error( + valdtr, *arg, + "the :%s match-type requires a constant string argument being " + "one of \"gt\", \"ge\", \"lt\", \"le\", \"eq\" or \"ne\", " + "but \"%s\" was found", + sieve_match_type_name(ctx->match_type), + str_sanitize(str_c(rel_match_ident), 32)); + return FALSE; + } + + /* Delete argument */ + *arg = sieve_ast_arguments_detach(*arg, 1); + + /* Not used just yet */ + ctx->ctx_data = (void *) rel_match; + + /* Override the actual match type with a parameter-specific one + * FIXME: ugly! + */ + mcht = p_new(pool, struct sieve_match_type, 1); + mcht->object.ext = ctx->match_type->object.ext; + SIEVE_OBJECT_SET_DEF(mcht, rel_match_types[ + REL_MATCH_INDEX(ctx->match_type->object.def->code, rel_match)]); + ctx->match_type = mcht; + + return TRUE; +} + +/* + * Relational match-type operand + */ + +const struct sieve_match_type_def *rel_match_types[] = { + &rel_match_value_gt, &rel_match_value_ge, &rel_match_value_lt, + &rel_match_value_le, &rel_match_value_eq, &rel_match_value_ne, + &rel_match_count_gt, &rel_match_count_ge, &rel_match_count_lt, + &rel_match_count_le, &rel_match_count_eq, &rel_match_count_ne, +}; + +static const struct sieve_extension_objects ext_match_types = + SIEVE_EXT_DEFINE_MATCH_TYPES(rel_match_types); + +const struct sieve_operand_def rel_match_type_operand = { + .name = "relational match", + .ext_def = &relational_extension, + .class = &sieve_match_type_operand_class, + .interface = &ext_match_types, +}; diff --git a/pigeonhole/src/lib-sieve/plugins/relational/ext-relational-common.h b/pigeonhole/src/lib-sieve/plugins/relational/ext-relational-common.h new file mode 100644 index 0000000..c0f7d3b --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/relational/ext-relational-common.h @@ -0,0 +1,86 @@ +#ifndef EXT_RELATIONAL_COMMON_H +#define EXT_RELATIONAL_COMMON_H + +#include "lib.h" +#include "str.h" + +#include "sieve-common.h" + +/* + * Types + */ + +enum ext_relational_match_type { + RELATIONAL_VALUE, + RELATIONAL_COUNT +}; + +enum relational_match { + REL_MATCH_GREATER, + REL_MATCH_GREATER_EQUAL, + REL_MATCH_LESS, + REL_MATCH_LESS_EQUAL, + REL_MATCH_EQUAL, + REL_MATCH_NOT_EQUAL, + REL_MATCH_INVALID +}; + +#define REL_MATCH_INDEX(type, match) (type * REL_MATCH_INVALID + match) +#define REL_MATCH_TYPE(index) (index / REL_MATCH_INVALID) +#define REL_MATCH(index) (index % REL_MATCH_INVALID) + +/* + * Extension definitions + */ + +extern const struct sieve_extension_def relational_extension; + +/* + * Match types + */ + +/* Registered for validation */ + +extern const struct sieve_match_type_def value_match_type; +extern const struct sieve_match_type_def count_match_type; + +/* Used in byte code */ + +extern const struct sieve_match_type_def rel_match_count_gt; +extern const struct sieve_match_type_def rel_match_count_ge; +extern const struct sieve_match_type_def rel_match_count_lt; +extern const struct sieve_match_type_def rel_match_count_le; +extern const struct sieve_match_type_def rel_match_count_eq; +extern const struct sieve_match_type_def rel_match_count_ne; + +extern const struct sieve_match_type_def rel_match_value_gt; +extern const struct sieve_match_type_def rel_match_value_ge; +extern const struct sieve_match_type_def rel_match_value_lt; +extern const struct sieve_match_type_def rel_match_value_le; +extern const struct sieve_match_type_def rel_match_value_eq; +extern const struct sieve_match_type_def rel_match_value_ne; + +/* + * Operand + */ + +extern const struct sieve_operand_def rel_match_type_operand; + + +/* + * Match type validation + */ + +bool mcht_relational_validate(struct sieve_validator *validator, + struct sieve_ast_argument **arg, + struct sieve_match_type_context *ctx); + +/* + * Value match function (also used by :count) + */ + +int mcht_value_match_key(struct sieve_match_context *mctx, + const char *val, size_t val_size, + const char *key, size_t key_size); + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/relational/ext-relational.c b/pigeonhole/src/lib-sieve/plugins/relational/ext-relational.c new file mode 100644 index 0000000..28c278f --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/relational/ext-relational.c @@ -0,0 +1,53 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension relational + * -------------------- + * + * Author: Stephan Bosch + * Specification: RFC 3431 + * Implementation: full + * Status: testing + * + */ + +#include "lib.h" +#include "str.h" + +#include "sieve-common.h" + +#include "sieve-ast.h" +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" + +#include "ext-relational-common.h" + +/* + * Extension + */ + +static bool ext_relational_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr); + +const struct sieve_extension_def relational_extension = { + .name = "relational", + .validator_load = ext_relational_validator_load, + SIEVE_EXT_DEFINE_OPERAND(rel_match_type_operand) +}; + +static bool ext_relational_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + sieve_match_type_register(valdtr, ext, &value_match_type); + sieve_match_type_register(valdtr, ext, &count_match_type); + + return TRUE; +} + + diff --git a/pigeonhole/src/lib-sieve/plugins/relational/mcht-count.c b/pigeonhole/src/lib-sieve/plugins/relational/mcht-count.c new file mode 100644 index 0000000..e31a63a --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/relational/mcht-count.c @@ -0,0 +1,119 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Match-type ':count' + */ + +#include "lib.h" +#include "str.h" +#include "str-sanitize.h" + +#include "sieve-common.h" +#include "sieve-ast.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-runtime-trace.h" +#include "sieve-match.h" + +#include "ext-relational-common.h" + +/* + * Forward declarations + */ + +static int mcht_count_match + (struct sieve_match_context *mctx, struct sieve_stringlist *value_list, + struct sieve_stringlist *key_list); + +/* + * Match-type objects + */ + +const struct sieve_match_type_def count_match_type = { + SIEVE_OBJECT("count", + &rel_match_type_operand, RELATIONAL_COUNT), + .validate = mcht_relational_validate +}; + +#define COUNT_MATCH_TYPE(name, rel_match) \ +const struct sieve_match_type_def rel_match_count_ ## name = { \ + SIEVE_OBJECT("count-" #name, \ + &rel_match_type_operand, \ + REL_MATCH_INDEX(RELATIONAL_COUNT, rel_match)), \ + .match = mcht_count_match, \ +} + +COUNT_MATCH_TYPE(gt, REL_MATCH_GREATER); +COUNT_MATCH_TYPE(ge, REL_MATCH_GREATER_EQUAL); +COUNT_MATCH_TYPE(lt, REL_MATCH_LESS); +COUNT_MATCH_TYPE(le, REL_MATCH_LESS_EQUAL); +COUNT_MATCH_TYPE(eq, REL_MATCH_EQUAL); +COUNT_MATCH_TYPE(ne, REL_MATCH_NOT_EQUAL); + +/* + * Match-type implementation + */ + +static int mcht_count_match +(struct sieve_match_context *mctx, struct sieve_stringlist *value_list, + struct sieve_stringlist *key_list) +{ + const struct sieve_runtime_env *renv = mctx->runenv; + bool trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING); + int count; + string_t *key_item; + int match, ret; + + if ( (count=sieve_stringlist_get_length(value_list)) < 0 ) { + mctx->exec_status = value_list->exec_status; + return -1; + } + + sieve_stringlist_reset(key_list); + + string_t *value = t_str_new(20); + str_printfa(value, "%d", count); + + if ( trace ) { + sieve_runtime_trace(renv, 0, + "matching count value `%s'", str_sanitize(str_c(value), 80)); + } + + sieve_runtime_trace_descend(renv); + + /* Match to all key values */ + key_item = NULL; + match = 0; + while ( match == 0 && + (ret=sieve_stringlist_next_item(key_list, &key_item)) > 0 ) + { + match = mcht_value_match_key + (mctx, str_c(value), str_len(value), str_c(key_item), str_len(key_item)); + + if ( trace ) { + sieve_runtime_trace(renv, 0, + "with key `%s' => %d", str_sanitize(str_c(key_item), 80), ret); + } + } + + sieve_runtime_trace_ascend(renv); + + if ( ret < 0 ) { + mctx->exec_status = key_list->exec_status; + match = -1; + } + + return match; +} + + + + + diff --git a/pigeonhole/src/lib-sieve/plugins/relational/mcht-value.c b/pigeonhole/src/lib-sieve/plugins/relational/mcht-value.c new file mode 100644 index 0000000..dd55590 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/relational/mcht-value.c @@ -0,0 +1,80 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" + +#include "sieve-common.h" + +#include "sieve-ast.h" +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-match.h" + +#include "ext-relational-common.h" + +/* + * Match-type objects + */ + +const struct sieve_match_type_def value_match_type = { + SIEVE_OBJECT("value", + &rel_match_type_operand, RELATIONAL_VALUE), + .validate = mcht_relational_validate +}; + +#define VALUE_MATCH_TYPE(name, rel_match) \ +const struct sieve_match_type_def rel_match_value_ ## name = { \ + SIEVE_OBJECT("value-" #name, \ + &rel_match_type_operand, \ + REL_MATCH_INDEX(RELATIONAL_VALUE, rel_match)), \ + .match_key = mcht_value_match_key, \ +} + +VALUE_MATCH_TYPE(gt, REL_MATCH_GREATER); +VALUE_MATCH_TYPE(ge, REL_MATCH_GREATER_EQUAL); +VALUE_MATCH_TYPE(lt, REL_MATCH_LESS); +VALUE_MATCH_TYPE(le, REL_MATCH_LESS_EQUAL); +VALUE_MATCH_TYPE(eq, REL_MATCH_EQUAL); +VALUE_MATCH_TYPE(ne, REL_MATCH_NOT_EQUAL); + +/* + * Match-type implementation + */ + +int mcht_value_match_key +(struct sieve_match_context *mctx, const char *val, size_t val_size, + const char *key, size_t key_size) +{ + const struct sieve_match_type *mtch = mctx->match_type; + unsigned int rel_match = REL_MATCH(mtch->object.def->code); + int cmp_result; + + cmp_result = mctx->comparator->def-> + compare(mctx->comparator, val, val_size, key, key_size); + + switch ( rel_match ) { + case REL_MATCH_GREATER: + return ( cmp_result > 0 ? 1 : 0 ); + case REL_MATCH_GREATER_EQUAL: + return ( cmp_result >= 0 ? 1 : 0 ); + case REL_MATCH_LESS: + return ( cmp_result < 0 ? 1 : 0 ); + case REL_MATCH_LESS_EQUAL: + return ( cmp_result <= 0 ? 1 : 0 ); + case REL_MATCH_EQUAL: + return ( cmp_result == 0 ? 1 : 0); + case REL_MATCH_NOT_EQUAL: + return ( cmp_result != 0 ? 1 : 0); + } + + i_unreached(); +} + + diff --git a/pigeonhole/src/lib-sieve/plugins/spamvirustest/Makefile.am b/pigeonhole/src/lib-sieve/plugins/spamvirustest/Makefile.am new file mode 100644 index 0000000..6a15bdd --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/spamvirustest/Makefile.am @@ -0,0 +1,16 @@ +noinst_LTLIBRARIES = libsieve_ext_spamvirustest.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +tests = \ + tst-spamvirustest.c + +libsieve_ext_spamvirustest_la_SOURCES = \ + $(tests) \ + ext-spamvirustest-common.c \ + ext-spamvirustest.c + +noinst_HEADERS = \ + ext-spamvirustest-common.h diff --git a/pigeonhole/src/lib-sieve/plugins/spamvirustest/Makefile.in b/pigeonhole/src/lib-sieve/plugins/spamvirustest/Makefile.in new file mode 100644 index 0000000..dca31e3 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/spamvirustest/Makefile.in @@ -0,0 +1,694 @@ +# 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/plugins/spamvirustest +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_ext_spamvirustest_la_LIBADD = +am__objects_1 = tst-spamvirustest.lo +am_libsieve_ext_spamvirustest_la_OBJECTS = $(am__objects_1) \ + ext-spamvirustest-common.lo ext-spamvirustest.lo +libsieve_ext_spamvirustest_la_OBJECTS = \ + $(am_libsieve_ext_spamvirustest_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)/ext-spamvirustest-common.Plo \ + ./$(DEPDIR)/ext-spamvirustest.Plo \ + ./$(DEPDIR)/tst-spamvirustest.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_ext_spamvirustest_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_spamvirustest_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_ext_spamvirustest.la +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +tests = \ + tst-spamvirustest.c + +libsieve_ext_spamvirustest_la_SOURCES = \ + $(tests) \ + ext-spamvirustest-common.c \ + ext-spamvirustest.c + +noinst_HEADERS = \ + ext-spamvirustest-common.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/plugins/spamvirustest/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/spamvirustest/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_ext_spamvirustest.la: $(libsieve_ext_spamvirustest_la_OBJECTS) $(libsieve_ext_spamvirustest_la_DEPENDENCIES) $(EXTRA_libsieve_ext_spamvirustest_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_spamvirustest_la_OBJECTS) $(libsieve_ext_spamvirustest_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-spamvirustest-common.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-spamvirustest.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-spamvirustest.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)/ext-spamvirustest-common.Plo + -rm -f ./$(DEPDIR)/ext-spamvirustest.Plo + -rm -f ./$(DEPDIR)/tst-spamvirustest.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)/ext-spamvirustest-common.Plo + -rm -f ./$(DEPDIR)/ext-spamvirustest.Plo + -rm -f ./$(DEPDIR)/tst-spamvirustest.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/plugins/spamvirustest/ext-spamvirustest-common.c b/pigeonhole/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest-common.c new file mode 100644 index 0000000..38cb5d8 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest-common.c @@ -0,0 +1,674 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "strfuncs.h" +#include "mail-storage.h" + +#include "sieve-common.h" +#include "sieve-settings.h" +#include "sieve-error.h" +#include "sieve-extensions.h" +#include "sieve-message.h" +#include "sieve-interpreter.h" +#include "sieve-runtime-trace.h" + +#include "ext-spamvirustest-common.h" + +#include <sys/types.h> +#include <regex.h> +#include <ctype.h> + +/* + * Extension data + */ + +enum ext_spamvirustest_status_type { + EXT_SPAMVIRUSTEST_STATUS_TYPE_SCORE, + EXT_SPAMVIRUSTEST_STATUS_TYPE_STRLEN, + EXT_SPAMVIRUSTEST_STATUS_TYPE_TEXT, +}; + +struct ext_spamvirustest_header_spec { + const char *header_name; + regex_t regexp; + bool regexp_match; +}; + +struct ext_spamvirustest_data { + pool_t pool; + + int reload; + + struct ext_spamvirustest_header_spec status_header; + struct ext_spamvirustest_header_spec max_header; + + enum ext_spamvirustest_status_type status_type; + + float max_value; + + const char *text_values[11]; +}; + +/* + * Regexp utility + */ + +static bool _regexp_compile +(regex_t *regexp, const char *data, const char **error_r) +{ + size_t errsize; + int ret; + + *error_r = ""; + + if ( (ret=regcomp(regexp, data, REG_EXTENDED)) == 0 ) { + return TRUE; + } + + errsize = regerror(ret, regexp, NULL, 0); + + if ( errsize > 0 ) { + char *errbuf = t_malloc0(errsize); + + (void)regerror(ret, regexp, errbuf, errsize); + + /* We don't want the error to start with a capital letter */ + errbuf[0] = i_tolower(errbuf[0]); + + *error_r = errbuf; + } + + return FALSE; +} + +static const char *_regexp_match_get_value +(const char *string, int index, regmatch_t pmatch[], int nmatch) +{ + if ( index > -1 && index < nmatch && pmatch[index].rm_so != -1 ) { + return t_strndup(string + pmatch[index].rm_so, + pmatch[index].rm_eo - pmatch[index].rm_so); + } + return NULL; +} + +/* + * Configuration parser + */ + +static bool ext_spamvirustest_header_spec_parse +(struct ext_spamvirustest_header_spec *spec, pool_t pool, const char *data, + const char **error_r) +{ + const char *p; + const char *regexp_error; + + if ( *data == '\0' ) { + *error_r = "empty header specification"; + return FALSE; + } + + /* Parse header name */ + + p = data; + + while ( *p == ' ' || *p == '\t' ) p++; + while ( *p != ':' && *p != '\0' && *p != ' ' && *p != '\t' ) p++; + + if ( *p == '\0' ) { + spec->header_name = p_strdup(pool, data); + return TRUE; + } + + spec->header_name = p_strdup_until(pool, data, p); + while ( *p == ' ' || *p == '\t' ) p++; + + if ( *p == '\0' ) { + spec->regexp_match = FALSE; + return TRUE; + } + + /* Parse and compile regular expression */ + + if ( *p != ':' ) { + *error_r = t_strdup_printf("expecting ':', but found '%c'", *p); + return FALSE; + } + p++; + while ( *p == ' ' || *p == '\t' ) p++; + + spec->regexp_match = TRUE; + if ( !_regexp_compile(&spec->regexp, p, ®exp_error) ) { + *error_r = t_strdup_printf("failed to compile regular expression '%s': " + "%s", p, regexp_error); + return FALSE; + } + + return TRUE; +} + +static void ext_spamvirustest_header_spec_free +(struct ext_spamvirustest_header_spec *spec) +{ + regfree(&spec->regexp); +} + +static bool ext_spamvirustest_parse_strlen_value +(const char *str_value, float *value_r, const char **error_r) +{ + const char *p = str_value; + char ch = *p; + + if ( *str_value == '\0' ) { + *value_r = 0; + return TRUE; + } + + while ( *p == ch ) p++; + + if ( *p != '\0' ) { + *error_r = t_strdup_printf( + "different character '%c' encountered in strlen value", + *p); + return FALSE; + } + + *value_r = ( p - str_value ); + + return TRUE; +} + +static bool ext_spamvirustest_parse_decimal_value +(const char *str_value, float *value_r, const char **error_r) +{ + const char *p = str_value; + float value; + float sign = 1; + int digits; + + if ( *p == '\0' ) { + *error_r = "empty value"; + return FALSE; + } + + if ( *p == '+' || *p == '-' ) { + if ( *p == '-' ) + sign = -1; + + p++; + } + + value = 0; + digits = 0; + while ( i_isdigit(*p) ) { + value = value*10 + (*p-'0'); + if ( digits++ > 4 ) { + *error_r = t_strdup_printf + ("decimal value has too many digits before radix point: %s", + str_value); + return FALSE; + } + p++; + } + + if ( *p == '.' || *p == ',' ) { + float radix = .1; + p++; + + digits = 0; + while ( i_isdigit(*p) ) { + value = value + (*p-'0')*radix; + + if ( digits++ > 4 ) { + *error_r = t_strdup_printf + ("decimal value has too many digits after radix point: %s", + str_value); + return FALSE; + } + radix /= 10; + p++; + } + } + + if ( *p != '\0' ) { + *error_r = t_strdup_printf + ("invalid decimal point value: %s", str_value); + return FALSE; + } + + *value_r = value * sign; + + return TRUE; +} + +/* + * Extension initialization + */ + +bool ext_spamvirustest_load +(const struct sieve_extension *ext, void **context) +{ + struct ext_spamvirustest_data *ext_data = + (struct ext_spamvirustest_data *) *context; + struct sieve_instance *svinst = ext->svinst; + const char *ext_name, *status_header, *max_header, *status_type, + *max_value; + enum ext_spamvirustest_status_type type; + const char *error; + pool_t pool; + bool result = TRUE; + int reload = 0; + + if ( *context != NULL ) { + reload = ext_data->reload + 1; + ext_spamvirustest_unload(ext); + *context = NULL; + } + + /* FIXME: + * Prevent loading of both spamtest and spamtestplus: let these share + * contexts. + */ + + if ( sieve_extension_is(ext, spamtest_extension) || + sieve_extension_is(ext, spamtestplus_extension) ) { + ext_name = spamtest_extension.name; + } else { + ext_name = sieve_extension_name(ext); + } + + /* Get settings */ + + status_header = sieve_setting_get + (svinst, t_strconcat("sieve_", ext_name, "_status_header", NULL)); + status_type = sieve_setting_get + (svinst, t_strconcat("sieve_", ext_name, "_status_type", NULL)); + max_header = sieve_setting_get + (svinst, t_strconcat("sieve_", ext_name, "_max_header", NULL)); + max_value = sieve_setting_get + (svinst, t_strconcat("sieve_", ext_name, "_max_value", NULL)); + + /* Base configuration */ + + if ( status_header == NULL ) { + return TRUE; + } + + if ( status_type == NULL || strcmp(status_type, "score") == 0 ) { + type = EXT_SPAMVIRUSTEST_STATUS_TYPE_SCORE; + } else if ( strcmp(status_type, "strlen") == 0 ) { + type = EXT_SPAMVIRUSTEST_STATUS_TYPE_STRLEN; + } else if ( strcmp(status_type, "text") == 0 ) { + type = EXT_SPAMVIRUSTEST_STATUS_TYPE_TEXT; + } else { + e_error(svinst->event, "%s: " + "invalid status type '%s'", ext_name, status_type); + return FALSE; + } + + /* Verify settings */ + + if ( type != EXT_SPAMVIRUSTEST_STATUS_TYPE_TEXT ) { + + if ( max_header != NULL && max_value != NULL ) { + e_error(svinst->event, "%s: " + "sieve_%s_max_header and sieve_%s_max_value " + "cannot both be configured", + ext_name, ext_name, ext_name); + return TRUE; + } + + if ( max_header == NULL && max_value == NULL ) { + e_error(svinst->event, "%s: " + "none of sieve_%s_max_header or sieve_%s_max_value " + "is configured", ext_name, ext_name, ext_name); + return TRUE; + } + } else { + if ( max_header != NULL ) { + e_warning(svinst->event, "%s: " + "setting sieve_%s_max_header has no meaning " + "for sieve_%s_status_type=text", + ext_name, ext_name, ext_name); + } + + if ( max_value != NULL ) { + e_warning(svinst->event, "%s: " + "setting sieve_%s_max_value has no meaning " + "for sieve_%s_status_type=text", + ext_name, ext_name, ext_name); + } + } + + pool = pool_alloconly_create("spamvirustest_data", 512); + ext_data = p_new(pool, struct ext_spamvirustest_data, 1); + ext_data->pool = pool; + ext_data->reload = reload; + ext_data->status_type = type; + + if ( !ext_spamvirustest_header_spec_parse + (&ext_data->status_header, ext_data->pool, status_header, &error) ) { + e_error(svinst->event, "%s: " + "invalid status header specification '%s': %s", + ext_name, status_header, error); + result = FALSE; + } + + if ( result ) { + if ( type != EXT_SPAMVIRUSTEST_STATUS_TYPE_TEXT ) { + /* Parse max header */ + + if ( max_header != NULL && !ext_spamvirustest_header_spec_parse + (&ext_data->max_header, ext_data->pool, max_header, &error) ) { + e_error(svinst->event, "%s: " + "invalid max header specification " + "'%s': %s", ext_name, max_header, + error); + result = FALSE; + } + + /* Parse max value */ + + if ( result && max_value != NULL ) { + if ( !ext_spamvirustest_parse_decimal_value + (max_value, &ext_data->max_value, &error) ) { + e_error(svinst->event, "%s: " + "invalid max value specification " + "'%s': %s", ext_name, max_value, + error); + result = FALSE; + } + } + + } else { + unsigned int i, max_text; + + max_text = ( sieve_extension_is(ext, virustest_extension) ? 5 : 10 ); + + /* Get text values */ + for ( i = 0; i <= max_text; i++ ) { + const char *value = sieve_setting_get + (svinst, t_strdup_printf("sieve_%s_text_value%d", ext_name, i)); + + if ( value != NULL && *value != '\0' ) + ext_data->text_values[i] = p_strdup(ext_data->pool, value); + } + + ext_data->max_value = 1; + } + } + + if ( result ) { + *context = (void *) ext_data; + } else { + e_warning(svinst->event, "%s: " + "extension not configured, " + "tests will always match against \"0\"", + ext_name); + ext_spamvirustest_unload(ext); + *context = NULL; + } + + return result; +} + +void ext_spamvirustest_unload(const struct sieve_extension *ext) +{ + struct ext_spamvirustest_data *ext_data = + (struct ext_spamvirustest_data *) ext->context; + + if ( ext_data != NULL ) { + ext_spamvirustest_header_spec_free(&ext_data->status_header); + ext_spamvirustest_header_spec_free(&ext_data->max_header); + pool_unref(&ext_data->pool); + } +} + +/* + * Runtime + */ + +struct ext_spamvirustest_message_context { + int reload; + float score_ratio; +}; + +static const char *ext_spamvirustest_get_score +(const struct sieve_extension *ext, float score_ratio, bool percent) +{ + int score; + + if ( score_ratio < 0 ) + return "0"; + + if ( score_ratio > 1 ) + score_ratio = 1; + + if ( percent ) + score = score_ratio * 100 + 0.001; + else if ( sieve_extension_is(ext, virustest_extension) ) + score = score_ratio * 4 + 1.001; + else + score = score_ratio * 9 + 1.001; + + return t_strdup_printf("%d", score); +} + +int ext_spamvirustest_get_value +(const struct sieve_runtime_env *renv, const struct sieve_extension *ext, + bool percent, const char **value_r) +{ + struct ext_spamvirustest_data *ext_data = + (struct ext_spamvirustest_data *) ext->context; + struct ext_spamvirustest_header_spec *status_header, *max_header; + struct sieve_message_context *msgctx = renv->msgctx; + struct ext_spamvirustest_message_context *mctx; + struct mail *mail; + regmatch_t match_values[2]; + const char *header_value, *error; + const char *status = NULL, *max = NULL; + float status_value, max_value; + unsigned int i, max_text; + pool_t pool = sieve_interpreter_pool(renv->interp); + + *value_r = "0"; + + /* + * Check whether extension is properly configured + */ + if ( ext_data == NULL ) { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "error: extension not configured"); + return SIEVE_EXEC_OK; + } + + /* + * Check wether a cached result is available + */ + + mctx = (struct ext_spamvirustest_message_context *) + sieve_message_context_extension_get(msgctx, ext); + + if ( mctx == NULL ) { + /* Create new context */ + mctx = p_new(pool, struct ext_spamvirustest_message_context, 1); + sieve_message_context_extension_set(msgctx, ext, (void *)mctx); + } else if ( mctx->reload == ext_data->reload ) { + /* Use cached result */ + *value_r = ext_spamvirustest_get_score(ext, mctx->score_ratio, percent); + return SIEVE_EXEC_OK; + } else { + /* Extension was reloaded (probably in testsuite) */ + } + + mctx->reload = ext_data->reload; + + /* + * Get max status value + */ + + mail = sieve_message_get_mail(renv->msgctx); + status_header = &ext_data->status_header; + max_header = &ext_data->max_header; + + if ( ext_data->status_type != EXT_SPAMVIRUSTEST_STATUS_TYPE_TEXT ) { + if ( max_header->header_name != NULL ) { + /* Get header from message */ + if ( mail_get_first_header_utf8 + (mail, max_header->header_name, &header_value) < 0 ) { + return sieve_runtime_mail_error (renv, mail, + "%s test: failed to read header field `%s'", + sieve_extension_name(ext), max_header->header_name); + } + if ( header_value == NULL ) { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "header '%s' not found in message", + max_header->header_name); + goto failed; + } + + if ( max_header->regexp_match ) { + /* Execute regex */ + if ( regexec(&max_header->regexp, header_value, 2, match_values, 0) + != 0 ) { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "regexp for header '%s' did not match " + "on value '%s'", max_header->header_name, header_value); + goto failed; + } + + max = _regexp_match_get_value(header_value, 1, match_values, 2); + if ( max == NULL ) { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "regexp did not return match value " + "for string '%s'", header_value); + goto failed; + } + } else { + max = header_value; + } + + if ( !ext_spamvirustest_parse_decimal_value(max, &max_value, &error) ) { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "failed to parse maximum value: %s", error); + goto failed; + } + } else { + max_value = ext_data->max_value; + } + + if ( max_value == 0 ) { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "error: max value is 0"); + goto failed; + } + } else { + max_value = ( sieve_extension_is(ext, virustest_extension) ? 5 : 10 ); + } + + /* + * Get status value + */ + + /* Get header from message */ + if ( mail_get_first_header_utf8 + (mail, status_header->header_name, &header_value) < 0 ) { + return sieve_runtime_mail_error (renv, mail, + "%s test: failed to read header field `%s'", + sieve_extension_name(ext), status_header->header_name); + } + if ( header_value == NULL ) { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "header '%s' not found in message", + status_header->header_name); + goto failed; + } + + /* Execute regex */ + if ( status_header->regexp_match ) { + if ( regexec(&status_header->regexp, header_value, 2, match_values, 0) + != 0 ) { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "regexp for header '%s' did not match on value '%s'", + status_header->header_name, header_value); + goto failed; + } + + status = _regexp_match_get_value(header_value, 1, match_values, 2); + if ( status == NULL ) { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "regexp did not return match value for string '%s'", + header_value); + goto failed; + } + } else { + status = header_value; + } + + switch ( ext_data->status_type ) { + case EXT_SPAMVIRUSTEST_STATUS_TYPE_SCORE: + if ( !ext_spamvirustest_parse_decimal_value + (status, &status_value, &error) ) { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "failed to parse status value '%s': %s", + status, error); + goto failed; + } + break; + case EXT_SPAMVIRUSTEST_STATUS_TYPE_STRLEN: + if ( !ext_spamvirustest_parse_strlen_value + (status, &status_value, &error) ) { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "failed to parse status value '%s': %s", + status, error); + goto failed; + } + break; + case EXT_SPAMVIRUSTEST_STATUS_TYPE_TEXT: + max_text = ( sieve_extension_is(ext, virustest_extension) ? 5 : 10 ); + status_value = 0; + + i = 0; + while ( i <= max_text ) { + if ( ext_data->text_values[i] != NULL && + strcmp(status, ext_data->text_values[i]) == 0 ) { + status_value = (float) i; + break; + } + i++; + } + + if ( i > max_text ) { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "failed to match textstatus value '%s'", + status); + goto failed; + } + break; + default: + i_unreached(); + break; + } + + /* Calculate value */ + if ( status_value < 0 ) + mctx->score_ratio = 0; + else if ( status_value > max_value ) + mctx->score_ratio = 1; + else + mctx->score_ratio = (status_value / max_value); + + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "extracted score=%.3f, max=%.3f, ratio=%.0f %%", + status_value, max_value, mctx->score_ratio * 100); + + *value_r = ext_spamvirustest_get_score(ext, mctx->score_ratio, percent); + return SIEVE_EXEC_OK; + +failed: + mctx->score_ratio = -1; + *value_r = "0"; + return SIEVE_EXEC_OK; +} + + diff --git a/pigeonhole/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest-common.h b/pigeonhole/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest-common.h new file mode 100644 index 0000000..331a637 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest-common.h @@ -0,0 +1,35 @@ +#ifndef EXT_SPAMVIRUSTEST_COMMON_H +#define EXT_SPAMVIRUSTEST_COMMON_H + +#include "sieve-common.h" + +/* + * Extensions + */ + +extern const struct sieve_extension_def spamtest_extension; +extern const struct sieve_extension_def spamtestplus_extension; +extern const struct sieve_extension_def virustest_extension; + +bool ext_spamvirustest_load(const struct sieve_extension *ext, void **context); +void ext_spamvirustest_unload(const struct sieve_extension *ext); + +/* + * Tests + */ + +extern const struct sieve_command_def spamtest_test; +extern const struct sieve_command_def virustest_test; + +int ext_spamvirustest_get_value +(const struct sieve_runtime_env *renv, const struct sieve_extension *ext, + bool percent, const char **value_r); + +/* + * Operations + */ + +extern const struct sieve_operation_def spamtest_operation; +extern const struct sieve_operation_def virustest_operation; + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest.c b/pigeonhole/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest.c new file mode 100644 index 0000000..e0c9b54 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest.c @@ -0,0 +1,146 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extensions spamtest, spamtestplus and virustest + * ----------------------------------------------- + * + * Authors: Stephan Bosch + * Specification: RFC 5235 + * Implementation: full + * Status: testing + * + */ + +/* Configuration examples: + * + * # 1: X-Spam-Score: No, score=-3.2 + * + * sieve_spamtest_status_header = \ + * X-Spam-Score: [[:alnum:]]+, score=(-?[[:digit:]]+\.[[:digit:]]) + * sieve_spamtest_max_value = 5.0 + * + * # 2: X-Spam-Status: Yes + * + * sieve_spamtest_status_header = X-Spam-Status + * sieve_spamtest_status_type = yesno + * sieve_spamtest_max_value = Yes + * + * # 3: X-Spam-Score: sssssss + * sieve_spamtest_status_header = X-Spam-Score + * sieve_spamtest_status_type = strlen + * sieve_spamtest_max_value = 5 + * + * # 4: X-Spam-Score: status=3.2 required=5.0 + * + * sieve_spamtest_status_header = \ + * X-Spam-Score: score=(-?[[:digit:]]+\.[[:digit:]]).* + * sieve_spamtest_max_header = \ + * X-Spam-Score: score=-?[[:digit:]]+\.[[:digit:]] required=([[:digit:]]+\.[[:digit:]]) + * + * # 5: X-Virus-Scan: Found to be clean. + * + * sieve_virustest_status_header = \ + * X-Virus-Scan: Found to be (.+)\. + * sieve_virustest_status_type = text + * sieve_virustest_text_value1 = clean + * sieve_virustest_text_value5 = infected + */ + +#include "lib.h" +#include "array.h" + +#include "sieve-common.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" + +#include "sieve-validator.h" + +#include "ext-spamvirustest-common.h" + +/* + * Extensions + */ + +/* Spamtest */ + +static bool ext_spamvirustest_validator_load +(const struct sieve_extension *ext, struct sieve_validator *validator); + +const struct sieve_extension_def spamtest_extension = { + .name = "spamtest", + .load = ext_spamvirustest_load, + .unload = ext_spamvirustest_unload, + .validator_load = ext_spamvirustest_validator_load, + SIEVE_EXT_DEFINE_OPERATION(spamtest_operation) +}; + +const struct sieve_extension_def spamtestplus_extension = { + .name = "spamtestplus", + .load = ext_spamvirustest_load, + .unload = ext_spamvirustest_unload, + .validator_load = ext_spamvirustest_validator_load, + SIEVE_EXT_DEFINE_OPERATION(spamtest_operation) +}; + +const struct sieve_extension_def virustest_extension = { + .name = "virustest", + .load = ext_spamvirustest_load, + .unload = ext_spamvirustest_unload, + .validator_load = ext_spamvirustest_validator_load, + SIEVE_EXT_DEFINE_OPERATION(virustest_operation) +}; + +/* + * Implementation + */ + +static bool ext_spamtest_validator_check_conflict + (const struct sieve_extension *ext, + struct sieve_validator *valdtr, void *context, + struct sieve_ast_argument *require_arg, + const struct sieve_extension *ext_other, + bool required); + +const struct sieve_validator_extension spamtest_validator_extension = { + .ext = &spamtest_extension, + .check_conflict = ext_spamtest_validator_check_conflict +}; + +static bool ext_spamvirustest_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + /* Register new test */ + + if ( sieve_extension_is(ext, virustest_extension) ) { + sieve_validator_register_command(valdtr, ext, &virustest_test); + } else { + if ( sieve_extension_is(ext, spamtest_extension) ) { + /* Register validator extension to warn for duplicate */ + sieve_validator_extension_register + (valdtr, ext, &spamtest_validator_extension, NULL); + } + + sieve_validator_register_command(valdtr, ext, &spamtest_test); + } + + return TRUE; +} + +static bool ext_spamtest_validator_check_conflict +(const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_validator *valdtr, void *context ATTR_UNUSED, + struct sieve_ast_argument *require_arg, + const struct sieve_extension *ext_other, + bool required ATTR_UNUSED) +{ + if ( sieve_extension_name_is(ext_other, "spamtestplus") ) { + sieve_argument_validate_warning(valdtr, require_arg, + "the spamtest and spamtestplus extensions should " + "not be specified at the same time"); + } + + return TRUE; +} + + diff --git a/pigeonhole/src/lib-sieve/plugins/spamvirustest/tst-spamvirustest.c b/pigeonhole/src/lib-sieve/plugins/spamvirustest/tst-spamvirustest.c new file mode 100644 index 0000000..26d051e --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/spamvirustest/tst-spamvirustest.c @@ -0,0 +1,304 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" + +#include "sieve-common.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-address-parts.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-match.h" + +#include "ext-spamvirustest-common.h" + +/* + * Tests + */ + +static bool tst_spamvirustest_validate + (struct sieve_validator *valdtr, struct sieve_command *tst); +static bool tst_spamvirustest_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx); +static bool tst_spamvirustest_registered + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); + +/* Spamtest test + * + * Syntax: + * spamtest [":percent"] [COMPARATOR] [MATCH-TYPE] <value: string> + */ + +const struct sieve_command_def spamtest_test = { + .identifier = "spamtest", + .type = SCT_TEST, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = tst_spamvirustest_registered, + .validate = tst_spamvirustest_validate, + .generate = tst_spamvirustest_generate +}; + +/* Virustest test + * + * Syntax: + * virustest [COMPARATOR] [MATCH-TYPE] <value: string> + */ + +const struct sieve_command_def virustest_test = { + .identifier = "virustest", + .type = SCT_TEST, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = tst_spamvirustest_registered, + .validate = tst_spamvirustest_validate, + .generate = tst_spamvirustest_generate +}; + +/* + * Tagged arguments + */ + +static bool tst_spamtest_validate_percent_tag + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *tst); + +static const struct sieve_argument_def spamtest_percent_tag = { + .identifier = "percent", + .validate = tst_spamtest_validate_percent_tag +}; + +/* + * Spamtest and virustest operations + */ + +static bool tst_spamvirustest_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int tst_spamvirustest_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def spamtest_operation = { + .mnemonic = "SPAMTEST", + .ext_def = &spamtest_extension, + .dump = tst_spamvirustest_operation_dump, + .execute = tst_spamvirustest_operation_execute +}; + +const struct sieve_operation_def virustest_operation = { + .mnemonic = "VIRUSTEST", + .ext_def = &virustest_extension, + .dump = tst_spamvirustest_operation_dump, + .execute = tst_spamvirustest_operation_execute +}; + +/* + * Optional operands + */ + +enum tst_spamvirustest_optional { + OPT_SPAMTEST_PERCENT = SIEVE_MATCH_OPT_LAST, + OPT_SPAMTEST_LAST +}; + +/* + * Test registration + */ + +static bool tst_spamvirustest_registered +(struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR); + sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE); + + if ( sieve_extension_is(ext, spamtestplus_extension) || + sieve_extension_is(ext, spamtest_extension) ) { + sieve_validator_register_tag + (valdtr, cmd_reg, ext, &spamtest_percent_tag, OPT_SPAMTEST_PERCENT); + } + + return TRUE; +} + +/* + * Validation + */ + +static bool tst_spamtest_validate_percent_tag +(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *tst) +{ + if ( !sieve_extension_is(tst->ext, spamtestplus_extension) ) { + sieve_argument_validate_error(valdtr, *arg, + "the spamtest test only accepts the :percent argument when " + "the spamtestplus extension is active"); + return FALSE; + } + + /* Skip tag */ + *arg = sieve_ast_argument_next(*arg); + + return TRUE; +} + +static bool tst_spamvirustest_validate +(struct sieve_validator *valdtr, struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + const struct sieve_match_type mcht_default = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + const struct sieve_comparator cmp_default = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + + /* Check value */ + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "value", 1, SAAT_STRING) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) + return FALSE; + + /* Validate the key argument to a specified match type */ + return sieve_match_type_validate + (valdtr, tst, arg, &mcht_default, &cmp_default); +} + +/* + * Code generation + */ + +static bool tst_spamvirustest_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *tst) +{ + if ( sieve_command_is(tst, spamtest_test) ) + sieve_operation_emit(cgenv->sblock, tst->ext, &spamtest_operation); + else if ( sieve_command_is(tst, virustest_test) ) + sieve_operation_emit(cgenv->sblock, tst->ext, &virustest_operation); + else + i_unreached(); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, tst, NULL); +} + +/* + * Code dump + */ + +static bool tst_spamvirustest_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + int opt_code = 0; + const struct sieve_operation *op = denv->oprtn; + + sieve_code_dumpf(denv, "%s", sieve_operation_mnemonic(op)); + sieve_code_descend(denv); + + /* Optional operands */ + for (;;) { + int opt; + + if ( (opt=sieve_match_opr_optional_dump(denv, address, &opt_code)) < 0 ) + return FALSE; + + if ( opt == 0 ) break; + + switch ( opt_code ) { + case OPT_SPAMTEST_PERCENT: + sieve_code_dumpf(denv, "percent"); + break; + default: + return FALSE; + } + } + + return + sieve_opr_string_dump(denv, address, "value"); +} + +/* + * Code execution + */ + +static int tst_spamvirustest_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + const struct sieve_operation *op = renv->oprtn; + const struct sieve_extension *this_ext = op->ext; + int opt_code = 0; + struct sieve_match_type mcht = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + struct sieve_comparator cmp = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + bool percent = FALSE; + struct sieve_stringlist *value_list, *key_list; + const char *score_value; + int match, ret; + + /* Read optional operands */ + for (;;) { + int opt; + + if ( (opt=sieve_match_opr_optional_read + (renv, address, &opt_code, &ret, &cmp, &mcht)) < 0 ) + return ret; + + if ( opt == 0 ) break; + + switch ( opt_code ) { + case OPT_SPAMTEST_PERCENT: + percent = TRUE; + break; + default: + sieve_runtime_trace_error(renv, "unknown optional operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + } + + /* Read value part */ + if ( (ret=sieve_opr_stringlist_read(renv, address, "value", &key_list)) <= 0 ) + return ret; + + /* Perform test */ + + if ( sieve_operation_is(op, spamtest_operation) ) { + sieve_runtime_trace + (renv, SIEVE_TRLVL_TESTS, "spamtest test [percent=%s]", + ( percent ? "true" : "false" )); + } else { + sieve_runtime_trace + (renv, SIEVE_TRLVL_TESTS, "virustest test"); + } + + /* Get score value */ + sieve_runtime_trace_descend(renv); + if ( (ret=ext_spamvirustest_get_value + (renv, this_ext, percent, &score_value)) <= 0 ) + return ret; + sieve_runtime_trace_ascend(renv); + + /* Construct value list */ + value_list = sieve_single_stringlist_create_cstr(renv, score_value, TRUE); + + /* Perform match */ + if ( (match=sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret)) < 0 ) + return ret; + + /* Set test result for subsequent conditional jump */ + sieve_interpreter_set_test_result(renv->interp, match > 0); + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/plugins/special-use/Makefile.am b/pigeonhole/src/lib-sieve/plugins/special-use/Makefile.am new file mode 100644 index 0000000..0f31aee --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/special-use/Makefile.am @@ -0,0 +1,22 @@ +noinst_LTLIBRARIES = libsieve_ext_special_use.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +tags = \ + tag-specialuse.c + +tests = \ + tst-specialuse-exists.c + +libsieve_ext_special_use_la_SOURCES = \ + $(tags) \ + $(tests) \ + ext-special-use-common.c \ + ext-special-use.c + +headers = \ + ext-special-use-common.h + +noinst_HEADERS = $(headers) diff --git a/pigeonhole/src/lib-sieve/plugins/special-use/Makefile.in b/pigeonhole/src/lib-sieve/plugins/special-use/Makefile.in new file mode 100644 index 0000000..5032dae --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/special-use/Makefile.in @@ -0,0 +1,703 @@ +# 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/plugins/special-use +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_ext_special_use_la_LIBADD = +am__objects_1 = tag-specialuse.lo +am__objects_2 = tst-specialuse-exists.lo +am_libsieve_ext_special_use_la_OBJECTS = $(am__objects_1) \ + $(am__objects_2) ext-special-use-common.lo ext-special-use.lo +libsieve_ext_special_use_la_OBJECTS = \ + $(am_libsieve_ext_special_use_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)/ext-special-use-common.Plo \ + ./$(DEPDIR)/ext-special-use.Plo ./$(DEPDIR)/tag-specialuse.Plo \ + ./$(DEPDIR)/tst-specialuse-exists.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_ext_special_use_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_special_use_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_ext_special_use.la +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +tags = \ + tag-specialuse.c + +tests = \ + tst-specialuse-exists.c + +libsieve_ext_special_use_la_SOURCES = \ + $(tags) \ + $(tests) \ + ext-special-use-common.c \ + ext-special-use.c + +headers = \ + ext-special-use-common.h + +noinst_HEADERS = $(headers) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/special-use/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/special-use/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_ext_special_use.la: $(libsieve_ext_special_use_la_OBJECTS) $(libsieve_ext_special_use_la_DEPENDENCIES) $(EXTRA_libsieve_ext_special_use_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_special_use_la_OBJECTS) $(libsieve_ext_special_use_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-special-use-common.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-special-use.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tag-specialuse.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-specialuse-exists.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)/ext-special-use-common.Plo + -rm -f ./$(DEPDIR)/ext-special-use.Plo + -rm -f ./$(DEPDIR)/tag-specialuse.Plo + -rm -f ./$(DEPDIR)/tst-specialuse-exists.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)/ext-special-use-common.Plo + -rm -f ./$(DEPDIR)/ext-special-use.Plo + -rm -f ./$(DEPDIR)/tag-specialuse.Plo + -rm -f ./$(DEPDIR)/tst-specialuse-exists.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/plugins/special-use/ext-special-use-common.c b/pigeonhole/src/lib-sieve/plugins/special-use/ext-special-use-common.c new file mode 100644 index 0000000..fcaf1b5 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/special-use/ext-special-use-common.c @@ -0,0 +1,31 @@ +/* Copyright (c) 2019 Pigeonhole authors, see the included COPYING file */ + +#include "lib.h" +#include "imap-arg.h" + +#include "ext-special-use-common.h" + +bool ext_special_use_flag_valid(const char *flag) +{ + const char *p = flag; + + /* RFC 6154, Section 6: + + use-attr = "\All" / "\Archive" / "\Drafts" / "\Flagged" / + "\Junk" / "\Sent" / "\Trash" / use-attr-ext + use-attr-ext = "\" atom + */ + + /* "\" */ + if (*p != '\\') + return FALSE; + p++; + + /* atom */ + for (; *p != '\0'; p++) { + if (!IS_ATOM_CHAR(*p)) + return FALSE; + } + + return TRUE; +} diff --git a/pigeonhole/src/lib-sieve/plugins/special-use/ext-special-use-common.h b/pigeonhole/src/lib-sieve/plugins/special-use/ext-special-use-common.h new file mode 100644 index 0000000..61f23d0 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/special-use/ext-special-use-common.h @@ -0,0 +1,43 @@ +#ifndef EXT_SPECIAL_USE_COMMON_H +#define EXT_SPECIAL_USE_COMMON_H + +#include "sieve-common.h" + +/* + * Tagged arguments + */ + +extern const struct sieve_argument_def specialuse_tag; + +/* + * Commands + */ + +extern const struct sieve_command_def specialuse_exists_test; + +/* + * Operands + */ + +extern const struct sieve_operand_def specialuse_operand; + +/* + * Operations + */ + +extern const struct sieve_operation_def specialuse_exists_operation; + +/* + * Extension + */ + +extern const struct sieve_extension_def special_use_extension; + +/* + * Flag checking + */ + +bool ext_special_use_flag_valid(const char *flag); + +#endif /* EXT_SPECIAL_USE_COMMON_H */ + diff --git a/pigeonhole/src/lib-sieve/plugins/special-use/ext-special-use.c b/pigeonhole/src/lib-sieve/plugins/special-use/ext-special-use.c new file mode 100644 index 0000000..acbb13e --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/special-use/ext-special-use.c @@ -0,0 +1,57 @@ +/* Copyright (c) 2019 Pigeonhole authors, see the included COPYING file */ + +/* Extension special-use + * --------------------- + * + * Authors: Stephan Bosch + * Specification: RFC 8579 + * Implementation: full + * Status: testing + * + */ + +#include "sieve-common.h" + +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-actions.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-result.h" + +#include "ext-special-use-common.h" + +/* + * Extension + */ + +static bool +ext_special_use_validator_load(const struct sieve_extension *ext, + struct sieve_validator *valdtr); + +const struct sieve_extension_def special_use_extension = { + .name = "special-use", + .validator_load = ext_special_use_validator_load, + SIEVE_EXT_DEFINE_OPERATION(specialuse_exists_operation), + SIEVE_EXT_DEFINE_OPERAND(specialuse_operand) +}; + +static bool +ext_special_use_validator_load(const struct sieve_extension *ext, + struct sieve_validator *valdtr) +{ + /* Register :specialuse tag with fileinto command and we don't care + whether this command is registered or even whether it will be + registered at all. The validator handles either situation gracefully. + */ + sieve_validator_register_external_tag( + valdtr, "fileinto", ext, &specialuse_tag, + SIEVE_OPT_SIDE_EFFECT); + + /* Register new test */ + sieve_validator_register_command(valdtr, ext, &specialuse_exists_test); + + return TRUE; +} diff --git a/pigeonhole/src/lib-sieve/plugins/special-use/tag-specialuse.c b/pigeonhole/src/lib-sieve/plugins/special-use/tag-specialuse.c new file mode 100644 index 0000000..0f6d32a --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/special-use/tag-specialuse.c @@ -0,0 +1,316 @@ +/* Copyright (c) 2019 Pigeonhole authors, see the included COPYING file */ + +#include "lib.h" +#include "str-sanitize.h" +#include "mail-storage.h" +#include "mail-namespace.h" + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-actions.h" +#include "sieve-code.h" +#include "sieve-actions.h" +#include "sieve-result.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" + +#include "ext-special-use-common.h" + +/* + * Flags tagged argument + */ + +static bool +tag_specialuse_validate(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd); +static bool +tag_specialuse_generate(const struct sieve_codegen_env *cgenv, + struct sieve_ast_argument *arg, + struct sieve_command *cmd); + +const struct sieve_argument_def specialuse_tag = { + .identifier = "specialuse", + .validate = tag_specialuse_validate, + .generate = tag_specialuse_generate +}; + +/* + * Side effect + */ + +static bool +seff_specialuse_dump_context(const struct sieve_side_effect *seffect, + const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +seff_specialuse_read_context(const struct sieve_side_effect *seffect, + const struct sieve_runtime_env *renv, + sieve_size_t *address, void **context); + +static int +seff_specialuse_merge(const struct sieve_runtime_env *renv, + const struct sieve_action *action, + const struct sieve_side_effect *old_seffect, + const struct sieve_side_effect *new_seffect, + void **old_context); + +static void +seff_specialuse_print(const struct sieve_side_effect *seffect, + const struct sieve_action *action, + const struct sieve_result_print_env *rpenv, bool *keep); + +static int +seff_specialuse_pre_execute(const struct sieve_side_effect *seffect, + const struct sieve_action_exec_env *aenv, + void *tr_context, void **se_tr_context ATTR_UNUSED); + +const struct sieve_side_effect_def specialuse_side_effect = { + SIEVE_OBJECT("specialuse", &specialuse_operand, 0), + .precedence = 200, + .to_action = &act_store, + .dump_context = seff_specialuse_dump_context, + .read_context = seff_specialuse_read_context, + .merge = seff_specialuse_merge, + .print = seff_specialuse_print, + .pre_execute = seff_specialuse_pre_execute +}; + +/* + * Operand + */ + +static const struct sieve_extension_objects ext_side_effects = + SIEVE_EXT_DEFINE_SIDE_EFFECT(specialuse_side_effect); + +const struct sieve_operand_def specialuse_operand = { + .name = "specialuse operand", + .ext_def = &special_use_extension, + .class = &sieve_side_effect_operand_class, + .interface = &ext_side_effects +}; + +/* + * Tag validation + */ + +static bool +tag_specialuse_validate(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + + /* Detach the tag itself */ + *arg = sieve_ast_argument_next(*arg); + + /* Check syntax: + * :specialuse <special-use-flag: string> + */ + if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0, + SAAT_STRING, FALSE)) + return FALSE; + if (sieve_argument_is_string_literal(*arg)) { + const char *use_flag = sieve_ast_argument_strc(*arg); + + if (!ext_special_use_flag_valid(use_flag)) { + sieve_argument_validate_error( + valdtr, *arg, "specialuse tag: " + "invalid special-use flag `%s' specified", + str_sanitize(use_flag, 64)); + return FALSE; + } + } + + tag->parameters = *arg; + + /* Detach parameter */ + *arg = sieve_ast_arguments_detach(*arg,1); + + return TRUE; +} + +/* + * Code generation + */ + +static bool +tag_specialuse_generate(const struct sieve_codegen_env *cgenv, + struct sieve_ast_argument *arg, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *param; + + if (sieve_ast_argument_type(arg) != SAAT_TAG) + return FALSE; + + sieve_opr_side_effect_emit(cgenv->sblock, arg->argument->ext, + &specialuse_side_effect); + + /* Explicit :specialuse tag */ + param = arg->parameters; + + /* Call the generation function for the argument */ + if (param->argument != NULL && param->argument->def != NULL && + param->argument->def->generate != NULL && + !param->argument->def->generate(cgenv, param, cmd)) + return FALSE; + + return TRUE; +} + +/* + * Side effect implementation + */ + +/* Context data */ + +struct seff_specialuse_context { + const char *special_use_flag; +}; + +/* Context coding */ + +static bool +seff_specialuse_dump_context( + const struct sieve_side_effect *seffect ATTR_UNUSED, + const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + return sieve_opr_stringlist_dump(denv, address, "specialuse"); +} + +static int +seff_specialuse_read_context( + const struct sieve_side_effect *seffect ATTR_UNUSED, + const struct sieve_runtime_env *renv, sieve_size_t *address, + void **se_context) +{ + pool_t pool = sieve_result_pool(renv->result); + struct seff_specialuse_context *ctx; + string_t *special_use_flag; + const char *use_flag; + int ret; + + if ((ret = sieve_opr_string_read(renv, address, "specialuse", + &special_use_flag)) <= 0) + return ret; + + use_flag = str_c(special_use_flag); + if (!ext_special_use_flag_valid(use_flag)) { + sieve_runtime_error( + renv, NULL, "specialuse tag: " + "invalid special-use flag `%s' specified", + str_sanitize(use_flag, 64)); + return SIEVE_EXEC_FAILURE; + } + + ctx = p_new(pool, struct seff_specialuse_context, 1); + ctx->special_use_flag = p_strdup(pool, use_flag); + + *se_context = (void *) ctx; + + return SIEVE_EXEC_OK; +} + +/* Result verification */ + +static int +seff_specialuse_merge(const struct sieve_runtime_env *renv ATTR_UNUSED, + const struct sieve_action *action ATTR_UNUSED, + const struct sieve_side_effect *old_seffect ATTR_UNUSED, + const struct sieve_side_effect *new_seffect, + void **old_context) +{ + if (new_seffect != NULL) + *old_context = new_seffect->context; + + return 1; +} + +/* Result printing */ + +static void +seff_specialuse_print(const struct sieve_side_effect *seffect, + const struct sieve_action *action ATTR_UNUSED, + const struct sieve_result_print_env *rpenv, + bool *keep ATTR_UNUSED) +{ + struct seff_specialuse_context *ctx = + (struct seff_specialuse_context *)seffect->context; + + sieve_result_seffect_printf( + rpenv, + "use mailbox with special-use flag `%s' instead if accessible", + ctx->special_use_flag); +} + +/* Result execution */ + +static int +seff_specialuse_pre_execute(const struct sieve_side_effect *seffect, + const struct sieve_action_exec_env *aenv, + void *tr_context, void **se_tr_context ATTR_UNUSED) +{ + struct seff_specialuse_context *ctx = + (struct seff_specialuse_context *)seffect->context; + const struct sieve_execute_env *eenv = aenv->exec_env; + struct act_store_transaction *trans = + (struct act_store_transaction *)tr_context; + struct mailbox *box; + + if (trans->box == NULL || trans->disabled) + return SIEVE_EXEC_OK; + + /* Check whether something already failed */ + switch (trans->error_code) { + case MAIL_ERROR_NONE: + break; + case MAIL_ERROR_TEMP: + return SIEVE_EXEC_TEMP_FAILURE; + default: + return SIEVE_EXEC_FAILURE; + } + + trans->error = NULL; + trans->error_code = MAIL_ERROR_NONE; + + box = mailbox_alloc_for_user(eenv->scriptenv->user, + ctx->special_use_flag, + (MAILBOX_FLAG_POST_SESSION | + MAILBOX_FLAG_SPECIAL_USE)); + + /* We still override the allocate default mailbox with ours below even + when the default and special-use mailbox are identical. Choosing + either one is (currently) equal and setting trans->mailbox_identifier + for SPECIAL-USE needs to be done either way, so we use the same code + path. */ + + /* Try to open the mailbox */ + eenv->exec_status->last_storage = mailbox_get_storage(box); + if (mailbox_open(box) == 0) { + pool_t pool = sieve_result_pool(aenv->result); + + /* Success */ + mailbox_free(&trans->box); + trans->box = box; + trans->mailbox_identifier = p_strdup_printf(pool, + "[SPECIAL-USE %s]", ctx->special_use_flag); + + } else { + /* Failure */ + if (mailbox_get_last_mail_error(box) == MAIL_ERROR_NOTFOUND) { + /* Not found; revert to default */ + mailbox_free(&box); + } else { + /* Total failure */ + mailbox_free(&trans->box); + trans->box = box; + sieve_act_store_get_storage_error(aenv, trans); + return (trans->error_code == MAIL_ERROR_TEMP ? + SIEVE_EXEC_TEMP_FAILURE : SIEVE_EXEC_FAILURE); + } + } + + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/plugins/special-use/tst-specialuse-exists.c b/pigeonhole/src/lib-sieve/plugins/special-use/tst-specialuse-exists.c new file mode 100644 index 0000000..a1aa878 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/special-use/tst-specialuse-exists.c @@ -0,0 +1,525 @@ +/* Copyright (c) 2019 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str-sanitize.h" +#include "mail-storage.h" +#include "mail-namespace.h" + +#include "sieve-common.h" +#include "sieve-actions.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "ext-special-use-common.h" + +/* + * specialuse_exists command + * + * Syntax: + * specialuse_exists [<mailbox: string>] + * <special-use-flags: string-list> + */ + +static bool +tst_specialuse_exists_validate(struct sieve_validator *valdtr, + struct sieve_command *tst); +static bool +tst_specialuse_exists_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +const struct sieve_command_def specialuse_exists_test = { + .identifier = "specialuse_exists", + .type = SCT_TEST, + .positional_args = -1, /* We check positional arguments ourselves */ + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = tst_specialuse_exists_validate, + .generate = tst_specialuse_exists_generate, +}; + +/* + * Mailboxexists operation + */ + +static bool +tst_specialuse_exists_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +tst_specialuse_exists_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def specialuse_exists_operation = { + .mnemonic = "SPECIALUSE_EXISTS", + .ext_def = &special_use_extension, + .dump = tst_specialuse_exists_operation_dump, + .execute = tst_specialuse_exists_operation_execute, +}; + +/* + * Test validation + */ + +struct _validate_context { + struct sieve_validator *valdtr; + struct sieve_command *tst; +}; + +static int +tst_specialuse_exists_flag_validate(void *context, + struct sieve_ast_argument *arg) +{ + struct _validate_context *valctx = (struct _validate_context *)context; + + if (sieve_argument_is_string_literal(arg)) { + const char *flag = sieve_ast_argument_strc(arg); + + if (!ext_special_use_flag_valid(flag)) { + sieve_argument_validate_error( + valctx->valdtr, arg, "%s test: " + "invalid special-use flag `%s' specified", + sieve_command_identifier(valctx->tst), + str_sanitize(flag, 64)); + } + } + + return 1; +} + +static bool +tst_specialuse_exists_validate(struct sieve_validator *valdtr, + struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + struct sieve_ast_argument *arg2; + struct sieve_ast_argument *aarg; + struct _validate_context valctx; + + if (arg == NULL) { + sieve_command_validate_error( + valdtr, tst, "the %s %s expects at least one argument, " + "but none was found", + sieve_command_identifier(tst), + sieve_command_type_name(tst)); + return FALSE; + } + + if (sieve_ast_argument_type(arg) != SAAT_STRING && + sieve_ast_argument_type(arg) != SAAT_STRING_LIST) { + sieve_argument_validate_error( + valdtr, arg, + "the %s %s expects either a string (mailbox) or " + "a string-list (special-use flags) as first argument, " + "but %s was found", + sieve_command_identifier(tst), + sieve_command_type_name(tst), + sieve_ast_argument_name(arg)); + return FALSE; + } + + arg2 = sieve_ast_argument_next(arg); + if (arg2 != NULL) { + /* First, check syntax sanity */ + if (sieve_ast_argument_type(arg) != SAAT_STRING) { + sieve_argument_validate_error( + valdtr, arg, + "if a second argument is specified for the %s %s, " + "the first must be a string (mailbox), " + "but %s was found", + sieve_command_identifier(tst), + sieve_command_type_name(tst), + sieve_ast_argument_name(arg)); + return FALSE; + } + if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE)) + return FALSE; + + /* Check name validity when mailbox argument is not a variable */ + if (sieve_argument_is_string_literal(arg)) { + const char *mailbox = sieve_ast_argument_strc(arg); + const char *error; + + if (!sieve_mailbox_check_name(mailbox, &error)) { + sieve_argument_validate_warning( + valdtr, arg, "%s test: " + "invalid mailbox name `%s' specified: %s", + sieve_command_identifier(tst), + str_sanitize(mailbox, 256), error); + } + } + + if (sieve_ast_argument_type(arg2) != SAAT_STRING && + sieve_ast_argument_type(arg2) != SAAT_STRING_LIST) { + sieve_argument_validate_error( + valdtr, arg2, + "the %s %s expects a string list (special-use flags) as " + "second argument when two arguments are specified, " + "but %s was found", + sieve_command_identifier(tst), + sieve_command_type_name(tst), + sieve_ast_argument_name(arg2)); + return FALSE; + } + } else + arg2 = arg; + + if (!sieve_validator_argument_activate(valdtr, tst, arg2, FALSE)) + return FALSE; + + aarg = arg2; + memset(&valctx, 0, sizeof(valctx)); + valctx.valdtr = valdtr; + valctx.tst = tst; + + return (sieve_ast_stringlist_map( + &aarg, (void*)&valctx, + tst_specialuse_exists_flag_validate) >= 0); +} + +/* + * Test generation + */ + +static bool +tst_specialuse_exists_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + struct sieve_ast_argument *arg2; + + sieve_operation_emit(cgenv->sblock, + tst->ext, &specialuse_exists_operation); + + /* Generate arguments */ + arg2 = sieve_ast_argument_next(arg); + if (arg2 != NULL) { + if (!sieve_generate_argument(cgenv, arg, tst)) + return FALSE; + } else { + sieve_opr_omitted_emit(cgenv->sblock); + arg2 = arg; + } + return sieve_generate_argument(cgenv, arg2, tst); +} + +/* + * Code dump + */ + +static bool +tst_specialuse_exists_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + struct sieve_operand oprnd; + + sieve_code_dumpf(denv, "SPECIALUSE_EXISTS"); + sieve_code_descend(denv); + + sieve_code_mark(denv); + if (!sieve_operand_read(denv->sblock, address, NULL, &oprnd)) { + sieve_code_dumpf(denv, "ERROR: INVALID OPERAND"); + return FALSE; + } + + if (!sieve_operand_is_omitted(&oprnd)) { + return (sieve_opr_string_dump_data(denv, &oprnd, + address, "mailbox") && + sieve_opr_stringlist_dump(denv, address, + "special-use-flags")); + } + + return sieve_opr_stringlist_dump(denv, address, "special-use-flags"); +} + +/* + * Code execution + */ + +static int +tst_specialuse_find_mailbox(const struct sieve_runtime_env *renv, + const char *mailbox, struct mailbox **box_r) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + struct mail_user *user = eenv->scriptenv->user; + struct mailbox *box; + bool trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING); + enum mail_error error_code; + const char *error; + + *box_r = NULL; + + if (user == NULL) + return 0; + + /* Open the box */ + box = mailbox_alloc_for_user(user, mailbox, MAILBOX_FLAG_POST_SESSION); + if (mailbox_open(box) < 0) { + error = mailbox_get_last_internal_error(box, &error_code); + + if (trace) { + sieve_runtime_trace( + renv, 0, "mailbox `%s' cannot be opened: %s", + str_sanitize(mailbox, 256), error); + } + + mailbox_free(&box); + + if (error_code == MAIL_ERROR_TEMP) { + sieve_runtime_error( + renv, NULL, "specialuse_exists test: " + "failed to open mailbox `%s': %s", + str_sanitize(mailbox, 256), error); + return -1; + } + return 0; + } + + /* Also fail when it is readonly */ + if (mailbox_is_readonly(box)) { + if (trace) { + sieve_runtime_trace( + renv, 0, "mailbox `%s' is read-only", + str_sanitize(mailbox, 256)); + } + + mailbox_free(&box); + return 0; + } + + *box_r = box; + return 1; +} + +static int +tst_specialuse_find_specialuse(const struct sieve_runtime_env *renv, + const char *special_use) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + struct mail_user *user = eenv->scriptenv->user; + struct mailbox *box; + bool trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING); + enum mail_error error_code; + const char *error; + + if (user == NULL) + return 0; + + /* Open the box */ + box = mailbox_alloc_for_user(user, special_use, + (MAILBOX_FLAG_POST_SESSION | + MAILBOX_FLAG_SPECIAL_USE)); + if (mailbox_open(box) < 0) { + error = mailbox_get_last_internal_error(box, &error_code); + + if (trace) { + sieve_runtime_trace( + renv, 0, "mailbox with special-use flag `%s' " + "cannot be opened: %s", + str_sanitize(special_use, 64), error); + } + + mailbox_free(&box); + + if (error_code == MAIL_ERROR_TEMP) { + sieve_runtime_error( + renv, NULL, "specialuse_exists test: " + "failed to open mailbox with special-use flag`%s': %s", + str_sanitize(special_use, 64), error); + return -1; + } + return 0; + } + + /* Also fail when it is readonly */ + if (mailbox_is_readonly(box)) { + if (trace) { + sieve_runtime_trace( + renv, 0, + "mailbox with special-use flag `%s' is read-only", + str_sanitize(special_use, 64)); + } + + mailbox_free(&box); + return 0; + } + + mailbox_free(&box); + return 1; +} + +static int +tst_specialuse_exists_check_flag(const struct sieve_runtime_env *renv, + struct mailbox *box, const char *use_flag, + bool trace, bool *all_exist_r) +{ + int ret; + + if (!ext_special_use_flag_valid(use_flag)) { + sieve_runtime_error( + renv, NULL, "specialuse_exists test: " + "invalid special-use flag `%s' specified", + str_sanitize(use_flag, 64)); + return SIEVE_EXEC_FAILURE; + } + + if (box != NULL) { + /* Mailbox has this SPECIAL-USE flag? */ + if (!mailbox_has_special_use(box, use_flag)) { + *all_exist_r = FALSE; + return SIEVE_EXEC_OK; + } + } else { + /* Is there mailbox with this SPECIAL-USE flag? */ + ret = tst_specialuse_find_specialuse(renv, use_flag); + if (ret < 0) + return SIEVE_EXEC_TEMP_FAILURE; + if (ret == 0) { + *all_exist_r = FALSE; + return SIEVE_EXEC_OK; + } + } + + if (trace) { + sieve_runtime_trace( + renv, 0, "special-use flag `%s' exists", + str_sanitize(use_flag, 80)); + } + + return SIEVE_EXEC_OK; +} + +static int +tst_specialuse_exists_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + struct sieve_operand oprnd; + struct sieve_stringlist *special_use_flags; + string_t *mailbox, *special_use_flag; + struct mailbox *box = NULL; + const char *error; + bool trace = FALSE, all_exist = TRUE; + int ret; + + /* + * Read operands + */ + + /* Read bare operand (two types possible) */ + ret = sieve_operand_runtime_read(renv, address, NULL, &oprnd); + if (ret <= 0) + return ret; + + /* Mailbox operand (optional) */ + mailbox = NULL; + if (!sieve_operand_is_omitted(&oprnd)) { + /* Read the mailbox operand */ + ret = sieve_opr_string_read_data(renv, &oprnd, address, + "mailbox", &mailbox); + if (ret <= 0) + return ret; + + /* Read flag list */ + ret = sieve_opr_stringlist_read(renv, address, + "special-use-flags", + &special_use_flags); + if (ret <= 0) + return ret; + + /* Flag-list operand */ + } else { + /* Read flag list */ + ret = sieve_opr_stringlist_read(renv, address, + "special-use-flags", + &special_use_flags); + if (ret <= 0) + return ret; + } + + /* + * Perform operation + */ + + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_TESTS)) { + sieve_runtime_trace(renv, 0, "specialuse_exists test"); + sieve_runtime_trace_descend(renv); + + trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING); + } + + if (mailbox != NULL) { + if (!sieve_mailbox_check_name(str_c(mailbox), &error)) { + sieve_runtime_warning( + renv, NULL, "specialuse_exists test: " + "invalid mailbox name `%s' specified: %s", + str_sanitize(str_c(mailbox), 256), error); + sieve_interpreter_set_test_result(renv->interp, FALSE); + return SIEVE_EXEC_OK; + } + + if (tst_specialuse_find_mailbox(renv, str_c(mailbox), &box) < 0) + return SIEVE_EXEC_TEMP_FAILURE; + } + + if (box == NULL && mailbox != NULL) { + sieve_runtime_trace( + renv, 0, "mailbox `%s' is not accessible", + str_sanitize(str_c(mailbox), 80)); + sieve_interpreter_set_test_result(renv->interp, FALSE); + return SIEVE_EXEC_OK; + } + + if (mailbox != NULL) { + sieve_runtime_trace( + renv, 0, "mailbox `%s' is accessible", + str_sanitize(str_c(mailbox), 80)); + } + + ret = 0; + special_use_flag = NULL; + while (all_exist && + (ret = sieve_stringlist_next_item( + special_use_flags, &special_use_flag)) > 0) { + const char *use_flag = str_c(special_use_flag); + + ret = tst_specialuse_exists_check_flag( + renv, box, use_flag, trace, &all_exist); + if (ret <= 0) { + if (box != NULL) { + /* Close mailbox */ + mailbox_free(&box); + } + return ret; + } + } + + if (box != NULL) { + /* Close mailbox */ + mailbox_free(&box); + } + + if (ret < 0) { + sieve_runtime_trace_error( + renv, "invalid special-use flag item"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if (trace) { + if (all_exist) { + sieve_runtime_trace( + renv, 0, "all special-use flags are set"); + } else { + sieve_runtime_trace( + renv, 0, "some special-use are not set"); + } + } + + sieve_interpreter_set_test_result(renv->interp, all_exist); + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/plugins/subaddress/Makefile.am b/pigeonhole/src/lib-sieve/plugins/subaddress/Makefile.am new file mode 100644 index 0000000..2bab53f --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/subaddress/Makefile.am @@ -0,0 +1,8 @@ +noinst_LTLIBRARIES = libsieve_ext_subaddress.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +libsieve_ext_subaddress_la_SOURCES = \ + ext-subaddress.c diff --git a/pigeonhole/src/lib-sieve/plugins/subaddress/Makefile.in b/pigeonhole/src/lib-sieve/plugins/subaddress/Makefile.in new file mode 100644 index 0000000..c5b6b7c --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/subaddress/Makefile.in @@ -0,0 +1,673 @@ +# 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/plugins/subaddress +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 $(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_ext_subaddress_la_LIBADD = +am_libsieve_ext_subaddress_la_OBJECTS = ext-subaddress.lo +libsieve_ext_subaddress_la_OBJECTS = \ + $(am_libsieve_ext_subaddress_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)/ext-subaddress.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_ext_subaddress_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_subaddress_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__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_ext_subaddress.la +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +libsieve_ext_subaddress_la_SOURCES = \ + ext-subaddress.c + +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/plugins/subaddress/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/subaddress/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_ext_subaddress.la: $(libsieve_ext_subaddress_la_OBJECTS) $(libsieve_ext_subaddress_la_DEPENDENCIES) $(EXTRA_libsieve_ext_subaddress_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_subaddress_la_OBJECTS) $(libsieve_ext_subaddress_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-subaddress.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) +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)/ext-subaddress.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)/ext-subaddress.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/plugins/subaddress/ext-subaddress.c b/pigeonhole/src/lib-sieve/plugins/subaddress/ext-subaddress.c new file mode 100644 index 0000000..abaa7ae --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/subaddress/ext-subaddress.c @@ -0,0 +1,191 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension subaddress + * -------------------- + * + * Author: Stephan Bosch + * Specification: RFC 3598 + * Implementation: full + * Status: testing + * + */ + +#include "sieve-common.h" + +#include "sieve-settings.h" +#include "sieve-code.h" +#include "sieve-address.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-address-parts.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" + +#include <string.h> + +/* + * Configuration + */ + +#define SUBADDRESS_DEFAULT_DELIM "+" + +struct ext_subaddress_config { + char *delimiter; +}; + +/* + * Forward declarations + */ + +const struct sieve_address_part_def user_address_part; +const struct sieve_address_part_def detail_address_part; + +static struct sieve_operand_def subaddress_operand; + +/* + * Extension + */ + +static bool ext_subaddress_load + (const struct sieve_extension *ext, void **context); +static void ext_subaddress_unload + (const struct sieve_extension *ext); +static bool ext_subaddress_validator_load + (const struct sieve_extension *ext, struct sieve_validator *validator); + +const struct sieve_extension_def subaddress_extension = { + .name = "subaddress", + .load = ext_subaddress_load, + .unload = ext_subaddress_unload, + .validator_load = ext_subaddress_validator_load, + SIEVE_EXT_DEFINE_OPERAND(subaddress_operand) +}; + +static bool ext_subaddress_load +(const struct sieve_extension *ext, void **context) +{ + struct ext_subaddress_config *config; + const char *delim; + + if ( *context != NULL ) { + ext_subaddress_unload(ext); + } + + delim = sieve_setting_get(ext->svinst, "recipient_delimiter"); + + if ( delim == NULL ) + delim = SUBADDRESS_DEFAULT_DELIM; + + config = i_new(struct ext_subaddress_config, 1); + config->delimiter = i_strdup(delim); + + *context = (void *) config; + + return TRUE; +} + +static void ext_subaddress_unload +(const struct sieve_extension *ext) +{ + struct ext_subaddress_config *config = + (struct ext_subaddress_config *) ext->context; + + i_free(config->delimiter); + i_free(config); +} + +static bool ext_subaddress_validator_load +(const struct sieve_extension *ext, struct sieve_validator *validator) +{ + sieve_address_part_register(validator, ext, &user_address_part); + sieve_address_part_register(validator, ext, &detail_address_part); + + return TRUE; +} + +/* + * Address parts + */ + +enum ext_subaddress_address_part { + SUBADDRESS_USER, + SUBADDRESS_DETAIL +}; + +/* Forward declarations */ + +static const char *subaddress_user_extract_from + (const struct sieve_address_part *addrp, const struct smtp_address *address); +static const char *subaddress_detail_extract_from + (const struct sieve_address_part *addrp, const struct smtp_address *address); + +/* Address part objects */ + +const struct sieve_address_part_def user_address_part = { + SIEVE_OBJECT("user", + &subaddress_operand, SUBADDRESS_USER), + subaddress_user_extract_from +}; + +const struct sieve_address_part_def detail_address_part = { + SIEVE_OBJECT("detail", + &subaddress_operand, SUBADDRESS_DETAIL), + .extract_from = subaddress_detail_extract_from +}; + +/* Address part implementation */ + +static const char *subaddress_user_extract_from +(const struct sieve_address_part *addrp, const struct smtp_address *address) +{ + struct ext_subaddress_config *config = + (struct ext_subaddress_config *) addrp->object.ext->context; + const char *delim; + size_t idx; + + idx = strcspn(address->localpart, config->delimiter); + delim = address->localpart[idx] != '\0' ? address->localpart + idx : NULL; + + if ( delim == NULL ) return address->localpart; + + return t_strdup_until(address->localpart, delim); +} + +static const char *subaddress_detail_extract_from +(const struct sieve_address_part *addrp, const struct smtp_address *address) +{ + struct ext_subaddress_config *config = + (struct ext_subaddress_config *) addrp->object.ext->context; + const char *delim; + size_t idx; + + idx = strcspn(address->localpart, config->delimiter); + delim = address->localpart[idx] != '\0' ? address->localpart + idx + 1: NULL; + + /* Just to be sure */ + if ( delim == NULL || + delim > (address->localpart + strlen(address->localpart)) ) + return NULL; + return delim; +} + +/* + * Operand + */ + +const struct sieve_address_part_def *ext_subaddress_parts[] = { + &user_address_part, &detail_address_part +}; + +static const struct sieve_extension_objects ext_address_parts = + SIEVE_EXT_DEFINE_ADDRESS_PARTS(ext_subaddress_parts); + +static struct sieve_operand_def subaddress_operand = { + .name = "address-part", + .ext_def = &subaddress_extension, + .class = &sieve_address_part_operand_class, + .interface = &ext_address_parts +}; + diff --git a/pigeonhole/src/lib-sieve/plugins/vacation/Makefile.am b/pigeonhole/src/lib-sieve/plugins/vacation/Makefile.am new file mode 100644 index 0000000..09df27b --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vacation/Makefile.am @@ -0,0 +1,19 @@ +noinst_LTLIBRARIES = libsieve_ext_vacation.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + -I$(srcdir)/../../util \ + $(LIBDOVECOT_INCLUDE) + +cmds = \ + cmd-vacation.c + +libsieve_ext_vacation_la_SOURCES = \ + $(cmds) \ + ext-vacation-common.c \ + ext-vacation.c \ + ext-vacation-seconds.c + +noinst_HEADERS = \ + ext-vacation-common.h + diff --git a/pigeonhole/src/lib-sieve/plugins/vacation/Makefile.in b/pigeonhole/src/lib-sieve/plugins/vacation/Makefile.in new file mode 100644 index 0000000..f18806a --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vacation/Makefile.in @@ -0,0 +1,700 @@ +# 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/plugins/vacation +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_ext_vacation_la_LIBADD = +am__objects_1 = cmd-vacation.lo +am_libsieve_ext_vacation_la_OBJECTS = $(am__objects_1) \ + ext-vacation-common.lo ext-vacation.lo ext-vacation-seconds.lo +libsieve_ext_vacation_la_OBJECTS = \ + $(am_libsieve_ext_vacation_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)/cmd-vacation.Plo \ + ./$(DEPDIR)/ext-vacation-common.Plo \ + ./$(DEPDIR)/ext-vacation-seconds.Plo \ + ./$(DEPDIR)/ext-vacation.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_ext_vacation_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_vacation_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_ext_vacation.la +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + -I$(srcdir)/../../util \ + $(LIBDOVECOT_INCLUDE) + +cmds = \ + cmd-vacation.c + +libsieve_ext_vacation_la_SOURCES = \ + $(cmds) \ + ext-vacation-common.c \ + ext-vacation.c \ + ext-vacation-seconds.c + +noinst_HEADERS = \ + ext-vacation-common.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/plugins/vacation/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/vacation/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_ext_vacation.la: $(libsieve_ext_vacation_la_OBJECTS) $(libsieve_ext_vacation_la_DEPENDENCIES) $(EXTRA_libsieve_ext_vacation_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_vacation_la_OBJECTS) $(libsieve_ext_vacation_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-vacation.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-vacation-common.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-vacation-seconds.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-vacation.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)/cmd-vacation.Plo + -rm -f ./$(DEPDIR)/ext-vacation-common.Plo + -rm -f ./$(DEPDIR)/ext-vacation-seconds.Plo + -rm -f ./$(DEPDIR)/ext-vacation.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)/cmd-vacation.Plo + -rm -f ./$(DEPDIR)/ext-vacation-common.Plo + -rm -f ./$(DEPDIR)/ext-vacation-seconds.Plo + -rm -f ./$(DEPDIR)/ext-vacation.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/plugins/vacation/cmd-vacation.c b/pigeonhole/src/lib-sieve/plugins/vacation/cmd-vacation.c new file mode 100644 index 0000000..ddc14be --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vacation/cmd-vacation.c @@ -0,0 +1,1578 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "strfuncs.h" +#include "md5.h" +#include "hostpid.h" +#include "str-sanitize.h" +#include "ostream.h" +#include "message-address.h" +#include "message-date.h" +#include "var-expand.h" +#include "ioloop.h" +#include "mail-storage.h" + +#include "rfc2822.h" + +#include "sieve-common.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-address.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-actions.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-result.h" +#include "sieve-message.h" +#include "sieve-smtp.h" + +#include "ext-vacation-common.h" + +#include <stdio.h> + +/* + * Forward declarations + */ + +static const struct sieve_argument_def vacation_days_tag; +static const struct sieve_argument_def vacation_subject_tag; +static const struct sieve_argument_def vacation_from_tag; +static const struct sieve_argument_def vacation_addresses_tag; +static const struct sieve_argument_def vacation_mime_tag; +static const struct sieve_argument_def vacation_handle_tag; + +/* + * Vacation command + * + * Syntax: + * vacation [":days" number] [":subject" string] + * [":from" string] [":addresses" string-list] + * [":mime"] [":handle" string] <reason: string> + */ + +static bool +cmd_vacation_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool +cmd_vacation_pre_validate(struct sieve_validator *valdtr, + struct sieve_command *cmd); +static bool +cmd_vacation_validate(struct sieve_validator *valdtr, + struct sieve_command *cmd); +static bool +cmd_vacation_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd); + +const struct sieve_command_def vacation_command = { + .identifier = "vacation", + .type = SCT_COMMAND, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = cmd_vacation_registered, + .pre_validate = cmd_vacation_pre_validate, + .validate = cmd_vacation_validate, + .generate = cmd_vacation_generate, +}; + +/* + * Vacation command tags + */ + +/* Forward declarations */ + +static bool +cmd_vacation_validate_number_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd); +static bool +cmd_vacation_validate_string_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd); +static bool +cmd_vacation_validate_stringlist_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd); +static bool +cmd_vacation_validate_mime_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd); + +/* Argument objects */ + +static const struct sieve_argument_def vacation_days_tag = { + .identifier = "days", + .validate = cmd_vacation_validate_number_tag +}; + +static const struct sieve_argument_def vacation_seconds_tag = { + .identifier = "seconds", + .validate = cmd_vacation_validate_number_tag +}; + +static const struct sieve_argument_def vacation_subject_tag = { + .identifier = "subject", + .validate = cmd_vacation_validate_string_tag +}; + +static const struct sieve_argument_def vacation_from_tag = { + .identifier = "from", + .validate = cmd_vacation_validate_string_tag +}; + +static const struct sieve_argument_def vacation_addresses_tag = { + .identifier = "addresses", + .validate = cmd_vacation_validate_stringlist_tag +}; + +static const struct sieve_argument_def vacation_mime_tag = { + .identifier = "mime", + .validate = cmd_vacation_validate_mime_tag +}; + +static const struct sieve_argument_def vacation_handle_tag = { + .identifier = "handle", + .validate = cmd_vacation_validate_string_tag +}; + +/* Codes for optional arguments */ + +enum cmd_vacation_optional { + OPT_END, + OPT_SECONDS, + OPT_SUBJECT, + OPT_FROM, + OPT_ADDRESSES, + OPT_MIME +}; + +/* + * Vacation operation + */ + +static bool +ext_vacation_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +ext_vacation_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def vacation_operation = { + .mnemonic = "VACATION", + .ext_def = &vacation_extension, + .dump = ext_vacation_operation_dump, + .execute = ext_vacation_operation_execute +}; + +/* + * Vacation action + */ + +/* Forward declarations */ + +static int +act_vacation_check_duplicate(const struct sieve_runtime_env *renv, + const struct sieve_action *act, + const struct sieve_action *act_other); +int act_vacation_check_conflict(const struct sieve_runtime_env *renv, + const struct sieve_action *act, + const struct sieve_action *act_other); +static void +act_vacation_print(const struct sieve_action *action, + const struct sieve_result_print_env *rpenv, bool *keep); +static int +act_vacation_commit(const struct sieve_action_exec_env *aenv, void *tr_context); + +/* Action object */ + +const struct sieve_action_def act_vacation = { + .name = "vacation", + .flags = SIEVE_ACTFLAG_SENDS_RESPONSE, + .check_duplicate = act_vacation_check_duplicate, + .check_conflict = act_vacation_check_conflict, + .print = act_vacation_print, + .commit = act_vacation_commit +}; + +/* Action context information */ + +struct act_vacation_context { + const char *reason; + + sieve_number_t seconds; + const char *subject; + const char *handle; + bool mime; + const char *from; + const struct smtp_address *from_address; + const struct smtp_address *const *addresses; +}; + +/* + * Command validation context + */ + +struct cmd_vacation_context_data { + string_t *from; + string_t *subject; + + bool mime; + + struct sieve_ast_argument *handle_arg; +}; + +/* + * Tag validation + */ + +static bool +cmd_vacation_validate_number_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + const struct sieve_extension *ext = sieve_argument_ext(*arg); + const struct ext_vacation_config *config = + (const struct ext_vacation_config *)ext->context; + struct sieve_ast_argument *tag = *arg; + sieve_number_t period, seconds; + + /* Detach the tag itself */ + *arg = sieve_ast_arguments_detach(*arg,1); + + /* Check syntax: + * :days number + */ + if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0, + SAAT_NUMBER, FALSE)) + return FALSE; + + period = sieve_ast_argument_number(*arg); + if (sieve_argument_is(tag, vacation_days_tag)) + seconds = period * (24*60*60); + else if (sieve_argument_is(tag, vacation_seconds_tag)) + seconds = period; + else + i_unreached(); + + /* Enforce :seconds >= min_period */ + if (seconds < config->min_period) { + seconds = config->min_period; + + sieve_argument_validate_warning( + valdtr, *arg, + "specified :%s value '%llu' is under the minimum", + sieve_argument_identifier(tag), + (unsigned long long)period); + /* Enforce :days <= max_period */ + } else if (config->max_period > 0 && seconds > config->max_period) { + seconds = config->max_period; + + sieve_argument_validate_warning( + valdtr, *arg, + "specified :%s value '%llu' is over the maximum", + sieve_argument_identifier(tag), + (unsigned long long)period); + } + + sieve_ast_argument_number_set(*arg, seconds); + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + + return TRUE; +} + +static bool +cmd_vacation_validate_string_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + struct cmd_vacation_context_data *ctx_data = + (struct cmd_vacation_context_data *)cmd->data; + + /* Detach the tag itself */ + *arg = sieve_ast_arguments_detach(*arg,1); + + /* Check syntax: + * :subject string + * :from string + * :handle string + */ + if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0, + SAAT_STRING, FALSE)) + return FALSE; + + if (sieve_argument_is(tag, vacation_from_tag)) { + if (sieve_argument_is_string_literal(*arg)) { + string_t *address = sieve_ast_argument_str(*arg); + const char *error; + bool result; + + T_BEGIN { + result = sieve_address_validate_str(address, + &error); + + if (!result) { + sieve_argument_validate_error( + valdtr, *arg, + "specified :from address '%s' is invalid for vacation action: %s", + str_sanitize(str_c(address), 128), + error); + } + } T_END; + + if (!result) + return FALSE; + } + + ctx_data->from = sieve_ast_argument_str(*arg); + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + } else if (sieve_argument_is(tag, vacation_subject_tag)) { + ctx_data->subject = sieve_ast_argument_str(*arg); + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + } else if (sieve_argument_is(tag, vacation_handle_tag)) { + ctx_data->handle_arg = *arg; + + /* Detach optional argument (emitted as mandatory) */ + *arg = sieve_ast_arguments_detach(*arg, 1); + } + return TRUE; +} + +static bool +cmd_vacation_validate_stringlist_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + + /* Detach the tag itself */ + *arg = sieve_ast_arguments_detach(*arg,1); + + /* Check syntax: + * :addresses string-list + */ + if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0, + SAAT_STRING_LIST, FALSE)) + return FALSE; + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + + return TRUE; +} + +static bool +cmd_vacation_validate_mime_tag(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct cmd_vacation_context_data *ctx_data = + (struct cmd_vacation_context_data *)cmd->data; + + ctx_data->mime = TRUE; + + /* Skip tag */ + *arg = sieve_ast_argument_next(*arg); + + return TRUE; +} + +/* + * Command registration + */ + +static bool +cmd_vacation_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &vacation_days_tag, OPT_SECONDS); + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &vacation_subject_tag, OPT_SUBJECT); + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &vacation_from_tag, OPT_FROM); + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &vacation_addresses_tag, OPT_ADDRESSES); + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &vacation_mime_tag, OPT_MIME); + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &vacation_handle_tag, 0); + return TRUE; +} + +bool ext_vacation_register_seconds_tag( + struct sieve_validator *valdtr, + const struct sieve_extension *vacation_ext) +{ + sieve_validator_register_external_tag( + valdtr, vacation_command.identifier, vacation_ext, + &vacation_seconds_tag, OPT_SECONDS); + + return TRUE; +} + +/* + * Command validation + */ + +static bool +cmd_vacation_pre_validate(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_command *cmd) +{ + struct cmd_vacation_context_data *ctx_data; + + /* Assign context */ + ctx_data = p_new(sieve_command_pool(cmd), + struct cmd_vacation_context_data, 1); + cmd->data = ctx_data; + + return TRUE; +} + +static const char _handle_empty_subject[] = "<default-subject>"; +static const char _handle_empty_from[] = "<default-from>"; +static const char _handle_mime_enabled[] = "<MIME>"; +static const char _handle_mime_disabled[] = "<NO-MIME>"; + +static bool +cmd_vacation_validate(struct sieve_validator *valdtr, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *arg = cmd->first_positional; + struct cmd_vacation_context_data *ctx_data = + (struct cmd_vacation_context_data *)cmd->data; + + if (!sieve_validate_positional_argument(valdtr, cmd, arg, "reason", 1, + SAAT_STRING)) + return FALSE; + + if (!sieve_validator_argument_activate(valdtr, cmd, arg, FALSE)) + return FALSE; + + /* Construct handle if not set explicitly */ + if (ctx_data->handle_arg == NULL) { + T_BEGIN { + string_t *handle; + string_t *reason = sieve_ast_argument_str(arg); + unsigned int size = str_len(reason); + + /* Precalculate the size of it all */ + size += (ctx_data->subject == NULL ? + sizeof(_handle_empty_subject) - 1 : + str_len(ctx_data->subject)); + size += (ctx_data->from == NULL ? + sizeof(_handle_empty_from) - 1 : + str_len(ctx_data->from)); + size += (ctx_data->mime ? + sizeof(_handle_mime_enabled) - 1 : + sizeof(_handle_mime_disabled) - 1); + + /* Construct the string */ + handle = t_str_new(size); + str_append_str(handle, reason); + + if (ctx_data->subject != NULL) + str_append_str(handle, ctx_data->subject); + else + str_append(handle, _handle_empty_subject); + + if (ctx_data->from != NULL) + str_append_str(handle, ctx_data->from); + else + str_append(handle, _handle_empty_from); + + str_append(handle, (ctx_data->mime ? + _handle_mime_enabled : + _handle_mime_disabled)); + + /* Create positional handle argument */ + ctx_data->handle_arg = + sieve_ast_argument_string_create( + cmd->ast_node, handle, + sieve_ast_node_line(cmd->ast_node)); + } T_END; + + if (!sieve_validator_argument_activate( + valdtr, cmd, ctx_data->handle_arg, TRUE)) + return FALSE; + } else { + /* Attach explicit handle argument as positional */ + (void)sieve_ast_argument_attach(cmd->ast_node, + ctx_data->handle_arg); + } + + return TRUE; +} + +/* + * Code generation + */ + +static bool +cmd_vacation_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd) +{ + sieve_operation_emit(cgenv->sblock, cmd->ext, &vacation_operation); + + /* Generate arguments */ + if (!sieve_generate_arguments(cgenv, cmd, NULL)) + return FALSE; + return TRUE; +} + +/* + * Code dump + */ + +static bool +ext_vacation_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + int opt_code = 0; + + sieve_code_dumpf(denv, "VACATION"); + sieve_code_descend(denv); + + /* Dump optional operands */ + + for (;;) { + int opt; + bool opok = TRUE; + + if ((opt = sieve_opr_optional_dump(denv, address, + &opt_code)) < 0) + return FALSE; + + if (opt == 0) + break; + + switch (opt_code) { + case OPT_SECONDS: + opok = sieve_opr_number_dump(denv, address, "seconds"); + break; + case OPT_SUBJECT: + opok = sieve_opr_string_dump(denv, address, "subject"); + break; + case OPT_FROM: + opok = sieve_opr_string_dump(denv, address, "from"); + break; + case OPT_ADDRESSES: + opok = sieve_opr_stringlist_dump(denv, address, + "addresses"); + break; + case OPT_MIME: + sieve_code_dumpf(denv, "mime"); + break; + default: + return FALSE; + } + + if (!opok) + return FALSE; + } + + /* Dump reason and handle operands */ + return (sieve_opr_string_dump(denv, address, "reason") && + sieve_opr_string_dump(denv, address, "handle")); +} + +/* + * Code execution + */ + +static int +ext_vacation_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + const struct sieve_extension *this_ext = renv->oprtn->ext; + const struct ext_vacation_config *config = + (const struct ext_vacation_config *)this_ext->context; + struct sieve_side_effects_list *slist = NULL; + struct act_vacation_context *act; + pool_t pool; + int opt_code = 0; + sieve_number_t seconds = config->default_period; + bool mime = FALSE; + struct sieve_stringlist *addresses = NULL; + string_t *reason, *subject = NULL, *from = NULL, *handle = NULL; + const struct smtp_address *from_address = NULL; + int ret; + + /* + * Read code + */ + + /* Optional operands */ + + for (;;) { + int opt; + + if ((opt = sieve_opr_optional_read(renv, address, + &opt_code)) < 0) + return SIEVE_EXEC_BIN_CORRUPT; + + if (opt == 0) + break; + + switch (opt_code) { + case OPT_SECONDS: + ret = sieve_opr_number_read(renv, address, "seconds", + &seconds); + break; + case OPT_SUBJECT: + ret = sieve_opr_string_read(renv, address, "subject", + &subject); + break; + case OPT_FROM: + ret = sieve_opr_string_read(renv, address, "from", + &from); + break; + case OPT_ADDRESSES: + ret = sieve_opr_stringlist_read(renv, address, + "addresses", + &addresses); + break; + case OPT_MIME: + mime = TRUE; + ret = SIEVE_EXEC_OK; + break; + default: + sieve_runtime_trace_error( + renv, "unknown optional operand"); + ret = SIEVE_EXEC_BIN_CORRUPT; + } + + if (ret <= 0) + return ret; + } + + /* Fixed operands */ + + if ((ret =sieve_opr_string_read(renv, address, + "reason", &reason)) <= 0 || + (ret = sieve_opr_string_read(renv, address, + "handle", &handle)) <= 0) + return ret; + + /* + * Perform operation + */ + + /* Trace */ + + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_ACTIONS)) { + sieve_runtime_trace(renv, 0, "vacation action"); + sieve_runtime_trace_descend(renv); + sieve_runtime_trace(renv, 0, "auto-reply with message `%s'", + str_sanitize(str_c(reason), 80)); + } + + /* Parse :from address */ + if (from != NULL) { + const char *error; + + from_address = sieve_address_parse_str(from, &error); + if (from_address == NULL) { + sieve_runtime_error( + renv, NULL, + "specified :from address '%s' is invalid for vacation action: %s", + str_sanitize(str_c(from), 128), error); + } + } + + /* Add vacation action to the result */ + + pool = sieve_result_pool(renv->result); + act = p_new(pool, struct act_vacation_context, 1); + act->reason = p_strdup(pool, str_c(reason)); + act->handle = p_strdup(pool, str_c(handle)); + act->seconds = seconds; + act->mime = mime; + if (subject != NULL) + act->subject = p_strdup(pool, str_c(subject)); + if (from != NULL) { + act->from = p_strdup(pool, str_c(from)); + act->from_address = smtp_address_clone(pool, from_address); + } + + /* Normalize all addresses */ + if (addresses != NULL) { + ARRAY_TYPE(smtp_address_const) addrs; + string_t *raw_address; + int ret; + + sieve_stringlist_reset(addresses); + + p_array_init(&addrs, pool, 4); + + raw_address = NULL; + while ((ret = sieve_stringlist_next_item(addresses, + &raw_address)) > 0) { + const struct smtp_address *addr; + const char *error; + + addr = sieve_address_parse_str(raw_address, &error); + if (addr != NULL) { + addr = smtp_address_clone(pool, addr); + array_append(&addrs, &addr, 1); + } else { + sieve_runtime_error( + renv, NULL, + "specified :addresses item '%s' is invalid: " + "%s for vacation action (ignored)", + str_sanitize(str_c(raw_address),128), + error); + } + } + + if (ret < 0) { + sieve_runtime_trace_error( + renv, "invalid addresses stringlist"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + (void)array_append_space(&addrs); + act->addresses = array_idx(&addrs, 0); + } + + if (sieve_result_add_action(renv, this_ext, "vacation", &act_vacation, + slist, (void *)act, 0, FALSE) < 0) + return SIEVE_EXEC_FAILURE; + + return SIEVE_EXEC_OK; +} + +/* + * Action + */ + +/* Runtime verification */ + +static int +act_vacation_check_duplicate(const struct sieve_runtime_env *renv ATTR_UNUSED, + const struct sieve_action *act, + const struct sieve_action *act_other) +{ + if (!sieve_action_is_executed(act_other, renv->result)) { + sieve_runtime_error( + renv, act->location, + "duplicate vacation action not allowed " + "(previously triggered one was here: %s)", + act_other->location); + return -1; + } + + /* Not an error if executed in preceeding script */ + return 1; +} + +int act_vacation_check_conflict(const struct sieve_runtime_env *renv, + const struct sieve_action *act, + const struct sieve_action *act_other) +{ + if ((act_other->def->flags & SIEVE_ACTFLAG_SENDS_RESPONSE) > 0) { + if (!sieve_action_is_executed(act_other, renv->result)) { + sieve_runtime_error( + renv, act->location, + "vacation action conflicts with other action: " + "the %s action (%s) also sends a response back to the sender", + act_other->def->name, act_other->location); + return -1; + } else { + /* Not an error if executed in preceeding script */ + return 1; + } + } + + return 0; +} + +/* Result printing */ + +static void act_vacation_print(const struct sieve_action *action ATTR_UNUSED, + const struct sieve_result_print_env *rpenv, + bool *keep ATTR_UNUSED) +{ + struct act_vacation_context *ctx = + (struct act_vacation_context *)action->context; + + sieve_result_action_printf(rpenv, "send vacation message:"); + sieve_result_printf(rpenv, " => seconds : %llu\n", + (unsigned long long)ctx->seconds); + if (ctx->subject != NULL) { + sieve_result_printf(rpenv, " => subject : %s\n", + ctx->subject); + } + if (ctx->from != NULL) { + sieve_result_printf(rpenv, " => from : %s\n", + ctx->from); + } + if (ctx->handle != NULL) { + sieve_result_printf(rpenv, " => handle : %s\n", + ctx->handle); + } + sieve_result_printf(rpenv, "\nSTART MESSAGE\n%s\nEND MESSAGE\n", + ctx->reason); +} + +/* Result execution */ + +/* Headers known to be associated with mailing lists + */ +static const char * const _list_headers[] = { + "list-id", + "list-owner", + "list-subscribe", + "list-post", + "list-unsubscribe", + "list-help", + "list-archive", + NULL +}; + +/* Headers that should be searched for the user's own mail address(es) + */ + +static const char * const _my_address_headers[] = { + "to", + "cc", + "bcc", + "resent-to", + "resent-cc", + "resent-bcc", + NULL +}; + +/* Headers that should be searched for the full sender address + */ + +static const char * const _sender_headers[] = { + "sender", + "resent-from", + "from", + NULL +}; + +static inline bool _is_system_address(const struct smtp_address *address) +{ + if (strcasecmp(address->localpart, "MAILER-DAEMON") == 0) + return TRUE; + if (strcasecmp(address->localpart, "LISTSERV") == 0) + return TRUE; + if (strcasecmp(address->localpart, "majordomo") == 0) + return TRUE; + if (strstr(address->localpart, "-request") != NULL) + return TRUE; + if (str_begins(address->localpart, "owner-")) + return TRUE; + return FALSE; +} + +static bool +_msg_address_equals(const struct message_address *addr1, + const struct smtp_address *addr2) +{ + struct smtp_address saddr; + + i_assert(addr1->mailbox != NULL); + return (smtp_address_init_from_msg(&saddr, addr1) >= 0 && + smtp_address_equals_icase(addr2, &saddr)); +} + +static inline bool +_header_contains_my_address(const char *header_val, + const struct smtp_address *my_address) +{ + const struct message_address *msg_addr; + + msg_addr = message_address_parse(pool_datastack_create(), + (const unsigned char *)header_val, + strlen(header_val), 256, 0); + while (msg_addr != NULL) { + if (msg_addr->domain != NULL) { + if (_msg_address_equals(msg_addr, my_address)) + return TRUE; + } + + msg_addr = msg_addr->next; + } + + return FALSE; +} + +static inline bool +_contains_my_address(const char * const *headers, + const struct smtp_address *my_address) +{ + const char *const *hdsp = headers; + + while (*hdsp != NULL) { + bool result; + + T_BEGIN { + result = _header_contains_my_address(*hdsp, my_address); + } T_END; + + if (result) + return TRUE; + + hdsp++; + } + + return FALSE; +} + +static bool _contains_8bit(const char *text) +{ + const unsigned char *p = (const unsigned char *)text; + + for (; *p != '\0'; p++) { + if ((*p & 0x80) != 0) + return TRUE; + } + return FALSE; +} + +static bool +_header_get_full_reply_recipient(const struct ext_vacation_config *config, + const struct smtp_address *smtp_to, + const char *header, + struct message_address *reply_to_r) +{ + const struct message_address *addr; + + addr = message_address_parse( + pool_datastack_create(), + (const unsigned char *)header, + strlen(header), 256, 0); + + for (; addr != NULL; addr = addr->next) { + bool matched = config->to_header_ignore_envelope; + + if (addr->domain == NULL || addr->invalid_syntax) + continue; + + if (!matched) + matched = _msg_address_equals(addr, smtp_to); + + if (matched) { + *reply_to_r = *addr; + return TRUE; + } + } + return FALSE; +} + +static int +_get_full_reply_recipient(const struct sieve_action_exec_env *aenv, + const struct ext_vacation_config *config, + const struct smtp_address *smtp_to, + struct message_address *reply_to_r) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + const struct sieve_message_data *msgdata = eenv->msgdata; + const char *const *hdsp; + int ret; + + hdsp = _sender_headers; + for (; *hdsp != NULL; hdsp++) { + const char *header; + + if ((ret = mail_get_first_header(msgdata->mail, *hdsp, + &header)) < 0) { + return sieve_result_mail_error( + aenv, msgdata->mail, + "failed to read header field `%s'", *hdsp); + } + if (ret == 0 || header == NULL) + continue; + + if (_header_get_full_reply_recipient(config, smtp_to, + header, reply_to_r)) + return SIEVE_EXEC_OK; + } + + reply_to_r->mailbox = smtp_to->localpart; + reply_to_r->domain = smtp_to->domain; + return SIEVE_EXEC_OK; +} + +static const struct var_expand_table * +_get_var_expand_table(const struct sieve_action_exec_env *aenv ATTR_UNUSED, + const char *subject) +{ + const struct var_expand_table stack_tab[] = { + { '$', subject, "subject" }, + { '\0', NULL, NULL } + }; + + return p_memdup(unsafe_data_stack_pool, stack_tab, sizeof(stack_tab)); +} + +static int +act_vacation_get_default_subject(const struct sieve_action_exec_env *aenv, + const struct ext_vacation_config *config, + const char **subject_r) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + const struct sieve_message_data *msgdata = eenv->msgdata; + const char *header, *error; + string_t *str; + const struct var_expand_table *tab; + int ret; + + *subject_r = (config->default_subject == NULL ? + "Automated reply" : config->default_subject); + if ((ret = mail_get_first_header_utf8(msgdata->mail, "subject", + &header)) < 0) { + return sieve_result_mail_error( + aenv, msgdata->mail, + "failed to read header field `subject'"); + } + if (ret == 0) + return SIEVE_EXEC_OK; + if (config->default_subject_template == NULL) { + *subject_r = t_strconcat("Auto: ", header, NULL); + return SIEVE_EXEC_OK; + } + + str = t_str_new(256); + tab = _get_var_expand_table(aenv, header); + if (var_expand(str, config->default_subject_template, + tab, &error) <= 0) { + i_error("Failed to expand deliver_log_format=%s: %s", + config->default_subject_template, error); + *subject_r = t_strconcat("Auto: ", header, NULL); + return SIEVE_EXEC_OK; + } + + *subject_r = str_c(str); + return SIEVE_EXEC_OK; +} + +static int +act_vacation_send(const struct sieve_action_exec_env *aenv, + const struct ext_vacation_config *config, + struct act_vacation_context *ctx, + const struct smtp_address *smtp_to, + const struct smtp_address *smtp_from, + const struct message_address *reply_from) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + const struct sieve_message_data *msgdata = eenv->msgdata; + const struct sieve_script_env *senv = eenv->scriptenv; + struct sieve_smtp_context *sctx; + struct ostream *output; + string_t *msg; + struct message_address reply_to; + const char *header, *outmsgid, *subject, *error; + int ret; + + /* Check smpt functions just to be sure */ + + if (!sieve_smtp_available(senv)) { + sieve_result_global_warning( + aenv, "vacation action has no means to send mail"); + return SIEVE_EXEC_OK; + } + + /* Make sure we have a subject for our reply */ + + if (ctx->subject == NULL || *(ctx->subject) == '\0') { + if ((ret = act_vacation_get_default_subject(aenv, config, + &subject)) <= 0) + return ret; + } else { + subject = ctx->subject; + } + + subject = str_sanitize_utf8(subject, config->max_subject_codepoints); + + /* Obtain full To address for reply */ + + i_zero(&reply_to); + reply_to.mailbox = smtp_to->localpart; + reply_to.domain = smtp_to->domain; + if ((ret = _get_full_reply_recipient(aenv, config, smtp_to, + &reply_to)) <= 0) + return ret; + + /* Open smtp session */ + + sctx = sieve_smtp_start_single(senv, smtp_to, smtp_from, &output); + + outmsgid = sieve_message_get_new_id(eenv->svinst); + + /* Produce a proper reply */ + + msg = t_str_new(512); + rfc2822_header_write(msg, "X-Sieve", SIEVE_IMPLEMENTATION); + rfc2822_header_write(msg, "Message-ID", outmsgid); + rfc2822_header_write(msg, "Date", message_date_create(ioloop_time)); + + if (ctx->from != NULL && *(ctx->from) != '\0') { + rfc2822_header_write_address(msg, "From", ctx->from); + } else { + if (reply_from == NULL || reply_from->mailbox == NULL || + *reply_from->mailbox == '\0') + reply_from = sieve_get_postmaster(senv); + rfc2822_header_write( + msg, "From", + message_address_first_to_string(reply_from)); + } + + rfc2822_header_write(msg, "To", + message_address_first_to_string(&reply_to)); + + if (_contains_8bit(subject)) + rfc2822_header_utf8_printf(msg, "Subject", "%s", subject); + else + rfc2822_header_printf(msg, "Subject", "%s", subject); + + /* Compose proper in-reply-to and references headers */ + + if ((ret = mail_get_first_header(msgdata->mail, "references", + &header)) < 0) { + sieve_smtp_abort(sctx); + return sieve_result_mail_error( + aenv, msgdata->mail, + "failed to read header field `references'"); + } + + if (msgdata->id != NULL) { + rfc2822_header_write(msg, "In-Reply-To", msgdata->id); + + if (ret > 0 && header != NULL) { + rfc2822_header_write( + msg, "References", + t_strconcat(header, " ", msgdata->id, NULL)); + } else { + rfc2822_header_write(msg, "References", msgdata->id); + } + } else if (ret > 0 && header != NULL) { + rfc2822_header_write(msg, "References", header); + } + + rfc2822_header_write(msg, "Auto-Submitted", "auto-replied (vacation)"); + rfc2822_header_write(msg, "Precedence", "bulk"); + + /* Prevent older Microsoft products from replying to this message */ + rfc2822_header_write(msg, "X-Auto-Response-Suppress", "All"); + + rfc2822_header_write(msg, "MIME-Version", "1.0"); + + if (!ctx->mime) { + rfc2822_header_write(msg, "Content-Type", + "text/plain; charset=utf-8"); + rfc2822_header_write(msg, "Content-Transfer-Encoding", "8bit"); + str_append(msg, "\r\n"); + } + + str_printfa(msg, "%s\r\n", ctx->reason); + o_stream_nsend(output, str_data(msg), str_len(msg)); + + /* Close smtp session */ + if ((ret = sieve_smtp_finish(sctx, &error)) <= 0) { + if (ret < 0) { + sieve_result_global_error( + aenv, "failed to send vacation response to %s: " + "<%s> (temporary error)", + smtp_address_encode(smtp_to), + str_sanitize(error, 512)); + } else { + sieve_result_global_log_error( + aenv, "failed to send vacation response to %s: " + "<%s> (permanent error)", + smtp_address_encode(smtp_to), + str_sanitize(error, 512)); + } + /* This error will be ignored in the end */ + return SIEVE_EXEC_FAILURE; + } + + eenv->exec_status->significant_action_executed = TRUE; + return SIEVE_EXEC_OK; +} + +static void +act_vacation_hash(struct act_vacation_context *vctx, const char *sender, + unsigned char hash_r[]) +{ + const char *rpath = t_str_lcase(sender); + struct md5_context ctx; + + md5_init(&ctx); + md5_update(&ctx, rpath, strlen(rpath)); + + md5_update(&ctx, vctx->handle, strlen(vctx->handle)); + + md5_final(&ctx, hash_r); +} + +static int +act_vacation_commit(const struct sieve_action_exec_env *aenv, + void *tr_context ATTR_UNUSED) +{ + const struct sieve_action *action = aenv->action; + const struct sieve_extension *ext = action->ext; + const struct sieve_execute_env *eenv = aenv->exec_env; + struct sieve_instance *svinst = eenv->svinst; + const struct ext_vacation_config *config = + (const struct ext_vacation_config *)ext->context; + struct act_vacation_context *ctx = + (struct act_vacation_context *)action->context; + unsigned char dupl_hash[MD5_RESULTLEN]; + struct mail *mail = sieve_message_get_mail(aenv->msgctx); + const struct smtp_address *sender, *recipient; + const struct smtp_address *orig_recipient, *user_email; + const struct smtp_address *smtp_from; + struct message_address reply_from; + const char *const *hdsp, *const *headers; + int ret; + + if ((eenv->flags & SIEVE_EXECUTE_FLAG_SKIP_RESPONSES) != 0) { + sieve_result_global_log( + aenv, "not sending vacation reply (skipped)"); + return SIEVE_EXEC_OK; + } + + sender = sieve_message_get_sender(aenv->msgctx); + recipient = sieve_message_get_final_recipient(aenv->msgctx); + + i_zero(&reply_from); + smtp_from = orig_recipient = user_email = NULL; + + /* Is the recipient unset? + */ + if (smtp_address_isnull(recipient)) { + sieve_result_global_warning( + aenv, "vacation action aborted: " + "envelope recipient is <>"); + return SIEVE_EXEC_OK; + } + + /* Is the return path unset ? + */ + if (smtp_address_isnull(sender)) { + sieve_result_global_log(aenv, "discarded vacation reply to <>"); + return SIEVE_EXEC_OK; + } + + /* Are we perhaps trying to respond to ourselves ? + */ + if (smtp_address_equals_icase(sender, recipient)) { + sieve_result_global_log( + aenv, "discarded vacation reply to own address <%s>", + smtp_address_encode(sender)); + return SIEVE_EXEC_OK; + } + + /* Are we perhaps trying to respond to one of our alternative :addresses? + */ + if (ctx->addresses != NULL) { + const struct smtp_address * const *alt_address; + + alt_address = ctx->addresses; + while (*alt_address != NULL) { + if (smtp_address_equals_icase(sender, *alt_address)) { + sieve_result_global_log( + aenv, + "discarded vacation reply to own address <%s> " + "(as specified using :addresses argument)", + smtp_address_encode(sender)); + return SIEVE_EXEC_OK; + } + alt_address++; + } + } + + /* Did whe respond to this user before? */ + if (sieve_action_duplicate_check_available(aenv)) { + bool duplicate; + + act_vacation_hash(ctx, smtp_address_encode(sender), dupl_hash); + + ret = sieve_action_duplicate_check(aenv, dupl_hash, + sizeof(dupl_hash), + &duplicate); + if (ret < SIEVE_EXEC_OK) { + sieve_result_critical( + aenv, "failed to check for duplicate vacation response", + "failed to check for duplicate vacation response%s", + (ret == SIEVE_EXEC_TEMP_FAILURE ? + " (temporaty failure)" : "")); + return ret; + } + if (duplicate) { + sieve_result_global_log( + aenv, + "discarded duplicate vacation response to <%s>", + smtp_address_encode(sender)); + return SIEVE_EXEC_OK; + } + } + + /* Are we trying to respond to a mailing list ? */ + hdsp = _list_headers; + while (*hdsp != NULL) { + if ((ret = mail_get_headers(mail, *hdsp, &headers)) < 0) { + return sieve_result_mail_error( + aenv, mail, + "failed to read header field `%s'", *hdsp); + } + + if (ret > 0 && headers[0] != NULL) { + /* Yes, bail out */ + sieve_result_global_log( + aenv, "discarding vacation response " + "to mailinglist recipient <%s>", + smtp_address_encode(sender)); + return SIEVE_EXEC_OK; + } + hdsp++; + } + + /* Is the message that we are replying to an automatic reply ? */ + if ((ret = mail_get_headers(mail, "auto-submitted", &headers)) < 0) { + return sieve_result_mail_error( + aenv, mail, + "failed to read header field `auto-submitted'"); + } + /* Theoretically multiple headers could exist, so lets make sure */ + if (ret > 0) { + hdsp = headers; + while (*hdsp != NULL) { + if (strcasecmp(*hdsp, "no") != 0) { + sieve_result_global_log( + aenv, "discarding vacation response " + "to auto-submitted message from <%s>", + smtp_address_encode(sender)); + return SIEVE_EXEC_OK; + } + hdsp++; + } + } + + /* Check for the (non-standard) precedence header */ + if ((ret = mail_get_headers(mail, "precedence", &headers)) < 0) { + return sieve_result_mail_error( + aenv, mail, "failed to read header field `precedence'"); + } + /* Theoretically multiple headers could exist, so lets make sure */ + if (ret > 0) { + hdsp = headers; + while (*hdsp != NULL) { + if (strcasecmp(*hdsp, "junk") == 0 || + strcasecmp(*hdsp, "bulk") == 0 || + strcasecmp(*hdsp, "list") == 0) { + sieve_result_global_log( + aenv, "discarding vacation response " + "to precedence=%s message from <%s>", + *hdsp, smtp_address_encode(sender)); + return SIEVE_EXEC_OK; + } + hdsp++; + } + } + + /* Check for the (non-standard) Microsoft X-Auto-Response-Suppress header */ + if ((ret = mail_get_headers(mail, "x-auto-response-suppress", + &headers)) < 0) { + return sieve_result_mail_error( + aenv, mail, + "failed to read header field `x-auto-response-suppress'"); + } + /* Theoretically multiple headers could exist, so lets make sure */ + if (ret > 0) { + hdsp = headers; + while (*hdsp != NULL) { + const char *const *flags = t_strsplit(*hdsp, ","); + + while (*flags != NULL) { + const char *flag = t_str_trim(*flags, " \t"); + + if (strcasecmp(flag, "All") == 0 || + strcasecmp(flag, "OOF") == 0) { + sieve_result_global_log( + aenv, "discarding vacation response to message from <%s> " + "(`%s' flag found in x-auto-response-suppress header)", + smtp_address_encode(sender), flag); + return SIEVE_EXEC_OK; + } + flags++; + } + hdsp++; + } + } + + /* Do not reply to system addresses */ + if (_is_system_address(sender)) { + sieve_result_global_log( + aenv, "not sending vacation response to system address <%s>", + smtp_address_encode(sender)); + return SIEVE_EXEC_OK; + } + + /* Fetch original recipient if necessary */ + if (config->use_original_recipient) + orig_recipient = sieve_message_get_orig_recipient(aenv->msgctx); + /* Fetch explicitly configured user email address */ + if (svinst->user_email != NULL) + user_email = svinst->user_email; + + /* Is the original message directly addressed to the user or the addresses + * specified using the :addresses tag? + */ + hdsp = _my_address_headers; + while (*hdsp != NULL) { + if ((ret = mail_get_headers(mail, *hdsp, &headers)) < 0) { + return sieve_result_mail_error( + aenv, mail, "failed to read header field `%s'", + *hdsp); + } + if (ret > 0 && headers[0] != NULL) { + /* Final recipient directly listed in headers? */ + if (_contains_my_address(headers, recipient)) { + smtp_from = recipient; + message_address_init_from_smtp( + &reply_from, NULL, recipient); + break; + } + + /* Original recipient directly listed in headers? */ + if (!smtp_address_isnull(orig_recipient) && + _contains_my_address(headers, orig_recipient)) { + smtp_from = orig_recipient; + message_address_init_from_smtp( + &reply_from, NULL, orig_recipient); + break; + } + + /* User-provided :addresses listed in headers? */ + if (ctx->addresses != NULL) { + bool found = FALSE; + const struct smtp_address * const *my_address; + + my_address = ctx->addresses; + while (!found && *my_address != NULL) { + if ((found = _contains_my_address(headers, *my_address))) { + /* Avoid letting user determine SMTP sender directly */ + smtp_from = (orig_recipient == NULL ? + recipient : orig_recipient); + message_address_init_from_smtp( + &reply_from, NULL, *my_address); + } + my_address++; + } + + if (found) break; + } + + /* Explicitly-configured user email address directly listed in + headers? */ + if (user_email != NULL && + _contains_my_address(headers, user_email)) { + smtp_from = user_email; + message_address_init_from_smtp( + &reply_from, NULL, smtp_from); + break; + } + } + hdsp++; + } + + /* My address not found in the headers; we got an implicit delivery */ + if (*hdsp == NULL) { + if (config->dont_check_recipient) { + /* Send reply from envelope recipient address */ + smtp_from = (orig_recipient == NULL ? + recipient : orig_recipient); + if (user_email == NULL) + user_email = sieve_get_user_email(svinst); + message_address_init_from_smtp(&reply_from, + NULL, user_email); + } else { + const char *orig_rcpt_str = "", *user_email_str = ""; + + /* Bail out */ + if (config->use_original_recipient) { + orig_rcpt_str = + t_strdup_printf("original-recipient=<%s>, ", + (orig_recipient == NULL ? "UNAVAILABLE" : + smtp_address_encode(orig_recipient))); + } + + if (user_email != NULL) { + user_email_str = t_strdup_printf( + "user-email=<%s>, ", + smtp_address_encode(user_email)); + } + + sieve_result_global_log( + aenv, "discarding vacation response for implicitly delivered message; " + "no known (envelope) recipient address found in message headers " + "(recipient=<%s>, %s%sand%s additional `:addresses' are specified)", + smtp_address_encode(recipient), + orig_rcpt_str, user_email_str, + (ctx->addresses == NULL || *ctx->addresses == NULL ? + " no" : "")); + return SIEVE_EXEC_OK; + } + } + + /* Send the message */ + + T_BEGIN { + ret = act_vacation_send( + aenv, config, ctx, sender, + (config->send_from_recipient ? smtp_from : NULL), + &reply_from); + } T_END; + + if (ret == SIEVE_EXEC_OK) { + sieve_number_t seconds; + + eenv->exec_status->significant_action_executed = TRUE; + + struct event_passthrough *e = + sieve_action_create_finish_event(aenv); + + sieve_result_event_log(aenv, e->event(), + "sent vacation response to <%s>", + smtp_address_encode(sender)); + + /* Check period limits once more */ + seconds = ctx->seconds; + if (seconds < config->min_period) + seconds = config->min_period; + else if (config->max_period > 0 && seconds > config->max_period) + seconds = config->max_period; + + /* Mark as replied */ + if (seconds > 0) { + sieve_action_duplicate_mark(aenv, dupl_hash, + sizeof(dupl_hash), + ioloop_time + seconds); + } + } + + if (ret == SIEVE_EXEC_TEMP_FAILURE) + return SIEVE_EXEC_TEMP_FAILURE; + + /* Ignore all other errors */ + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation-common.c b/pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation-common.c new file mode 100644 index 0000000..97be3a5 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation-common.c @@ -0,0 +1,114 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-settings.h" +#include "sieve-extensions.h" + +#include "ext-vacation-common.h" + +bool ext_vacation_load +(const struct sieve_extension *ext, void **context) +{ + struct sieve_instance *svinst = ext->svinst; + struct ext_vacation_config *config; + sieve_number_t min_period, max_period, default_period; + bool use_original_recipient, dont_check_recipient, send_from_recipient, + to_header_ignore_envelope; + unsigned long long max_subject_codepoints; + const char *default_subject, *default_subject_template; + + if ( *context != NULL ) { + ext_vacation_unload(ext); + } + + if ( !sieve_setting_get_duration_value + (svinst, "sieve_vacation_min_period", &min_period) ) { + min_period = EXT_VACATION_DEFAULT_MIN_PERIOD; + } + + if ( !sieve_setting_get_duration_value + (svinst, "sieve_vacation_max_period", &max_period) ) { + max_period = EXT_VACATION_DEFAULT_MAX_PERIOD; + } + + if ( !sieve_setting_get_duration_value + (svinst, "sieve_vacation_default_period", &default_period) ) { + default_period = EXT_VACATION_DEFAULT_PERIOD; + } + + if ( max_period > 0 + && (min_period > max_period || default_period < min_period + || default_period > max_period) ) { + min_period = EXT_VACATION_DEFAULT_MIN_PERIOD; + max_period = EXT_VACATION_DEFAULT_MAX_PERIOD; + default_period = EXT_VACATION_DEFAULT_PERIOD; + + e_warning(svinst->event, "vacation extension: " + "invalid settings: violated " + "sieve_vacation_min_period < " + "sieve_vacation_default_period < " + "sieve_vacation_max_period"); + } + + default_subject = sieve_setting_get( + svinst, "sieve_vacation_default_subject"); + default_subject_template = sieve_setting_get( + svinst, "sieve_vacation_default_subject_template"); + + if ( !sieve_setting_get_uint_value + (svinst, "sieve_vacation_max_subject_codepoints", &max_subject_codepoints) ) { + max_subject_codepoints = EXT_VACATION_DEFAULT_MAX_SUBJECT_CODEPOINTS; + } + + if ( !sieve_setting_get_bool_value + (svinst, "sieve_vacation_use_original_recipient", &use_original_recipient) ) { + use_original_recipient = FALSE; + } + + if ( !sieve_setting_get_bool_value + (svinst, "sieve_vacation_dont_check_recipient", &dont_check_recipient) ) { + dont_check_recipient = FALSE; + } + + if ( !sieve_setting_get_bool_value + (svinst, "sieve_vacation_send_from_recipient", &send_from_recipient) ) { + send_from_recipient = FALSE; + } + + if ( !sieve_setting_get_bool_value(svinst, + "sieve_vacation_to_header_ignore_envelope", + &to_header_ignore_envelope) ) { + to_header_ignore_envelope = FALSE; + } + + config = i_new(struct ext_vacation_config, 1); + config->min_period = min_period; + config->max_period = max_period; + config->default_period = default_period; + config->max_subject_codepoints = max_subject_codepoints; + config->default_subject = i_strdup_empty(default_subject); + config->default_subject_template = i_strdup_empty(default_subject_template); + config->use_original_recipient = use_original_recipient; + config->dont_check_recipient = dont_check_recipient; + config->send_from_recipient = send_from_recipient; + config->to_header_ignore_envelope = to_header_ignore_envelope; + + *context = (void *) config; + + return TRUE; +} + +void ext_vacation_unload +(const struct sieve_extension *ext) +{ + struct ext_vacation_config *config = + (struct ext_vacation_config *) ext->context; + + i_free(config->default_subject); + i_free(config->default_subject_template); + i_free(config); +} diff --git a/pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation-common.h b/pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation-common.h new file mode 100644 index 0000000..3a38cf6 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation-common.h @@ -0,0 +1,60 @@ +#ifndef EXT_VACATION_COMMON_H +#define EXT_VACATION_COMMON_H + +#include "sieve-common.h" + +/* + * Extension configuration + */ + +#define EXT_VACATION_DEFAULT_PERIOD (7*24*60*60) +#define EXT_VACATION_DEFAULT_MIN_PERIOD (24*60*60) +#define EXT_VACATION_DEFAULT_MAX_PERIOD 0 +#define EXT_VACATION_DEFAULT_MAX_SUBJECT_CODEPOINTS 256 + +struct ext_vacation_config { + unsigned int min_period; + unsigned int max_period; + unsigned int default_period; + unsigned long long max_subject_codepoints; + char *default_subject; + char *default_subject_template; + bool use_original_recipient; + bool dont_check_recipient; + bool send_from_recipient; + bool to_header_ignore_envelope; +}; + +/* + * Commands + */ + +extern const struct sieve_command_def vacation_command; + +/* + * Operations + */ + +extern const struct sieve_operation_def vacation_operation; + +/* + * Extensions + */ + +/* Vacation */ + +extern const struct sieve_extension_def vacation_extension; + +bool ext_vacation_load + (const struct sieve_extension *ext, void **context); +void ext_vacation_unload + (const struct sieve_extension *ext); + +/* Vacation-seconds */ + +extern const struct sieve_extension_def vacation_seconds_extension; + +bool ext_vacation_register_seconds_tag + (struct sieve_validator *valdtr, const struct sieve_extension *vacation_ext); + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation-seconds.c b/pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation-seconds.c new file mode 100644 index 0000000..41c0f06 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation-seconds.c @@ -0,0 +1,66 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension vacation-seconds + * -------------------------- + * + * Authors: Stephan Bosch <stephan@rename-it.nl> + * Specification: RFC 6131 + * Implementation: full + * Status: testing + * + */ + +#include "lib.h" + +#include "sieve-common.h" + +#include "sieve-extensions.h" +#include "sieve-validator.h" + +#include "ext-vacation-common.h" + +/* + * Extension + */ + +bool ext_vacation_seconds_load + (const struct sieve_extension *ext, void **context); +static bool ext_vacation_seconds_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); + +const struct sieve_extension_def vacation_seconds_extension = { + .name = "vacation-seconds", + .load = ext_vacation_seconds_load, + .validator_load = ext_vacation_seconds_validator_load, +}; + +bool ext_vacation_seconds_load +(const struct sieve_extension *ext, void **context) +{ + if ( *context == NULL ) { + /* Make sure vacation extension is registered */ + *context = (void *) + sieve_extension_require(ext->svinst, &vacation_extension, TRUE); + } + + return TRUE; +} + +static bool ext_vacation_seconds_validator_load +(const struct sieve_extension *ext ATTR_UNUSED, struct sieve_validator *valdtr) +{ + const struct sieve_extension *vacation_ext; + + /* Load vacation extension implicitly */ + + vacation_ext = sieve_validator_extension_load_implicit + (valdtr, vacation_extension.name); + + if ( vacation_ext == NULL ) + return FALSE; + + /* Add seconds tag to vacation command */ + + return ext_vacation_register_seconds_tag(valdtr, vacation_ext); +} diff --git a/pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation.c b/pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation.c new file mode 100644 index 0000000..8d3d9a7 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation.c @@ -0,0 +1,131 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension vacation + * ------------------ + * + * Authors: Stephan Bosch <stephan@rename-it.nl> + * Specification: RFC 5230 + * Implementation: full + * Status: testing + * + */ + +#include "lib.h" + +#include "sieve-common.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "ext-vacation-common.h" + +/* + * Extension + */ + +static bool +ext_vacation_validator_load(const struct sieve_extension *ext, + struct sieve_validator *valdtr); +static bool +ext_vacation_interpreter_load(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv, + sieve_size_t *address); + +static bool +ext_vacation_validator_validate(const struct sieve_extension *ext, + struct sieve_validator *valdtr, void *context, + struct sieve_ast_argument *require_arg, + bool required); +static int +ext_vacation_interpreter_run(const struct sieve_extension *this_ext, + const struct sieve_runtime_env *renv, + void *context, bool deferred); + +const struct sieve_extension_def vacation_extension = { + .name = "vacation", + .load = ext_vacation_load, + .unload = ext_vacation_unload, + .validator_load = ext_vacation_validator_load, + .interpreter_load = ext_vacation_interpreter_load, + SIEVE_EXT_DEFINE_OPERATION(vacation_operation) +}; +const struct sieve_validator_extension +vacation_validator_extension = { + .ext = &vacation_extension, + .validate = ext_vacation_validator_validate +}; +const struct sieve_interpreter_extension +vacation_interpreter_extension = { + .ext_def = &vacation_extension, + .run = ext_vacation_interpreter_run +}; + +static bool +ext_vacation_validator_load(const struct sieve_extension *ext, + struct sieve_validator *valdtr) +{ + /* Register new command */ + sieve_validator_register_command(valdtr, ext, &vacation_command); + + sieve_validator_extension_register(valdtr, ext, + &vacation_validator_extension, NULL); + return TRUE; +} + +static bool +ext_vacation_interpreter_load(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv, + sieve_size_t *address ATTR_UNUSED) +{ + sieve_interpreter_extension_register( + renv->interp, ext, &vacation_interpreter_extension, NULL); + return TRUE; +} + +static bool +ext_vacation_validator_validate(const struct sieve_extension *ext, + struct sieve_validator *valdtr, + void *context ATTR_UNUSED, + struct sieve_ast_argument *require_arg, + bool required) +{ + if (required) { + enum sieve_compile_flags flags = + sieve_validator_compile_flags(valdtr); + + if ((flags & SIEVE_COMPILE_FLAG_NO_ENVELOPE) != 0) { + sieve_argument_validate_error( + valdtr, require_arg, + "the %s extension cannot be used in this context " + "(needs access to message envelope)", + sieve_extension_name(ext)); + return FALSE; + } + } + return TRUE; +} + +static int +ext_vacation_interpreter_run(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv, + void *context ATTR_UNUSED, bool deferred) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + + if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) != 0) { + if (!deferred) { + sieve_runtime_error( + renv, NULL, + "the %s extension cannot be used in this context " + "(needs access to message envelope)", + sieve_extension_name(ext)); + } + return SIEVE_EXEC_FAILURE; + } + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/plugins/variables/Makefile.am b/pigeonhole/src/lib-sieve/plugins/variables/Makefile.am new file mode 100644 index 0000000..354bad2 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/variables/Makefile.am @@ -0,0 +1,41 @@ +noinst_LTLIBRARIES = libsieve_ext_variables.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +cmds = \ + cmd-set.c + +tsts = \ + tst-string.c + +libsieve_ext_variables_la_SOURCES = \ + ext-variables-common.c \ + ext-variables-name.c \ + ext-variables-namespaces.c \ + ext-variables-arguments.c \ + ext-variables-operands.c \ + ext-variables-modifiers.c \ + ext-variables-dump.c \ + $(cmds) \ + $(tsts) \ + ext-variables.c + +public_headers = \ + sieve-ext-variables.h + +headers = \ + ext-variables-common.h \ + ext-variables-limits.h \ + ext-variables-name.h \ + ext-variables-namespaces.h \ + ext-variables-arguments.h \ + ext-variables-operands.h \ + ext-variables-modifiers.h \ + ext-variables-dump.h + +pkginc_libdir=$(dovecot_pkgincludedir)/sieve +pkginc_lib_HEADERS = $(public_headers) +noinst_HEADERS = $(headers) + diff --git a/pigeonhole/src/lib-sieve/plugins/variables/Makefile.in b/pigeonhole/src/lib-sieve/plugins/variables/Makefile.in new file mode 100644 index 0000000..701d574 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/variables/Makefile.in @@ -0,0 +1,801 @@ +# 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/plugins/variables +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) \ + $(pkginc_lib_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_ext_variables_la_LIBADD = +am__objects_1 = cmd-set.lo +am__objects_2 = tst-string.lo +am_libsieve_ext_variables_la_OBJECTS = ext-variables-common.lo \ + ext-variables-name.lo ext-variables-namespaces.lo \ + ext-variables-arguments.lo ext-variables-operands.lo \ + ext-variables-modifiers.lo ext-variables-dump.lo \ + $(am__objects_1) $(am__objects_2) ext-variables.lo +libsieve_ext_variables_la_OBJECTS = \ + $(am_libsieve_ext_variables_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)/cmd-set.Plo \ + ./$(DEPDIR)/ext-variables-arguments.Plo \ + ./$(DEPDIR)/ext-variables-common.Plo \ + ./$(DEPDIR)/ext-variables-dump.Plo \ + ./$(DEPDIR)/ext-variables-modifiers.Plo \ + ./$(DEPDIR)/ext-variables-name.Plo \ + ./$(DEPDIR)/ext-variables-namespaces.Plo \ + ./$(DEPDIR)/ext-variables-operands.Plo \ + ./$(DEPDIR)/ext-variables.Plo ./$(DEPDIR)/tst-string.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_ext_variables_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_variables_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(pkginc_libdir)" +HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +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_ext_variables.la +AM_CPPFLAGS = \ + -I$(srcdir)/../.. \ + $(LIBDOVECOT_INCLUDE) + +cmds = \ + cmd-set.c + +tsts = \ + tst-string.c + +libsieve_ext_variables_la_SOURCES = \ + ext-variables-common.c \ + ext-variables-name.c \ + ext-variables-namespaces.c \ + ext-variables-arguments.c \ + ext-variables-operands.c \ + ext-variables-modifiers.c \ + ext-variables-dump.c \ + $(cmds) \ + $(tsts) \ + ext-variables.c + +public_headers = \ + sieve-ext-variables.h + +headers = \ + ext-variables-common.h \ + ext-variables-limits.h \ + ext-variables-name.h \ + ext-variables-namespaces.h \ + ext-variables-arguments.h \ + ext-variables-operands.h \ + ext-variables-modifiers.h \ + ext-variables-dump.h + +pkginc_libdir = $(dovecot_pkgincludedir)/sieve +pkginc_lib_HEADERS = $(public_headers) +noinst_HEADERS = $(headers) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/variables/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/variables/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_ext_variables.la: $(libsieve_ext_variables_la_OBJECTS) $(libsieve_ext_variables_la_DEPENDENCIES) $(EXTRA_libsieve_ext_variables_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_variables_la_OBJECTS) $(libsieve_ext_variables_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-set.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-variables-arguments.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-variables-common.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-variables-dump.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-variables-modifiers.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-variables-name.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-variables-namespaces.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-variables-operands.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-variables.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-string.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) + @$(NORMAL_INSTALL) + @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ + done + +uninstall-pkginc_libHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/cmd-set.Plo + -rm -f ./$(DEPDIR)/ext-variables-arguments.Plo + -rm -f ./$(DEPDIR)/ext-variables-common.Plo + -rm -f ./$(DEPDIR)/ext-variables-dump.Plo + -rm -f ./$(DEPDIR)/ext-variables-modifiers.Plo + -rm -f ./$(DEPDIR)/ext-variables-name.Plo + -rm -f ./$(DEPDIR)/ext-variables-namespaces.Plo + -rm -f ./$(DEPDIR)/ext-variables-operands.Plo + -rm -f ./$(DEPDIR)/ext-variables.Plo + -rm -f ./$(DEPDIR)/tst-string.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pkginc_libHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/cmd-set.Plo + -rm -f ./$(DEPDIR)/ext-variables-arguments.Plo + -rm -f ./$(DEPDIR)/ext-variables-common.Plo + -rm -f ./$(DEPDIR)/ext-variables-dump.Plo + -rm -f ./$(DEPDIR)/ext-variables-modifiers.Plo + -rm -f ./$(DEPDIR)/ext-variables-name.Plo + -rm -f ./$(DEPDIR)/ext-variables-namespaces.Plo + -rm -f ./$(DEPDIR)/ext-variables-operands.Plo + -rm -f ./$(DEPDIR)/ext-variables.Plo + -rm -f ./$(DEPDIR)/tst-string.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pkginc_libHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstLTLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-pkginc_libHEADERS install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-pkginc_libHEADERS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/pigeonhole/src/lib-sieve/plugins/variables/cmd-set.c b/pigeonhole/src/lib-sieve/plugins/variables/cmd-set.c new file mode 100644 index 0000000..d0fdc97 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/variables/cmd-set.c @@ -0,0 +1,235 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "array.h" + +#include "sieve-common.h" +#include "sieve-extensions.h" + +#include "sieve-code.h" +#include "sieve-ast.h" +#include "sieve-commands.h" +#include "sieve-binary.h" + +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "ext-variables-common.h" + +/* + * Set command + * + * Syntax: + * set [MODIFIER] <name: string> <value: string> + */ + +static bool cmd_set_registered + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool cmd_set_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); +static bool cmd_set_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx); + +const struct sieve_command_def cmd_set = { + .identifier = "set", + .type = SCT_COMMAND, + .positional_args = 2, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = cmd_set_registered, + .validate = cmd_set_validate, + .generate = cmd_set_generate, +}; + +/* + * Set operation + */ + +static bool cmd_set_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int cmd_set_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def cmd_set_operation = { + .mnemonic = "SET", + .ext_def = &variables_extension, + .code = EXT_VARIABLES_OPERATION_SET, + .dump = cmd_set_operation_dump, + .execute = cmd_set_operation_execute +}; + +/* + * Compiler context + */ + +struct cmd_set_context { + ARRAY_TYPE(sieve_variables_modifier) modifiers; +}; + +/* Command registration */ + +static bool cmd_set_registered +(struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + sieve_variables_modifiers_link_tag(valdtr, ext, cmd_reg); + + return TRUE; +} + +/* + * Command validation + */ + +static bool cmd_set_validate(struct sieve_validator *valdtr, + struct sieve_command *cmd) +{ + const struct sieve_extension *this_ext = cmd->ext; + struct sieve_ast_argument *arg = cmd->first_positional; + pool_t pool = sieve_command_pool(cmd); + struct cmd_set_context *sctx; + + /* Create command context */ + sctx = p_new(pool, struct cmd_set_context, 1); + p_array_init(&sctx->modifiers, pool, 4); + cmd->data = (void *) sctx; + + /* Validate modifiers */ + if ( !sieve_variables_modifiers_validate + (valdtr, cmd, &sctx->modifiers) ) + return FALSE; + + /* Validate name argument */ + if ( !sieve_validate_positional_argument + (valdtr, cmd, arg, "name", 1, SAAT_STRING) ) { + return FALSE; + } + if ( !sieve_variable_argument_activate + (this_ext, this_ext, valdtr, cmd, arg, TRUE) ) { + return FALSE; + } + arg = sieve_ast_argument_next(arg); + + /* Validate value argument */ + if ( !sieve_validate_positional_argument + (valdtr, cmd, arg, "value", 2, SAAT_STRING) ) { + return FALSE; + } + return sieve_validator_argument_activate + (valdtr, cmd, arg, FALSE); +} + +/* + * Code generation + */ + +static bool cmd_set_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + const struct sieve_extension *this_ext = cmd->ext; + struct sieve_binary_block *sblock = cgenv->sblock; + struct cmd_set_context *sctx = (struct cmd_set_context *) cmd->data; + + sieve_operation_emit(sblock, this_ext, &cmd_set_operation); + + /* Generate arguments */ + if ( !sieve_generate_arguments(cgenv, cmd, NULL) ) + return FALSE; + + /* Generate modifiers */ + if ( !sieve_variables_modifiers_generate + (cgenv, &sctx->modifiers) ) + return FALSE; + + return TRUE; +} + +/* + * Code dump + */ + +static bool cmd_set_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + sieve_code_dumpf(denv, "SET"); + sieve_code_descend(denv); + + /* Print both variable name and string value */ + if ( !sieve_opr_string_dump(denv, address, "variable") || + !sieve_opr_string_dump(denv, address, "value") ) + return FALSE; + + return sieve_variables_modifiers_code_dump(denv, address); +} + +/* + * Code execution + */ + +static int cmd_set_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + const struct sieve_extension *this_ext = renv->oprtn->ext; + struct sieve_variable_storage *storage; + ARRAY_TYPE(sieve_variables_modifier) modifiers; + unsigned int var_index; + string_t *value; + int ret = SIEVE_EXEC_OK; + + /* + * Read the normal operands + */ + + if ( (ret=sieve_variable_operand_read + (renv, address, "variable", &storage, &var_index)) <= 0 ) + return ret; + + if ( (ret=sieve_opr_string_read(renv, address, "string", &value)) <= 0 ) + return ret; + + if ( (ret=sieve_variables_modifiers_code_read + (renv, this_ext, address, &modifiers)) <= 0 ) + return ret; + + /* + * Determine and assign the value + */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "set command"); + sieve_runtime_trace_descend(renv); + + /* Apply modifiers */ + if ( (ret=sieve_variables_modifiers_apply + (renv, this_ext, &modifiers, &value)) <= 0 ) + return ret; + + /* Actually assign the value if all is well */ + i_assert ( value != NULL ); + if ( !sieve_variable_assign(storage, var_index, value) ) + return SIEVE_EXEC_BIN_CORRUPT; + + /* Trace */ + if ( sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS) ) { + const char *var_name, *var_id; + + (void)sieve_variable_get_identifier(storage, var_index, &var_name); + var_id = sieve_variable_get_varid(storage, var_index); + + sieve_runtime_trace_here(renv, 0, "assign `%s' [%s] = \"%s\"", + var_name, var_id, str_c(value)); + } + + return SIEVE_EXEC_OK; +} + + + + + + diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-arguments.c b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-arguments.c new file mode 100644 index 0000000..2ac773c --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-arguments.c @@ -0,0 +1,420 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "str-sanitize.h" +#include "array.h" + +#include "sieve-common.h" +#include "sieve-ast.h" +#include "sieve-commands.h" +#include "sieve-code.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-dump.h" + +#include "ext-variables-common.h" +#include "ext-variables-limits.h" +#include "ext-variables-name.h" +#include "ext-variables-operands.h" +#include "ext-variables-namespaces.h" +#include "ext-variables-arguments.h" + +/* + * Variable argument implementation + */ + +static bool arg_variable_generate + (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *context); + +const struct sieve_argument_def variable_argument = { + .identifier = "@variable", + .generate = arg_variable_generate +}; + +static bool ext_variables_variable_argument_activate +(const struct sieve_extension *var_ext, + const struct sieve_extension *this_ext, + struct sieve_validator *valdtr, struct sieve_ast_argument *arg, + const char *variable) +{ + struct sieve_ast *ast = arg->ast; + struct sieve_variable *var; + + var = ext_variables_validator_declare_variable(this_ext, valdtr, variable); + + if ( var == NULL ) { + sieve_argument_validate_error(valdtr, arg, + "(implicit) declaration of new variable '%s' exceeds the limit " + "(max variables: %u)", variable, + sieve_variables_get_max_scope_size(var_ext)); + return FALSE; + } + + arg->argument = sieve_argument_create(ast, &variable_argument, this_ext, 0); + arg->argument->data = (void *) var; + return TRUE; +} + +static struct sieve_ast_argument *ext_variables_variable_argument_create +(const struct sieve_extension *this_ext, struct sieve_validator *valdtr, + struct sieve_ast_argument *parent_arg, const char *variable) +{ + struct sieve_ast *ast = parent_arg->ast; + struct sieve_ast_argument *new_arg; + + new_arg = sieve_ast_argument_create(ast, sieve_ast_argument_line(parent_arg)); + new_arg->type = SAAT_STRING; + + if ( !ext_variables_variable_argument_activate + (this_ext, this_ext, valdtr, new_arg, variable) ) + return NULL; + + return new_arg; +} + +static bool arg_variable_generate +(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *context ATTR_UNUSED) +{ + struct sieve_argument *argument = arg->argument; + struct sieve_variable *var = (struct sieve_variable *) argument->data; + + sieve_variables_opr_variable_emit(cgenv->sblock, argument->ext, var); + + return TRUE; +} + +/* + * Match value argument implementation + */ + +static bool arg_match_value_generate +(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *context ATTR_UNUSED); + +const struct sieve_argument_def match_value_argument = { + .identifier = "@match_value", + .generate = arg_match_value_generate +}; + +static bool ext_variables_match_value_argument_activate +(const struct sieve_extension *this_ext, + struct sieve_validator *valdtr, struct sieve_ast_argument *arg, + unsigned int index, bool assignment) +{ + struct sieve_ast *ast = arg->ast; + + if ( assignment ) { + sieve_argument_validate_error(valdtr, arg, + "cannot assign to match variable"); + return FALSE; + } + + if ( index > EXT_VARIABLES_MAX_MATCH_INDEX ) { + sieve_argument_validate_error(valdtr, arg, + "match value index %u out of range (max: %u)", index, + EXT_VARIABLES_MAX_MATCH_INDEX); + return FALSE; + } + + arg->argument = sieve_argument_create + (ast, &match_value_argument, this_ext, 0); + arg->argument->data = (void *) POINTER_CAST(index); + return TRUE; +} + +static struct sieve_ast_argument *ext_variables_match_value_argument_create +(const struct sieve_extension *this_ext, struct sieve_validator *valdtr, + struct sieve_ast_argument *parent_arg, unsigned int index) +{ + struct sieve_ast *ast = parent_arg->ast; + struct sieve_ast_argument *new_arg; + + new_arg = sieve_ast_argument_create(ast, sieve_ast_argument_line(parent_arg)); + new_arg->type = SAAT_STRING; + + if ( !ext_variables_match_value_argument_activate + (this_ext, valdtr, new_arg, index, FALSE) ) { + return NULL; + } + + return new_arg; +} + +static bool arg_match_value_generate +(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *context ATTR_UNUSED) +{ + struct sieve_argument *argument = arg->argument; + unsigned int index = POINTER_CAST_TO(argument->data, unsigned int); + + sieve_variables_opr_match_value_emit(cgenv->sblock, argument->ext, index); + + return TRUE; +} + +/* + * Variable string argument implementation + */ + +static bool arg_variable_string_validate + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd); + +const struct sieve_argument_def variable_string_argument = { + .identifier = "@variable-string", + .validate = arg_variable_string_validate, + .generate = sieve_arg_catenated_string_generate, +}; + +static bool arg_variable_string_validate +(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + const struct sieve_extension *this_ext = (*arg)->argument->ext; + enum { ST_NONE, ST_OPEN, ST_VARIABLE, ST_CLOSE } state = ST_NONE; + pool_t pool = sieve_ast_pool((*arg)->ast); + struct sieve_arg_catenated_string *catstr = NULL; + string_t *str = sieve_ast_argument_str(*arg); + const char *p, *strstart, *substart = NULL; + const char *strval = (const char *) str_data(str); + const char *strend = strval + str_len(str); + bool result = TRUE; + ARRAY_TYPE(sieve_variable_name) substitution; + int nelements = 0; + + T_BEGIN { + /* Initialize substitution structure */ + t_array_init(&substitution, 2); + + p = strval; + strstart = p; + while ( result && p < strend ) { + switch ( state ) { + + /* Nothing found yet */ + case ST_NONE: + if ( *p == '$' ) { + substart = p; + state = ST_OPEN; + } + p++; + break; + + /* Got '$' */ + case ST_OPEN: + if ( *p == '{' ) { + state = ST_VARIABLE; + p++; + } else + state = ST_NONE; + break; + + /* Got '${' */ + case ST_VARIABLE: + nelements = ext_variable_name_parse(&substitution, &p, strend); + + if ( nelements < 0 ) + state = ST_NONE; + else + state = ST_CLOSE; + + break; + + /* Finished parsing name, expecting '}' */ + case ST_CLOSE: + if ( *p == '}' ) { + struct sieve_ast_argument *strarg; + + /* We now know that the substitution is valid */ + + if ( catstr == NULL ) { + catstr = sieve_arg_catenated_string_create(*arg); + } + + /* Add the substring that is before the substitution to the + * variable-string AST. + * + * FIXME: For efficiency, if the variable is not found we should + * coalesce this substring with the one after the substitution. + */ + if ( substart > strstart ) { + string_t *newstr = str_new(pool, substart - strstart); + str_append_data(newstr, strstart, substart - strstart); + + strarg = sieve_ast_argument_string_create_raw + ((*arg)->ast, newstr, (*arg)->source_line); + sieve_arg_catenated_string_add_element(catstr, strarg); + + /* Give other substitution extensions a chance to do their work */ + if ( !sieve_validator_argument_activate_super + (valdtr, cmd, strarg, FALSE) ) { + result = FALSE; + break; + } + } + + /* Find the variable */ + if ( nelements == 1 ) { + const struct sieve_variable_name *cur_element = + array_idx(&substitution, 0); + + if ( cur_element->num_variable == -1 ) { + /* Add variable argument '${identifier}' */ + + strarg = ext_variables_variable_argument_create + (this_ext, valdtr, *arg, str_c(cur_element->identifier)); + + } else { + /* Add match value argument '${000}' */ + + strarg = ext_variables_match_value_argument_create + (this_ext, valdtr, *arg, cur_element->num_variable); + } + } else { + strarg = ext_variables_namespace_argument_create + (this_ext, valdtr, *arg, cmd, &substitution); + } + + if ( strarg != NULL ) + sieve_arg_catenated_string_add_element(catstr, strarg); + + strstart = p + 1; + substart = strstart; + + p++; + } + + /* Finished, reset for the next substitution */ + state = ST_NONE; + } + } + } T_END; + + /* Bail out early if substitution is invalid */ + if ( !result ) return FALSE; + + /* Check whether any substitutions were found */ + if ( catstr == NULL ) { + /* No substitutions in this string, pass it on to any other substution + * extension. + */ + return sieve_validator_argument_activate_super(valdtr, cmd, *arg, TRUE); + } + + /* Add the final substring that comes after the last substitution to the + * variable-string AST. + */ + if ( strend > strstart ) { + struct sieve_ast_argument *strarg; + string_t *newstr = str_new(pool, strend - strstart); + str_append_data(newstr, strstart, strend - strstart); + + strarg = sieve_ast_argument_string_create_raw + ((*arg)->ast, newstr, (*arg)->source_line); + sieve_arg_catenated_string_add_element(catstr, strarg); + + /* Give other substitution extensions a chance to do their work */ + if ( !sieve_validator_argument_activate_super + (valdtr, cmd, strarg, FALSE) ) + return FALSE; + } + + return TRUE; +} + +/* + * Variable argument interface + */ + +static bool _sieve_variable_argument_activate +(const struct sieve_extension *var_ext, + const struct sieve_extension *this_ext, + struct sieve_validator *valdtr, struct sieve_command *cmd, + struct sieve_ast_argument *arg, bool assignment) +{ + bool result = FALSE; + string_t *variable; + const char *varstr, *varend; + ARRAY_TYPE(sieve_variable_name) vname; + int nelements = 0; + + T_BEGIN { + t_array_init(&vname, 2); + + variable = sieve_ast_argument_str(arg); + varstr = str_c(variable); + varend = PTR_OFFSET(varstr, str_len(variable)); + nelements = ext_variable_name_parse(&vname, &varstr, varend); + + /* Check whether name parsing succeeded */ + if ( nelements <= 0 || varstr != varend ) { + /* Parse failed */ + sieve_argument_validate_error(valdtr, arg, + "invalid variable name '%s'", str_sanitize(str_c(variable),80)); + } else if ( nelements == 1 ) { + /* Normal (match) variable */ + + const struct sieve_variable_name *cur_element = + array_idx(&vname, 0); + + if ( cur_element->num_variable < 0 ) { + /* Variable */ + result = ext_variables_variable_argument_activate(var_ext, + this_ext, valdtr, arg, str_c(cur_element->identifier)); + + } else { + /* Match value */ + result = ext_variables_match_value_argument_activate + (this_ext, valdtr, arg, cur_element->num_variable, assignment); + } + + } else { + /* Namespace variable */ + result = ext_variables_namespace_argument_activate + (this_ext, valdtr, arg, cmd, &vname, assignment); + } + } T_END; + + return result; +} + +bool sieve_variable_argument_activate +(const struct sieve_extension *var_ext, + const struct sieve_extension *this_ext, + struct sieve_validator *valdtr, struct sieve_command *cmd, + struct sieve_ast_argument *arg, bool assignment) +{ + if ( sieve_ast_argument_type(arg) == SAAT_STRING ) { + /* Single string */ + return _sieve_variable_argument_activate(var_ext, + this_ext, valdtr, cmd, arg, assignment); + + } else if ( sieve_ast_argument_type(arg) == SAAT_STRING_LIST ) { + /* String list */ + struct sieve_ast_argument *stritem; + + i_assert ( !assignment ); + + stritem = sieve_ast_strlist_first(arg); + while ( stritem != NULL ) { + if ( !_sieve_variable_argument_activate(var_ext, + this_ext, valdtr, cmd, stritem, assignment) ) + return FALSE; + + stritem = sieve_ast_strlist_next(stritem); + } + + arg->argument = sieve_argument_create + (arg->ast, &string_list_argument, NULL, 0); + + return TRUE; + } + + return FALSE; +} + + diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-arguments.h b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-arguments.h new file mode 100644 index 0000000..87413c8 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-arguments.h @@ -0,0 +1,24 @@ +#ifndef EXT_VARIABLES_ARGUMENTS_H +#define EXT_VARIABLES_ARGUMENTS_H + +#include "sieve-common.h" + +/* + * Variable argument + */ + +extern const struct sieve_argument_def variable_argument; + +/* + * Match value argument + */ + +extern const struct sieve_argument_def match_value_argument; + +/* + * Variable string argument + */ + +extern const struct sieve_argument_def variable_string_argument; + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-common.c b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-common.c new file mode 100644 index 0000000..be9f677 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-common.c @@ -0,0 +1,950 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "hash.h" +#include "str.h" +#include "array.h" + +#include "sieve-common.h" +#include "sieve-settings.h" + +#include "sieve-ast.h" +#include "sieve-binary.h" +#include "sieve-code.h" +#include "sieve-objects.h" +#include "sieve-match-types.h" + +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-dump.h" +#include "sieve-interpreter.h" + +#include "ext-variables-common.h" +#include "ext-variables-limits.h" +#include "ext-variables-name.h" +#include "ext-variables-modifiers.h" + +/* + * Limits + */ + +unsigned int +sieve_variables_get_max_scope_size(const struct sieve_extension *var_ext) +{ + const struct ext_variables_config *config = + ext_variables_get_config(var_ext); + + return config->max_scope_size; +} + +size_t +sieve_variables_get_max_variable_size(const struct sieve_extension *var_ext) +{ + const struct ext_variables_config *config = + ext_variables_get_config(var_ext); + + return config->max_variable_size; +} + +/* + * Extension configuration + */ + +bool +ext_variables_load(const struct sieve_extension *ext, void **context) +{ + struct sieve_instance *svinst = ext->svinst; + struct ext_variables_config *config; + unsigned long long int uint_setting; + size_t size_setting; + + if (*context != NULL) + ext_variables_unload(ext); + + config = i_new(struct ext_variables_config, 1); + + /* Get limits */ + config->max_scope_size = EXT_VARIABLES_DEFAULT_MAX_SCOPE_SIZE; + config->max_variable_size = EXT_VARIABLES_DEFAULT_MAX_VARIABLE_SIZE; + + if (sieve_setting_get_uint_value( + svinst, "sieve_variables_max_scope_size", &uint_setting)) { + if (uint_setting < EXT_VARIABLES_REQUIRED_MAX_SCOPE_SIZE) { + e_warning(svinst->event, "variables: " + "setting sieve_variables_max_scope_size " + "is lower than required by standards " + "(>= %llu items)", + (unsigned long long)EXT_VARIABLES_REQUIRED_MAX_SCOPE_SIZE); + } else { + config->max_scope_size = (unsigned int)uint_setting; + } + } + + if (sieve_setting_get_size_value( + svinst, "sieve_variables_max_variable_size", &size_setting)) { + if (size_setting < EXT_VARIABLES_REQUIRED_MAX_VARIABLE_SIZE) { + e_warning(svinst->event, "variables: " + "setting sieve_variables_max_variable_size " + "is lower than required by standards " + "(>= %zu bytes)", + (size_t)EXT_VARIABLES_REQUIRED_MAX_VARIABLE_SIZE); + } else { + config->max_variable_size = size_setting; + } + } + + *context = (void *)config; + return TRUE; +} + +void ext_variables_unload(const struct sieve_extension *ext) +{ + struct ext_variables_config *config = + (struct ext_variables_config *)ext->context; + + i_free(config); +} + +const struct ext_variables_config * +ext_variables_get_config(const struct sieve_extension *var_ext) +{ + const struct ext_variables_config *config = + (const struct ext_variables_config *)var_ext->context; + + i_assert(var_ext->def == &variables_extension); + return config; +} + +/* + * Variable scope + */ + +struct sieve_variable_scope { + pool_t pool; + int refcount; + + struct sieve_instance *svinst; + const struct sieve_extension *var_ext; + const struct sieve_extension *ext; + + struct sieve_variable *error_var; + + HASH_TABLE(const char *, struct sieve_variable *) variables; + ARRAY(struct sieve_variable *) variable_index; +}; + +struct sieve_variable_scope_binary { + struct sieve_variable_scope *scope; + + unsigned int size; + struct sieve_binary_block *sblock; + sieve_size_t address; +}; + +struct sieve_variable_scope_iter { + struct sieve_variable_scope *scope; + struct hash_iterate_context *hctx; +}; + +struct sieve_variable_scope * +sieve_variable_scope_create(struct sieve_instance *svinst, + const struct sieve_extension *var_ext, + const struct sieve_extension *ext) +{ + struct sieve_variable_scope *scope; + pool_t pool; + + i_assert(var_ext->def == &variables_extension); + + pool = pool_alloconly_create("sieve_variable_scope", 4096); + scope = p_new(pool, struct sieve_variable_scope, 1); + scope->pool = pool; + scope->refcount = 1; + + scope->svinst = svinst; + scope->var_ext = var_ext; + scope->ext = ext; + + hash_table_create(&scope->variables, pool, 0, strcase_hash, strcasecmp); + p_array_init(&scope->variable_index, pool, 128); + + return scope; +} + +void sieve_variable_scope_ref(struct sieve_variable_scope *scope) +{ + scope->refcount++; +} + +void sieve_variable_scope_unref(struct sieve_variable_scope **_scope) +{ + struct sieve_variable_scope *scope = *_scope; + + i_assert(scope->refcount > 0); + + if (--scope->refcount != 0) + return; + + hash_table_destroy(&scope->variables); + + *_scope = NULL; + pool_unref(&scope->pool); +} + +pool_t sieve_variable_scope_pool(struct sieve_variable_scope *scope) +{ + return scope->pool; +} + +struct sieve_variable * +sieve_variable_scope_declare(struct sieve_variable_scope *scope, + const char *identifier) +{ + unsigned int max_scope_size; + struct sieve_variable *var; + + var = hash_table_lookup(scope->variables, identifier); + if (var != NULL) + return var; + + max_scope_size = sieve_variables_get_max_scope_size(scope->var_ext); + if (array_count(&scope->variable_index) >= max_scope_size) { + if (scope->error_var == NULL) { + var = p_new(scope->pool, struct sieve_variable, 1); + var->identifier = "@ERROR@"; + var->index = 0; + + scope->error_var = var; + return NULL; + } + + return scope->error_var; + } + + var = p_new(scope->pool, struct sieve_variable, 1); + var->ext = scope->ext; + var->identifier = p_strdup(scope->pool, identifier); + var->index = array_count(&scope->variable_index); + + hash_table_insert(scope->variables, var->identifier, var); + array_append(&scope->variable_index, &var, 1); + return var; +} + +struct sieve_variable * +sieve_variable_scope_get_variable(struct sieve_variable_scope *scope, + const char *identifier) +{ + return hash_table_lookup(scope->variables, identifier); +} + +struct sieve_variable * +sieve_variable_scope_import(struct sieve_variable_scope *scope, + struct sieve_variable *var) +{ + struct sieve_variable *old_var, *new_var; + + old_var = sieve_variable_scope_get_variable(scope, var->identifier); + if (old_var != NULL) { + i_assert(memcmp(old_var, var, sizeof(*var)) == 0); + return old_var; + } + + new_var = p_new(scope->pool, struct sieve_variable, 1); + memcpy(new_var, var, sizeof(*new_var)); + + hash_table_insert(scope->variables, new_var->identifier, new_var); + + /* Not entered into the index because it is an external variable + (This can be done unlimited; only limited by the size of the external + scope) + */ + return new_var; +} + +struct sieve_variable_scope_iter * +sieve_variable_scope_iterate_init(struct sieve_variable_scope *scope) +{ + struct sieve_variable_scope_iter *iter; + + iter = t_new(struct sieve_variable_scope_iter, 1); + iter->scope = scope; + iter->hctx = hash_table_iterate_init(scope->variables); + + return iter; +} + +bool sieve_variable_scope_iterate(struct sieve_variable_scope_iter *iter, + struct sieve_variable **var_r) +{ + const char *key; + + return hash_table_iterate(iter->hctx, iter->scope->variables, + &key, var_r); +} + +void sieve_variable_scope_iterate_deinit( + struct sieve_variable_scope_iter **iter) +{ + hash_table_iterate_deinit(&(*iter)->hctx); + *iter = NULL; +} + +unsigned int +sieve_variable_scope_declarations(struct sieve_variable_scope *scope) +{ + return hash_table_count(scope->variables); +} + +unsigned int sieve_variable_scope_size(struct sieve_variable_scope *scope) +{ + return array_count(&scope->variable_index); +} + +struct sieve_variable * const * +sieve_variable_scope_get_variables(struct sieve_variable_scope *scope, + unsigned int *size_r) +{ + return array_get(&scope->variable_index, size_r); +} + +struct sieve_variable * +sieve_variable_scope_get_indexed(struct sieve_variable_scope *scope, + unsigned int index) +{ + struct sieve_variable * const *var; + + if (index >= array_count(&scope->variable_index)) + return NULL; + + var = array_idx(&scope->variable_index, index); + return *var; +} + +/* Scope binary */ + +struct sieve_variable_scope * +sieve_variable_scope_binary_dump(struct sieve_instance *svinst, + const struct sieve_extension *var_ext, + const struct sieve_extension *ext, + const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + struct sieve_variable_scope *local_scope; + unsigned int i, scope_size; + sieve_size_t pc; + sieve_offset_t end_offset; + + /* Read scope size */ + sieve_code_mark(denv); + if (!sieve_binary_read_unsigned(denv->sblock, address, &scope_size)) + return NULL; + + /* Read offset */ + pc = *address; + if (!sieve_binary_read_offset(denv->sblock, address, &end_offset)) + return NULL; + + /* Create scope */ + local_scope = sieve_variable_scope_create(svinst, var_ext, ext); + + /* Read and dump scope itself */ + + sieve_code_dumpf(denv, "VARIABLES SCOPE [%u] (end: %08x)", + scope_size, (unsigned int)(pc + end_offset)); + + for (i = 0; i < scope_size; i++) { + string_t *identifier; + + sieve_code_mark(denv); + if (!sieve_binary_read_string(denv->sblock, address, + &identifier)) + return NULL; + + sieve_code_dumpf(denv, "%3d: '%s'", i, str_c(identifier)); + + (void)sieve_variable_scope_declare(local_scope, + str_c(identifier)); + } + + return local_scope; +} + +struct sieve_variable_scope_binary * +sieve_variable_scope_binary_create(struct sieve_variable_scope *scope) +{ + struct sieve_variable_scope_binary *scpbin; + + scpbin = p_new(scope->pool, struct sieve_variable_scope_binary, 1); + scpbin->scope = scope; + + return scpbin; +} + +void sieve_variable_scope_binary_ref(struct sieve_variable_scope_binary *scpbin) +{ + sieve_variable_scope_ref(scpbin->scope); +} + +void sieve_variable_scope_binary_unref( + struct sieve_variable_scope_binary **scpbin) +{ + sieve_variable_scope_unref(&(*scpbin)->scope); + *scpbin = NULL; +} + +struct sieve_variable_scope_binary * +sieve_variable_scope_binary_read(struct sieve_instance *svinst, + const struct sieve_extension *var_ext, + const struct sieve_extension *ext, + struct sieve_binary_block *sblock, + sieve_size_t *address) +{ + struct sieve_variable_scope *scope; + struct sieve_variable_scope_binary *scpbin; + unsigned int scope_size, max_scope_size; + const char *ext_name = (ext == NULL ? "variables" : + sieve_extension_name(ext)); + sieve_size_t pc; + sieve_offset_t end_offset; + + /* Read scope size */ + if (!sieve_binary_read_unsigned(sblock, address, &scope_size)) { + e_error(svinst->event, "%s: " + "variable scope: failed to read size", ext_name); + return NULL; + } + + /* Check size limit */ + max_scope_size = sieve_variables_get_max_scope_size(var_ext); + if (scope_size > max_scope_size) { + e_error(svinst->event, "%s: " + "variable scope: size exceeds the limit (%u > %u)", + ext_name, scope_size, max_scope_size); + return NULL; + } + + /* Read offset */ + pc = *address; + if (!sieve_binary_read_offset(sblock, address, &end_offset)) { + e_error(svinst->event, "%s: " + "variable scope: failed to read end offset", ext_name); + return NULL; + } + + /* Create scope */ + scope = sieve_variable_scope_create(svinst, var_ext, ext); + + scpbin = sieve_variable_scope_binary_create(scope); + scpbin->size = scope_size; + scpbin->sblock = sblock; + scpbin->address = *address; + + *address = pc + end_offset; + + return scpbin; +} + +struct sieve_variable_scope * +sieve_variable_scope_binary_get(struct sieve_variable_scope_binary *scpbin) +{ + const struct sieve_extension *ext = scpbin->scope->ext; + struct sieve_instance *svinst = scpbin->scope->svinst; + const char *ext_name = (ext == NULL ? "variables" : + sieve_extension_name(ext)); + unsigned int i; + + if (scpbin->sblock != NULL) { + sieve_size_t *address = &scpbin->address; + + /* Read scope itself */ + for (i = 0; i < scpbin->size; i++) { + struct sieve_variable *var; + string_t *identifier; + + if (!sieve_binary_read_string(scpbin->sblock, address, + &identifier)) { + e_error(svinst->event, "%s: variable scope: " + "failed to read variable name", + ext_name); + return NULL; + } + + var = sieve_variable_scope_declare(scpbin->scope, + str_c(identifier)); + + i_assert(var != NULL); + i_assert(var->index == i); + } + + scpbin->sblock = NULL; + } + + return scpbin->scope; +} + +unsigned int +sieve_variable_scope_binary_get_size( + struct sieve_variable_scope_binary *scpbin) +{ + if (scpbin->sblock != NULL) + return scpbin->size; + + return array_count(&scpbin->scope->variable_index); +} + +/* + * Variable storage + */ + +struct sieve_variable_storage { + pool_t pool; + const struct sieve_extension *var_ext; + struct sieve_variable_scope *scope; + struct sieve_variable_scope_binary *scope_bin; + unsigned int max_size; + ARRAY(string_t *) var_values; +}; + +struct sieve_variable_storage * +sieve_variable_storage_create(const struct sieve_extension *var_ext, + pool_t pool, + struct sieve_variable_scope_binary *scpbin) +{ + struct sieve_variable_storage *storage; + + storage = p_new(pool, struct sieve_variable_storage, 1); + storage->pool = pool; + storage->var_ext = var_ext; + storage->scope_bin = scpbin; + storage->scope = NULL; + + storage->max_size = sieve_variable_scope_binary_get_size(scpbin); + + p_array_init(&storage->var_values, pool, 4); + + return storage; +} + +static inline bool +sieve_variable_valid(struct sieve_variable_storage *storage, + unsigned int index) +{ + if (storage->scope_bin == NULL) + return TRUE; + + return (index < storage->max_size); +} + +bool sieve_variable_get_identifier(struct sieve_variable_storage *storage, + unsigned int index, const char **identifier) +{ + struct sieve_variable * const *var; + + *identifier = NULL; + + if (storage->scope_bin == NULL) + return TRUE; + + if (storage->scope == NULL) { + storage->scope = + sieve_variable_scope_binary_get(storage->scope_bin); + if (storage->scope == NULL) + return FALSE; + } + + /* FIXME: direct invasion of the scope object is a bit ugly */ + if (index >= array_count(&storage->scope->variable_index)) + return FALSE; + + var = array_idx(&storage->scope->variable_index, index); + if (*var != NULL) + *identifier = (*var)->identifier; + return TRUE; +} + +const char * +sieve_variable_get_varid(struct sieve_variable_storage *storage, + unsigned int index) +{ + if (storage->scope_bin == NULL) + return t_strdup_printf("%ld", (long)index); + + if (storage->scope == NULL) { + storage->scope = + sieve_variable_scope_binary_get(storage->scope_bin); + if (storage->scope == NULL) + return NULL; + } + + return sieve_ext_variables_get_varid(storage->scope->ext, index); +} + +bool sieve_variable_get(struct sieve_variable_storage *storage, + unsigned int index, string_t **value) +{ + *value = NULL; + + if (index < array_count(&storage->var_values)) { + string_t * const *varent; + + varent = array_idx(&storage->var_values, index); + + *value = *varent; + } else if (!sieve_variable_valid(storage, index)) { + return FALSE; + } + + return TRUE; +} + +bool sieve_variable_get_modifiable(struct sieve_variable_storage *storage, + unsigned int index, string_t **value) +{ + string_t *dummy; + + if (value == NULL) + value = &dummy; + + if (!sieve_variable_get(storage, index, value)) + return FALSE; + + if (*value == NULL) { + *value = str_new(storage->pool, 256); + array_idx_set(&storage->var_values, index, value); + } + return TRUE; +} + +bool sieve_variable_assign(struct sieve_variable_storage *storage, + unsigned int index, const string_t *value) +{ + const struct ext_variables_config *config = + ext_variables_get_config(storage->var_ext); + string_t *varval; + + if (!sieve_variable_get_modifiable(storage, index, &varval)) + return FALSE; + + str_truncate(varval, 0); + str_append_str(varval, value); + + /* Just a precaution, caller should prevent this in the first place */ + if (str_len(varval) > config->max_variable_size) + str_truncate_utf8(varval, config->max_variable_size); + + return TRUE; +} + +bool sieve_variable_assign_cstr(struct sieve_variable_storage *storage, + unsigned int index, const char *value) +{ + const struct ext_variables_config *config = + ext_variables_get_config(storage->var_ext); + string_t *varval; + + if (!sieve_variable_get_modifiable(storage, index, &varval)) + return FALSE; + + str_truncate(varval, 0); + str_append(varval, value); + + /* Just a precaution, caller should prevent this in the first place */ + if (str_len(varval) > config->max_variable_size) + str_truncate_utf8(varval, config->max_variable_size); + + return TRUE; +} + +/* + * AST Context + */ + +static void +ext_variables_ast_free(const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_ast *ast ATTR_UNUSED, void *context) +{ + struct sieve_variable_scope *local_scope = + (struct sieve_variable_scope *)context; + + /* Unreference main variable scope */ + sieve_variable_scope_unref(&local_scope); +} + +static const struct sieve_ast_extension variables_ast_extension = { + &variables_extension, + ext_variables_ast_free +}; + +static struct sieve_variable_scope * +ext_variables_create_local_scope(const struct sieve_extension *this_ext, + struct sieve_ast *ast) +{ + struct sieve_variable_scope *scope; + + scope = sieve_variable_scope_create(this_ext->svinst, this_ext, NULL); + + sieve_ast_extension_register(ast, this_ext, &variables_ast_extension, + (void *)scope); + return scope; +} + +static struct sieve_variable_scope * +ext_variables_ast_get_local_scope(const struct sieve_extension *this_ext, + struct sieve_ast *ast) +{ + struct sieve_variable_scope *local_scope = + (struct sieve_variable_scope *) + sieve_ast_extension_get_context(ast, this_ext); + + return local_scope; +} + +/* + * Validator context + */ + +static struct ext_variables_validator_context * +ext_variables_validator_context_create(const struct sieve_extension *this_ext, + struct sieve_validator *valdtr) +{ + pool_t pool = sieve_validator_pool(valdtr); + struct ext_variables_validator_context *ctx; + struct sieve_ast *ast = sieve_validator_ast(valdtr); + + ctx = p_new(pool, struct ext_variables_validator_context, 1); + ctx->modifiers = sieve_validator_object_registry_create(valdtr); + ctx->namespaces = sieve_validator_object_registry_create(valdtr); + ctx->local_scope = ext_variables_create_local_scope(this_ext, ast); + + sieve_validator_extension_set_context(valdtr, this_ext, (void *)ctx); + return ctx; +} + +struct ext_variables_validator_context * +ext_variables_validator_context_get(const struct sieve_extension *this_ext, + struct sieve_validator *valdtr) +{ + struct ext_variables_validator_context *ctx; + + i_assert(sieve_extension_is(this_ext, variables_extension)); + ctx = (struct ext_variables_validator_context *) + sieve_validator_extension_get_context(valdtr, this_ext); + + if (ctx == NULL) + ctx = ext_variables_validator_context_create(this_ext, valdtr); + return ctx; +} + +void ext_variables_validator_initialize(const struct sieve_extension *this_ext, + struct sieve_validator *valdtr) +{ + struct ext_variables_validator_context *ctx; + + /* Create our context */ + ctx = ext_variables_validator_context_get(this_ext, valdtr); + + ext_variables_register_core_modifiers(this_ext, ctx); + + ctx->active = TRUE; +} + +struct sieve_variable *ext_variables_validator_get_variable( + const struct sieve_extension *this_ext, + struct sieve_validator *validator, const char *variable) +{ + struct ext_variables_validator_context *ctx = + ext_variables_validator_context_get(this_ext, validator); + + return sieve_variable_scope_get_variable(ctx->local_scope, variable); +} + +struct sieve_variable * +ext_variables_validator_declare_variable(const struct sieve_extension *this_ext, + struct sieve_validator *validator, + const char *variable) +{ + struct ext_variables_validator_context *ctx = + ext_variables_validator_context_get(this_ext, validator); + + return sieve_variable_scope_declare(ctx->local_scope, variable); +} + +struct sieve_variable_scope * +sieve_ext_variables_get_local_scope(const struct sieve_extension *var_ext, + struct sieve_validator *validator) +{ + struct ext_variables_validator_context *ctx = + ext_variables_validator_context_get(var_ext, validator); + + return ctx->local_scope; +} + +bool sieve_ext_variables_is_active(const struct sieve_extension *var_ext, + struct sieve_validator *valdtr) +{ + struct ext_variables_validator_context *ctx = + ext_variables_validator_context_get(var_ext, valdtr); + + return (ctx != NULL && ctx->active); +} + +/* + * Code generation + */ + +bool ext_variables_generator_load(const struct sieve_extension *ext, + const struct sieve_codegen_env *cgenv) +{ + struct sieve_variable_scope *local_scope = + ext_variables_ast_get_local_scope(ext, cgenv->ast); + unsigned int count = sieve_variable_scope_size(local_scope); + sieve_size_t jump; + + sieve_binary_emit_unsigned(cgenv->sblock, count); + + jump = sieve_binary_emit_offset(cgenv->sblock, 0); + + if (count > 0) { + unsigned int size, i; + struct sieve_variable *const *vars = + sieve_variable_scope_get_variables(local_scope, &size); + + for (i = 0; i < size; i++) { + sieve_binary_emit_cstring(cgenv->sblock, + vars[i]->identifier); + } + } + + sieve_binary_resolve_offset(cgenv->sblock, jump); + return TRUE; +} + +/* + * Interpreter context + */ + +struct ext_variables_interpreter_context { + pool_t pool; + + struct sieve_variable_scope *local_scope; + struct sieve_variable_scope_binary *local_scope_bin; + + struct sieve_variable_storage *local_storage; + ARRAY(struct sieve_variable_storage *) ext_storages; +}; + +static void +ext_variables_interpreter_free(const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_interpreter *interp ATTR_UNUSED, + void *context) +{ + struct ext_variables_interpreter_context *ctx = + (struct ext_variables_interpreter_context *)context; + + sieve_variable_scope_binary_unref(&ctx->local_scope_bin); +} + +static struct sieve_interpreter_extension +variables_interpreter_extension = { + .ext_def = &variables_extension, + .free = ext_variables_interpreter_free +}; + +static struct ext_variables_interpreter_context * +ext_variables_interpreter_context_create( + const struct sieve_extension *this_ext, + struct sieve_interpreter *interp, + struct sieve_variable_scope_binary *scpbin) +{ + pool_t pool = sieve_interpreter_pool(interp); + struct ext_variables_interpreter_context *ctx; + + ctx = p_new(pool, struct ext_variables_interpreter_context, 1); + ctx->pool = pool; + ctx->local_scope = NULL; + ctx->local_scope_bin = scpbin; + ctx->local_storage = + sieve_variable_storage_create(this_ext, pool, scpbin); + p_array_init(&ctx->ext_storages, pool, + sieve_extensions_get_count(this_ext->svinst)); + + sieve_interpreter_extension_register(interp, this_ext, + &variables_interpreter_extension, + (void *)ctx); + return ctx; +} + +bool ext_variables_interpreter_load(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + struct sieve_variable_scope_binary *scpbin; + + scpbin = sieve_variable_scope_binary_read(eenv->svinst, ext, NULL, + renv->sblock, address); + if (scpbin == NULL) + return FALSE; + + /* Create our context */ + (void)ext_variables_interpreter_context_create(ext, renv->interp, + scpbin); + + /* Enable support for match values */ + (void)sieve_match_values_set_enabled(renv, TRUE); + + return TRUE; +} + +static inline struct ext_variables_interpreter_context * +ext_variables_interpreter_context_get(const struct sieve_extension *this_ext, + struct sieve_interpreter *interp) +{ + struct ext_variables_interpreter_context *ctx; + + i_assert(sieve_extension_is(this_ext, variables_extension)); + ctx = (struct ext_variables_interpreter_context *) + sieve_interpreter_extension_get_context(interp, this_ext); + return ctx; +} + +struct sieve_variable_storage * +sieve_ext_variables_runtime_get_storage(const struct sieve_extension *var_ext, + const struct sieve_runtime_env *renv, + const struct sieve_extension *ext) +{ + struct ext_variables_interpreter_context *ctx = + ext_variables_interpreter_context_get(var_ext, renv->interp); + struct sieve_variable_storage * const *storage; + + if (ext == NULL) + return ctx->local_storage; + + if (ext->id >= (int)array_count(&ctx->ext_storages)) + storage = NULL; + else + storage = array_idx(&ctx->ext_storages, ext->id); + + if (storage == NULL) + return NULL; + return *storage; +} + +void sieve_ext_variables_runtime_set_storage( + const struct sieve_extension *var_ext, + const struct sieve_runtime_env *renv, const struct sieve_extension *ext, + struct sieve_variable_storage *storage) +{ + struct ext_variables_interpreter_context *ctx = + ext_variables_interpreter_context_get(var_ext, renv->interp); + + if (ctx == NULL || ext == NULL || storage == NULL) + return; + if (ext->id < 0) + return; + + array_idx_set(&ctx->ext_storages, (unsigned int) ext->id, &storage); +} diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-common.h b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-common.h new file mode 100644 index 0000000..401d943 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-common.h @@ -0,0 +1,102 @@ +#ifndef EXT_VARIABLES_COMMON_H +#define EXT_VARIABLES_COMMON_H + +#include "sieve-common.h" +#include "sieve-validator.h" + +#include "sieve-ext-variables.h" + +/* + * Extension + */ + +struct ext_variables_config { + /* Maximum number of variables (in a scope) */ + unsigned int max_scope_size; + /* Maximum size of variable value */ + size_t max_variable_size; +}; + +extern const struct sieve_extension_def variables_extension; + +bool ext_variables_load(const struct sieve_extension *ext, void **context); +void ext_variables_unload(const struct sieve_extension *ext); + +const struct ext_variables_config * +ext_variables_get_config(const struct sieve_extension *var_ext); + +/* + * Commands + */ + +extern const struct sieve_command_def cmd_set; +extern const struct sieve_command_def tst_string; + +/* + * Operands + */ + +enum ext_variables_operand { + EXT_VARIABLES_OPERAND_VARIABLE, + EXT_VARIABLES_OPERAND_MATCH_VALUE, + EXT_VARIABLES_OPERAND_NAMESPACE_VARIABLE, + EXT_VARIABLES_OPERAND_MODIFIER +}; + +/* + * Operations + */ + +extern const struct sieve_operation_def cmd_set_operation; +extern const struct sieve_operation_def tst_string_operation; + +enum ext_variables_opcode { + EXT_VARIABLES_OPERATION_SET, + EXT_VARIABLES_OPERATION_STRING +}; + +/* + * Validator context + */ + +struct ext_variables_validator_context { + bool active; + + struct sieve_validator_object_registry *modifiers; + struct sieve_validator_object_registry *namespaces; + + struct sieve_variable_scope *local_scope; +}; + +void ext_variables_validator_initialize(const struct sieve_extension *this_ext, + struct sieve_validator *validator); + +struct ext_variables_validator_context * +ext_variables_validator_context_get(const struct sieve_extension *this_ext, + struct sieve_validator *valdtr); + +struct sieve_variable * +ext_variables_validator_get_variable(const struct sieve_extension *this_ext, + struct sieve_validator *validator, + const char *variable); +struct sieve_variable * +ext_variables_validator_declare_variable(const struct sieve_extension *this_ext, + struct sieve_validator *validator, + const char *variable); + +/* + * Code generation + */ + +bool ext_variables_generator_load(const struct sieve_extension *ext, + const struct sieve_codegen_env *cgenv); + +/* + * Interpreter context + */ + +bool ext_variables_interpreter_load(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv, + sieve_size_t *address); + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-dump.c b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-dump.c new file mode 100644 index 0000000..26bd015 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-dump.c @@ -0,0 +1,137 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" + +#include "sieve-common.h" +#include "sieve-dump.h" +#include "sieve-binary.h" +#include "sieve-code.h" + +#include "ext-variables-common.h" +#include "ext-variables-dump.h" + +/* + * Code dumper extension + */ + +static void ext_variables_code_dumper_free + (struct sieve_code_dumper *dumper, void *context); + +static const struct sieve_code_dumper_extension +variables_dump_extension = { + &variables_extension, + ext_variables_code_dumper_free +}; + +/* + * Code dump context + */ + +struct ext_variables_dump_context { + struct sieve_variable_scope *local_scope; + ARRAY(struct sieve_variable_scope *) ext_scopes; +}; + +static void ext_variables_code_dumper_free +(struct sieve_code_dumper *dumper ATTR_UNUSED, void *context) +{ + struct ext_variables_dump_context *dctx = + (struct ext_variables_dump_context *) context; + + if ( dctx == NULL || dctx->local_scope == NULL ) + return; + + sieve_variable_scope_unref(&dctx->local_scope); +} + +static struct ext_variables_dump_context *ext_variables_dump_get_context +(const struct sieve_extension *this_ext, const struct sieve_dumptime_env *denv) +{ + struct sieve_code_dumper *dumper = denv->cdumper; + struct ext_variables_dump_context *dctx; + pool_t pool; + + i_assert( sieve_extension_is(this_ext, variables_extension) ); + dctx = sieve_dump_extension_get_context(dumper, this_ext); + + if ( dctx == NULL ) { + /* Create dumper context */ + pool = sieve_code_dumper_pool(dumper); + dctx = p_new(pool, struct ext_variables_dump_context, 1); + p_array_init(&dctx->ext_scopes, pool, + sieve_extensions_get_count(this_ext->svinst)); + + sieve_dump_extension_register + (dumper, this_ext, &variables_dump_extension, dctx); + } + + return dctx; +} + +bool ext_variables_code_dump +(const struct sieve_extension *ext, + const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + struct ext_variables_dump_context *dctx; + struct sieve_variable_scope *local_scope; + + local_scope = sieve_variable_scope_binary_dump + (ext->svinst, ext, NULL, denv, address); + + dctx = ext_variables_dump_get_context(ext, denv); + dctx->local_scope = local_scope; + + return TRUE; +} + +/* + * Scope registry + */ + +void sieve_ext_variables_dump_set_scope +(const struct sieve_extension *var_ext, const struct sieve_dumptime_env *denv, + const struct sieve_extension *ext, struct sieve_variable_scope *scope) +{ + struct ext_variables_dump_context *dctx = + ext_variables_dump_get_context(var_ext, denv); + + if ( ext->id < 0 ) return; + + array_idx_set(&dctx->ext_scopes, (unsigned int) ext->id, &scope); +} + +/* + * Variable identifier dump + */ + +const char *ext_variables_dump_get_identifier +(const struct sieve_extension *var_ext, const struct sieve_dumptime_env *denv, + const struct sieve_extension *ext, unsigned int index) +{ + struct ext_variables_dump_context *dctx = + ext_variables_dump_get_context(var_ext, denv); + struct sieve_variable_scope *scope; + struct sieve_variable *var; + + if ( ext == NULL ) + scope = dctx->local_scope; + else { + struct sieve_variable_scope *const *ext_scope; + + if ( ext->id < 0 || ext->id >= (int) array_count(&dctx->ext_scopes) ) + return NULL; + + ext_scope = array_idx(&dctx->ext_scopes, (unsigned int) ext->id); + scope = *ext_scope; + } + + if ( scope == NULL ) + return NULL; + + var = sieve_variable_scope_get_indexed(scope, index); + + return var->identifier; +} + diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-dump.h b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-dump.h new file mode 100644 index 0000000..da72a5d --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-dump.h @@ -0,0 +1,22 @@ +#ifndef EXT_VARIABLES_DUMP_H +#define EXT_VARIABLES_DUMP_H + +#include "sieve-common.h" + +/* + * Code dump context + */ + +bool ext_variables_code_dump + (const struct sieve_extension *ext, const struct sieve_dumptime_env *denv, + sieve_size_t *address); + +/* + * Variable identifier dump + */ + +const char *ext_variables_dump_get_identifier +(const struct sieve_extension *var_ext, const struct sieve_dumptime_env *denv, + const struct sieve_extension *ext, unsigned int index); + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-limits.h b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-limits.h new file mode 100644 index 0000000..61260c8 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-limits.h @@ -0,0 +1,35 @@ +#ifndef EXT_VARIABLES_LIMITS_H +#define EXT_VARIABLES_LIMITS_H + +#include "sieve-limits.h" + +/* From RFC 5229: + * + * 6. Implementation Limits + * + * An implementation of this document MUST support at least 128 distinct + * variables. The supported length of variable names MUST be at least + * 32 characters. Each variable MUST be able to hold at least 4000 + * characters. Attempts to set the variable to a value larger than what + * the implementation supports SHOULD be reported as an error at + * compile-time if possible. If the attempt is discovered during run- + * time, the value SHOULD be truncated, and it MUST NOT be treated as an + * error. + + * Match variables ${1} through ${9} MUST be supported. References to + * higher indices than those the implementation supports MUST be treated + * as a syntax error, which SHOULD be discovered at compile-time. + */ + +#define EXT_VARIABLES_DEFAULT_MAX_SCOPE_SIZE 255 +#define EXT_VARIABLES_DEFAULT_MAX_VARIABLE_SIZE (4 * 1024) + +#define EXT_VARIABLES_REQUIRED_MAX_SCOPE_SIZE 128 +#define EXT_VARIABLES_REQUIRED_MAX_VARIABLE_SIZE 4000 + +#define EXT_VARIABLES_MAX_VARIABLE_NAME_LEN 64 +#define EXT_VARIABLES_MAX_NAMESPACE_ELEMENTS 10 + +#define EXT_VARIABLES_MAX_MATCH_INDEX SIEVE_MAX_MATCH_VALUES + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-modifiers.c b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-modifiers.c new file mode 100644 index 0000000..dd21c88 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-modifiers.c @@ -0,0 +1,578 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "unichar.h" +#include "str-sanitize.h" + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-code.h" +#include "sieve-binary.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-runtime.h" + +#include "ext-variables-common.h" +#include "ext-variables-limits.h" +#include "ext-variables-modifiers.h" + +#include <ctype.h> + +/* + * Core modifiers + */ + +extern const struct sieve_variables_modifier_def lower_modifier; +extern const struct sieve_variables_modifier_def upper_modifier; +extern const struct sieve_variables_modifier_def lowerfirst_modifier; +extern const struct sieve_variables_modifier_def upperfirst_modifier; +extern const struct sieve_variables_modifier_def quotewildcard_modifier; +extern const struct sieve_variables_modifier_def length_modifier; + +enum ext_variables_modifier_code { + EXT_VARIABLES_MODIFIER_LOWER, + EXT_VARIABLES_MODIFIER_UPPER, + EXT_VARIABLES_MODIFIER_LOWERFIRST, + EXT_VARIABLES_MODIFIER_UPPERFIRST, + EXT_VARIABLES_MODIFIER_QUOTEWILDCARD, + EXT_VARIABLES_MODIFIER_LENGTH +}; + +const struct sieve_variables_modifier_def *ext_variables_core_modifiers[] = { + &lower_modifier, + &upper_modifier, + &lowerfirst_modifier, + &upperfirst_modifier, + "ewildcard_modifier, + &length_modifier +}; + +const unsigned int ext_variables_core_modifiers_count = + N_ELEMENTS(ext_variables_core_modifiers); + +#define ext_variables_modifier_name(modf) \ + (modf)->object->def->name +#define ext_variables_modifiers_equal(modf1, modf2) \ + ( (modf1)->def == (modf2)->def ) +#define ext_variables_modifiers_equal_precedence(modf1, modf2) \ + ( (modf1)->def->precedence == (modf2)->def->precendence ) + +/* + * Modifier registry + */ + +void sieve_variables_modifier_register +(const struct sieve_extension *var_ext, struct sieve_validator *valdtr, + const struct sieve_extension *ext, + const struct sieve_variables_modifier_def *smodf_def) +{ + struct ext_variables_validator_context *ctx = + ext_variables_validator_context_get(var_ext, valdtr); + + sieve_validator_object_registry_add(ctx->modifiers, ext, &smodf_def->obj_def); +} + +bool ext_variables_modifier_exists +(const struct sieve_extension *var_ext, struct sieve_validator *valdtr, + const char *identifier) +{ + struct ext_variables_validator_context *ctx = + ext_variables_validator_context_get(var_ext, valdtr); + + return sieve_validator_object_registry_find(ctx->modifiers, identifier, NULL); +} + +const struct sieve_variables_modifier *ext_variables_modifier_create_instance +(const struct sieve_extension *var_ext, struct sieve_validator *valdtr, + struct sieve_command *cmd, const char *identifier) +{ + struct ext_variables_validator_context *ctx = + ext_variables_validator_context_get(var_ext, valdtr); + struct sieve_object object; + struct sieve_variables_modifier *modf; + pool_t pool; + + if ( !sieve_validator_object_registry_find + (ctx->modifiers, identifier, &object) ) + return NULL; + + pool = sieve_command_pool(cmd); + modf = p_new(pool, struct sieve_variables_modifier, 1); + modf->object = object; + modf->var_ext = var_ext; + modf->def = (const struct sieve_variables_modifier_def *) object.def; + + return modf; +} + +void ext_variables_register_core_modifiers +(const struct sieve_extension *ext, struct ext_variables_validator_context *ctx) +{ + unsigned int i; + + /* Register core modifiers*/ + for ( i = 0; i < ext_variables_core_modifiers_count; i++ ) { + sieve_validator_object_registry_add + (ctx->modifiers, ext, &(ext_variables_core_modifiers[i]->obj_def)); + } +} + +/* + * Core modifiers + */ + +/* Forward declarations */ + +static bool +mod_lower_modify(const struct sieve_variables_modifier *modf, + string_t *in, string_t **result); +static bool +mod_upper_modify(const struct sieve_variables_modifier *modf, + string_t *in, string_t **result); +static bool +mod_lowerfirst_modify(const struct sieve_variables_modifier *modf, + string_t *in, string_t **result); +static bool +mod_upperfirst_modify(const struct sieve_variables_modifier *modf, + string_t *in, string_t **result); +static bool +mod_length_modify(const struct sieve_variables_modifier *modf, + string_t *in, string_t **result); +static bool +mod_quotewildcard_modify(const struct sieve_variables_modifier *modf, + string_t *in, string_t **result); + +/* Modifier objects */ + +const struct sieve_variables_modifier_def lower_modifier = { + SIEVE_OBJECT("lower", &modifier_operand, EXT_VARIABLES_MODIFIER_LOWER), + 40, + mod_lower_modify +}; + +const struct sieve_variables_modifier_def upper_modifier = { + SIEVE_OBJECT("upper", &modifier_operand, EXT_VARIABLES_MODIFIER_UPPER), + 40, + mod_upper_modify +}; + +const struct sieve_variables_modifier_def lowerfirst_modifier = { + SIEVE_OBJECT + ("lowerfirst", &modifier_operand, EXT_VARIABLES_MODIFIER_LOWERFIRST), + 30, + mod_lowerfirst_modify +}; + +const struct sieve_variables_modifier_def upperfirst_modifier = { + SIEVE_OBJECT + ("upperfirst", &modifier_operand, EXT_VARIABLES_MODIFIER_UPPERFIRST), + 30, + mod_upperfirst_modify +}; + +const struct sieve_variables_modifier_def quotewildcard_modifier = { + SIEVE_OBJECT + ("quotewildcard", &modifier_operand, EXT_VARIABLES_MODIFIER_QUOTEWILDCARD), + 20, + mod_quotewildcard_modify +}; + +const struct sieve_variables_modifier_def length_modifier = { + SIEVE_OBJECT("length", &modifier_operand, EXT_VARIABLES_MODIFIER_LENGTH), + 10, + mod_length_modify +}; + +/* Modifier implementations */ + +static bool +mod_upperfirst_modify(const struct sieve_variables_modifier *modf ATTR_UNUSED, + string_t *in, string_t **result) +{ + char *content; + + if ( str_len(in) == 0 ) { + *result = in; + return TRUE; + } + + *result = t_str_new(str_len(in)); + str_append_str(*result, in); + + content = str_c_modifiable(*result); + content[0] = i_toupper(content[0]); + + return TRUE; +} + +static bool +mod_lowerfirst_modify(const struct sieve_variables_modifier *modf ATTR_UNUSED, + string_t *in, string_t **result) +{ + char *content; + + if ( str_len(in) == 0 ) { + *result = in; + return TRUE; + } + + *result = t_str_new(str_len(in)); + str_append_str(*result, in); + + content = str_c_modifiable(*result); + content[0] = i_tolower(content[0]); + + return TRUE; +} + +static bool +mod_upper_modify(const struct sieve_variables_modifier *modf ATTR_UNUSED, + string_t *in, string_t **result) +{ + char *content; + + if ( str_len(in) == 0 ) { + *result = in; + return TRUE; + } + + *result = t_str_new(str_len(in)); + str_append_str(*result, in); + + content = str_c_modifiable(*result); + (void)str_ucase(content); + + return TRUE; +} + +static bool +mod_lower_modify(const struct sieve_variables_modifier *modf ATTR_UNUSED, + string_t *in, string_t **result) +{ + char *content; + + if ( str_len(in) == 0 ) { + *result = in; + return TRUE; + } + + *result = t_str_new(str_len(in)); + str_append_str(*result, in); + + content = str_c_modifiable(*result); + (void)str_lcase(content); + + return TRUE; +} + +static bool +mod_length_modify(const struct sieve_variables_modifier *modf ATTR_UNUSED, + string_t *in, string_t **result) +{ + *result = t_str_new(64); + str_printfa(*result, "%llu", (unsigned long long) + uni_utf8_strlen_n(str_data(in), str_len(in))); + return TRUE; +} + +static bool +mod_quotewildcard_modify(const struct sieve_variables_modifier *modf, + string_t *in, string_t **result) +{ + size_t max_var_size = + sieve_variables_get_max_variable_size(modf->var_ext); + const unsigned char *p, *poff, *pend; + size_t new_size; + + if ( str_len(in) == 0 ) { + /* empty string */ + *result = in; + return TRUE; + } + + /* allocate new string */ + new_size = str_len(in) + 16; + if (new_size > max_var_size) + new_size = max_var_size; + *result = t_str_new(new_size + 1); + + /* escape string */ + p = str_data(in); + pend = p + str_len(in); + poff = p; + while (p < pend) { + unsigned int n = uni_utf8_char_bytes((char)*p); + + if (n == 1 && (*p == '*' || *p == '?' || *p == '\\')) { + str_append_data(*result, poff, p - poff); + poff = p; + + if (str_len(*result) + 2 > max_var_size) + break; + + str_append_c(*result, '\\'); + } else if ((str_len(*result) + (p - poff) + n) > max_var_size) { + break; + } + if (p + n > pend) { + p = pend; + break; + } + p += n; + } + + str_append_data(*result, poff, p - poff); + + return TRUE; +} + +/* + * Modifier argument + */ + +/* [MODIFIER]: + * ":lower" / ":upper" / ":lowerfirst" / ":upperfirst" / + * ":quotewildcard" / ":length" + */ + +/* Forward declarations */ + +static bool tag_modifier_is_instance_of + (struct sieve_validator *valdtr, struct sieve_command *cmd, + const struct sieve_extension *ext, const char *identifier, void **context); + +/* Modifier tag object */ + +static const struct sieve_argument_def modifier_tag = { + .identifier = "MODIFIER", + .flags = SIEVE_ARGUMENT_FLAG_MULTIPLE, + .is_instance_of = tag_modifier_is_instance_of +}; + +/* Modifier tag implementation */ + +static bool tag_modifier_is_instance_of +(struct sieve_validator *valdtr, struct sieve_command *cmd, + const struct sieve_extension *ext, const char *identifier, void **data) +{ + const struct sieve_variables_modifier *modf; + + if ( data == NULL ) { + return ext_variables_modifier_exists(ext, valdtr, identifier); + } + + if ( (modf=ext_variables_modifier_create_instance + (ext, valdtr, cmd, identifier)) == NULL ) + return FALSE; + + *data = (void *) modf; + + return TRUE; +} + +/* Registration */ + +void sieve_variables_modifiers_link_tag +(struct sieve_validator *valdtr, const struct sieve_extension *var_ext, + struct sieve_command_registration *cmd_reg) +{ + sieve_validator_register_tag(valdtr, cmd_reg, var_ext, &modifier_tag, 0); +} + +/* Validation */ + +bool sieve_variables_modifiers_validate +(struct sieve_validator *valdtr, struct sieve_command *cmd, + ARRAY_TYPE(sieve_variables_modifier) *modifiers) +{ + struct sieve_ast_argument *arg; + + arg = sieve_command_first_argument(cmd); + while ( arg != NULL && arg != cmd->first_positional ) { + const struct sieve_variables_modifier *modfs; + const struct sieve_variables_modifier *modf; + unsigned int i, modf_count; + bool inserted; + + if ( !sieve_argument_is(arg, modifier_tag) ) { + arg = sieve_ast_argument_next(arg); + continue; + } + modf = (const struct sieve_variables_modifier *) + arg->argument->data; + + inserted = FALSE; + modfs = array_get(modifiers, &modf_count); + for ( i = 0; i < modf_count && !inserted; i++ ) { + + if ( modfs[i].def->precedence == modf->def->precedence ) { + sieve_argument_validate_error(valdtr, arg, + "modifiers :%s and :%s specified for the set command conflict " + "having equal precedence", + modfs[i].def->obj_def.identifier, modf->def->obj_def.identifier); + return FALSE; + } + + if ( modfs[i].def->precedence < modf->def->precedence ) { + array_insert(modifiers, i, modf, 1); + inserted = TRUE; + } + } + + if ( !inserted ) + array_append(modifiers, modf, 1); + + /* Added to modifier list; + self-destruct to prevent implicit code generation */ + arg = sieve_ast_arguments_detach(arg, 1); + } + return TRUE; +} + +bool sieve_variables_modifiers_generate +(const struct sieve_codegen_env *cgenv, + ARRAY_TYPE(sieve_variables_modifier) *modifiers) +{ + struct sieve_binary_block *sblock = cgenv->sblock; + const struct sieve_variables_modifier *modfs; + unsigned int i, modf_count; + + sieve_binary_emit_byte(sblock, array_count(modifiers)); + + modfs = array_get(modifiers, &modf_count); + for ( i = 0; i < modf_count; i++ ) { + ext_variables_opr_modifier_emit(sblock, + modfs[i].object.ext, modfs[i].def); + } + return TRUE; +} + +/* + * Modifier coding + */ + +const struct sieve_operand_class sieve_variables_modifier_operand_class = + { "modifier" }; + +static const struct sieve_extension_objects core_modifiers = + SIEVE_VARIABLES_DEFINE_MODIFIERS(ext_variables_core_modifiers); + +const struct sieve_operand_def modifier_operand = { + .name = "modifier", + .ext_def = &variables_extension, + .code = EXT_VARIABLES_OPERAND_MODIFIER, + .class = &sieve_variables_modifier_operand_class, + .interface = &core_modifiers +}; + +bool sieve_variables_modifiers_code_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + unsigned int mdfs, i; + + /* Read the number of applied modifiers we need to read */ + if ( !sieve_binary_read_byte(denv->sblock, address, &mdfs) ) + return FALSE; + + /* Print all modifiers (sorted during code generation already) */ + for ( i = 0; i < mdfs; i++ ) { + if ( !ext_variables_opr_modifier_dump(denv, address) ) + return FALSE; + } + return TRUE; +} + +int sieve_variables_modifiers_code_read( + const struct sieve_runtime_env *renv, + const struct sieve_extension *var_ext, sieve_size_t *address, + ARRAY_TYPE(sieve_variables_modifier) *modifiers) +{ + unsigned int lprec, mdfs, i; + + if ( !sieve_binary_read_byte(renv->sblock, address, &mdfs) ) { + sieve_runtime_trace_error(renv, "invalid modifier count"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + t_array_init(modifiers, mdfs); + + lprec = (unsigned int)-1; + for ( i = 0; i < mdfs; i++ ) { + struct sieve_variables_modifier modf; + + if ( !ext_variables_opr_modifier_read(renv, var_ext, + address, &modf) ) + return SIEVE_EXEC_BIN_CORRUPT; + if ( modf.def != NULL ) { + if ( modf.def->precedence >= lprec ) { + sieve_runtime_trace_error(renv, + "unsorted modifier precedence"); + return SIEVE_EXEC_BIN_CORRUPT; + } + lprec = modf.def->precedence; + } + + array_append(modifiers, &modf, 1); + } + + return SIEVE_EXEC_OK; +} + +/* + * Modifier application + */ + +int sieve_variables_modifiers_apply +(const struct sieve_runtime_env *renv, + const struct sieve_extension *var_ext, + ARRAY_TYPE(sieve_variables_modifier) *modifiers, + string_t **value) +{ + const struct ext_variables_config *config = + ext_variables_get_config(var_ext); + const struct sieve_variables_modifier *modfs; + unsigned int i, modf_count; + + /* Hold value within limits */ + if ( str_len(*value) > config->max_variable_size ) { + /* assume variable originates from code, so copy it first */ + string_t *new_value = t_str_new(config->max_variable_size+3); + str_append_str(new_value, *value); + *value = new_value; + str_truncate_utf8(*value, config->max_variable_size); + } + + if ( !array_is_created(modifiers) ) + return SIEVE_EXEC_OK; + + modfs = array_get(modifiers, &modf_count); + if ( modf_count == 0 ) + return SIEVE_EXEC_OK; + + for ( i = 0; i < modf_count; i++ ) { + string_t *new_value; + const struct sieve_variables_modifier *modf = &modfs[i]; + + if ( modf->def != NULL && modf->def->modify != NULL ) { + if ( !modf->def->modify(modf, *value, &new_value) ) + return SIEVE_EXEC_FAILURE; + + *value = new_value; + if ( *value == NULL ) + return SIEVE_EXEC_FAILURE; + + sieve_runtime_trace_here + (renv, SIEVE_TRLVL_COMMANDS, + "modify :%s \"%s\" => \"%s\"", + sieve_variables_modifier_name(modf), + str_sanitize(str_c(*value), 256), + str_sanitize(str_c(new_value), 256)); + + /* Hold value within limits */ + if ( str_len(*value) > config->max_variable_size ) + str_truncate_utf8(*value, config->max_variable_size); + } + } + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-modifiers.h b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-modifiers.h new file mode 100644 index 0000000..50b4256 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-modifiers.h @@ -0,0 +1,66 @@ +#ifndef EXT_VARIABLES_MODIFIERS_H +#define EXT_VARIABLES_MODIFIERS_H + +#include "sieve-common.h" +#include "sieve-runtime-trace.h" + +#include "ext-variables-common.h" + +#define ext_variables_namespace_name(nspc) \ + (nspc)->object->def->name +#define ext_variables_namespaces_equal(nspc1, nspc2) \ + ( (nspc1)->def == (nspc2)->def )) + +/* + * Modifier registry + */ + +bool ext_variables_modifier_exists + (const struct sieve_extension *var_ext, struct sieve_validator *valdtr, + const char *identifier); +const struct sieve_variables_modifier *ext_variables_modifier_create_instance + (const struct sieve_extension *var_ext, struct sieve_validator *valdtr, + struct sieve_command *cmd, const char *identifier); + +void ext_variables_register_core_modifiers + (const struct sieve_extension *var_ext, + struct ext_variables_validator_context *ctx); + +/* + * Modifier operand + */ + +extern const struct sieve_operand_def modifier_operand; + +static inline void ext_variables_opr_modifier_emit +(struct sieve_binary_block *sblock, const struct sieve_extension *ext, + const struct sieve_variables_modifier_def *modf_def) +{ + sieve_opr_object_emit(sblock, ext, &modf_def->obj_def); +} + +static inline bool +ext_variables_opr_modifier_read(const struct sieve_runtime_env *renv, + const struct sieve_extension *var_ext, + sieve_size_t *address, + struct sieve_variables_modifier *modf) +{ + if ( !sieve_opr_object_read + (renv, &sieve_variables_modifier_operand_class, address, &modf->object) ) { + sieve_runtime_trace_error(renv, "invalid modifier operand"); + return FALSE; + } + + modf->def = (const struct sieve_variables_modifier_def *) modf->object.def; + modf->var_ext = var_ext; + return TRUE; +} + +static inline bool ext_variables_opr_modifier_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + return sieve_opr_object_dump + (denv, &sieve_variables_modifier_operand_class, address, NULL); +} + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-name.c b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-name.c new file mode 100644 index 0000000..fac0b71 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-name.c @@ -0,0 +1,110 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "array.h" + +#include "sieve-common.h" + +#include "ext-variables-common.h" +#include "ext-variables-limits.h" +#include "ext-variables-name.h" + +#include <ctype.h> + +bool sieve_variable_identifier_is_valid(const char *identifier) +{ + const char *p = identifier; + size_t plen = strlen(identifier); + const char *pend; + + if ( plen == 0 || plen >= EXT_VARIABLES_MAX_VARIABLE_NAME_LEN ) + return FALSE; + + pend = PTR_OFFSET(identifier, plen); + + if ( *p == '_' || i_isalpha(*p) ) { + p++; + + while ( p < pend && (*p == '_' || i_isalnum(*p)) ) { + p++; + } + } + + return ( p == pend ); +} + +int ext_variable_name_parse +(ARRAY_TYPE(sieve_variable_name) *vname, const char **str, const char *strend) +{ + const char *p = *str; + + array_clear(vname); + + while ( p < strend ) { + struct sieve_variable_name *cur_element; + string_t *cur_ident; + + /* Acquire current position in the array */ + + if ( array_count(vname) >= EXT_VARIABLES_MAX_NAMESPACE_ELEMENTS ) + return -1; + + cur_element = array_append_space(vname); + cur_ident = cur_element->identifier = t_str_new(32); + + /* Parse element */ + + /* Identifier */ + if ( *p == '_' || i_isalpha(*p) ) { + cur_element->num_variable = -1; + str_truncate(cur_ident, 0); + str_append_c(cur_ident, *p); + p++; + + while ( p < strend && (*p == '_' || i_isalnum(*p)) ) { + if ( str_len(cur_ident) >= EXT_VARIABLES_MAX_VARIABLE_NAME_LEN ) + return -1; + str_append_c(cur_ident, *p); + p++; + } + + /* Num-variable */ + } else if ( i_isdigit(*p) ) { + cur_element->num_variable = *p - '0'; + p++; + + while ( p < strend && i_isdigit(*p) ) { + cur_element->num_variable = cur_element->num_variable*10 + (*p - '0'); + p++; + } + + /* If a num-variable is first, no more elements can follow because no + * namespace is specified. + */ + if ( array_count(vname) == 1 ) { + *str = p; + return 1; + } + } else { + *str = p; + return -1; + } + + /* Check whether next name element is present */ + if ( p < strend && *p == '.' ) { + p++; + + /* It may not be empty */ + if ( p >= strend ) + return -1; + } else + break; + } + + *str = p; + return array_count(vname); +} + + diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-name.h b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-name.h new file mode 100644 index 0000000..289a978 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-name.h @@ -0,0 +1,43 @@ +#ifndef EXT_VARIABLES_NAME_H +#define EXT_VARIABLES_NAME_H + +/* Variable Substitution + * --------------------- + * + * The variable strings are preprocessed into an AST list consisting of variable + * substitutions and constant parts of the string. The variables to which + * the substitutions link are looked up and their index in their scope storage + * is what is added to the list and eventually emitted as byte code. So, in + * bytecode a variable string will look as a series of substrings interrupted by + * integer operands that refer to variables. During execution, the strings and + * the looked-up variables are concatenated to obtain the desired result. The + * the variable references are simple indexes into an array of variables, so + * looking these up during execution is a trivial process. + * + * However (RFC 5229): + * Tests or actions in future extensions may need to access the + * unexpanded version of the string argument and, e.g., do the expansion + * after setting variables in its namespace. The design of the + * implementation should allow this. + * + * Various options exist to provide this feature. If the extension is entirely + * namespace-based there is actually not very much of a problem. The variable + * list can easily be extended with new argument-types that refer to a variable + * identifier instead of an index in the variable's storage. + */ + +#include "lib.h" +#include "array.h" + +#include "sieve-common.h" + +#include "ext-variables-common.h" + +/* + * Variable name parsing + */ + +int ext_variable_name_parse + (ARRAY_TYPE(sieve_variable_name) *vname, const char **str, const char *strend); + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-namespaces.c b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-namespaces.c new file mode 100644 index 0000000..4df62e2 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-namespaces.c @@ -0,0 +1,236 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-code.h" +#include "sieve-binary.h" +#include "sieve-dump.h" + +#include "ext-variables-common.h" +#include "ext-variables-namespaces.h" + +#include <ctype.h> + +/* + * Namespace registry + */ + +void sieve_variables_namespace_register +(const struct sieve_extension *var_ext, struct sieve_validator *valdtr, + const struct sieve_extension *ext, + const struct sieve_variables_namespace_def *nspc_def) +{ + struct ext_variables_validator_context *ctx = + ext_variables_validator_context_get(var_ext, valdtr); + + sieve_validator_object_registry_add(ctx->namespaces, ext, &nspc_def->obj_def); +} + +bool ext_variables_namespace_exists +(const struct sieve_extension *var_ext, struct sieve_validator *valdtr, + const char *identifier) +{ + struct ext_variables_validator_context *ctx = + ext_variables_validator_context_get(var_ext, valdtr); + + return sieve_validator_object_registry_find + (ctx->namespaces, identifier, NULL); +} + +const struct sieve_variables_namespace *ext_variables_namespace_create_instance +(const struct sieve_extension *var_ext, struct sieve_validator *valdtr, + struct sieve_command *cmd, const char *identifier) +{ + struct ext_variables_validator_context *ctx = + ext_variables_validator_context_get(var_ext, valdtr); + struct sieve_object object; + struct sieve_variables_namespace *nspc; + pool_t pool; + + if ( !sieve_validator_object_registry_find + (ctx->namespaces, identifier, &object) ) + return NULL; + + pool = sieve_command_pool(cmd); + nspc = p_new(pool, struct sieve_variables_namespace, 1); + nspc->object = object; + nspc->def = (const struct sieve_variables_namespace_def *) object.def; + + return nspc; +} + +/* + * Namespace variable argument + */ + +struct arg_namespace_variable { + const struct sieve_variables_namespace *namespace; + + void *data; +}; + +static bool arg_namespace_generate +(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *context ATTR_UNUSED); + +const struct sieve_argument_def namespace_argument = { + .identifier = "@namespace", + .generate = arg_namespace_generate +}; + +bool ext_variables_namespace_argument_activate +(const struct sieve_extension *this_ext, struct sieve_validator *valdtr, + struct sieve_ast_argument *arg, struct sieve_command *cmd, + ARRAY_TYPE(sieve_variable_name) *var_name, bool assignment) +{ + pool_t pool = sieve_command_pool(cmd); + struct sieve_ast *ast = arg->ast; + const struct sieve_variables_namespace *nspc; + struct arg_namespace_variable *var; + const struct sieve_variable_name *name_element = array_idx(var_name, 0); + void *var_data = NULL; + + nspc = ext_variables_namespace_create_instance + (this_ext, valdtr, cmd, str_c(name_element->identifier)); + if ( nspc == NULL ) { + sieve_argument_validate_error(valdtr, arg, + "referring to variable in unknown namespace '%s'", + str_c(name_element->identifier)); + return FALSE; + } + + if ( nspc->def != NULL && nspc->def->validate != NULL && + !nspc->def->validate + (valdtr, nspc, arg, cmd, var_name, &var_data, assignment) ) { + return FALSE; + } + + var = p_new(pool, struct arg_namespace_variable, 1); + var->namespace = nspc; + var->data = var_data; + + arg->argument = sieve_argument_create(ast, &namespace_argument, this_ext, 0); + arg->argument->data = (void *) var; + + return TRUE; +} + +struct sieve_ast_argument *ext_variables_namespace_argument_create +(const struct sieve_extension *this_ext, + struct sieve_validator *valdtr, struct sieve_ast_argument *parent_arg, + struct sieve_command *cmd, ARRAY_TYPE(sieve_variable_name) *var_name) +{ + struct sieve_ast *ast = parent_arg->ast; + struct sieve_ast_argument *new_arg; + + new_arg = sieve_ast_argument_create(ast, sieve_ast_argument_line(parent_arg)); + new_arg->type = SAAT_STRING; + + if ( !ext_variables_namespace_argument_activate + (this_ext, valdtr, new_arg, cmd, var_name, FALSE) ) + return NULL; + + return new_arg; +} + +static bool arg_namespace_generate +(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *cmd) +{ + struct sieve_argument *argument = arg->argument; + struct arg_namespace_variable *var = + (struct arg_namespace_variable *) argument->data; + const struct sieve_variables_namespace *nspc = var->namespace; + + if ( nspc->def != NULL && nspc->def->generate != NULL ) + return nspc->def->generate(cgenv, nspc, arg, cmd, var->data); + + return TRUE; +} + +/* + * Namespace variable operands + */ + +const struct sieve_operand_class sieve_variables_namespace_operand_class = + { "variable-namespace" }; + +static bool opr_namespace_variable_dump + (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, + sieve_size_t *address); +static int opr_namespace_variable_read + (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + sieve_size_t *address, string_t **str_r); + +static const struct sieve_opr_string_interface namespace_variable_interface = { + opr_namespace_variable_dump, + opr_namespace_variable_read +}; + +const struct sieve_operand_def namespace_variable_operand = { + .name = "namespace", + .ext_def = &variables_extension, + .code = EXT_VARIABLES_OPERAND_NAMESPACE_VARIABLE, + .class = &string_class, + .interface = &namespace_variable_interface +}; + +void sieve_variables_opr_namespace_variable_emit +(struct sieve_binary_block *sblock, const struct sieve_extension *var_ext, + const struct sieve_extension *ext, + const struct sieve_variables_namespace_def *nspc_def) +{ + i_assert( sieve_extension_is(var_ext, variables_extension) ); + sieve_operand_emit(sblock, var_ext, &namespace_variable_operand); + sieve_opr_object_emit(sblock, ext, &nspc_def->obj_def); +} + +static bool opr_namespace_variable_dump +(const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, + sieve_size_t *address) +{ + struct sieve_variables_namespace nspc; + struct sieve_operand nsoprnd; + + if ( !sieve_operand_read(denv->sblock, address, NULL, &nsoprnd) ) { + return FALSE; + } + + if ( !sieve_opr_object_read_data + (denv->sblock, &nsoprnd, &sieve_variables_namespace_operand_class, address, + &nspc.object) ) { + return FALSE; + } + + nspc.def = (const struct sieve_variables_namespace_def *) nspc.object.def; + + if ( nspc.def == NULL || nspc.def->dump_variable == NULL ) + return FALSE; + + return nspc.def->dump_variable(denv, &nspc, oprnd, address); +} + +static int opr_namespace_variable_read +(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + sieve_size_t *address, string_t **str_r) +{ + struct sieve_variables_namespace nspc; + + if ( !sieve_opr_object_read + (renv, &sieve_variables_namespace_operand_class, address, &nspc.object) ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "variable namespace operand corrupt: failed to read"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + nspc.def = (const struct sieve_variables_namespace_def *) nspc.object.def; + + if ( nspc.def == NULL || nspc.def->read_variable == NULL ) + return SIEVE_EXEC_FAILURE; + + return nspc.def->read_variable(renv, &nspc, oprnd, address, str_r); +} + + + diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-namespaces.h b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-namespaces.h new file mode 100644 index 0000000..a687582 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-namespaces.h @@ -0,0 +1,43 @@ +#ifndef EXT_VARIABLES_NAMESPACES_H +#define EXT_VARIABLES_NAMESPACES_H + +#include "sieve-common.h" + +#include "ext-variables-common.h" +#include "sieve-ext-variables.h" + +/* + * Namespace registry + */ + +bool ext_variables_namespace_exists + (const struct sieve_extension *var_ext, struct sieve_validator *valdtr, + const char *identifier); +const struct sieve_variables_namespace *ext_variables_namespace_create_instance + (const struct sieve_extension *var_ext, struct sieve_validator *valdtr, + struct sieve_command *cmd, const char *identifier); + +void ext_variables_register_core_namespaces + (const struct sieve_extension *var_ext, + struct ext_variables_validator_context *ctx); + +/* + * Namespace argument + */ + +struct sieve_ast_argument *ext_variables_namespace_argument_create + (const struct sieve_extension *this_ext, + struct sieve_validator *valdtr, struct sieve_ast_argument *parent_arg, + struct sieve_command *cmd, ARRAY_TYPE(sieve_variable_name) *var_name); +bool ext_variables_namespace_argument_activate + (const struct sieve_extension *this_ext, struct sieve_validator *valdtr, + struct sieve_ast_argument *arg, struct sieve_command *cmd, + ARRAY_TYPE(sieve_variable_name) *var_name, bool assignment); + +/* + * Namespace operand + */ + +extern const struct sieve_operand_def namespace_variable_operand; + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-operands.c b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-operands.c new file mode 100644 index 0000000..359f3bf --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-operands.c @@ -0,0 +1,279 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "hash.h" +#include "str.h" +#include "array.h" + +#include "sieve-common.h" +#include "sieve-extensions.h" +#include "sieve-ast.h" +#include "sieve-binary.h" +#include "sieve-code.h" +#include "sieve-match-types.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-dump.h" +#include "sieve-interpreter.h" + +#include "ext-variables-common.h" +#include "ext-variables-limits.h" +#include "ext-variables-name.h" +#include "ext-variables-dump.h" +#include "ext-variables-operands.h" + +/* + * Variable operand + */ + +static bool opr_variable_dump + (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, + sieve_size_t *address); +static int opr_variable_read_value + (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + sieve_size_t *address, string_t **str_r); + +const struct sieve_opr_string_interface variable_interface = { + opr_variable_dump, + opr_variable_read_value +}; + +const struct sieve_operand_def variable_operand = { + .name = "variable", + .ext_def = &variables_extension, + .code = EXT_VARIABLES_OPERAND_VARIABLE, + .class = &string_class, + .interface = &variable_interface +}; + +void sieve_variables_opr_variable_emit +(struct sieve_binary_block *sblock, const struct sieve_extension *var_ext, + struct sieve_variable *var) +{ + i_assert( sieve_extension_is(var_ext, variables_extension) ); + + if ( var->ext == NULL ) { + /* Default variable storage */ + (void) sieve_operand_emit(sblock, var_ext, &variable_operand); + (void) sieve_binary_emit_byte(sblock, 0); /* Default */ + (void) sieve_binary_emit_unsigned(sblock, var->index); + return; + } + + (void) sieve_operand_emit(sblock, var_ext, &variable_operand); + (void) sieve_binary_emit_extension(sblock, var->ext, 1); /* Extension */ + (void) sieve_binary_emit_unsigned(sblock, var->index); +} + +static bool opr_variable_dump +(const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, + sieve_size_t *address) +{ + const struct sieve_extension *this_ext = oprnd->ext; + unsigned int index = 0; + const struct sieve_extension *ext; + unsigned int code = 1; /* Initially set to offset value */ + const char *identifier; + + if ( !sieve_binary_read_extension(denv->sblock, address, &code, &ext) ) + return FALSE; + + if ( !sieve_binary_read_unsigned(denv->sblock, address, &index) ) + return FALSE; + + identifier = ext_variables_dump_get_identifier(this_ext, denv, ext, index); + identifier = identifier == NULL ? "??" : identifier; + + if ( oprnd->field_name != NULL ) + sieve_code_dumpf(denv, "%s: VAR[%s] ${%s}", + oprnd->field_name, sieve_ext_variables_get_varid(ext, index), identifier); + else + sieve_code_dumpf(denv, "VAR[%s] ${%s}", + sieve_ext_variables_get_varid(ext, index), identifier); + + return TRUE; +} + +static int opr_variable_read_value +(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + sieve_size_t *address, string_t **str_r) +{ + const struct sieve_extension *this_ext = oprnd->ext; + const struct sieve_extension *ext; + unsigned int code = 1; /* Initially set to offset value */ + struct sieve_variable_storage *storage; + unsigned int index = 0; + + if ( !sieve_binary_read_extension(renv->sblock, address, &code, &ext) ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "variable operand corrupt: invalid extension byte"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + storage = sieve_ext_variables_runtime_get_storage + (this_ext, renv, ext); + if ( storage == NULL ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "variable operand corrupt: extension has no storage"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if ( sieve_binary_read_unsigned(renv->sblock, address, &index) ) { + /* Parameter str can be NULL if we are requested to only skip and not + * actually read the argument. + */ + if ( str_r != NULL ) { + if ( !sieve_variable_get(storage, index, str_r) ) + return SIEVE_EXEC_FAILURE; + + if ( *str_r == NULL ) *str_r = t_str_new(0); + } + + return SIEVE_EXEC_OK; + } + + sieve_runtime_trace_operand_error(renv, oprnd, + "variable operand corrupt: invalid variable index"); + return SIEVE_EXEC_BIN_CORRUPT; +} + +int sieve_variable_operand_read_data +(const struct sieve_runtime_env *renv, struct sieve_operand *oprnd, + sieve_size_t *address, const char *field_name, + struct sieve_variable_storage **storage_r, unsigned int *var_index_r) +{ + const struct sieve_extension *ext; + unsigned int code = 1; /* Initially set to offset value */ + unsigned int idx = 0; + + oprnd->field_name = field_name; + + if ( !sieve_operand_is_variable(oprnd) ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "expected variable operand but found %s", sieve_operand_name(oprnd)); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if ( !sieve_binary_read_extension(renv->sblock, address, &code, &ext) ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "variable operand corrupt: invalid extension byte"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + *storage_r = sieve_ext_variables_runtime_get_storage + (oprnd->ext, renv, ext); + if ( *storage_r == NULL ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "variable operand corrupt: extension has no storage"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if ( !sieve_binary_read_unsigned(renv->sblock, address, &idx) ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "variable operand corrupt: invalid variable index"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + *var_index_r = idx; + return SIEVE_EXEC_OK; +} + +int sieve_variable_operand_read +(const struct sieve_runtime_env *renv, sieve_size_t *address, + const char *field_name, struct sieve_variable_storage **storage_r, + unsigned int *var_index_r) +{ + struct sieve_operand operand; + int ret; + + if ( (ret=sieve_operand_runtime_read(renv, address, field_name, &operand)) + <= 0) + return ret; + + return sieve_variable_operand_read_data + (renv, &operand, address, field_name, storage_r, var_index_r); +} + +/* + * Match value operand + */ + +static bool opr_match_value_dump + (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, + sieve_size_t *address); +static int opr_match_value_read + (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + sieve_size_t *address, string_t **str_r); + +const struct sieve_opr_string_interface match_value_interface = { + opr_match_value_dump, + opr_match_value_read +}; + +const struct sieve_operand_def match_value_operand = { + .name = "match-value", + .ext_def = &variables_extension, + .code = EXT_VARIABLES_OPERAND_MATCH_VALUE, + .class = &string_class, + .interface = &match_value_interface +}; + +void sieve_variables_opr_match_value_emit +(struct sieve_binary_block *sblock, const struct sieve_extension *var_ext, + unsigned int index) +{ + i_assert( sieve_extension_is(var_ext, variables_extension) ); + (void) sieve_operand_emit(sblock, var_ext, &match_value_operand); + (void) sieve_binary_emit_unsigned(sblock, index); +} + +static bool opr_match_value_dump +(const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, + sieve_size_t *address) +{ + unsigned int index = 0; + + if (sieve_binary_read_unsigned(denv->sblock, address, &index) ) { + if ( oprnd->field_name != NULL ) + sieve_code_dumpf + (denv, "%s: MATCHVAL %lu", oprnd->field_name, (unsigned long) index); + else + sieve_code_dumpf(denv, "MATCHVAL %lu", (unsigned long) index); + + return TRUE; + } + + return FALSE; +} + +static int opr_match_value_read +(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + sieve_size_t *address, string_t **str_r) +{ + const struct sieve_extension *this_ext = oprnd->ext; + const struct ext_variables_config *config = + ext_variables_get_config(this_ext); + unsigned int index = 0; + + if ( sieve_binary_read_unsigned(renv->sblock, address, &index) ) { + /* Parameter str can be NULL if we are requested to only skip and not + * actually read the argument. + */ + if ( str_r != NULL ) { + sieve_match_values_get(renv, index, str_r); + + if ( *str_r == NULL ) + *str_r = t_str_new(0); + else if ( str_len(*str_r) > config->max_variable_size ) + str_truncate_utf8(*str_r, config->max_variable_size); + } + + return SIEVE_EXEC_OK; + } + + sieve_runtime_trace_operand_error(renv, oprnd, + "match value operand corrupt: invalid index data"); + return SIEVE_EXEC_BIN_CORRUPT; +} diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-operands.h b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-operands.h new file mode 100644 index 0000000..64a7781 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-operands.h @@ -0,0 +1,37 @@ +#ifndef EXT_VARIABLES_OPERANDS_H +#define EXT_VARIABLES_OPERANDS_H + +#include "lib.h" +#include "hash.h" +#include "str.h" +#include "array.h" + +#include "sieve-common.h" +#include "ext-variables-common.h" + +/* + * Variable operand + */ + +extern const struct sieve_operand_def variable_operand; + +bool ext_variables_opr_variable_read + (const struct sieve_runtime_env *renv, sieve_size_t *address, + struct sieve_variable_storage **storage, unsigned int *var_index); + +/* + * Match value operand + */ + +extern const struct sieve_operand_def match_value_operand; + +/* + * Variable string operand + */ + +void ext_variables_opr_variable_string_emit + (struct sieve_binary *sbin, unsigned int elements); + + +#endif + diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables.c b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables.c new file mode 100644 index 0000000..6bdef58 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables.c @@ -0,0 +1,84 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension variables + * ------------------- + * + * Authors: Stephan Bosch + * Specification: RFC 5229 + * Implementation: full + * Status: testing + * + */ + +#include "lib.h" +#include "str.h" +#include "unichar.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-binary.h" +#include "sieve-interpreter.h" + +#include "sieve-validator.h" + +#include "ext-variables-common.h" +#include "ext-variables-arguments.h" +#include "ext-variables-operands.h" +#include "ext-variables-namespaces.h" +#include "ext-variables-modifiers.h" +#include "ext-variables-dump.h" + +/* + * Operations + */ + +const struct sieve_operation_def *ext_variables_operations[] = { + &cmd_set_operation, + &tst_string_operation +}; + +/* + * Operands + */ + +const struct sieve_operand_def *ext_variables_operands[] = { + &variable_operand, + &match_value_operand, + &namespace_variable_operand, + &modifier_operand +}; + +/* + * Extension + */ + +static bool ext_variables_validator_load + (const struct sieve_extension *ext, struct sieve_validator *validator); + +const struct sieve_extension_def variables_extension = { + .name = "variables", + .load = ext_variables_load, + .unload = ext_variables_unload, + .validator_load = ext_variables_validator_load, + .generator_load = ext_variables_generator_load, + .interpreter_load = ext_variables_interpreter_load, + .code_dump = ext_variables_code_dump, + SIEVE_EXT_DEFINE_OPERATIONS(ext_variables_operations), + SIEVE_EXT_DEFINE_OPERANDS(ext_variables_operands) +}; + +static bool ext_variables_validator_load +(const struct sieve_extension *ext, struct sieve_validator *validator) +{ + sieve_validator_argument_override + (validator, SAT_VAR_STRING, ext, &variable_string_argument); + + sieve_validator_register_command(validator, ext, &cmd_set); + sieve_validator_register_command(validator, ext, &tst_string); + + ext_variables_validator_initialize(ext, validator); + + return TRUE; +} + diff --git a/pigeonhole/src/lib-sieve/plugins/variables/sieve-ext-variables.h b/pigeonhole/src/lib-sieve/plugins/variables/sieve-ext-variables.h new file mode 100644 index 0000000..ce0d0bc --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/variables/sieve-ext-variables.h @@ -0,0 +1,364 @@ +#ifndef SIEVE_EXT_VARIABLES_H +#define SIEVE_EXT_VARIABLES_H + +#include "lib.h" + +#include "sieve-common.h" +#include "sieve-extensions.h" +#include "sieve-objects.h" +#include "sieve-code.h" + +/* Public interface for other extensions to use + */ + +/* + * Limits + */ + +unsigned int +sieve_variables_get_max_scope_size(const struct sieve_extension *var_ext); +size_t +sieve_variables_get_max_variable_size(const struct sieve_extension *var_ext); + +/* + * Variable extension + */ + +/* FIXME: this is not suitable for future plugin support */ + +extern const struct sieve_extension_def variables_extension; + +static inline const struct sieve_extension *sieve_ext_variables_get_extension +(struct sieve_instance *svinst) +{ + return sieve_extension_register(svinst, &variables_extension, FALSE); +} + +/* + * Variable name + */ + +struct sieve_variable_name { + string_t *identifier; + int num_variable; +}; + +ARRAY_DEFINE_TYPE(sieve_variable_name, struct sieve_variable_name); + +bool sieve_variable_identifier_is_valid(const char *identifier); + +/* + * Variable scope + */ + +struct sieve_variable { + const char *identifier; + unsigned int index; + + const struct sieve_extension *ext; + void *context; +}; + +struct sieve_variable_scope; + +struct sieve_variable_scope *sieve_variable_scope_create + (struct sieve_instance *svinst, const struct sieve_extension *var_ext, + const struct sieve_extension *ext); +void sieve_variable_scope_ref + (struct sieve_variable_scope *scope); +void sieve_variable_scope_unref + (struct sieve_variable_scope **scope); +pool_t sieve_variable_scope_pool + (struct sieve_variable_scope *scope); + +struct sieve_variable *sieve_variable_scope_declare + (struct sieve_variable_scope *scope, const char *identifier); +struct sieve_variable *sieve_variable_scope_import + (struct sieve_variable_scope *scope, struct sieve_variable *var); +struct sieve_variable *sieve_variable_scope_get_variable + (struct sieve_variable_scope *scope, const char *identifier); +struct sieve_variable *sieve_variable_scope_get_indexed + (struct sieve_variable_scope *scope, unsigned int index); + +/* Binary */ + +struct sieve_variable_scope_binary *sieve_variable_scope_binary_create + (struct sieve_variable_scope *scope); + +void sieve_variable_scope_binary_ref + (struct sieve_variable_scope_binary *scpbin); +void sieve_variable_scope_binary_unref + (struct sieve_variable_scope_binary **scpbin); + +struct sieve_variable_scope *sieve_variable_scope_binary_dump + (struct sieve_instance *svinst, + const struct sieve_extension *var_ext, + const struct sieve_extension *ext, + const struct sieve_dumptime_env *denv, sieve_size_t *address); +struct sieve_variable_scope_binary *sieve_variable_scope_binary_read + (struct sieve_instance *svinst, + const struct sieve_extension *var_ext, + const struct sieve_extension *ext, + struct sieve_binary_block *sblock, sieve_size_t *address); + +struct sieve_variable_scope *sieve_variable_scope_binary_get + (struct sieve_variable_scope_binary *scpbin); +unsigned int sieve_variable_scope_binary_get_size + (struct sieve_variable_scope_binary *scpbin); + +/* + * Variable namespaces + */ + +struct sieve_variables_namespace; + +struct sieve_variables_namespace_def { + struct sieve_object_def obj_def; + + bool (*validate) + (struct sieve_validator *valdtr, + const struct sieve_variables_namespace *nspc, + struct sieve_ast_argument *arg, struct sieve_command *cmd, + ARRAY_TYPE(sieve_variable_name) *var_name, void **var_data, + bool assignment); + bool (*generate) + (const struct sieve_codegen_env *cgenv, + const struct sieve_variables_namespace *nspc, + struct sieve_ast_argument *arg, struct sieve_command *cmd, + void *var_data); + + bool (*dump_variable) + (const struct sieve_dumptime_env *denv, + const struct sieve_variables_namespace *nspc, + const struct sieve_operand *oprnd, sieve_size_t *address); + int (*read_variable) + (const struct sieve_runtime_env *renv, + const struct sieve_variables_namespace *nspc, + const struct sieve_operand *oprnd, sieve_size_t *address, string_t **str); +}; + +#define SIEVE_VARIABLES_DEFINE_NAMESPACE(OP) SIEVE_EXT_DEFINE_OBJECT(OP) +#define SIEVE_VARIABLES_DEFINE_NAMESPACES(OPS) SIEVE_EXT_DEFINE_OBJECTS(OPS) + +struct sieve_variables_namespace { + struct sieve_object object; + + const struct sieve_variables_namespace_def *def; +}; + +void sieve_variables_namespace_register +(const struct sieve_extension *var_ext, struct sieve_validator *valdtr, + const struct sieve_extension *ext, + const struct sieve_variables_namespace_def *nspc_def); + +extern const struct sieve_operand_class sieve_variables_namespace_operand_class; + +void sieve_variables_opr_namespace_variable_emit + (struct sieve_binary_block *sblock, const struct sieve_extension *var_ext, + const struct sieve_extension *ext, + const struct sieve_variables_namespace_def *nspc_def); + +/* Iteration over all declared variables */ + +struct sieve_variable_scope_iter; + +struct sieve_variable_scope_iter *sieve_variable_scope_iterate_init + (struct sieve_variable_scope *scope); +bool sieve_variable_scope_iterate + (struct sieve_variable_scope_iter *iter, struct sieve_variable **var_r); +void sieve_variable_scope_iterate_deinit + (struct sieve_variable_scope_iter **iter); + +/* Statistics */ + +unsigned int sieve_variable_scope_declarations + (struct sieve_variable_scope *scope); +unsigned int sieve_variable_scope_size + (struct sieve_variable_scope *scope); + +/* Get all native variables */ + +struct sieve_variable * const *sieve_variable_scope_get_variables + (struct sieve_variable_scope *scope, unsigned int *size_r); + +/* + * Variable storage + */ + +struct sieve_variable_storage; + +struct sieve_variable_storage *sieve_variable_storage_create + (const struct sieve_extension *var_ext, pool_t pool, + struct sieve_variable_scope_binary *scpbin); +bool sieve_variable_get + (struct sieve_variable_storage *storage, unsigned int index, + string_t **value); +bool sieve_variable_get_modifiable + (struct sieve_variable_storage *storage, unsigned int index, + string_t **value); +bool sieve_variable_assign + (struct sieve_variable_storage *storage, unsigned int index, + const string_t *value); +bool sieve_variable_assign_cstr + (struct sieve_variable_storage *storage, unsigned int index, + const char *value); +bool sieve_variable_get_identifier + (struct sieve_variable_storage *storage, unsigned int index, + const char **identifier); +const char *sieve_variable_get_varid + (struct sieve_variable_storage *storage, unsigned int index); + +/* + * Variables access + */ + +bool sieve_ext_variables_is_active + (const struct sieve_extension *var_ext, struct sieve_validator *valdtr); + +struct sieve_variable_scope *sieve_ext_variables_get_local_scope + (const struct sieve_extension *var_ext, struct sieve_validator *valdtr); + +/* Runtime */ + +static inline const char *sieve_ext_variables_get_varid +(const struct sieve_extension *ext, unsigned int index) +{ + if ( ext == NULL ) + return t_strdup_printf("%ld", (long) index); + + return t_strdup_printf("%s:%ld", sieve_extension_name(ext), (long) index); +} + +struct sieve_variable_storage *sieve_ext_variables_runtime_get_storage + (const struct sieve_extension *var_ext, const struct sieve_runtime_env *renv, + const struct sieve_extension *ext); +void sieve_ext_variables_runtime_set_storage + (const struct sieve_extension *var_ext, const struct sieve_runtime_env *renv, + const struct sieve_extension *ext, struct sieve_variable_storage *storage); + +const char *sieve_ext_variables_runtime_get_identifier +(const struct sieve_extension *var_ext, const struct sieve_runtime_env *renv, + const struct sieve_extension *ext, unsigned int index); + +/* + * Variable arguments + */ + +bool sieve_variable_argument_activate + (const struct sieve_extension *var_ext, + const struct sieve_extension *this_ext, + struct sieve_validator *valdtr, struct sieve_command *cmd, + struct sieve_ast_argument *arg, bool assignment); + +/* + * Variable operands + */ + +extern const struct sieve_operand_def variable_operand; + +void sieve_variables_opr_variable_emit + (struct sieve_binary_block *sblock, const struct sieve_extension *var_ext, + struct sieve_variable *var); +void sieve_variables_opr_match_value_emit + (struct sieve_binary_block *sblock, const struct sieve_extension *var_ext, + unsigned int index); + +int sieve_variable_operand_read_data + (const struct sieve_runtime_env *renv, struct sieve_operand *operand, + sieve_size_t *address, const char *field_name, + struct sieve_variable_storage **storage_r, unsigned int *var_index_r); +int sieve_variable_operand_read + (const struct sieve_runtime_env *renv, sieve_size_t *address, + const char *field_name, struct sieve_variable_storage **storage_r, + unsigned int *var_index_r); + +static inline bool sieve_operand_is_variable +(const struct sieve_operand *operand) +{ + return ( operand != NULL && operand->def != NULL && + operand->def == &variable_operand ); +} + +/* + * Modifiers + */ + +/* Definition */ + +struct sieve_variables_modifier; + +struct sieve_variables_modifier_def { + struct sieve_object_def obj_def; + + unsigned int precedence; + + bool (*modify)(const struct sieve_variables_modifier *modf, + string_t *in, string_t **result); +}; + +struct sieve_variables_modifier { + struct sieve_object object; + const struct sieve_extension *var_ext; + + const struct sieve_variables_modifier_def *def; +}; + +#define SIEVE_VARIABLES_DEFINE_MODIFIER(OP) SIEVE_EXT_DEFINE_OBJECT(OP) +#define SIEVE_VARIABLES_DEFINE_MODIFIERS(OPS) SIEVE_EXT_DEFINE_OBJECTS(OPS) + +#define sieve_variables_modifier_name(smodf) \ + ( (smodf)->object.def->identifier ) + +ARRAY_DEFINE_TYPE(sieve_variables_modifier, + struct sieve_variables_modifier); + +/* Registry */ + +void sieve_variables_modifier_register + (const struct sieve_extension *var_ext, struct sieve_validator *valdtr, + const struct sieve_extension *ext, + const struct sieve_variables_modifier_def *smodf); + +/* Tagged argument */ + +void sieve_variables_modifiers_link_tag + (struct sieve_validator *valdtr, const struct sieve_extension *var_ext, + struct sieve_command_registration *cmd_reg); + +bool sieve_variables_modifiers_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd, + ARRAY_TYPE(sieve_variables_modifier) *modifiers); + +bool sieve_variables_modifiers_generate + (const struct sieve_codegen_env *cgenv, + ARRAY_TYPE(sieve_variables_modifier) *modifiers); + +/* Coding */ + +extern const struct sieve_operand_class + sieve_variables_modifier_operand_class; + +bool sieve_variables_modifiers_code_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +int sieve_variables_modifiers_code_read( + const struct sieve_runtime_env *renv, + const struct sieve_extension *var_ext, sieve_size_t *address, + ARRAY_TYPE(sieve_variables_modifier) *modifiers); + +/* Application */ + +int sieve_variables_modifiers_apply +(const struct sieve_runtime_env *renv, + const struct sieve_extension *var_ext, + ARRAY_TYPE(sieve_variables_modifier) *modifiers, + string_t **value); + +/* + * Code dumping + */ + +void sieve_ext_variables_dump_set_scope +(const struct sieve_extension *var_ext, const struct sieve_dumptime_env *denv, + const struct sieve_extension *ext, struct sieve_variable_scope *scope); + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/variables/tst-string.c b/pigeonhole/src/lib-sieve/plugins/variables/tst-string.c new file mode 100644 index 0000000..91b0b82 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/variables/tst-string.c @@ -0,0 +1,271 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-match.h" + +#include "ext-variables-common.h" + +/* + * String test + * + * Syntax: + * string [COMPARATOR] [MATCH-TYPE] + * <source: string-list> <key-list: string-list> + */ + +static bool tst_string_registered + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool tst_string_validate + (struct sieve_validator *valdtr, struct sieve_command *tst); +static bool tst_string_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx); + +const struct sieve_command_def tst_string = { + .identifier = "string", + .type = SCT_TEST, + .positional_args = 2, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = tst_string_registered, + .validate = tst_string_validate, + .generate = tst_string_generate +}; + +/* + * String operation + */ + +static bool tst_string_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int tst_string_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def tst_string_operation = { + .mnemonic = "STRING", + .ext_def = &variables_extension, + .code = EXT_VARIABLES_OPERATION_STRING, + .dump = tst_string_operation_dump, + .execute = tst_string_operation_execute +}; + +/* + * Optional arguments + */ + +enum tst_string_optional { + OPT_END, + OPT_COMPARATOR, + OPT_MATCH_TYPE +}; + +/* + * Test registration + */ + +static bool tst_string_registered +(struct sieve_validator *valdtr, const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_command_registration *cmd_reg) +{ + /* The order of these is not significant */ + sieve_comparators_link_tag(valdtr, cmd_reg, OPT_COMPARATOR); + sieve_match_types_link_tags(valdtr, cmd_reg, OPT_MATCH_TYPE); + + return TRUE; +} + +/* + * Test validation + */ + +static bool tst_string_validate +(struct sieve_validator *valdtr, struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + const struct sieve_match_type mcht_default = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + const struct sieve_comparator cmp_default = + SIEVE_COMPARATOR_DEFAULT(i_octet_comparator); + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "source", 1, SAAT_STRING_LIST) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) + return FALSE; + + arg = sieve_ast_argument_next(arg); + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "key list", 2, SAAT_STRING_LIST) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) + return FALSE; + + /* Validate the key argument to a specified match type */ + return sieve_match_type_validate + (valdtr, tst, arg, &mcht_default, &cmp_default); +} + +/* + * Test generation + */ + +static bool tst_string_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + sieve_operation_emit(cgenv->sblock, cmd->ext, &tst_string_operation); + + /* Generate arguments */ + if ( !sieve_generate_arguments(cgenv, cmd, NULL) ) + return FALSE; + + return TRUE; +} + +/* + * Code dump + */ + +static bool tst_string_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + sieve_code_dumpf(denv, "STRING-TEST"); + sieve_code_descend(denv); + + /* Optional operands */ + if ( sieve_match_opr_optional_dump(denv, address, NULL) != 0 ) + return FALSE; + + return + sieve_opr_stringlist_dump(denv, address, "source") && + sieve_opr_stringlist_dump(denv, address, "key list"); +} + +/* + * Code execution + */ + +static int tst_string_stringlist_next_item + (struct sieve_stringlist *_strlist, string_t **str_r); +static void tst_string_stringlist_reset + (struct sieve_stringlist *_strlist); +static int tst_string_stringlist_get_length + (struct sieve_stringlist *_strlist); + +struct tst_string_stringlist { + struct sieve_stringlist strlist; + + struct sieve_stringlist *value_list; +}; + +static struct sieve_stringlist *tst_string_stringlist_create +(const struct sieve_runtime_env *renv, struct sieve_stringlist *value_list) +{ + struct tst_string_stringlist *strlist; + + strlist = t_new(struct tst_string_stringlist, 1); + strlist->strlist.runenv = renv; + strlist->strlist.exec_status = SIEVE_EXEC_OK; + strlist->strlist.next_item = tst_string_stringlist_next_item; + strlist->strlist.reset = tst_string_stringlist_reset; + strlist->strlist.get_length = tst_string_stringlist_get_length; + strlist->value_list = value_list; + + return &strlist->strlist; +} + +static int tst_string_stringlist_next_item +(struct sieve_stringlist *_strlist, string_t **str_r) +{ + struct tst_string_stringlist *strlist = + (struct tst_string_stringlist *)_strlist; + + return sieve_stringlist_next_item(strlist->value_list, str_r); +} + +static void tst_string_stringlist_reset +(struct sieve_stringlist *_strlist) +{ + struct tst_string_stringlist *strlist = + (struct tst_string_stringlist *)_strlist; + + sieve_stringlist_reset(strlist->value_list); +} + +static int tst_string_stringlist_get_length +(struct sieve_stringlist *_strlist) +{ + struct tst_string_stringlist *strlist = + (struct tst_string_stringlist *)_strlist; + string_t *item; + int length = 0; + int ret; + + while ( (ret=sieve_stringlist_next_item(strlist->value_list, &item)) > 0 ) { + if ( str_len(item) > 0 ) + length++; + } + + return ( ret < 0 ? -1 : length ); +} + +static int tst_string_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + struct sieve_match_type mcht = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + struct sieve_comparator cmp = + SIEVE_COMPARATOR_DEFAULT(i_octet_comparator); + struct sieve_stringlist *source, *value_list, *key_list; + int match, ret; + + /* + * Read operands + */ + + /* Handle match-type and comparator operands */ + if ( sieve_match_opr_optional_read + (renv, address, NULL, &ret, &cmp, &mcht) < 0 ) + return ret; + + /* Read source */ + if ( (ret=sieve_opr_stringlist_read(renv, address, "source", &source)) <= 0 ) + return ret; + + /* Read key-list */ + if ( (ret=sieve_opr_stringlist_read(renv, address, "key-list", &key_list)) + <= 0 ) + return ret; + + /* + * Perform operation + */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "string test"); + + /* Create wrapper string list wich does not count empty string items */ + value_list = tst_string_stringlist_create(renv, source); + + /* Perform match */ + if ( (match=sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret)) < 0 ) + return ret; + + /* Set test result for subsequent conditional jump */ + sieve_interpreter_set_test_result(renv->interp, match > 0); + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/Makefile.am b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/Makefile.am new file mode 100644 index 0000000..f409e2b --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS = debug environment report + diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/Makefile.in b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/Makefile.in new file mode 100644 index 0000000..43680f1 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/Makefile.in @@ -0,0 +1,692 @@ +# 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/plugins/vnd.dovecot +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 $(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 = +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 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +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 +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +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@ +SUBDIRS = debug environment report +all: all-recursive + +.SUFFIXES: +$(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/plugins/vnd.dovecot/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/vnd.dovecot/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): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(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-recursive + +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-recursive + +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 + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +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-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-libtool cscopelist-am ctags \ + ctags-am distclean 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 \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean 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/plugins/vnd.dovecot/debug/Makefile.am b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/Makefile.am new file mode 100644 index 0000000..1e08946 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/Makefile.am @@ -0,0 +1,15 @@ +noinst_LTLIBRARIES = libsieve_ext_debug.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../../.. \ + $(LIBDOVECOT_INCLUDE) + +commands = \ + cmd-debug-log.c + +libsieve_ext_debug_la_SOURCES = \ + $(commands) \ + ext-debug.c + +noinst_HEADERS = \ + ext-debug-common.h diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/Makefile.in b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/Makefile.in new file mode 100644 index 0000000..f9b5450 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/Makefile.in @@ -0,0 +1,687 @@ +# 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/plugins/vnd.dovecot/debug +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_ext_debug_la_LIBADD = +am__objects_1 = cmd-debug-log.lo +am_libsieve_ext_debug_la_OBJECTS = $(am__objects_1) ext-debug.lo +libsieve_ext_debug_la_OBJECTS = $(am_libsieve_ext_debug_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)/cmd-debug-log.Plo \ + ./$(DEPDIR)/ext-debug.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_ext_debug_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_debug_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_ext_debug.la +AM_CPPFLAGS = \ + -I$(srcdir)/../../.. \ + $(LIBDOVECOT_INCLUDE) + +commands = \ + cmd-debug-log.c + +libsieve_ext_debug_la_SOURCES = \ + $(commands) \ + ext-debug.c + +noinst_HEADERS = \ + ext-debug-common.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/plugins/vnd.dovecot/debug/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/vnd.dovecot/debug/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_ext_debug.la: $(libsieve_ext_debug_la_OBJECTS) $(libsieve_ext_debug_la_DEPENDENCIES) $(EXTRA_libsieve_ext_debug_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_debug_la_OBJECTS) $(libsieve_ext_debug_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-debug-log.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-debug.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)/cmd-debug-log.Plo + -rm -f ./$(DEPDIR)/ext-debug.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)/cmd-debug-log.Plo + -rm -f ./$(DEPDIR)/ext-debug.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/plugins/vnd.dovecot/debug/cmd-debug-log.c b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/cmd-debug-log.c new file mode 100644 index 0000000..355daa5 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/cmd-debug-log.c @@ -0,0 +1,130 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str-sanitize.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-code.h" + +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-binary.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "ext-debug-common.h" + +/* + * Debug_log command + * + * Syntax + * debug_log <message: string> + */ + +static bool cmd_debug_log_validate + (struct sieve_validator *valdtr, struct sieve_command *tst); +static bool cmd_debug_log_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx); + +const struct sieve_command_def debug_log_command = { + .identifier = "debug_log", + .type = SCT_COMMAND, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = cmd_debug_log_validate, + .generate = cmd_debug_log_generate +}; + +/* + * Body operation + */ + +static bool cmd_debug_log_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int cmd_debug_log_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def debug_log_operation = { + .mnemonic = "DEBUG_LOG", + .ext_def = &vnd_debug_extension, + .dump = cmd_debug_log_operation_dump, + .execute = cmd_debug_log_operation_execute +}; + +/* + * Validation + */ + +static bool cmd_debug_log_validate +(struct sieve_validator *valdtr, struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "message", 1, SAAT_STRING) ) { + return FALSE; + } + + return sieve_validator_argument_activate(valdtr, tst, arg, FALSE); +} + +/* + * Code generation + */ + +static bool cmd_debug_log_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + (void)sieve_operation_emit(cgenv->sblock, cmd->ext, &debug_log_operation); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, cmd, NULL); +} + +/* + * Code dump + */ + +static bool cmd_debug_log_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + sieve_code_dumpf(denv, "DEBUG_LOG"); + sieve_code_descend(denv); + + return sieve_opr_string_dump(denv, address, "key list"); +} + +/* + * Interpretation + */ + +static int cmd_debug_log_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + string_t *message; + int ret; + + /* + * Read operands + */ + + /* Read message */ + + if ( (ret=sieve_opr_string_read(renv, address, "message", &message)) <= 0 ) + return ret; + + /* + * Perform operation + */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "debug_log \"%s\"", + str_sanitize(str_c(message), 80)); + + sieve_runtime_log(renv, NULL, "DEBUG: %s", str_c(message)); + + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/ext-debug-common.h b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/ext-debug-common.h new file mode 100644 index 0000000..88ae3db --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/ext-debug-common.h @@ -0,0 +1,22 @@ +#ifndef EXT_DEBUG_COMMON_H +#define EXT_DEBUG_COMMON_H + +/* + * Extensions + */ + +extern const struct sieve_extension_def vnd_debug_extension; + +/* + * Commands + */ + +extern const struct sieve_command_def debug_log_command; + +/* + * Operations + */ + +extern const struct sieve_operation_def debug_log_operation; + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/ext-debug.c b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/ext-debug.c new file mode 100644 index 0000000..2781e83 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/ext-debug.c @@ -0,0 +1,70 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension debug + * --------------- + * + * Authors: Stephan Bosch + * Specification: vendor-defined; spec-bosch-sieve-debug + * Implementation: full + * Status: experimental + * + */ + +#include "lib.h" +#include "array.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-address-parts.h" + +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-binary.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "ext-debug-common.h" + +/* + * Extension + */ + +static bool ext_debug_validator_load + (const struct sieve_extension *ext, struct sieve_validator *validator); +static bool ext_debug_interpreter_load + (const struct sieve_extension *ext ATTR_UNUSED, + const struct sieve_runtime_env *renv, sieve_size_t *address ATTR_UNUSED); + + +const struct sieve_extension_def vnd_debug_extension = { + .name = "vnd.dovecot.debug", + .validator_load = ext_debug_validator_load, + .interpreter_load = ext_debug_interpreter_load, + SIEVE_EXT_DEFINE_OPERATION(debug_log_operation), +}; + +static bool ext_debug_validator_load +(const struct sieve_extension *ext, struct sieve_validator *validator) +{ + /* Register new test */ + sieve_validator_register_command(validator, ext, &debug_log_command); + + return TRUE; +} + +static bool ext_debug_interpreter_load +(const struct sieve_extension *ext ATTR_UNUSED, + const struct sieve_runtime_env *renv, sieve_size_t *address ATTR_UNUSED) +{ + if ( renv->ehandler != NULL ) { + sieve_error_handler_accept_infolog(renv->ehandler, TRUE); + } + + return TRUE; +} + + + diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/Makefile.am b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/Makefile.am new file mode 100644 index 0000000..96618ff --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/Makefile.am @@ -0,0 +1,16 @@ +noinst_LTLIBRARIES = libsieve_ext_vnd_environment.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../../.. \ + -I$(srcdir)/../../environment \ + -I$(srcdir)/../../variables \ + $(LIBDOVECOT_INCLUDE) + +libsieve_ext_vnd_environment_la_SOURCES = \ + ext-vnd-environment.c \ + ext-vnd-environment-items.c \ + ext-vnd-environment-variables.c + +noinst_HEADERS = \ + ext-vnd-environment-common.h + diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/Makefile.in b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/Makefile.in new file mode 100644 index 0000000..b5d8b20 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/Makefile.in @@ -0,0 +1,692 @@ +# 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/plugins/vnd.dovecot/environment +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_ext_vnd_environment_la_LIBADD = +am_libsieve_ext_vnd_environment_la_OBJECTS = ext-vnd-environment.lo \ + ext-vnd-environment-items.lo ext-vnd-environment-variables.lo +libsieve_ext_vnd_environment_la_OBJECTS = \ + $(am_libsieve_ext_vnd_environment_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)/ext-vnd-environment-items.Plo \ + ./$(DEPDIR)/ext-vnd-environment-variables.Plo \ + ./$(DEPDIR)/ext-vnd-environment.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_ext_vnd_environment_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_vnd_environment_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_ext_vnd_environment.la +AM_CPPFLAGS = \ + -I$(srcdir)/../../.. \ + -I$(srcdir)/../../environment \ + -I$(srcdir)/../../variables \ + $(LIBDOVECOT_INCLUDE) + +libsieve_ext_vnd_environment_la_SOURCES = \ + ext-vnd-environment.c \ + ext-vnd-environment-items.c \ + ext-vnd-environment-variables.c + +noinst_HEADERS = \ + ext-vnd-environment-common.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/plugins/vnd.dovecot/environment/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/vnd.dovecot/environment/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_ext_vnd_environment.la: $(libsieve_ext_vnd_environment_la_OBJECTS) $(libsieve_ext_vnd_environment_la_DEPENDENCIES) $(EXTRA_libsieve_ext_vnd_environment_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_vnd_environment_la_OBJECTS) $(libsieve_ext_vnd_environment_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-vnd-environment-items.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-vnd-environment-variables.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-vnd-environment.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)/ext-vnd-environment-items.Plo + -rm -f ./$(DEPDIR)/ext-vnd-environment-variables.Plo + -rm -f ./$(DEPDIR)/ext-vnd-environment.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)/ext-vnd-environment-items.Plo + -rm -f ./$(DEPDIR)/ext-vnd-environment-variables.Plo + -rm -f ./$(DEPDIR)/ext-vnd-environment.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/plugins/vnd.dovecot/environment/ext-vnd-environment-common.h b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-common.h new file mode 100644 index 0000000..980c830 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-common.h @@ -0,0 +1,37 @@ +#ifndef EXT_VND_ENVIRONMENT_COMMON_H +#define EXT_VND_ENVIRONMENT_COMMON_H + +#include "sieve-ext-environment.h" + +/* + * Extension + */ + +struct ext_vnd_environment_context { + const struct sieve_extension *env_ext; + const struct sieve_extension *var_ext; +}; + +extern const struct sieve_extension_def vnd_environment_extension; + +/* + * Operands + */ + +extern const struct sieve_operand_def environment_namespace_operand; + +/* + * Environment items + */ + +void ext_vnd_environment_items_register +(const struct sieve_extension *ext, const struct sieve_runtime_env *renv); + +/* + * Variables + */ + +void ext_environment_variables_init +(const struct sieve_extension *this_ext, struct sieve_validator *valdtr); + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-items.c b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-items.c new file mode 100644 index 0000000..af03255 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-items.c @@ -0,0 +1,95 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" + +#include "sieve-settings.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-address-parts.h" + +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-binary.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "ext-vnd-environment-common.h" + +/* + * Environment items + */ + +/* default_mailbox */ + +static const char * +envit_default_mailbox_get_value(const struct sieve_runtime_env *renv, + const char *name ATTR_UNUSED) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + + i_assert(eenv->scriptenv->default_mailbox != NULL); + return eenv->scriptenv->default_mailbox; +} + +const struct sieve_environment_item default_mailbox_env_item = { + .name = "vnd.dovecot.default-mailbox", + .get_value = envit_default_mailbox_get_value +}; + +/* username */ + +static const char * +envit_username_get_value(const struct sieve_runtime_env *renv, + const char *name ATTR_UNUSED) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + + return eenv->svinst->username; +} + +const struct sieve_environment_item username_env_item = { + .name = "vnd.dovecot.username", + .get_value = envit_username_get_value +}; + +/* config.* */ + +static const char * +envit_config_get_value(const struct sieve_runtime_env *renv, const char *name) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + + if (*name == '\0') + return NULL; + + return sieve_setting_get(eenv->svinst, + t_strconcat("sieve_env_", name, NULL)); +} + +const struct sieve_environment_item config_env_item = { + .name = "vnd.dovecot.config", + .prefix = TRUE, + .get_value = envit_config_get_value +}; + +/* + * Register + */ + +void ext_vnd_environment_items_register(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv) +{ + struct ext_vnd_environment_context *ectx = + (struct ext_vnd_environment_context *)ext->context; + + sieve_environment_item_register(ectx->env_ext, renv->interp, + &default_mailbox_env_item); + sieve_environment_item_register(ectx->env_ext, renv->interp, + &username_env_item); + sieve_environment_item_register(ectx->env_ext, renv->interp, + &config_env_item); +} diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-variables.c b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-variables.c new file mode 100644 index 0000000..1466ee7 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-variables.c @@ -0,0 +1,206 @@ +/* Copyright (c) 2015-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" + +#include "sieve-common.h" +#include "sieve-ast.h" +#include "sieve-binary.h" +#include "sieve-code.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "sieve-ext-variables.h" + +#include "ext-vnd-environment-common.h" + +static bool vnspc_vnd_environment_validate + (struct sieve_validator *valdtr, + const struct sieve_variables_namespace *nspc, + struct sieve_ast_argument *arg, struct sieve_command *cmd, + ARRAY_TYPE(sieve_variable_name) *var_name, void **var_data, + bool assignment); +static bool vnspc_vnd_environment_generate + (const struct sieve_codegen_env *cgenv, + const struct sieve_variables_namespace *nspc, + struct sieve_ast_argument *arg, + struct sieve_command *cmd, void *var_data); +static bool vnspc_vnd_environment_dump_variable + (const struct sieve_dumptime_env *denv, + const struct sieve_variables_namespace *nspc, + const struct sieve_operand *oprnd, sieve_size_t *address); +static int vnspc_vnd_environment_read_variable + (const struct sieve_runtime_env *renv, + const struct sieve_variables_namespace *nspc, + const struct sieve_operand *oprnd, + sieve_size_t *address, string_t **str_r); + +static const struct sieve_variables_namespace_def +environment_namespace = { + SIEVE_OBJECT("env", &environment_namespace_operand, 0), + .validate = vnspc_vnd_environment_validate, + .generate = vnspc_vnd_environment_generate, + .dump_variable = vnspc_vnd_environment_dump_variable, + .read_variable = vnspc_vnd_environment_read_variable +}; + +static bool vnspc_vnd_environment_validate +(struct sieve_validator *valdtr, + const struct sieve_variables_namespace *nspc ATTR_UNUSED, + struct sieve_ast_argument *arg, struct sieve_command *cmd ATTR_UNUSED, + ARRAY_TYPE(sieve_variable_name) *var_name, void **var_data, + bool assignment) +{ + struct sieve_ast *ast = arg->ast; + const struct sieve_variable_name *name_elements; + unsigned int i, count; + const char *variable; + string_t *name; + + /* Compose environment name from parsed variable name */ + name = t_str_new(64); + name_elements = array_get(var_name, &count); + i_assert(count > 1); + for (i = 1; i < count; i++) { + if ( name_elements[i].num_variable >= 0 ) { + sieve_argument_validate_error(valdtr, arg, + "vnd.dovecot.environment: invalid variable name within " + "env namespace `env.%d': " + "encountered numeric variable name", + name_elements[i].num_variable); + return FALSE; + } + if (str_len(name) > 0) + str_append_c(name, '.'); + str_append_str(name, name_elements[i].identifier); + } + + variable = str_c(name); + + if ( assignment ) { + sieve_argument_validate_error(valdtr, arg, + "vnd.dovecot.environment: cannot assign to environment " + "variable `env.%s'", variable); + return FALSE; + } + + *var_data = (void *) p_strdup(sieve_ast_pool(ast), variable); + return TRUE; +} + +static bool vnspc_vnd_environment_generate +(const struct sieve_codegen_env *cgenv, + const struct sieve_variables_namespace *nspc, + struct sieve_ast_argument *arg ATTR_UNUSED, + struct sieve_command *cmd ATTR_UNUSED, void *var_data) +{ + const struct sieve_extension *this_ext = SIEVE_OBJECT_EXTENSION(nspc); + const char *variable = (const char *) var_data; + struct ext_vnd_environment_context *ext_data; + + if ( this_ext == NULL ) + return FALSE; + + ext_data = (struct ext_vnd_environment_context *) this_ext->context; + + sieve_variables_opr_namespace_variable_emit + (cgenv->sblock, ext_data->var_ext, this_ext, &environment_namespace); + sieve_binary_emit_cstring(cgenv->sblock, variable); + + return TRUE; +} + +static bool vnspc_vnd_environment_dump_variable +(const struct sieve_dumptime_env *denv, + const struct sieve_variables_namespace *nspc ATTR_UNUSED, + const struct sieve_operand *oprnd, sieve_size_t *address) +{ + string_t *var_name; + + if ( !sieve_binary_read_string(denv->sblock, address, &var_name) ) + return FALSE; + + if ( oprnd->field_name != NULL ) + sieve_code_dumpf(denv, "%s: VAR ${env.%s}", + oprnd->field_name, str_c(var_name)); + else + sieve_code_dumpf(denv, "VAR ${env.%s}", + str_c(var_name)); + + return TRUE; +} + +static int vnspc_vnd_environment_read_variable +(const struct sieve_runtime_env *renv, + const struct sieve_variables_namespace *nspc, + const struct sieve_operand *oprnd, sieve_size_t *address, + string_t **str_r) +{ + const struct sieve_extension *this_ext = SIEVE_OBJECT_EXTENSION(nspc); + struct ext_vnd_environment_context *ectx = + (struct ext_vnd_environment_context *) this_ext->context; + string_t *var_name; + const char *ext_value; + + if ( !sieve_binary_read_string(renv->sblock, address, &var_name) ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "environment variable operand corrupt: invalid name"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if ( str_r != NULL ) { + const char *vname = str_c(var_name); + + ext_value = ext_environment_item_get_value + (ectx->env_ext, renv, vname); + + if ( ext_value == NULL && strchr(vname, '_') != NULL) { + char *p, *aname; + + /* Try again with '_' replaced with '-' */ + aname = t_strdup_noconst(vname); + for (p = aname; *p != '\0'; p++) { + if (*p == '_') + *p = '-'; + } + ext_value = ext_environment_item_get_value + (ectx->env_ext, renv, aname); + } + + if ( ext_value == NULL ) { + *str_r = t_str_new_const("", 0); + return SIEVE_EXEC_OK; + } + + *str_r = t_str_new_const(ext_value, strlen(ext_value)); + } + return SIEVE_EXEC_OK; +} + +/* + * Namespace registration + */ + +static const struct sieve_extension_objects environment_namespaces = + SIEVE_VARIABLES_DEFINE_NAMESPACE(environment_namespace); + +const struct sieve_operand_def environment_namespace_operand = { + .name = "env-namespace", + .ext_def = &vnd_environment_extension, + .class = &sieve_variables_namespace_operand_class, + .interface = &environment_namespaces +}; + +void ext_environment_variables_init +(const struct sieve_extension *this_ext, struct sieve_validator *valdtr) +{ + struct ext_vnd_environment_context *ext_data = + (struct ext_vnd_environment_context *) this_ext->context; + + sieve_variables_namespace_register + (ext_data->var_ext, valdtr, this_ext, &environment_namespace); +} diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment.c b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment.c new file mode 100644 index 0000000..f8c7028 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment.c @@ -0,0 +1,112 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension vnd.dovecot.environment + * --------------------------------- + * + * Authors: Stephan Bosch + * Specification: vendor-defined; + * spec-bosch-sieve-dovecot-environment + * Implementation: preliminary + * Status: experimental + * + */ + +#include "lib.h" +#include "array.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-address-parts.h" + +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-binary.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "sieve-ext-variables.h" + +#include "ext-vnd-environment-common.h" + +/* + * Extension + */ + +static bool ext_vnd_environment_load + (const struct sieve_extension *ext, void **context); +static void ext_vnd_environment_unload + (const struct sieve_extension *ext); +static bool ext_vnd_environment_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); +static bool ext_vnd_environment_interpreter_load + (const struct sieve_extension *ext, const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_extension_def vnd_environment_extension = { + .name = "vnd.dovecot.environment", + .load = ext_vnd_environment_load, + .unload = ext_vnd_environment_unload, + .validator_load = ext_vnd_environment_validator_load, + .interpreter_load = ext_vnd_environment_interpreter_load, + SIEVE_EXT_DEFINE_OPERAND(environment_namespace_operand) +}; + +static bool ext_vnd_environment_load +(const struct sieve_extension *ext, void **context) +{ + struct ext_vnd_environment_context *ectx; + + if ( *context != NULL ) + ext_vnd_environment_unload(ext); + + ectx = i_new(struct ext_vnd_environment_context, 1); + ectx->env_ext = sieve_ext_environment_require_extension(ext->svinst); + ectx->var_ext = sieve_ext_variables_get_extension(ext->svinst); + *context = (void *) ectx; + + return TRUE; +} + +static void ext_vnd_environment_unload +(const struct sieve_extension *ext) +{ + struct ext_vnd_environment_context *ectx = + (struct ext_vnd_environment_context *) ext->context; + + i_free(ectx); +} + +/* + * Validator + */ + +static bool ext_vnd_environment_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + const struct sieve_extension *env_ext; + + /* Load environment extension implicitly */ + + env_ext = sieve_validator_extension_load_implicit + (valdtr, environment_extension.name); + if ( env_ext == NULL ) + return FALSE; + + ext_environment_variables_init(ext, valdtr); + return TRUE; +} + +/* + * Interpreter + */ + +static bool ext_vnd_environment_interpreter_load +(const struct sieve_extension *ext, const struct sieve_runtime_env *renv, + sieve_size_t *address ATTR_UNUSED) +{ + ext_vnd_environment_items_register(ext, renv); + return TRUE; +} diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/Makefile.am b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/Makefile.am new file mode 100644 index 0000000..599765f --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/Makefile.am @@ -0,0 +1,17 @@ +noinst_LTLIBRARIES = libsieve_ext_vnd_report.la + +AM_CPPFLAGS = \ + -I$(srcdir)/../../.. \ + -I$(srcdir)/../../../util \ + $(LIBDOVECOT_INCLUDE) + +commands = \ + cmd-report.c + +libsieve_ext_vnd_report_la_SOURCES = \ + ext-vnd-report.c \ + ext-vnd-report-common.c \ + $(commands) + +noinst_HEADERS = \ + ext-vnd-report-common.h diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/Makefile.in b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/Makefile.in new file mode 100644 index 0000000..1c2ec8c --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/Makefile.in @@ -0,0 +1,695 @@ +# 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/plugins/vnd.dovecot/report +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_ext_vnd_report_la_LIBADD = +am__objects_1 = cmd-report.lo +am_libsieve_ext_vnd_report_la_OBJECTS = ext-vnd-report.lo \ + ext-vnd-report-common.lo $(am__objects_1) +libsieve_ext_vnd_report_la_OBJECTS = \ + $(am_libsieve_ext_vnd_report_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)/cmd-report.Plo \ + ./$(DEPDIR)/ext-vnd-report-common.Plo \ + ./$(DEPDIR)/ext-vnd-report.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_ext_vnd_report_la_SOURCES) +DIST_SOURCES = $(libsieve_ext_vnd_report_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_ext_vnd_report.la +AM_CPPFLAGS = \ + -I$(srcdir)/../../.. \ + -I$(srcdir)/../../../util \ + $(LIBDOVECOT_INCLUDE) + +commands = \ + cmd-report.c + +libsieve_ext_vnd_report_la_SOURCES = \ + ext-vnd-report.c \ + ext-vnd-report-common.c \ + $(commands) + +noinst_HEADERS = \ + ext-vnd-report-common.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/plugins/vnd.dovecot/report/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/plugins/vnd.dovecot/report/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_ext_vnd_report.la: $(libsieve_ext_vnd_report_la_OBJECTS) $(libsieve_ext_vnd_report_la_DEPENDENCIES) $(EXTRA_libsieve_ext_vnd_report_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_ext_vnd_report_la_OBJECTS) $(libsieve_ext_vnd_report_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-report.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-vnd-report-common.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-vnd-report.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)/cmd-report.Plo + -rm -f ./$(DEPDIR)/ext-vnd-report-common.Plo + -rm -f ./$(DEPDIR)/ext-vnd-report.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)/cmd-report.Plo + -rm -f ./$(DEPDIR)/ext-vnd-report-common.Plo + -rm -f ./$(DEPDIR)/ext-vnd-report.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/plugins/vnd.dovecot/report/cmd-report.c b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/cmd-report.c new file mode 100644 index 0000000..af53e42 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/cmd-report.c @@ -0,0 +1,692 @@ +/* Copyright (c) 2016-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" +#include "str.h" +#include "ioloop.h" +#include "hostpid.h" +#include "str-sanitize.h" +#include "istream.h" +#include "ostream.h" +#include "message-date.h" +#include "message-size.h" +#include "mail-storage.h" + +#include "rfc2822.h" + +#include "sieve-common.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-address.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-message.h" +#include "sieve-actions.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-result.h" +#include "sieve-address.h" +#include "sieve-message.h" +#include "sieve-smtp.h" + +#include "ext-vnd-report-common.h" + +#include <ctype.h> + +/* Report command + * + * Syntax: + * report [:headers_only] <feedback-type: string> + * <message: string> <address: string> + * + */ + +static bool +cmd_report_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool +cmd_report_validate(struct sieve_validator *valdtr, struct sieve_command *cmd); +static bool +cmd_report_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +const struct sieve_command_def cmd_report = { + .identifier = "report", + .type = SCT_COMMAND, + .positional_args = 3, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = cmd_report_registered, + .validate = cmd_report_validate, + .generate = cmd_report_generate +}; + +/* + * Tagged arguments + */ + +static const struct sieve_argument_def report_headers_only_tag = { + .identifier = "headers_only" +}; + +/* + * Report operation + */ + +static bool +cmd_report_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +cmd_report_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def report_operation = { + .mnemonic = "REPORT", + .ext_def = &vnd_report_extension, + .code = 0, + .dump = cmd_report_operation_dump, + .execute = cmd_report_operation_execute +}; + +/* Codes for optional operands */ + +enum cmd_report_optional { + OPT_END, + OPT_HEADERS_ONLY +}; + +/* + * Report action + */ + +/* Forward declarations */ + +static int +act_report_check_duplicate(const struct sieve_runtime_env *renv, + const struct sieve_action *act, + const struct sieve_action *act_other); +static void +act_report_print(const struct sieve_action *action, + const struct sieve_result_print_env *rpenv, bool *keep); +static int +act_report_commit(const struct sieve_action_exec_env *aenv, void *tr_context); + +/* Action object */ + +const struct sieve_action_def act_report = { + .name = "report", + .check_duplicate = act_report_check_duplicate, + .print = act_report_print, + .commit = act_report_commit +}; + +/* Action data */ + +struct act_report_data { + const char *feedback_type; + const char *message; + struct smtp_address *to_address; + bool headers_only:1; +}; + +/* + * Command registration + */ + +static bool +cmd_report_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &report_headers_only_tag, + OPT_HEADERS_ONLY); + return TRUE; +} + +/* + * Command validation + */ + +static bool +cmd_report_validate(struct sieve_validator *valdtr, struct sieve_command *cmd) +{ + struct sieve_ast_argument *arg = cmd->first_positional; + + /* type */ + if (!sieve_validate_positional_argument(valdtr, cmd, arg, + "feedback-type", 1, + SAAT_STRING)) + return FALSE; + if (!sieve_validator_argument_activate(valdtr, cmd, arg, FALSE)) + return FALSE; + + if (sieve_argument_is_string_literal(arg)) { + string_t *fbtype = sieve_ast_argument_str(arg); + const char *feedback_type; + + T_BEGIN { + /* Check feedback type */ + feedback_type = + ext_vnd_report_parse_feedback_type(str_c(fbtype)); + + if (feedback_type == NULL) { + sieve_argument_validate_error( + valdtr, arg, + "specified feedback type `%s' is invalid", + str_sanitize(str_c(fbtype),128)); + } + } T_END; + + if (feedback_type == NULL) + return FALSE; + } + arg = sieve_ast_argument_next(arg); + + /* message */ + if (!sieve_validate_positional_argument(valdtr, cmd, arg, "message", + 2, SAAT_STRING)) + return FALSE; + if (!sieve_validator_argument_activate(valdtr, cmd, arg, FALSE)) + return FALSE; + arg = sieve_ast_argument_next(arg); + + /* address */ + if (!sieve_validate_positional_argument(valdtr, cmd, arg, "address", + 3, SAAT_STRING)) + return FALSE; + if (!sieve_validator_argument_activate(valdtr, cmd, arg, FALSE)) + return FALSE; + + /* We can only assess the validity of the outgoing address when it is a + string literal. For runtime-generated strings this needs to be done + at runtime. + */ + if (sieve_argument_is_string_literal(arg)) { + string_t *raw_address = sieve_ast_argument_str(arg); + const char *error; + bool result; + + T_BEGIN { + /* Parse the address */ + result = sieve_address_validate_str(raw_address, &error); + if (!result) { + sieve_argument_validate_error( + valdtr, arg, + "specified report address '%s' is invalid: %s", + str_sanitize(str_c(raw_address),128), + error); + } + } T_END; + + return result; + } + + return TRUE; +} + +/* + * Code generation + */ + +static bool +cmd_report_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd) +{ + sieve_operation_emit(cgenv->sblock, cmd->ext, &report_operation); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, cmd, NULL); +} + +/* + * Code dump + */ + +static bool +cmd_report_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + int opt_code = 0; + + sieve_code_dumpf(denv, "REPORT"); + sieve_code_descend(denv); + + /* Dump optional operands */ + for (;;) { + int opt; + + if ((opt = sieve_opr_optional_dump(denv, address, + &opt_code)) < 0) + return FALSE; + + if (opt == 0) + break; + + switch (opt_code) { + case OPT_HEADERS_ONLY: + sieve_code_dumpf(denv, "headers_only"); + break; + default: + return FALSE; + } + } + + return (sieve_opr_string_dump(denv, address, "feedback-type") && + sieve_opr_string_dump(denv, address, "message") && + sieve_opr_string_dump(denv, address, "address")); +} + +/* + * Code execution + */ + + +static int +cmd_report_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + const struct sieve_extension *this_ext = renv->oprtn->ext; + struct act_report_data *act; + string_t *fbtype, *message, *to_address; + const char *feedback_type, *error; + const struct smtp_address *parsed_address; + int opt_code = 0, ret = 0; + bool headers_only = FALSE; + pool_t pool; + + /* + * Read operands + */ + + /* Optional operands */ + + for (;;) { + int opt; + + if ((opt = sieve_opr_optional_read(renv, address, + &opt_code)) < 0) + return SIEVE_EXEC_BIN_CORRUPT; + + if (opt == 0) + break; + + switch (opt_code) { + case OPT_HEADERS_ONLY: + headers_only = TRUE; + break; + default: + sieve_runtime_trace_error( + renv, "unknown optional operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + } + + /* Fixed operands */ + + if ((ret = sieve_opr_string_read(renv, address, "feedback-type", + &fbtype)) <= 0) + return ret; + if ((ret = sieve_opr_string_read(renv, address, "message", + &message)) <= 0) + return ret; + if ((ret = sieve_opr_string_read(renv, address, "address", + &to_address)) <= 0) + return ret; + + /* + * Perform operation + */ + + /* Verify and trim feedback type */ + feedback_type = ext_vnd_report_parse_feedback_type(str_c(fbtype)); + if (feedback_type == NULL) { + sieve_runtime_error( + renv, NULL, + "specified report feedback type `%s' is invalid", + str_sanitize(str_c(fbtype), 256)); + return SIEVE_EXEC_FAILURE; + } + + /* Verify and normalize the address to 'local_part@domain' */ + parsed_address = sieve_address_parse_str(to_address, &error); + if (parsed_address == NULL) { + sieve_runtime_error( + renv, NULL, + "specified report address '%s' is invalid: %s", + str_sanitize(str_c(to_address),128), error); + return SIEVE_EXEC_FAILURE; + } + + /* Trace */ + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_ACTIONS)) { + sieve_runtime_trace(renv, 0, "report action"); + sieve_runtime_trace_descend(renv); + sieve_runtime_trace( + renv, 0, "report incoming message as `%s' to address %s", + str_sanitize(str_c(fbtype), 32), + smtp_address_encode_path(parsed_address)); + } + + /* Add report action to the result */ + + pool = sieve_result_pool(renv->result); + act = p_new(pool, struct act_report_data, 1); + act->headers_only = headers_only; + act->feedback_type = p_strdup(pool, feedback_type); + act->message = p_strdup(pool, str_c(message)); + act->to_address = smtp_address_clone(pool, parsed_address); + + if (sieve_result_add_action(renv, this_ext, "report", &act_report, NULL, + (void *)act, 0, TRUE) < 0) + return SIEVE_EXEC_FAILURE; + return SIEVE_EXEC_OK; +} + +/* + * Action + */ + +/* Runtime verification */ + +static bool +act_report_equals(const struct sieve_script_env *senv ATTR_UNUSED, + const struct sieve_action *act1, + const struct sieve_action *act2) +{ + struct act_report_data *rdd1 = (struct act_report_data *)act1->context; + struct act_report_data *rdd2 = (struct act_report_data *)act2->context; + + /* Address is already normalized */ + return (smtp_address_equals(rdd1->to_address, rdd2->to_address)); +} + +static int +act_report_check_duplicate(const struct sieve_runtime_env *renv, + const struct sieve_action *act, + const struct sieve_action *act_other) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + + return (act_report_equals(eenv->scriptenv, act, act_other) ? 1 : 0); +} + +/* Result printing */ + +static void +act_report_print(const struct sieve_action *action, + const struct sieve_result_print_env *rpenv, + bool *keep ATTR_UNUSED) +{ + const struct act_report_data *rdd = + (struct act_report_data *)action->context; + + sieve_result_action_printf(rpenv, + "report incoming message as `%s' to: %s", + str_sanitize(rdd->feedback_type, 32), + smtp_address_encode_path(rdd->to_address)); +} + +/* Result execution */ + +static bool _contains_8bit(const char *msg) +{ + const unsigned char *s = (const unsigned char *)msg; + + for (; *s != '\0'; s++) { + if ((*s & 0x80) != 0) + return TRUE; + } + return FALSE; +} + +static int +act_report_send(const struct sieve_action_exec_env *aenv, + const struct ext_report_config *config, + const struct act_report_data *act) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + struct sieve_instance *svinst = eenv->svinst; + struct sieve_message_context *msgctx = aenv->msgctx; + const struct sieve_script_env *senv = eenv->scriptenv; + const struct sieve_message_data *msgdata = eenv->msgdata; + struct sieve_address_source report_from = config->report_from; + const struct smtp_address *sender, *user; + struct sieve_smtp_context *sctx; + struct istream *input; + struct ostream *output; + string_t *msg; + const char *const *headers; + const char *outmsgid, *boundary, *error, *subject, *from; + int ret; + + /* Just to be sure */ + if (!sieve_smtp_available(senv)) { + sieve_result_global_warning( + aenv, "report action has no means to send mail"); + return SIEVE_EXEC_OK; + } + + /* Make sure we have a subject for our report */ + if ((ret = mail_get_headers_utf8(msgdata->mail, "subject", + &headers)) < 0) { + return sieve_result_mail_error( + aenv, msgdata->mail, + "failed to read header field `subject'"); + } + if (ret > 0 && headers[0] != NULL) + subject = t_strconcat("Report: ", headers[0], NULL); + else + subject = "Report: (message without subject)"; + + /* Determine from address */ + if (report_from.type == SIEVE_ADDRESS_SOURCE_POSTMASTER) { + report_from.type = SIEVE_ADDRESS_SOURCE_DEFAULT; + report_from.address = NULL; + } + if (sieve_address_source_get_address( + &report_from, svinst, senv, msgctx, eenv->flags, + &sender) > 0 && sender != NULL) + from = smtp_address_encode_path(sender); + else + from = sieve_get_postmaster_address(senv); + + /* Start message */ + sctx = sieve_smtp_start_single(senv, act->to_address, NULL, &output); + + outmsgid = sieve_message_get_new_id(svinst); + boundary = t_strdup_printf("%s/%s", my_pid, svinst->hostname); + + /* Compose main report headers */ + msg = t_str_new(512); + rfc2822_header_write(msg, "X-Sieve", SIEVE_IMPLEMENTATION); + rfc2822_header_write(msg, "Message-ID", outmsgid); + rfc2822_header_write(msg, "Date", message_date_create(ioloop_time)); + + rfc2822_header_write(msg, "From", from); + rfc2822_header_write(msg, "To", + smtp_address_encode_path(act->to_address)); + + if (_contains_8bit(subject)) + rfc2822_header_utf8_printf(msg, "Subject", "%s", subject); + else + rfc2822_header_printf(msg, "Subject", "%s", subject); + + rfc2822_header_write(msg, "Auto-Submitted", "auto-generated (report)"); + + rfc2822_header_write(msg, "MIME-Version", "1.0"); + rfc2822_header_printf(msg, "Content-Type", + "multipart/report; report-type=feedback-report;\n" + "boundary=\"%s\"", boundary); + + str_append(msg, "\r\nThis is a MIME-encapsulated message\r\n\r\n"); + + /* Human-readable report */ + str_printfa(msg, "--%s\r\n", boundary); + if (_contains_8bit(act->message)) { + rfc2822_header_write(msg, "Content-Type", + "text/plain; charset=utf-8"); + rfc2822_header_write(msg, "Content-Transfer-Encoding", "8bit"); + } else { + rfc2822_header_write(msg, "Content-Type", + "text/plain; charset=us-ascii"); + rfc2822_header_write(msg, "Content-Transfer-Encoding", "7bit"); + } + rfc2822_header_write(msg, "Content-Disposition", "inline"); + + str_printfa(msg, "\r\n%s\r\n\r\n", act->message); + o_stream_nsend(output, str_data(msg), str_len(msg)); + + /* Machine-readable report */ + str_truncate(msg, 0); + str_printfa(msg, "--%s\r\n", boundary); + rfc2822_header_write(msg, "Content-Type", "message/feedback-report"); + str_append(msg, "\r\n"); + + rfc2822_header_write(msg, "Version", "1"); + rfc2822_header_write(msg, "Feedback-Type", act->feedback_type); + rfc2822_header_write(msg, "User-Agent", + PACKAGE_NAME "/" PACKAGE_VERSION " " + PIGEONHOLE_NAME "/" PIGEONHOLE_VERSION); + + if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0) { + const struct smtp_address *sender, *orig_recipient; + + sender = sieve_message_get_sender(msgctx); + orig_recipient = sieve_message_get_orig_recipient(msgctx); + + rfc2822_header_write(msg, "Original-Mail-From", + smtp_address_encode_path(sender)); + if (orig_recipient != NULL) { + rfc2822_header_write( + msg, "Original-Rcpt-To", + smtp_address_encode_path(orig_recipient)); + } + } + if (svinst->user_email != NULL) + user = svinst->user_email; + else if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) != 0 || + (user = sieve_message_get_orig_recipient(msgctx)) == NULL) + user = sieve_get_user_email(svinst); + if (user != NULL) { + rfc2822_header_write(msg, "Dovecot-Reporting-User", + smtp_address_encode_path(user)); + } + str_append(msg, "\r\n"); + + o_stream_nsend(output, str_data(msg), str_len(msg)); + + /* Original message */ + str_truncate(msg, 0); + str_printfa(msg, "--%s\r\n", boundary); + if (act->headers_only) { + rfc2822_header_write(msg, "Content-Type", + "text/rfc822-headers"); + } else { + rfc2822_header_write(msg, "Content-Type", "message/rfc822"); + } + rfc2822_header_write(msg, "Content-Disposition", "attachment"); + str_append(msg, "\r\n"); + o_stream_nsend(output, str_data(msg), str_len(msg)); + + if (act->headers_only) { + struct message_size hdr_size; + ret = mail_get_hdr_stream(msgdata->mail, &hdr_size, &input); + if (ret >= 0) { + input = i_stream_create_limit( + input, hdr_size.physical_size); + } + } else { + ret = mail_get_stream(msgdata->mail, NULL, NULL, &input); + if (ret >= 0) + i_stream_ref(input); + } + if (ret < 0) { + sieve_smtp_abort(sctx); + return sieve_result_mail_error(aenv, msgdata->mail, + "failed to read input message"); + } + + o_stream_nsend_istream(output, input); + + if (input->stream_errno != 0) { + /* Error; clean up */ + sieve_result_critical(aenv, "failed to read input message", + "read(%s) failed: %s", + i_stream_get_name(input), + i_stream_get_error(input)); + i_stream_unref(&input); + sieve_smtp_abort(sctx); + return SIEVE_EXEC_OK; + } + i_stream_unref(&input); + + str_truncate(msg, 0); + if (!act->headers_only) + str_printfa(msg, "\r\n"); + str_printfa(msg, "\r\n--%s--\r\n", boundary); + o_stream_nsend(output, str_data(msg), str_len(msg)); + + /* Finish sending message */ + if ((ret = sieve_smtp_finish(sctx, &error)) <= 0) { + if (ret < 0) { + sieve_result_global_error( + aenv, "failed to send `%s' report to <%s>: %s " + "(temporary failure)", + str_sanitize(act->feedback_type, 32), + smtp_address_encode(act->to_address), + str_sanitize(error, 512)); + } else { + sieve_result_global_log_error( + aenv, "failed to send `%s' report to <%s>: %s " + "(permanent failure)", + str_sanitize(act->feedback_type, 32), + smtp_address_encode(act->to_address), + str_sanitize(error, 512)); + } + } else { + eenv->exec_status->significant_action_executed = TRUE; + + struct event_passthrough *e = + sieve_action_create_finish_event(aenv)-> + add_str("report_target", + smtp_address_encode(act->to_address))-> + add_str("report_type", + str_sanitize(act->feedback_type, 32)); + + sieve_result_event_log(aenv, e->event(), + "sent `%s' report to <%s>", + str_sanitize(act->feedback_type, 32), + smtp_address_encode(act->to_address)); + } + + return SIEVE_EXEC_OK; +} + +static int +act_report_commit(const struct sieve_action_exec_env *aenv, + void *tr_context ATTR_UNUSED) +{ + const struct sieve_action *action = aenv->action; + const struct sieve_extension *ext = action->ext; + const struct ext_report_config *config = + (const struct ext_report_config *)ext->context; + const struct act_report_data *act = + (const struct act_report_data *)action->context; + int ret; + + T_BEGIN { + ret = act_report_send(aenv, config, act); + } T_END; + + if (ret == SIEVE_EXEC_TEMP_FAILURE) + return SIEVE_EXEC_TEMP_FAILURE; + + /* Ignore all other errors */ + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report-common.c b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report-common.c new file mode 100644 index 0000000..d22ad16 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report-common.c @@ -0,0 +1,51 @@ +/* Copyright (c) 2016-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "rfc822-parser.h" + +#include "sieve-common.h" +#include "sieve-extensions.h" + +#include "ext-vnd-report-common.h" + +bool ext_report_load +(const struct sieve_extension *ext, void **context) +{ + struct sieve_instance *svinst = ext->svinst; + struct ext_report_config *config; + + config = p_new(svinst->pool, struct ext_report_config, 1); + + (void)sieve_address_source_parse_from_setting(svinst, + svinst->pool, "sieve_report_from", &config->report_from); + + *context = (void *) config; + return TRUE; +} + +const char * +ext_vnd_report_parse_feedback_type(const char *feedback_type) +{ + struct rfc822_parser_context parser; + string_t *token; + + /* Initialize parsing */ + rfc822_parser_init(&parser, + (const unsigned char *)feedback_type, strlen(feedback_type), NULL); + (void)rfc822_skip_lwsp(&parser); + + /* Parse MIME token */ + token = t_str_new(64); + if (rfc822_parse_mime_token(&parser, token) < 0) + return NULL; + + /* Content-type value must end here, otherwise it is invalid after all */ + (void)rfc822_skip_lwsp(&parser); + if ( parser.data != parser.end ) + return NULL; + + /* Success */ + return str_c(token); +} diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report-common.h b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report-common.h new file mode 100644 index 0000000..11a3757 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report-common.h @@ -0,0 +1,40 @@ +#ifndef EXT_REPORT_COMMON_H +#define EXT_REPORT_COMMON_H + +/* + * Extension configuration + */ + +struct ext_report_config { + struct sieve_address_source report_from; +}; + +/* + * Extension + */ + +extern const struct sieve_extension_def vnd_report_extension; + +bool ext_report_load + (const struct sieve_extension *ext, void **context); + +/* + * Commands + */ + +extern const struct sieve_command_def cmd_report; + +/* + * Operations + */ + +extern const struct sieve_operation_def report_operation; + +/* + * RFC 5965 feedback-type + */ + +const char * +ext_vnd_report_parse_feedback_type(const char *feedback_type); + +#endif diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report.c b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report.c new file mode 100644 index 0000000..a5fb64c --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report.c @@ -0,0 +1,52 @@ +/* Copyright (c) 2016-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension report + * ---------------- + * + * Authors: Stephan Bosch + * Specification: draft-ietf-sieve-report-00.txt + * Implementation: full, but deprecated; provided for backwards compatibility + * Status: testing + * + */ + +#include "sieve-common.h" + +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-actions.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-result.h" + +#include "ext-vnd-report-common.h" + +/* + * Extension + */ + +static bool ext_report_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); + +const struct sieve_extension_def vnd_report_extension = { + .name = "vnd.dovecot.report", + .load = ext_report_load, + .validator_load = ext_report_validator_load, + SIEVE_EXT_DEFINE_OPERATION(report_operation) +}; + +/* + * Extension validation + */ + +static bool ext_report_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + /* Register new commands */ + sieve_validator_register_command(valdtr, ext, &cmd_report); + + return TRUE; +} diff --git a/pigeonhole/src/lib-sieve/sieve-actions.c b/pigeonhole/src/lib-sieve/sieve-actions.c new file mode 100644 index 0000000..86c9954 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-actions.c @@ -0,0 +1,1096 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "strfuncs.h" +#include "ioloop.h" +#include "hostpid.h" +#include "str-sanitize.h" +#include "unichar.h" +#include "istream.h" +#include "istream-header-filter.h" +#include "ostream.h" +#include "smtp-params.h" +#include "mail-storage.h" +#include "message-date.h" +#include "message-size.h" + +#include "rfc2822.h" + +#include "sieve-code.h" +#include "sieve-settings.h" +#include "sieve-extensions.h" +#include "sieve-binary.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-result.h" +#include "sieve-actions.h" +#include "sieve-message.h" +#include "sieve-smtp.h" + +/* + * Action execution environment + */ + +struct event_passthrough * +sieve_action_create_finish_event(const struct sieve_action_exec_env *aenv) +{ + struct event_passthrough *e = + event_create_passthrough(aenv->event)-> + set_name("sieve_action_finished"); + + return e; +} + +/* + * Action instance + */ + +bool sieve_action_is_executed(const struct sieve_action *act, + struct sieve_result *result) +{ + unsigned int cur_exec_seq = sieve_result_get_exec_seq(result); + + i_assert(act->exec_seq <= cur_exec_seq); + return (act->exec_seq < cur_exec_seq); +} + +/* + * Side-effect operand + */ + +const struct sieve_operand_class +sieve_side_effect_operand_class = { "SIDE-EFFECT" }; + +bool sieve_opr_side_effect_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + struct sieve_side_effect seffect; + const struct sieve_side_effect_def *sdef; + + if (!sieve_opr_object_dump(denv, &sieve_side_effect_operand_class, + address, &seffect.object)) + return FALSE; + + sdef = seffect.def = + (const struct sieve_side_effect_def *)seffect.object.def; + + if (sdef->dump_context != NULL) { + sieve_code_descend(denv); + if (!sdef->dump_context(&seffect, denv, address)) + return FALSE; + sieve_code_ascend(denv); + } + return TRUE; +} + +int sieve_opr_side_effect_read(const struct sieve_runtime_env *renv, + sieve_size_t *address, + struct sieve_side_effect *seffect) +{ + const struct sieve_side_effect_def *sdef; + int ret; + + seffect->context = NULL; + + if (!sieve_opr_object_read(renv, &sieve_side_effect_operand_class, + address, &seffect->object)) + return SIEVE_EXEC_BIN_CORRUPT; + + sdef = seffect->def = + (const struct sieve_side_effect_def *)seffect->object.def; + + if (sdef->read_context != NULL && + (ret = sdef->read_context(seffect, renv, address, + &seffect->context)) <= 0) + return ret; + return SIEVE_EXEC_OK; +} + +/* + * Optional operands + */ + +int sieve_action_opr_optional_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address, signed int *opt_code) +{ + signed int _opt_code = 0; + bool final = FALSE, opok = TRUE; + + if (opt_code == NULL) { + opt_code = &_opt_code; + final = TRUE; + } + + while (opok) { + int opt; + + opt = sieve_opr_optional_dump(denv, address, opt_code); + if (opt <= 0) + return opt; + + if (*opt_code == SIEVE_OPT_SIDE_EFFECT) + opok = sieve_opr_side_effect_dump(denv, address); + else + return (final ? -1 : 1); + } + + return -1; +} + +int sieve_action_opr_optional_read(const struct sieve_runtime_env *renv, + sieve_size_t *address, + signed int *opt_code, int *exec_status, + struct sieve_side_effects_list **list) +{ + signed int _opt_code = 0; + bool final = FALSE; + int ret; + + if (opt_code == NULL) { + opt_code = &_opt_code; + final = TRUE; + } + + if (exec_status != NULL) + *exec_status = SIEVE_EXEC_OK; + + for (;;) { + int opt; + + opt = sieve_opr_optional_read(renv, address, opt_code); + if (opt <= 0) { + if (opt < 0 && exec_status != NULL) + *exec_status = SIEVE_EXEC_BIN_CORRUPT; + return opt; + } + + if (*opt_code == SIEVE_OPT_SIDE_EFFECT) { + struct sieve_side_effect seffect; + + i_assert(list != NULL); + + ret = sieve_opr_side_effect_read(renv, address, + &seffect); + if (ret <= 0) { + if (exec_status != NULL) + *exec_status = ret; + return -1; + } + + if (*list == NULL) { + *list = sieve_side_effects_list_create( + renv->result); + } + + sieve_side_effects_list_add(*list, &seffect); + } else { + if (final) { + sieve_runtime_trace_error( + renv, "invalid optional operand"); + if (exec_status != NULL) + *exec_status = SIEVE_EXEC_BIN_CORRUPT; + return -1; + } + return 1; + } + } + + i_unreached(); + return -1; +} + +/* + * Store action + */ + +/* Forward declarations */ + +static bool +act_store_equals(const struct sieve_script_env *senv, + const struct sieve_action *act1, + const struct sieve_action *act2); + +static int +act_store_check_duplicate(const struct sieve_runtime_env *renv, + const struct sieve_action *act, + const struct sieve_action *act_other); +static void +act_store_print(const struct sieve_action *action, + const struct sieve_result_print_env *rpenv, bool *keep); + +static int +act_store_start(const struct sieve_action_exec_env *aenv, void **tr_context); +static int +act_store_execute(const struct sieve_action_exec_env *aenv, void *tr_context, + bool *keep); +static int +act_store_commit(const struct sieve_action_exec_env *aenv, void *tr_context); +static void +act_store_rollback(const struct sieve_action_exec_env *aenv, void *tr_context, + bool success); + +/* Action object */ + +const struct sieve_action_def act_store = { + .name = "store", + .flags = + SIEVE_ACTFLAG_TRIES_DELIVER | + SIEVE_ACTFLAG_MAIL_STORAGE, + .equals = act_store_equals, + .check_duplicate = act_store_check_duplicate, + .print = act_store_print, + .start = act_store_start, + .execute = act_store_execute, + .commit = act_store_commit, + .rollback = act_store_rollback, +}; + +/* API */ + +int sieve_act_store_add_to_result(const struct sieve_runtime_env *renv, + const char *name, + struct sieve_side_effects_list *seffects, + const char *mailbox) +{ + pool_t pool; + struct act_store_context *act; + + /* Add redirect action to the result */ + pool = sieve_result_pool(renv->result); + act = p_new(pool, struct act_store_context, 1); + act->mailbox = p_strdup(pool, mailbox); + + return sieve_result_add_action(renv, NULL, name, &act_store, seffects, + (void *)act, 0, TRUE); +} + +void sieve_act_store_add_flags(const struct sieve_action_exec_env *aenv, + void *tr_context, const char *const *keywords, + enum mail_flags flags) +{ + struct act_store_transaction *trans = + (struct act_store_transaction *)tr_context; + + i_assert(trans != NULL); + + /* Assign mail keywords for subsequent mailbox_copy() */ + if (*keywords != NULL) { + const char *const *kw; + + if (!array_is_created(&trans->keywords)) { + pool_t pool = sieve_result_pool(aenv->result); + p_array_init(&trans->keywords, pool, 2); + } + + kw = keywords; + while (*kw != NULL) { + array_append(&trans->keywords, kw, 1); + kw++; + } + } + + /* Assign mail flags for subsequent mailbox_copy() */ + trans->flags |= flags; + + trans->flags_altered = TRUE; +} + +/* Equality */ + +static bool +act_store_equals(const struct sieve_script_env *senv, + const struct sieve_action *act1, + const struct sieve_action *act2) +{ + struct act_store_context *st_ctx1 = + (act1 == NULL ? + NULL : (struct act_store_context *)act1->context); + struct act_store_context *st_ctx2 = + (act2 == NULL ? + NULL : (struct act_store_context *)act2->context); + const char *mailbox1, *mailbox2; + + /* FIXME: consider namespace aliases */ + + if (st_ctx1 == NULL && st_ctx2 == NULL) + return TRUE; + + mailbox1 = (st_ctx1 == NULL ? + SIEVE_SCRIPT_DEFAULT_MAILBOX(senv) : st_ctx1->mailbox); + mailbox2 = (st_ctx2 == NULL ? + SIEVE_SCRIPT_DEFAULT_MAILBOX(senv) : st_ctx2->mailbox); + + if (strcmp(mailbox1, mailbox2) == 0) + return TRUE; + + return (strcasecmp(mailbox1, "INBOX") == 0 && + strcasecmp(mailbox2, "INBOX") == 0); +} + +/* Result verification */ + +static int +act_store_check_duplicate(const struct sieve_runtime_env *renv, + const struct sieve_action *act, + const struct sieve_action *act_other) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + + return (act_store_equals(eenv->scriptenv, act, act_other) ? 1 : 0); +} + +/* Result printing */ + +static void +act_store_print(const struct sieve_action *action, + const struct sieve_result_print_env *rpenv, bool *keep) +{ + struct act_store_context *ctx = + (struct act_store_context *)action->context; + const char *mailbox; + + mailbox = (ctx == NULL ? + SIEVE_SCRIPT_DEFAULT_MAILBOX(rpenv->scriptenv) : + ctx->mailbox); + + sieve_result_action_printf(rpenv, "store message in folder: %s", + str_sanitize(mailbox, 128)); + + *keep = FALSE; +} + +/* Action implementation */ + +void sieve_act_store_get_storage_error(const struct sieve_action_exec_env *aenv, + struct act_store_transaction *trans) +{ + pool_t pool = sieve_result_pool(aenv->result); + + trans->error = p_strdup(pool, + mailbox_get_last_internal_error(trans->box, + &trans->error_code)); +} + +static bool +act_store_mailbox_alloc(const struct sieve_action_exec_env *aenv, + const char *mailbox, struct mailbox **box_r, + enum mail_error *error_code_r, const char **error_r) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + struct mailbox *box; + struct mail_storage **storage = &(eenv->exec_status->last_storage); + enum mailbox_flags flags = MAILBOX_FLAG_POST_SESSION; + + *box_r = NULL; + *error_code_r = MAIL_ERROR_NONE; + *error_r = NULL; + + if (!uni_utf8_str_is_valid(mailbox)) { + /* Just a precaution; already (supposed to be) checked at + compiletime/runtime. + */ + *error_r = t_strdup_printf("mailbox name not utf-8: %s", + mailbox); + *error_code_r = MAIL_ERROR_PARAMS; + return FALSE; + } + + if (eenv->scriptenv->mailbox_autocreate) + flags |= MAILBOX_FLAG_AUTO_CREATE; + if (eenv->scriptenv->mailbox_autosubscribe) + flags |= MAILBOX_FLAG_AUTO_SUBSCRIBE; + *box_r = box = mailbox_alloc_for_user(eenv->scriptenv->user, mailbox, + flags); + *storage = mailbox_get_storage(box); + + return TRUE; +} + +static int +act_store_start(const struct sieve_action_exec_env *aenv, void **tr_context) +{ + const struct sieve_action *action = aenv->action; + struct act_store_context *ctx = + (struct act_store_context *)action->context; + const struct sieve_execute_env *eenv = aenv->exec_env; + const struct sieve_script_env *senv = eenv->scriptenv; + struct act_store_transaction *trans; + struct mailbox *box = NULL; + pool_t pool = sieve_result_pool(aenv->result); + const char *error = NULL; + enum mail_error error_code = MAIL_ERROR_NONE; + bool disabled = FALSE, alloc_failed = FALSE; + + /* If context is NULL, the store action is the result of (implicit) + keep. + */ + if (ctx == NULL) { + ctx = p_new(pool, struct act_store_context, 1); + ctx->mailbox = + p_strdup(pool, SIEVE_SCRIPT_DEFAULT_MAILBOX(senv)); + } + + e_debug(aenv->event, "Start storing into mailbox %s", ctx->mailbox); + + /* Open the requested mailbox */ + + /* NOTE: The caller of the sieve library is allowed to leave user set + to NULL. This implementation will then skip actually storing the + message. + */ + if (senv->user != NULL) { + if (!act_store_mailbox_alloc(aenv, ctx->mailbox, &box, + &error_code, &error)) + alloc_failed = TRUE; + } else { + disabled = TRUE; + } + + /* Create transaction context */ + trans = p_new(pool, struct act_store_transaction, 1); + + trans->context = ctx; + trans->box = box; + trans->flags = 0; + + trans->mailbox_name = ctx->mailbox; + trans->mailbox_identifier = + p_strdup_printf(pool, "'%s'", str_sanitize(ctx->mailbox, 256)); + + trans->disabled = disabled; + + if (alloc_failed) { + trans->error = p_strdup(pool, error); + trans->error_code = error_code; + e_debug(aenv->event, "Failed to open mailbox %s: %s", + trans->mailbox_identifier, trans->error); + } else { + trans->error_code = MAIL_ERROR_NONE; + } + + *tr_context = (void *)trans; + + switch (trans->error_code) { + case MAIL_ERROR_NONE: + case MAIL_ERROR_NOTFOUND: + return SIEVE_EXEC_OK; + case MAIL_ERROR_TEMP: + return SIEVE_EXEC_TEMP_FAILURE; + default: + break; + } + return SIEVE_EXEC_FAILURE; +} + +static struct mail_keywords * +act_store_keywords_create(const struct sieve_action_exec_env *aenv, + ARRAY_TYPE(const_string) *keywords, + struct mailbox *box, bool create_empty) +{ + struct mail_keywords *box_keywords = NULL; + const char *const *kwds = NULL; + + if (!array_is_created(keywords) || array_count(keywords) == 0) { + if (!create_empty) + return NULL; + } else { + ARRAY_TYPE(const_string) valid_keywords; + const char *error; + unsigned int count, i; + + kwds = array_get(keywords, &count); + t_array_init(&valid_keywords, count); + + for (i = 0; i < count; i++) { + if (mailbox_keyword_is_valid(box, kwds[i], &error)) { + array_append(&valid_keywords, &kwds[i], 1); + continue; + } + + sieve_result_warning(aenv, + "specified IMAP keyword '%s' is invalid " + "(ignored): %s", str_sanitize(kwds[i], 64), + sieve_error_from_external(error)); + } + + array_append_zero(keywords); + kwds = array_idx(keywords, 0); + } + + if (mailbox_keywords_create(box, kwds, &box_keywords) < 0) { + sieve_result_error( + aenv, "invalid keywords set for stored message"); + return NULL; + } + + return box_keywords; +} + +static bool have_equal_keywords(struct mail *mail, struct mail_keywords *new_kw) +{ + const ARRAY_TYPE(keyword_indexes) *old_kw_arr = + mail_get_keyword_indexes(mail); + const unsigned int *old_kw; + unsigned int i, j; + + if (array_count(old_kw_arr) != new_kw->count) + return FALSE; + if (new_kw->count == 0) + return TRUE; + + old_kw = array_front(old_kw_arr); + for (i = 0; i < new_kw->count; i++) { + /* new_kw->count equals old_kw's count and it's easier to use */ + for (j = 0; j < new_kw->count; j++) { + if (old_kw[j] == new_kw->idx[i]) + break; + } + if (j == new_kw->count) + return FALSE; + } + return TRUE; +} + +static int +act_store_execute(const struct sieve_action_exec_env *aenv, void *tr_context, + bool *keep) +{ + const struct sieve_action *action = aenv->action; + const struct sieve_execute_env *eenv = aenv->exec_env; + struct act_store_transaction *trans = + (struct act_store_transaction *)tr_context; + struct mail *mail = (action->mail != NULL ? + action->mail : eenv->msgdata->mail); + struct mail_save_context *save_ctx; + struct mail_keywords *keywords = NULL; + struct mailbox *box; + bool backends_equal = FALSE; + int status = SIEVE_EXEC_OK; + + /* Verify transaction */ + if (trans == NULL) + return SIEVE_EXEC_FAILURE; + box = trans->box; + + /* Check whether we need to do anything */ + if (trans->disabled) { + e_debug(aenv->event, "Skip storing into mailbox %s", + trans->mailbox_identifier); + *keep = FALSE; + return SIEVE_EXEC_OK; + } + + /* Exit early if mailbox is not available */ + if (box == NULL) + return SIEVE_EXEC_FAILURE; + + e_debug(aenv->event, "Execute storing into mailbox %s", + trans->mailbox_identifier); + + /* Mark attempt to use storage. Can only get here when all previous + actions succeeded. + */ + eenv->exec_status->last_storage = mailbox_get_storage(box); + + /* Open the mailbox (may already be open) */ + if (trans->error_code == MAIL_ERROR_NONE) { + if (mailbox_open(box) < 0) { + sieve_act_store_get_storage_error(aenv, trans); + e_debug(aenv->event, "Failed to open mailbox %s: %s", + trans->mailbox_identifier, trans->error); + } + } + + /* Exit early if transaction already failed */ + switch (trans->error_code) { + case MAIL_ERROR_NONE: + break; + case MAIL_ERROR_TEMP: + return SIEVE_EXEC_TEMP_FAILURE; + default: + return SIEVE_EXEC_FAILURE; + } + + /* If the message originates from the target mailbox, only update the + flags and keywords (if not read-only) + */ + if (mailbox_backends_equal(box, mail->box)) { + backends_equal = TRUE; + } else { + struct mail *real_mail; + + if (mail_get_backend_mail(mail, &real_mail) < 0) + return SIEVE_EXEC_FAILURE; + if (real_mail != mail && + mailbox_backends_equal(box, real_mail->box)) + backends_equal = TRUE; + } + if (backends_equal) { + trans->redundant = TRUE; + + if (trans->flags_altered && !mailbox_is_readonly(mail->box)) { + keywords = act_store_keywords_create( + aenv, &trans->keywords, mail->box, TRUE); + + if (keywords != NULL) { + if (!have_equal_keywords(mail, keywords)) { + eenv->exec_status->significant_action_executed = TRUE; + mail_update_keywords(mail, MODIFY_REPLACE, keywords); + } + mailbox_keywords_unref(&keywords); + } + + if ((mail_get_flags(mail) & MAIL_FLAGS_NONRECENT) != trans->flags) { + eenv->exec_status->significant_action_executed = TRUE; + mail_update_flags(mail, MODIFY_REPLACE, trans->flags); + } + } + e_debug(aenv->event, "Updated existing mail in mailbox %s", + trans->mailbox_identifier); + return SIEVE_EXEC_OK; + + /* If the message is modified, only store it in the source mailbox when + it is not opened read-only. Mail structs of modified messages have + their own mailbox, unrelated to the orignal mail, so this case needs + to be handled separately. + */ + } else if (mail != eenv->msgdata->mail && + mailbox_is_readonly(eenv->msgdata->mail->box) && + (mailbox_backends_equal(box, eenv->msgdata->mail->box))) { + e_debug(aenv->event, + "Not modifying exsiting mail in read-only mailbox %s", + trans->mailbox_identifier); + trans->redundant = TRUE; + return SIEVE_EXEC_OK; + } + + /* Mark attempt to store in default mailbox */ + if (strcmp(trans->context->mailbox, + SIEVE_SCRIPT_DEFAULT_MAILBOX(eenv->scriptenv)) == 0) + eenv->exec_status->tried_default_save = TRUE; + + /* Start mail transaction */ + trans->mail_trans = mailbox_transaction_begin( + box, MAILBOX_TRANSACTION_FLAG_EXTERNAL, __func__); + + /* Store the message */ + save_ctx = mailbox_save_alloc(trans->mail_trans); + + /* Apply keywords and flags that side-effects may have added */ + if (trans->flags_altered) { + keywords = act_store_keywords_create(aenv, &trans->keywords, + box, FALSE); + + if (trans->flags != 0 || keywords != NULL) { + eenv->exec_status->significant_action_executed = TRUE; + mailbox_save_set_flags(save_ctx, trans->flags, keywords); + } + } else { + mailbox_save_copy_flags(save_ctx, mail); + } + + if (mailbox_save_using_mail(&save_ctx, mail) < 0) { + sieve_act_store_get_storage_error(aenv, trans); + e_debug(aenv->event, "Failed to save to mailbox %s: %s", + trans->mailbox_identifier, trans->error); + + status = (trans->error_code == MAIL_ERROR_TEMP ? + SIEVE_EXEC_TEMP_FAILURE : SIEVE_EXEC_FAILURE); + } else { + e_debug(aenv->event, "Saving to mailbox %s successful so far", + trans->mailbox_identifier); + eenv->exec_status->significant_action_executed = TRUE; + } + + /* Deallocate keywords */ + if (keywords != NULL) + mailbox_keywords_unref(&keywords); + + /* Cancel implicit keep if all went well so far */ + *keep = (status < SIEVE_EXEC_OK); + + return status; +} + +static void +act_store_log_status(struct act_store_transaction *trans, + const struct sieve_action_exec_env *aenv, + bool rolled_back, bool status) +{ + const char *mailbox_name = trans->mailbox_name; + const char *mailbox_identifier = trans->mailbox_identifier; + + if (trans->box != NULL) { + const char *mailbox_vname = str_sanitize(mailbox_get_vname(trans->box), 128); + + if (strcmp(trans->mailbox_name, mailbox_vname) != 0) { + mailbox_identifier = t_strdup_printf( + "%s (%s)", mailbox_identifier, + str_sanitize(mailbox_vname, 256)); + } + } + + /* Store disabled? */ + if (trans->disabled) { + sieve_result_global_log(aenv, "store into mailbox %s skipped", + mailbox_identifier); + /* Store redundant? */ + } else if (trans->redundant) { + sieve_result_global_log(aenv, "left message in mailbox %s", + mailbox_identifier); + /* Store failed? */ + } else if (!status) { + const char *errstr; + enum mail_error error_code; + + if (trans->error == NULL) + sieve_act_store_get_storage_error(aenv, trans); + + errstr = trans->error; + error_code = trans->error_code; + + if (error_code == MAIL_ERROR_NOQUOTA) { + /* Never log quota problems as error in global log */ + sieve_result_global_log_error( + aenv, "failed to store into mailbox %s: %s", + mailbox_identifier, errstr); + } else if (error_code == MAIL_ERROR_NOTFOUND || + error_code == MAIL_ERROR_PARAMS || + error_code == MAIL_ERROR_PERM) { + sieve_result_error( + aenv, "failed to store into mailbox %s: %s", + mailbox_identifier, errstr); + } else { + sieve_result_global_error( + aenv, "failed to store into mailbox %s: %s", + mailbox_identifier, errstr); + } + /* Store aborted? */ + } else if (rolled_back) { + if (!aenv->action->keep) { + sieve_result_global_log( + aenv, "store into mailbox %s aborted", + mailbox_identifier); + } else { + e_debug(aenv->event, "Store into mailbox %s aborted", + mailbox_identifier); + } + /* Succeeded */ + } else { + struct event_passthrough *e = + sieve_action_create_finish_event(aenv)-> + add_str("fileinto_mailbox_name", mailbox_name)-> + add_str("fileinto_mailbox", mailbox_identifier); + sieve_result_event_log(aenv, e->event(), + "stored mail into mailbox %s", + mailbox_identifier); + } +} + +static void act_store_cleanup(struct act_store_transaction *trans) +{ + if (trans->mail_trans != NULL) + mailbox_transaction_rollback(&trans->mail_trans); + if (trans->box != NULL) + mailbox_free(&trans->box); +} + +static int +act_store_commit(const struct sieve_action_exec_env *aenv, void *tr_context) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + struct act_store_transaction *trans = + (struct act_store_transaction *)tr_context; + bool bail_out = FALSE, status = TRUE; + int ret = SIEVE_EXEC_OK; + + /* Verify transaction */ + if (trans == NULL) + return SIEVE_EXEC_FAILURE; + + e_debug(aenv->event, "Commit storing into mailbox %s", + trans->mailbox_identifier); + + /* Check whether we can commit this transaction */ + if (trans->error_code != MAIL_ERROR_NONE) { + /* Transaction already failed */ + bail_out = TRUE; + status = FALSE; + if (trans->error_code == MAIL_ERROR_TEMP) + ret = SIEVE_EXEC_TEMP_FAILURE; + else + ret = SIEVE_EXEC_FAILURE; + /* Check whether we need to do anything */ + } else if (trans->disabled) { + /* Nothing to do */ + bail_out = TRUE; + } else if (trans->redundant) { + /* This transaction is redundant */ + bail_out = TRUE; + eenv->exec_status->keep_original = TRUE; + eenv->exec_status->message_saved = TRUE; + } + if (bail_out) { + act_store_log_status(trans, aenv, FALSE, status); + act_store_cleanup(trans); + return ret; + } + + i_assert(trans->box != NULL); + i_assert(trans->mail_trans != NULL); + + /* Mark attempt to use storage. Can only get here when all previous + actions succeeded. + */ + eenv->exec_status->last_storage = mailbox_get_storage(trans->box); + + /* Commit mailbox transaction */ + status = (mailbox_transaction_commit(&trans->mail_trans) == 0); + + /* Note the fact that the message was stored at least once */ + if (status) + eenv->exec_status->message_saved = TRUE; + else + eenv->exec_status->store_failed = TRUE; + + /* Log our status */ + act_store_log_status(trans, aenv, FALSE, status); + + /* Clean up */ + act_store_cleanup(trans); + + if (status) + return SIEVE_EXEC_OK; + + return (trans->error_code == MAIL_ERROR_TEMP ? + SIEVE_EXEC_TEMP_FAILURE : SIEVE_EXEC_FAILURE); +} + +static void +act_store_rollback(const struct sieve_action_exec_env *aenv, void *tr_context, + bool success) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + struct act_store_transaction *trans = + (struct act_store_transaction *)tr_context; + + if (trans == NULL) + return; + + e_debug(aenv->event, "Roll back storing into mailbox %s", + trans->mailbox_identifier); + + i_assert(trans->box != NULL); + + if (!success) { + eenv->exec_status->last_storage = + mailbox_get_storage(trans->box); + eenv->exec_status->store_failed = TRUE; + } + + /* Log status */ + act_store_log_status(trans, aenv, TRUE, success); + + /* Rollback mailbox transaction and clean up */ + act_store_cleanup(trans); +} + +/* + * Redirect action + */ + +int sieve_act_redirect_add_to_result(const struct sieve_runtime_env *renv, + const char *name, + struct sieve_side_effects_list *seffects, + const struct smtp_address *to_address) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + struct sieve_instance *svinst = eenv->svinst; + struct act_redirect_context *act; + pool_t pool; + + pool = sieve_result_pool(renv->result); + act = p_new(pool, struct act_redirect_context, 1); + act->to_address = smtp_address_clone(pool, to_address); + + if (sieve_result_add_action(renv, NULL, name, &act_redirect, seffects, + (void *)act, svinst->max_redirects, + TRUE) < 0) + return SIEVE_EXEC_FAILURE; + + return SIEVE_EXEC_OK; +} + +/* + * Action utility functions + */ + +/* Rejecting the mail */ + +static int +sieve_action_do_reject_mail(const struct sieve_action_exec_env *aenv, + const struct smtp_address *recipient, + const char *reason) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + struct sieve_instance *svinst = eenv->svinst; + const struct sieve_script_env *senv = eenv->scriptenv; + const struct sieve_message_data *msgdata = eenv->msgdata; + const struct smtp_address *sender, *orig_recipient; + struct istream *input; + struct ostream *output; + struct sieve_smtp_context *sctx; + const char *new_msgid, *boundary, *error; + string_t *hdr; + int ret; + + sender = sieve_message_get_sender(aenv->msgctx); + orig_recipient = msgdata->envelope.rcpt_params->orcpt.addr; + + sctx = sieve_smtp_start_single(senv, sender, NULL, &output); + + /* Just to be sure */ + if (sctx == NULL) { + sieve_result_global_warning( + aenv, "reject action has no means to send mail"); + return SIEVE_EXEC_OK; + } + + new_msgid = sieve_message_get_new_id(svinst); + boundary = t_strdup_printf("%s/%s", my_pid, svinst->hostname); + + hdr = t_str_new(512); + rfc2822_header_write(hdr, "X-Sieve", SIEVE_IMPLEMENTATION); + rfc2822_header_write(hdr, "Message-ID", new_msgid); + rfc2822_header_write(hdr, "Date", message_date_create(ioloop_time)); + rfc2822_header_write(hdr, "From", sieve_get_postmaster_address(senv)); + rfc2822_header_printf(hdr, "To", "<%s>", smtp_address_encode(sender)); + rfc2822_header_write(hdr, "Subject", "Automatically rejected mail"); + rfc2822_header_write(hdr, "Auto-Submitted", "auto-replied (rejected)"); + rfc2822_header_write(hdr, "Precedence", "bulk"); + + rfc2822_header_write(hdr, "MIME-Version", "1.0"); + rfc2822_header_printf(hdr, "Content-Type", + "multipart/report; " + "report-type=disposition-notification;\r\n" + "boundary=\"%s\"", boundary); + + str_append(hdr, "\r\nThis is a MIME-encapsulated message\r\n\r\n"); + + /* Human readable status report */ + str_printfa(hdr, "--%s\r\n", boundary); + rfc2822_header_write(hdr, "Content-Type", "text/plain; charset=utf-8"); + rfc2822_header_write(hdr, "Content-Disposition", "inline"); + rfc2822_header_write(hdr, "Content-Transfer-Encoding", "8bit"); + + str_printfa(hdr, "\r\nYour message to <%s> was automatically rejected:\r\n" + "%s\r\n", smtp_address_encode(recipient), reason); + + /* MDN status report */ + str_printfa(hdr, "--%s\r\n", boundary); + rfc2822_header_write(hdr, "Content-Type", + "message/disposition-notification"); + str_append(hdr, "\r\n"); + rfc2822_header_write(hdr, + "Reporting-UA: %s; Dovecot Mail Delivery Agent", + svinst->hostname); + if (orig_recipient != NULL) { + rfc2822_header_printf(hdr, "Original-Recipient", "rfc822; %s", + smtp_address_encode(orig_recipient)); + } + rfc2822_header_printf(hdr, "Final-Recipient", "rfc822; %s", + smtp_address_encode(recipient)); + + if (msgdata->id != NULL) + rfc2822_header_write(hdr, "Original-Message-ID", msgdata->id); + rfc2822_header_write(hdr, "Disposition", + "automatic-action/MDN-sent-automatically; deleted"); + str_append(hdr, "\r\n"); + + /* original message's headers */ + str_printfa(hdr, "--%s\r\n", boundary); + rfc2822_header_write(hdr, "Content-Type", "message/rfc822"); + str_append(hdr, "\r\n"); + o_stream_nsend(output, str_data(hdr), str_len(hdr)); + + if (mail_get_hdr_stream(msgdata->mail, NULL, &input) == 0) { + /* NOTE: If you add more headers, they need to be sorted. We'll + drop Content-Type because we're not including the message + body, and having a multipart Content-Type may confuse some + MIME parsers when they don't see the message boundaries. + */ + static const char *const exclude_headers[] = { + "Content-Type" + }; + + input = i_stream_create_header_filter( + input, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR | + HEADER_FILTER_HIDE_BODY, + exclude_headers, N_ELEMENTS(exclude_headers), + *null_header_filter_callback, (void *)NULL); + o_stream_nsend_istream(output, input); + i_stream_unref(&input); + } + + str_truncate(hdr, 0); + str_printfa(hdr, "\r\n\r\n--%s--\r\n", boundary); + o_stream_nsend(output, str_data(hdr), str_len(hdr)); + + if ((ret = sieve_smtp_finish(sctx, &error)) <= 0) { + if (ret < 0) { + sieve_result_global_error(aenv, + "failed to send rejection message to <%s>: %s " + "(temporary failure)", + smtp_address_encode(sender), + str_sanitize(error, 512)); + } else { + sieve_result_global_log_error(aenv, + "failed to send rejection message to <%s>: %s " + "(permanent failure)", + smtp_address_encode(sender), + str_sanitize(error, 512)); + } + return SIEVE_EXEC_FAILURE; + } + + return SIEVE_EXEC_OK; +} + +int sieve_action_reject_mail(const struct sieve_action_exec_env *aenv, + const struct smtp_address *recipient, + const char *reason) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + const struct sieve_script_env *senv = eenv->scriptenv; + int result; + + T_BEGIN { + if (senv->reject_mail != NULL) { + result = (senv->reject_mail(senv, recipient, + reason) >= 0 ? + SIEVE_EXEC_OK : SIEVE_EXEC_FAILURE); + } else { + result = sieve_action_do_reject_mail(aenv, recipient, + reason); + } + } T_END; + + return result; +} + +/* + * Mailbox + */ + +bool sieve_mailbox_check_name(const char *mailbox, const char **error_r) +{ + if (!uni_utf8_str_is_valid(mailbox)) { + *error_r = "invalid utf-8"; + return FALSE; + } + return TRUE; +} + + diff --git a/pigeonhole/src/lib-sieve/sieve-actions.h b/pigeonhole/src/lib-sieve/sieve-actions.h new file mode 100644 index 0000000..f1a447f --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-actions.h @@ -0,0 +1,311 @@ +#ifndef SIEVE_ACTIONS_H +#define SIEVE_ACTIONS_H + +#include "lib.h" +#include "mail-types.h" +#include "mail-error.h" + +#include "sieve-common.h" +#include "sieve-objects.h" +#include "sieve-extensions.h" +#include "sieve-execute.h" + +/* + * Action execution environment + */ + +struct sieve_action_exec_env { + const struct sieve_execute_env *exec_env; + struct sieve_result_execution *rexec; + const struct sieve_action *action; + struct event *event; + + struct sieve_result *result; + struct sieve_error_handler *ehandler; + + struct sieve_message_context *msgctx; +}; + +struct event_passthrough * +sieve_action_create_finish_event(const struct sieve_action_exec_env *aenv); + +/* + * Action flags + */ + +enum sieve_action_flags { + SIEVE_ACTFLAG_TRIES_DELIVER = (1 << 0), + SIEVE_ACTFLAG_SENDS_RESPONSE = (1 << 1), + SIEVE_ACTFLAG_MAIL_STORAGE = (1 << 2) +}; + +/* + * Action definition + */ + +struct sieve_action_def { + const char *name; + unsigned int flags; + + bool (*equals)(const struct sieve_script_env *senv, + const struct sieve_action *act1, + const struct sieve_action *act2); + + /* Result verification */ + int (*check_duplicate)(const struct sieve_runtime_env *renv, + const struct sieve_action *act, + const struct sieve_action *act_other); + int (*check_conflict)(const struct sieve_runtime_env *renv, + const struct sieve_action *act, + const struct sieve_action *act_other); + + /* Result printing */ + void (*print)(const struct sieve_action *action, + const struct sieve_result_print_env *penv, bool *keep); + + /* Result execution */ + int (*start)(const struct sieve_action_exec_env *aenv, + void **tr_context); + int (*execute)(const struct sieve_action_exec_env *aenv, + void *tr_context, bool *keep); + int (*commit)(const struct sieve_action_exec_env *aenv, + void *tr_context); + void (*rollback)(const struct sieve_action_exec_env *aenv, + void *tr_context, bool success); + void (*finish)(const struct sieve_action_exec_env *aenv, + void *tr_context, int status); +}; + +/* + * Action instance + */ + +struct sieve_action { + const struct sieve_action_def *def; + const struct sieve_extension *ext; + struct event *event; + + const char *name; + const char *location; + unsigned int exec_seq; + void *context; + struct mail *mail; + + bool keep:1; +}; + +#define sieve_action_is(act, definition) ((act)->def == &(definition)) +#define sieve_action_name(act) ((act)->name) + +bool sieve_action_is_executed(const struct sieve_action *act, + struct sieve_result *result); + +/* + * Action side effects + */ + +/* Side effect object */ + +struct sieve_side_effect_def { + struct sieve_object_def obj_def; + + /* Precedence (side effects with higher value are executed first) */ + + unsigned int precedence; + + /* The action it is supposed to link to */ + const struct sieve_action_def *to_action; + + /* Context coding */ + bool (*dump_context)(const struct sieve_side_effect *seffect, + const struct sieve_dumptime_env *renv, + sieve_size_t *address); + int (*read_context)(const struct sieve_side_effect *seffect, + const struct sieve_runtime_env *renv, + sieve_size_t *address, void **se_context); + + /* Result verification */ + int (*merge)(const struct sieve_runtime_env *renv, + const struct sieve_action *action, + const struct sieve_side_effect *old_seffect, + const struct sieve_side_effect *new_seffect, + void **old_context); + + /* Result printing */ + void (*print)(const struct sieve_side_effect *seffect, + const struct sieve_action *action, + const struct sieve_result_print_env *penv, bool *keep); + + /* Result execution */ + + int (*pre_execute)(const struct sieve_side_effect *seffect, + const struct sieve_action_exec_env *aenv, + void *tr_context, void **se_tr_context); + int (*post_execute)(const struct sieve_side_effect *seffect, + const struct sieve_action_exec_env *aenv, + void *tr_context, void *se_tr_context, bool *keep); + void (*post_commit)(const struct sieve_side_effect *seffect, + const struct sieve_action_exec_env *aenv, + void *tr_context, void *se_tr_context, + int commit_status); + void (*rollback)(const struct sieve_side_effect *seffect, + const struct sieve_action_exec_env *aenv, + void *tr_context, void *se_tr_context, bool success); +}; + +struct sieve_side_effect { + struct sieve_object object; + + const struct sieve_side_effect_def *def; + + void *context; +}; + +/* + * Side effect operand + */ + +#define SIEVE_EXT_DEFINE_SIDE_EFFECT(SEF) SIEVE_EXT_DEFINE_OBJECT(SEF) +#define SIEVE_EXT_DEFINE_SIDE_EFFECTS(SEFS) SIEVE_EXT_DEFINE_OBJECTS(SEFS) + +#define SIEVE_OPT_SIDE_EFFECT (-1) + +extern const struct sieve_operand_class sieve_side_effect_operand_class; + +static inline void +sieve_opr_side_effect_emit(struct sieve_binary_block *sblock, + const struct sieve_extension *ext, + const struct sieve_side_effect_def *seff) +{ + sieve_opr_object_emit(sblock, ext, &seff->obj_def); +} + +bool sieve_opr_side_effect_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +int sieve_opr_side_effect_read(const struct sieve_runtime_env *renv, + sieve_size_t *address, + struct sieve_side_effect *seffect); + +/* + * Optional operands + */ + +int sieve_action_opr_optional_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address, signed int *opt_code); + +int sieve_action_opr_optional_read(const struct sieve_runtime_env *renv, + sieve_size_t *address, signed int *opt_code, + int *exec_status, + struct sieve_side_effects_list **list); + +/* + * Core actions + */ + +extern const struct sieve_action_def act_redirect; +extern const struct sieve_action_def act_store; +extern const struct sieve_action_def act_discard; + +/* + * Store action + */ + +struct act_store_context { + /* Folder name represented in utf-8 */ + const char *mailbox; +}; + +struct act_store_transaction { + struct act_store_context *context; + struct mailbox *box; + struct mailbox_transaction_context *mail_trans; + + const char *mailbox_name; + const char *mailbox_identifier; + + const char *error; + enum mail_error error_code; + + enum mail_flags flags; + ARRAY_TYPE(const_string) keywords; + + bool flags_altered:1; + bool disabled:1; + bool redundant:1; +}; + +int sieve_act_store_add_to_result(const struct sieve_runtime_env *renv, + const char *name, + struct sieve_side_effects_list *seffects, + const char *folder); + +void sieve_act_store_add_flags(const struct sieve_action_exec_env *aenv, + void *tr_context, const char *const *keywords, + enum mail_flags flags); + +void sieve_act_store_get_storage_error(const struct sieve_action_exec_env *aenv, + struct act_store_transaction *trans); + +/* + * Redirect action + */ + +struct act_redirect_context { + const struct smtp_address *to_address; +}; + +int sieve_act_redirect_add_to_result(const struct sieve_runtime_env *renv, + const char *name, + struct sieve_side_effects_list *seffects, + const struct smtp_address *to_address); + +/* + * Checking for duplicates + */ + +static inline bool +sieve_action_duplicate_check_available(const struct sieve_action_exec_env *aenv) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + + return sieve_execute_duplicate_check_available(eenv); +} + +static inline int +sieve_action_duplicate_check(const struct sieve_action_exec_env *aenv, + const void *id, size_t id_size, + bool *duplicate_r) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + + return sieve_execute_duplicate_check(eenv, id, id_size, + duplicate_r); +} + +static inline void +sieve_action_duplicate_mark(const struct sieve_action_exec_env *aenv, + const void *id, size_t id_size, time_t time) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + + return sieve_execute_duplicate_mark(eenv, id, id_size, time); +} + +/* + * Action utility functions + */ + +/* Rejecting mail */ + +int sieve_action_reject_mail(const struct sieve_action_exec_env *aenv, + const struct smtp_address *recipient, + const char *reason); + +/* + * Mailbox + */ + +// FIXME: move this to a more appropriate location +bool sieve_mailbox_check_name(const char *mailbox, const char **error_r); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-address-parts.c b/pigeonhole/src/lib-sieve/sieve-address-parts.c new file mode 100644 index 0000000..a856e20 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-address-parts.c @@ -0,0 +1,495 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "compat.h" +#include "mempool.h" +#include "hash.h" +#include "array.h" +#include "str-sanitize.h" + +#include "sieve-extensions.h" +#include "sieve-code.h" +#include "sieve-address.h" +#include "sieve-commands.h" +#include "sieve-binary.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-match.h" + +#include "sieve-address-parts.h" + +#include <string.h> + +/* + * Default address parts + */ + +const struct sieve_address_part_def *sieve_core_address_parts[] = { + &all_address_part, &local_address_part, &domain_address_part +}; + +const unsigned int sieve_core_address_parts_count = + N_ELEMENTS(sieve_core_address_parts); + +/* + * Address-part 'extension' + */ + +static bool addrp_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); + +const struct sieve_extension_def address_part_extension = { + .name = "@address-parts", + .validator_load = addrp_validator_load +}; + +/* + * Validator context: + * name-based address-part registry. + */ + +static struct sieve_validator_object_registry *_get_object_registry +(struct sieve_validator *valdtr) +{ + struct sieve_instance *svinst; + const struct sieve_extension *adrp_ext; + + svinst = sieve_validator_svinst(valdtr); + adrp_ext = sieve_get_address_part_extension(svinst); + return sieve_validator_object_registry_get(valdtr, adrp_ext); +} + +void sieve_address_part_register +(struct sieve_validator *valdtr, const struct sieve_extension *ext, + const struct sieve_address_part_def *addrp_def) +{ + struct sieve_validator_object_registry *regs = _get_object_registry(valdtr); + + sieve_validator_object_registry_add(regs, ext, &addrp_def->obj_def); +} + +static bool sieve_address_part_exists +(struct sieve_validator *valdtr, const char *identifier) +{ + struct sieve_validator_object_registry *regs = _get_object_registry(valdtr); + + return sieve_validator_object_registry_find(regs, identifier, NULL); +} + +static const struct sieve_address_part *sieve_address_part_create_instance +(struct sieve_validator *valdtr, struct sieve_command *cmd, + const char *identifier) +{ + struct sieve_validator_object_registry *regs = _get_object_registry(valdtr); + struct sieve_object object; + struct sieve_address_part *addrp; + + if ( !sieve_validator_object_registry_find(regs, identifier, &object) ) + return NULL; + + addrp = p_new(sieve_command_pool(cmd), struct sieve_address_part, 1); + addrp->object = object; + addrp->def = (const struct sieve_address_part_def *) object.def; + + return addrp; +} + +static bool addrp_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + struct sieve_validator_object_registry *regs = + sieve_validator_object_registry_init(valdtr, ext); + unsigned int i; + + /* Register core address-parts */ + for ( i = 0; i < sieve_core_address_parts_count; i++ ) { + sieve_validator_object_registry_add + (regs, NULL, &(sieve_core_address_parts[i]->obj_def)); + } + + return TRUE; +} + +void sieve_address_parts_link_tags +(struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg, + int id_code) +{ + struct sieve_instance *svinst; + const struct sieve_extension *adrp_ext; + + svinst = sieve_validator_svinst(valdtr); + adrp_ext = sieve_get_address_part_extension(svinst); + + sieve_validator_register_tag + (valdtr, cmd_reg, adrp_ext, &address_part_tag, id_code); +} + +/* + * Address-part tagged argument + */ + +/* Forward declarations */ + +static bool tag_address_part_is_instance_of + (struct sieve_validator *valdtr, struct sieve_command *cmd, + const struct sieve_extension *ext, const char *identifier, void **data); +static bool tag_address_part_validate + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd); +static bool tag_address_part_generate + (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *cmd); + +/* Argument object */ + +const struct sieve_argument_def address_part_tag = { + .identifier = "ADDRESS-PART", + .is_instance_of = tag_address_part_is_instance_of, + .validate = tag_address_part_validate, + .generate = tag_address_part_generate +}; + +/* Argument implementation */ + +static bool tag_address_part_is_instance_of +(struct sieve_validator *valdtr, struct sieve_command *cmd, + const struct sieve_extension *ext ATTR_UNUSED, const char *identifier, + void **data) +{ + const struct sieve_address_part *addrp; + + if ( data == NULL ) + return sieve_address_part_exists(valdtr, identifier); + + if ( (addrp=sieve_address_part_create_instance + (valdtr, cmd, identifier)) == NULL ) + return FALSE; + + *data = (void *) addrp; + return TRUE; +} + +static bool tag_address_part_validate +(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_ast_argument **arg, + struct sieve_command *cmd ATTR_UNUSED) +{ + /* NOTE: Currenly trivial, but might need to allow for further validation for + * future extensions. + */ + + /* Syntax: + * ":localpart" / ":domain" / ":all" (subject to extension) + */ + + /* Skip tag */ + *arg = sieve_ast_argument_next(*arg); + + return TRUE; +} + +static bool tag_address_part_generate +(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *cmd ATTR_UNUSED) +{ + struct sieve_address_part *addrp = + (struct sieve_address_part *) arg->argument->data; + + sieve_opr_address_part_emit(cgenv->sblock, addrp); + + return TRUE; +} + +/* + * Address-part operand + */ + +const struct sieve_operand_class sieve_address_part_operand_class = + { "address part" }; + +static const struct sieve_extension_objects core_address_parts = + SIEVE_EXT_DEFINE_MATCH_TYPES(sieve_core_address_parts); + +const struct sieve_operand_def address_part_operand = { + .name = "address-part", + .code = SIEVE_OPERAND_ADDRESS_PART, + .class = &sieve_address_part_operand_class, + .interface = &core_address_parts +}; + +/* + * Address-part string list + */ + +static int sieve_address_part_stringlist_next_item + (struct sieve_stringlist *_strlist, string_t **str_r); +static void sieve_address_part_stringlist_reset + (struct sieve_stringlist *_strlist); +static int sieve_address_part_stringlist_get_length + (struct sieve_stringlist *_strlist); +static void sieve_address_part_stringlist_set_trace +(struct sieve_stringlist *_strlist, bool trace); + +struct sieve_address_part_stringlist { + struct sieve_stringlist strlist; + + const struct sieve_address_part *addrp; + struct sieve_address_list *addresses; +}; + +struct sieve_stringlist *sieve_address_part_stringlist_create +(const struct sieve_runtime_env *renv, const struct sieve_address_part *addrp, + struct sieve_address_list *addresses) +{ + struct sieve_address_part_stringlist *strlist; + + strlist = t_new(struct sieve_address_part_stringlist, 1); + strlist->strlist.runenv = renv; + strlist->strlist.next_item = sieve_address_part_stringlist_next_item; + strlist->strlist.reset = sieve_address_part_stringlist_reset; + strlist->strlist.get_length = sieve_address_part_stringlist_get_length; + strlist->strlist.set_trace = sieve_address_part_stringlist_set_trace; + + strlist->addrp = addrp; + strlist->addresses = addresses; + + return &strlist->strlist; +} + +static int sieve_address_part_stringlist_next_item + (struct sieve_stringlist *_strlist, string_t **str_r) +{ + struct sieve_address_part_stringlist *strlist = + (struct sieve_address_part_stringlist *)_strlist; + struct smtp_address item; + string_t *item_unparsed; + int ret; + + *str_r = NULL; + + while ( *str_r == NULL ) { + if ( (ret=sieve_address_list_next_item + (strlist->addresses, &item, &item_unparsed)) <= 0 ) + return ret; + + if ( item.localpart == NULL ) { + if ( item_unparsed != NULL ) { + if ( _strlist->trace ) { + sieve_runtime_trace(_strlist->runenv, 0, + "extracting `%s' part from non-address value `%s'", + sieve_address_part_name(strlist->addrp), + str_sanitize(str_c(item_unparsed), 80)); + } + + if ( str_len(item_unparsed) == 0 || + sieve_address_part_is(strlist->addrp, all_address_part) ) + *str_r = item_unparsed; + } + } else { + const struct sieve_address_part *addrp = strlist->addrp; + const char *part = NULL; + + if ( _strlist->trace ) { + sieve_runtime_trace(_strlist->runenv, 0, + "extracting `%s' part from address %s", + sieve_address_part_name(strlist->addrp), + smtp_address_encode_path(&item)); + } + + if ( addrp->def != NULL && addrp->def->extract_from != NULL ) + part = addrp->def->extract_from(addrp, &item); + + if ( part != NULL ) + *str_r = t_str_new_const(part, strlen(part)); + } + } + + return 1; +} + +static void sieve_address_part_stringlist_reset + (struct sieve_stringlist *_strlist) +{ + struct sieve_address_part_stringlist *strlist = + (struct sieve_address_part_stringlist *)_strlist; + + sieve_address_list_reset(strlist->addresses); +} + +static int sieve_address_part_stringlist_get_length + (struct sieve_stringlist *_strlist) +{ + struct sieve_address_part_stringlist *strlist = + (struct sieve_address_part_stringlist *)_strlist; + + return sieve_address_list_get_length(strlist->addresses); +} + +static void sieve_address_part_stringlist_set_trace +(struct sieve_stringlist *_strlist, bool trace) +{ + struct sieve_address_part_stringlist *strlist = + (struct sieve_address_part_stringlist *)_strlist; + + sieve_address_list_set_trace(strlist->addresses, trace); +} + +/* + * Default ADDRESS-PART, MATCH-TYPE, COMPARATOR access + */ + +int sieve_addrmatch_opr_optional_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address, + signed int *opt_code) +{ + signed int _opt_code = 0; + bool final = FALSE, opok = TRUE; + + if ( opt_code == NULL ) { + opt_code = &_opt_code; + final = TRUE; + } + + while ( opok ) { + int opt; + + if ( (opt=sieve_opr_optional_dump(denv, address, opt_code)) <= 0 ) + return opt; + + switch ( *opt_code ) { + case SIEVE_MATCH_OPT_COMPARATOR: + opok = sieve_opr_comparator_dump(denv, address); + break; + case SIEVE_MATCH_OPT_MATCH_TYPE: + opok = sieve_opr_match_type_dump(denv, address); + break; + case SIEVE_AM_OPT_ADDRESS_PART: + opok = sieve_opr_address_part_dump(denv, address); + break; + default: + return ( final ? -1 : 1 ); + } + } + + return -1; +} + +int sieve_addrmatch_opr_optional_read +(const struct sieve_runtime_env *renv, sieve_size_t *address, + signed int *opt_code, int *exec_status, struct sieve_address_part *addrp, + struct sieve_match_type *mtch, struct sieve_comparator *cmp) +{ + signed int _opt_code = 0; + bool final = FALSE; + int status = SIEVE_EXEC_OK; + + if ( opt_code == NULL ) { + opt_code = &_opt_code; + final = TRUE; + } + + if ( exec_status != NULL ) + *exec_status = SIEVE_EXEC_OK; + + while ( status == SIEVE_EXEC_OK ) { + int opt; + + if ( (opt=sieve_opr_optional_read(renv, address, opt_code)) <= 0 ){ + if ( opt < 0 && exec_status != NULL ) + *exec_status = SIEVE_EXEC_BIN_CORRUPT; + return opt; + } + + switch ( *opt_code ) { + case SIEVE_MATCH_OPT_COMPARATOR: + if (cmp == NULL) { + sieve_runtime_trace_error(renv, "unexpected comparator operand"); + if ( exec_status != NULL ) + *exec_status = SIEVE_EXEC_BIN_CORRUPT; + return -1; + } + status = sieve_opr_comparator_read(renv, address, cmp); + break; + case SIEVE_MATCH_OPT_MATCH_TYPE: + if (mtch == NULL) { + sieve_runtime_trace_error(renv, "unexpected match-type operand"); + if ( exec_status != NULL ) + *exec_status = SIEVE_EXEC_BIN_CORRUPT; + return -1; + } + status = sieve_opr_match_type_read(renv, address, mtch); + break; + case SIEVE_AM_OPT_ADDRESS_PART: + if (addrp == NULL) { + sieve_runtime_trace_error(renv, "unexpected address-part operand"); + if ( exec_status != NULL ) + *exec_status = SIEVE_EXEC_BIN_CORRUPT; + return -1; + } + status = sieve_opr_address_part_read(renv, address, addrp); + break; + default: + if ( final ) { + sieve_runtime_trace_error(renv, "invalid optional operand"); + if ( exec_status != NULL ) + *exec_status = SIEVE_EXEC_BIN_CORRUPT; + return -1; + } + return 1; + } + } + + if ( exec_status != NULL ) + *exec_status = status; + return -1; +} + +/* + * Core address-part modifiers + */ + +static const char *addrp_all_extract_from +(const struct sieve_address_part *addrp ATTR_UNUSED, + const struct smtp_address *address) +{ + if ( address->localpart == NULL ) + return NULL; + return smtp_address_encode(address); +} + +static const char *addrp_domain_extract_from +(const struct sieve_address_part *addrp ATTR_UNUSED, + const struct smtp_address *address) +{ + return address->domain; +} + +static const char *addrp_localpart_extract_from +(const struct sieve_address_part *addrp ATTR_UNUSED, + const struct smtp_address *address) +{ + return address->localpart; +} + +const struct sieve_address_part_def all_address_part = { + SIEVE_OBJECT("all", + &address_part_operand, SIEVE_ADDRESS_PART_ALL), + .extract_from = addrp_all_extract_from +}; + +const struct sieve_address_part_def local_address_part = { + SIEVE_OBJECT("localpart", + &address_part_operand, SIEVE_ADDRESS_PART_LOCAL), + .extract_from = addrp_localpart_extract_from +}; + +const struct sieve_address_part_def domain_address_part = { + SIEVE_OBJECT("domain", + &address_part_operand, SIEVE_ADDRESS_PART_DOMAIN), + .extract_from = addrp_domain_extract_from +}; + diff --git a/pigeonhole/src/lib-sieve/sieve-address-parts.h b/pigeonhole/src/lib-sieve/sieve-address-parts.h new file mode 100644 index 0000000..c774dc3 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-address-parts.h @@ -0,0 +1,135 @@ +#ifndef SIEVE_ADDRESS_PARTS_H +#define SIEVE_ADDRESS_PARTS_H + +#include "message-address.h" + +#include "sieve-common.h" +#include "sieve-match.h" +#include "sieve-extensions.h" +#include "sieve-objects.h" + +/* + * Address part definition + */ + +struct sieve_address_part_def { + struct sieve_object_def obj_def; + + const char *(*extract_from) + (const struct sieve_address_part *addrp, + const struct smtp_address *address); +}; + +/* + * Address part instance + */ + +struct sieve_address_part { + struct sieve_object object; + + const struct sieve_address_part_def *def; +}; + +#define SIEVE_ADDRESS_PART_DEFAULT(definition) \ + { SIEVE_OBJECT_DEFAULT(definition), &(definition) }; + +#define sieve_address_part_name(addrp) \ + ( (addrp)->object.def->identifier ) +#define sieve_address_part_is(addrp, definition) \ + ( (addrp)->def == &(definition) ) + +/* + * Core address parts + */ + +enum sieve_address_part_code { + SIEVE_ADDRESS_PART_ALL, + SIEVE_ADDRESS_PART_LOCAL, + SIEVE_ADDRESS_PART_DOMAIN, + SIEVE_ADDRESS_PART_CUSTOM +}; + +extern const struct sieve_address_part_def all_address_part; +extern const struct sieve_address_part_def local_address_part; +extern const struct sieve_address_part_def domain_address_part; + +/* + * Address part tagged argument + */ + +extern const struct sieve_argument_def address_part_tag; + +void sieve_address_parts_link_tags + (struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg, + int id_code); + +/* + * Address part registry + */ + +void sieve_address_part_register + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + const struct sieve_address_part_def *addrp); + +/* + * Address part operand + */ + +extern const struct sieve_operand_def address_part_operand; +extern const struct sieve_operand_class sieve_address_part_operand_class; + +#define SIEVE_EXT_DEFINE_ADDRESS_PART(OP) SIEVE_EXT_DEFINE_OBJECT(OP) +#define SIEVE_EXT_DEFINE_ADDRESS_PARTS(OPS) SIEVE_EXT_DEFINE_OBJECTS(OPS) + +static inline void sieve_opr_address_part_emit +(struct sieve_binary_block *sblock, const struct sieve_address_part *addrp) +{ + sieve_opr_object_emit(sblock, addrp->object.ext, addrp->object.def); +} + +static inline bool sieve_opr_address_part_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + return sieve_opr_object_dump + (denv, &sieve_address_part_operand_class, address, NULL); +} + +static inline int sieve_opr_address_part_read +(const struct sieve_runtime_env *renv, sieve_size_t *address, + struct sieve_address_part *addrp) +{ + if ( !sieve_opr_object_read + (renv, &sieve_address_part_operand_class, address, &addrp->object) ) + return SIEVE_EXEC_BIN_CORRUPT; + + addrp->def = (const struct sieve_address_part_def *) addrp->object.def; + return SIEVE_EXEC_OK; +} + +/* + * Address-part string list + */ + +struct sieve_stringlist *sieve_address_part_stringlist_create + (const struct sieve_runtime_env *renv, const struct sieve_address_part *addrp, + struct sieve_address_list *addresses); + +/* + * Match utility + */ + +enum sieve_addrmatch_opt_operand { + SIEVE_AM_OPT_ADDRESS_PART = SIEVE_MATCH_OPT_LAST, + SIEVE_AM_OPT_LAST +}; + +int sieve_addrmatch_opr_optional_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address, + signed int *opt_code); + +int sieve_addrmatch_opr_optional_read + (const struct sieve_runtime_env *renv, sieve_size_t *address, + signed int *opt_code, int *exec_status, struct sieve_address_part *addrp, + struct sieve_match_type *mtch, struct sieve_comparator *cmp); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-address-source.c b/pigeonhole/src/lib-sieve/sieve-address-source.c new file mode 100644 index 0000000..a5ec6f6 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-address-source.c @@ -0,0 +1,119 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-settings.h" +#include "sieve-address.h" +#include "sieve-message.h" + +#include "sieve-address-source.h" + +bool sieve_address_source_parse +(pool_t pool, const char *value, + struct sieve_address_source *asrc) +{ + struct smtp_address *address; + const char *error; + size_t val_len; + + i_zero(asrc); + + value = t_str_trim(value, "\t "); + value = t_str_lcase(value); + val_len = strlen(value); + if ( val_len > 0 ) { + if ( strcmp(value, "default") == 0 ) { + asrc->type = SIEVE_ADDRESS_SOURCE_DEFAULT; + } else if ( strcmp(value, "sender") == 0 ) { + asrc->type = SIEVE_ADDRESS_SOURCE_SENDER; + } else if ( strcmp(value, "recipient") == 0 ) { + asrc->type = SIEVE_ADDRESS_SOURCE_RECIPIENT; + } else if ( strcmp(value, "orig_recipient") == 0 ) { + asrc->type = SIEVE_ADDRESS_SOURCE_ORIG_RECIPIENT; + } else if ( strcmp(value, "user_email") == 0 ) { + asrc->type = SIEVE_ADDRESS_SOURCE_USER_EMAIL; + } else if ( strcmp(value, "postmaster") == 0 ) { + asrc->type = SIEVE_ADDRESS_SOURCE_POSTMASTER; + } else if ( smtp_address_parse_path(pool_datastack_create(), value, + SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY, &address, &error) >= 0 ) { + asrc->type = SIEVE_ADDRESS_SOURCE_EXPLICIT; + asrc->address = smtp_address_clone(pool, address); + } else { + return FALSE; + } + } + return TRUE; +} + +bool sieve_address_source_parse_from_setting +(struct sieve_instance *svinst, pool_t pool, + const char *setting, struct sieve_address_source *asrc) +{ + const char *value; + + value = sieve_setting_get(svinst, setting); + if ( value == NULL ) + return FALSE; + + if ( !sieve_address_source_parse(pool, value, asrc) ) { + e_warning(svinst->event, "Invalid value for setting '%s': '%s'", + setting, value); + return FALSE; + } + return TRUE; +} + +int sieve_address_source_get_address +(struct sieve_address_source *asrc, + struct sieve_instance *svinst, + const struct sieve_script_env *senv, + struct sieve_message_context *msgctx, + enum sieve_execute_flags flags, + const struct smtp_address **addr_r) +{ + enum sieve_address_source_type type = asrc->type; + + if ( type == SIEVE_ADDRESS_SOURCE_USER_EMAIL && + svinst->user_email == NULL ) + type = SIEVE_ADDRESS_SOURCE_RECIPIENT; + + if ( (flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) != 0 ) { + switch ( type ) { + case SIEVE_ADDRESS_SOURCE_SENDER: + case SIEVE_ADDRESS_SOURCE_RECIPIENT: + case SIEVE_ADDRESS_SOURCE_ORIG_RECIPIENT: + /* We have no envelope */ + type = SIEVE_ADDRESS_SOURCE_DEFAULT; + break; + default: + break; + } + } + + switch ( type ) { + case SIEVE_ADDRESS_SOURCE_SENDER: + *addr_r = sieve_message_get_sender(msgctx); + return 1; + case SIEVE_ADDRESS_SOURCE_RECIPIENT: + *addr_r = sieve_message_get_final_recipient(msgctx); + return 1; + case SIEVE_ADDRESS_SOURCE_ORIG_RECIPIENT: + *addr_r = sieve_message_get_orig_recipient(msgctx); + return 1; + case SIEVE_ADDRESS_SOURCE_USER_EMAIL: + *addr_r = svinst->user_email; + return 1; + case SIEVE_ADDRESS_SOURCE_POSTMASTER: + *addr_r = sieve_get_postmaster_smtp(senv); + return 1; + case SIEVE_ADDRESS_SOURCE_EXPLICIT: + *addr_r = asrc->address; + return 1; + case SIEVE_ADDRESS_SOURCE_DEFAULT: + break; + } + return 0; +} diff --git a/pigeonhole/src/lib-sieve/sieve-address-source.h b/pigeonhole/src/lib-sieve/sieve-address-source.h new file mode 100644 index 0000000..fac4b49 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-address-source.h @@ -0,0 +1,36 @@ +#ifndef SIEVE_ADDRESS_SOURCE_H +#define SIEVE_ADDRESS_SOURCE_H + +#include "sieve-common.h" + +enum sieve_address_source_type { + SIEVE_ADDRESS_SOURCE_DEFAULT = 0, + SIEVE_ADDRESS_SOURCE_SENDER, + SIEVE_ADDRESS_SOURCE_RECIPIENT, + SIEVE_ADDRESS_SOURCE_ORIG_RECIPIENT, + SIEVE_ADDRESS_SOURCE_USER_EMAIL, + SIEVE_ADDRESS_SOURCE_POSTMASTER, + SIEVE_ADDRESS_SOURCE_EXPLICIT +}; + +struct sieve_address_source { + enum sieve_address_source_type type; + const struct smtp_address *address; +}; + +bool sieve_address_source_parse + (pool_t pool, const char *value, + struct sieve_address_source *asrc); +bool sieve_address_source_parse_from_setting + (struct sieve_instance *svinst, pool_t pool, + const char *setting, struct sieve_address_source *asrc); + +int sieve_address_source_get_address + (struct sieve_address_source *asrc, + struct sieve_instance *svinst, + const struct sieve_script_env *senv, + struct sieve_message_context *msgctx, + enum sieve_execute_flags flags, + const struct smtp_address **addr_r); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-address.c b/pigeonhole/src/lib-sieve/sieve-address.c new file mode 100644 index 0000000..f9456f8 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-address.c @@ -0,0 +1,558 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "str-sanitize.h" +#include "rfc822-parser.h" +#include "message-address.h" + +#include "sieve-common.h" +#include "sieve-runtime-trace.h" + +#include "sieve-address.h" + +#include <ctype.h> + +/* + * Header address list + */ + +/* Forward declarations */ + +static int +sieve_header_address_list_next_string_item(struct sieve_stringlist *_strlist, + string_t **str_r); +static int +sieve_header_address_list_next_item(struct sieve_address_list *_addrlist, + struct smtp_address *addr_r, + string_t **unparsed_r); +static void +sieve_header_address_list_reset(struct sieve_stringlist *_strlist); +static void +sieve_header_address_list_set_trace(struct sieve_stringlist *_strlist, + bool trace); + +/* Stringlist object */ + +struct sieve_header_address_list { + struct sieve_address_list addrlist; + + struct sieve_stringlist *field_values; + const struct message_address *cur_address; +}; + +struct sieve_address_list * +sieve_header_address_list_create(const struct sieve_runtime_env *renv, + struct sieve_stringlist *field_values) +{ + struct sieve_header_address_list *addrlist; + + addrlist = t_new(struct sieve_header_address_list, 1); + addrlist->addrlist.strlist.runenv = renv; + addrlist->addrlist.strlist.exec_status = SIEVE_EXEC_OK; + addrlist->addrlist.strlist.next_item = + sieve_header_address_list_next_string_item; + addrlist->addrlist.strlist.reset = sieve_header_address_list_reset; + addrlist->addrlist.strlist.set_trace = + sieve_header_address_list_set_trace; + addrlist->addrlist.next_item = sieve_header_address_list_next_item; + addrlist->field_values = field_values; + + return &addrlist->addrlist; +} + +static int +sieve_header_address_list_next_address( + struct sieve_header_address_list *addrlist, struct smtp_address *addr_r) +{ + struct smtp_address adummy; + int ret = 0; + + if (addr_r == NULL) + addr_r = &adummy; + + while (addrlist->cur_address != NULL) { + const struct message_address *aitem = addrlist->cur_address; + + addrlist->cur_address = addrlist->cur_address->next; + + if (!aitem->invalid_syntax && aitem->domain != NULL && + smtp_address_init_from_msg(addr_r, aitem) >= 0) + return 1; + ret = -1; + } + return ret; +} + +static int +sieve_header_address_list_next_item(struct sieve_address_list *_addrlist, + struct smtp_address *addr_r, + string_t **unparsed_r) +{ + struct sieve_header_address_list *addrlist = + (struct sieve_header_address_list *)_addrlist; + const struct sieve_runtime_env *runenv = _addrlist->strlist.runenv; + string_t *value_item = NULL; + bool trace = _addrlist->strlist.trace; + + if (addr_r != NULL) + smtp_address_init(addr_r, NULL, NULL); + if (unparsed_r != NULL) + *unparsed_r = NULL; + + for (;;) { + int ret; + + if ((ret = sieve_header_address_list_next_address( + addrlist, addr_r)) < 0 && + value_item != NULL) { + /* completely invalid address list is returned as-is */ + if (trace) { + sieve_runtime_trace( + runenv, 0, + "invalid address value `%s'", + str_sanitize(str_c(value_item), 80)); + } + if (unparsed_r != NULL) + *unparsed_r = value_item; + return 1; + } + if (ret > 0) { + if (trace) { + sieve_runtime_trace( + runenv, 0, + "address value `%s'", + str_sanitize(smtp_address_encode(addr_r), + 80)); + } + return 1; + } + + /* Read next header value from source list */ + if ((ret = sieve_stringlist_next_item(addrlist->field_values, + &value_item)) <= 0) + return ret; + if (str_len(value_item) == 0) { + /* empty header value is returned as-is */ + if (trace) { + sieve_runtime_trace(runenv, 0, + "empty address value"); + } + addrlist->cur_address = NULL; + if (unparsed_r != NULL) + *unparsed_r = value_item; + return 1; + } + + if (trace) { + sieve_runtime_trace( + runenv, 0, + "parsing address header value `%s'", + str_sanitize(str_c(value_item), 80)); + } + + addrlist->cur_address = message_address_parse( + pool_datastack_create(), + (const unsigned char *)str_data(value_item), + str_len(value_item), 256, 0); + } + i_unreached(); +} + +static int +sieve_header_address_list_next_string_item(struct sieve_stringlist *_strlist, + string_t **str_r) +{ + struct sieve_address_list *addrlist = + (struct sieve_address_list *)_strlist; + struct smtp_address addr; + int ret; + + if ((ret = sieve_header_address_list_next_item( + addrlist, &addr, str_r)) <= 0) + return ret; + + if (addr.localpart != NULL) { + const char *addr_str = smtp_address_encode(&addr); + + if (str_r != NULL) + *str_r = t_str_new_const(addr_str, strlen(addr_str)); + } + return 1; +} + +static void sieve_header_address_list_reset(struct sieve_stringlist *_strlist) +{ + struct sieve_header_address_list *addrlist = + (struct sieve_header_address_list *)_strlist; + + sieve_stringlist_reset(addrlist->field_values); + addrlist->cur_address = NULL; +} + +static void +sieve_header_address_list_set_trace(struct sieve_stringlist *_strlist, + bool trace) +{ + struct sieve_header_address_list *addrlist = + (struct sieve_header_address_list *)_strlist; + + sieve_stringlist_set_trace(addrlist->field_values, trace); +} + +/* + * RFC 2822 addresses + */ + +/* Mail message address according to RFC 2822 and implemented in the Dovecot + message address parser: + + address = mailbox / group + mailbox = name-addr / addr-spec + name-addr = [display-name] angle-addr + angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr + group = display-name ":" [mailbox-list / CFWS] ";" [CFWS] + display-name = phrase + + addr-spec = local-part "@" domain + local-part = dot-atom / quoted-string / obs-local-part + domain = dot-atom / domain-literal / obs-domain + domain-literal = [CFWS] "[" *([FWS] dcontent) [FWS] "]" [CFWS] + dcontent = dtext / quoted-pair + dtext = NO-WS-CTL / ; Non white space controls + %d33-90 / ; The rest of the US-ASCII + %d94-126 ; characters not including "[", + ; "]", or "\" + + atext = ALPHA / DIGIT / ; Any character except controls, + "!" / "#" / ; SP, and specials. + "$" / "%" / ; Used for atoms + "&" / "'" / + "*" / "+" / + "-" / "/" / + "=" / "?" / + "^" / "_" / + "`" / "{" / + "|" / "}" / + "~" + atom = [CFWS] 1*atext [CFWS] + dot-atom = [CFWS] dot-atom-text [CFWS] + dot-atom-text = 1*atext *("." 1*atext) + word = atom / quoted-string + phrase = 1*word / obs-phrase + + Message address specification as allowed bij the RFC 5228 SIEVE + specification: + sieve-address = addr-spec ; simple address + / phrase "<" addr-spec ">" ; name & addr-spec\ + + Which concisely is about equal to: + sieve-address = mailbox + */ + +/* + * Address parse context + */ + +struct sieve_message_address_parser { + struct rfc822_parser_context parser; + + string_t *str; + string_t *local_part; + string_t *domain; + + string_t *error; +}; + +/* + * Error handling + */ + +static inline void ATTR_FORMAT(2, 3) +sieve_address_error(struct sieve_message_address_parser *ctx, + const char *fmt, ...) +{ + va_list args; + + if (str_len(ctx->error) == 0) { + va_start(args, fmt); + str_vprintfa(ctx->error, fmt, args); + va_end(args); + } +} + +/* + Partial RFC 2822 address parser + + FIXME: lots of overlap with dovecot/src/lib-mail/message-parser.c + --> this implementation adds textual error reporting + MERGE! + */ + +static int check_local_part(struct sieve_message_address_parser *ctx) +{ + const unsigned char *p, *pend; + + p = str_data(ctx->local_part); + pend = p + str_len(ctx->local_part); + while (p < pend) { + if (*p < 0x20 || *p > 0x7e) + return -1; + p++; + } + return 0; +} + +static int parse_local_part(struct sieve_message_address_parser *ctx) +{ + int ret; + + /* + local-part = dot-atom / quoted-string / obs-local-part + obs-local-part = word *("." word) + */ + if (ctx->parser.data == ctx->parser.end) { + sieve_address_error(ctx, "empty local part"); + return -1; + } + + str_truncate(ctx->local_part, 0); + if (*ctx->parser.data == '"') { + ret = rfc822_parse_quoted_string(&ctx->parser, ctx->local_part); + } else { + ret = -1; + /* NOTE: this deviates from dot-atom syntax to allow some + Japanese mail addresses with dots at non-standard places to + be accepted. */ + do { + while (*ctx->parser.data == '.') { + str_append_c(ctx->local_part, '.'); + ctx->parser.data++; + if (ctx->parser.data == ctx->parser.end) { + /* @domain is missing, but local-part + parsing was successful */ + return 0; + } + ret = 1; + } + if (*ctx->parser.data == '@') + break; + ret = rfc822_parse_atom(&ctx->parser, ctx->local_part); + } while (ret > 0 && *ctx->parser.data == '.'); + } + + if (ret < 0 || check_local_part(ctx) < 0) { + sieve_address_error(ctx, "invalid local part"); + return -1; + } + return ret; +} + +static int parse_domain(struct sieve_message_address_parser *ctx) +{ + int ret; + + str_truncate(ctx->domain, 0); + if ((ret = rfc822_parse_domain(&ctx->parser, ctx->domain)) < 0) { + sieve_address_error(ctx, "invalid or missing domain"); + return -1; + } + + return ret; +} + +static int parse_addr_spec(struct sieve_message_address_parser *ctx) +{ + /* addr-spec = local-part "@" domain */ + int ret; + + if ((ret = parse_local_part(ctx)) < 0) + return ret; + + if (ret > 0 && *ctx->parser.data == '@') { + return parse_domain(ctx); + } + + sieve_address_error( + ctx, "invalid or lonely local part '%s' (expecting '@')", + str_sanitize(str_c(ctx->local_part), 80)); + return -1; +} + +static int parse_mailbox(struct sieve_message_address_parser *ctx) +{ + int ret; + const unsigned char *start; + + /* sieve-address = addr-spec ; simple address + / phrase "<" addr-spec ">" ; name & addr-spec + */ + + /* Record parser state in case we fail at our first attempt */ + start = ctx->parser.data; + + /* First try: phrase "<" addr-spec ">" ; name & addr-spec */ + str_truncate(ctx->str, 0); + if (rfc822_parse_phrase(&ctx->parser, ctx->str) <= 0 || + *ctx->parser.data != '<') { + /* Failed; try just bare addr-spec */ + ctx->parser.data = start; + return parse_addr_spec(ctx); + } + + /* "<" addr-spec ">" */ + ctx->parser.data++; + + if ((ret = rfc822_skip_lwsp(&ctx->parser)) <= 0) { + if (ret < 0) + sieve_address_error(ctx, "invalid characters after <"); + return ret; + } + + if (parse_addr_spec(ctx) < 0) + return -1; + + if (*ctx->parser.data != '>') { + sieve_address_error(ctx, "missing '>'"); + return -1; + } + ctx->parser.data++; + + if ((ret = rfc822_skip_lwsp(&ctx->parser)) < 0) { + sieve_address_error( + ctx, "address ends with invalid characters"); + } + return ret; +} + +static bool +parse_mailbox_address(struct sieve_message_address_parser *ctx, + const unsigned char *address, unsigned int addr_size) +{ + /* Initialize parser */ + + rfc822_parser_init(&ctx->parser, address, addr_size, NULL); + + /* Parse */ + + rfc822_skip_lwsp(&ctx->parser); + + if (ctx->parser.data == ctx->parser.end) { + sieve_address_error(ctx, "empty address"); + return FALSE; + } + + if (parse_mailbox(ctx) < 0) + return FALSE; + + if (ctx->parser.data != ctx->parser.end) { + if (*ctx->parser.data == ',') { + sieve_address_error( + ctx, "not a single addres (found ',')"); + } else { + sieve_address_error( + ctx, "address ends in invalid characters"); + } + return FALSE; + } + + if (str_len(ctx->domain) == 0) { + /* Not gonna happen */ + sieve_address_error(ctx, "missing domain"); + return FALSE; + } + + if (str_len(ctx->local_part) == 0) { + sieve_address_error(ctx, "missing local part"); + return FALSE; + } + return TRUE; +} + +static bool +sieve_address_do_validate(const unsigned char *address, size_t size, + const char **error_r) +{ + struct sieve_message_address_parser ctx; + + *error_r = NULL; + + if (address == NULL) { + *error_r = "null address"; + return FALSE; + } + + i_zero(&ctx); + + ctx.local_part = t_str_new(128); + ctx.domain = t_str_new(128); + ctx.str = t_str_new(128); + ctx.error = t_str_new(128); + + if (!parse_mailbox_address(&ctx, address, size)) { + *error_r = str_c(ctx.error); + return FALSE; + } + + return TRUE; +} + +static const struct smtp_address * +sieve_address_do_parse(const unsigned char *address, size_t size, + const char **error_r) +{ + struct sieve_message_address_parser ctx; + + *error_r = NULL; + + if (address == NULL) + return NULL; + + i_zero(&ctx); + + ctx.local_part = t_str_new(128); + ctx.domain = t_str_new(128); + ctx.str = t_str_new(128); + ctx.error = t_str_new(128); + + if (!parse_mailbox_address(&ctx, address, size)) { + *error_r = str_c(ctx.error); + return NULL; + } + + (void)str_lcase(str_c_modifiable(ctx.domain)); + + return smtp_address_create_temp(str_c(ctx.local_part), + str_c(ctx.domain)); +} + +/* + * Sieve address + */ + +const struct smtp_address * +sieve_address_parse(const char *address, const char **error_r) +{ + return sieve_address_do_parse((const unsigned char *)address, + strlen(address), error_r); +} + +const struct smtp_address * +sieve_address_parse_str(string_t *address, const char **error_r) +{ + return sieve_address_do_parse(str_data(address), str_len(address), + error_r); +} + +bool sieve_address_validate(const char *address, const char **error_r) +{ + return sieve_address_do_validate((const unsigned char *)address, + strlen(address), error_r); +} + +bool sieve_address_validate_str(string_t *address, const char **error_r) +{ + return sieve_address_do_validate(str_data(address), str_len(address), + error_r); +} diff --git a/pigeonhole/src/lib-sieve/sieve-address.h b/pigeonhole/src/lib-sieve/sieve-address.h new file mode 100644 index 0000000..3779a07 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-address.h @@ -0,0 +1,67 @@ +#ifndef SIEVE_ADDRESS_H +#define SIEVE_ADDRESS_H + +#include "lib.h" +#include "strfuncs.h" +#include "smtp-address.h" + +#include "sieve-common.h" +#include "sieve-stringlist.h" + +/* + * Address list API + */ + +struct sieve_address_list { + struct sieve_stringlist strlist; + + int (*next_item)(struct sieve_address_list *_addrlist, + struct smtp_address *addr_r, string_t **unparsed_r); +}; + +static inline int +sieve_address_list_next_item(struct sieve_address_list *addrlist, + struct smtp_address *addr_r, string_t **unparsed_r) +{ + return addrlist->next_item(addrlist, addr_r, unparsed_r); +} + +static inline void +sieve_address_list_reset(struct sieve_address_list *addrlist) +{ + sieve_stringlist_reset(&addrlist->strlist); +} + +static inline int +sieve_address_list_get_length(struct sieve_address_list *addrlist) +{ + return sieve_stringlist_get_length(&addrlist->strlist); +} + +static inline void +sieve_address_list_set_trace(struct sieve_address_list *addrlist, bool trace) +{ + sieve_stringlist_set_trace(&addrlist->strlist, trace); +} + +/* + * Header address list + */ + +struct sieve_address_list * +sieve_header_address_list_create(const struct sieve_runtime_env *renv, + struct sieve_stringlist *field_values); + +/* + * Sieve address parsing/validatin + */ + +const struct smtp_address * +sieve_address_parse(const char *address, const char **error_r); +const struct smtp_address * +sieve_address_parse_str(string_t *address, const char **error_r); + +bool sieve_address_validate(const char *address, const char **error_r); +bool sieve_address_validate_str(string_t *address, const char **error_r); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-ast.c b/pigeonhole/src/lib-sieve/sieve-ast.c new file mode 100644 index 0000000..197dbad --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-ast.c @@ -0,0 +1,1111 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "mempool.h" +#include "array.h" + +#include "sieve-common.h" +#include "sieve-script.h" +#include "sieve-extensions.h" + +#include "sieve-ast.h" + +#include <stdio.h> + +/* + * Forward declarations + */ + +static struct sieve_ast_node * +sieve_ast_node_create(struct sieve_ast *ast, struct sieve_ast_node *parent, + enum sieve_ast_type type, unsigned int source_line); + +/* + * Types + */ + +/* Extensions to the AST */ + +struct sieve_ast_extension_reg { + const struct sieve_extension *ext; + const struct sieve_ast_extension *ast_ext; + void *context; + + bool required:1; +}; + +/* + * AST object + */ + +struct sieve_ast { + pool_t pool; + int refcount; + + struct sieve_instance *svinst; + + struct sieve_script *script; + + struct sieve_ast_node *root; + + ARRAY(const struct sieve_extension *) linked_extensions; + ARRAY(struct sieve_ast_extension_reg) extensions; +}; + +struct sieve_ast *sieve_ast_create(struct sieve_script *script) +{ + pool_t pool; + struct sieve_ast *ast; + unsigned int ext_count; + + pool = pool_alloconly_create("sieve_ast", 32768); + ast = p_new(pool, struct sieve_ast, 1); + ast->pool = pool; + ast->refcount = 1; + + ast->script = script; + sieve_script_ref(script); + ast->svinst = sieve_script_svinst(script); + + ast->root = sieve_ast_node_create(ast, NULL, SAT_ROOT, 0); + ast->root->identifier = "ROOT"; + + ext_count = sieve_extensions_get_count(ast->svinst); + p_array_init(&ast->linked_extensions, pool, ext_count); + p_array_init(&ast->extensions, pool, ext_count); + + return ast; +} + +void sieve_ast_ref(struct sieve_ast *ast) +{ + ast->refcount++; +} + +void sieve_ast_unref(struct sieve_ast **ast) +{ + unsigned int i, ext_count; + const struct sieve_ast_extension_reg *extrs; + + i_assert((*ast)->refcount > 0); + + if (--(*ast)->refcount != 0) + return; + + /* Release script reference */ + sieve_script_unref(&(*ast)->script); + + /* Signal registered extensions that the AST is being destroyed */ + extrs = array_get(&(*ast)->extensions, &ext_count); + for (i = 0; i < ext_count; i++) { + if (extrs[i].ast_ext != NULL && extrs[i].ast_ext->free != NULL) + extrs[i].ast_ext->free(extrs[i].ext, *ast, + extrs[i].context); + } + + /* Destroy AST */ + pool_unref(&(*ast)->pool); + + *ast = NULL; +} + +struct sieve_ast_node *sieve_ast_root(struct sieve_ast *ast) +{ + return ast->root; +} + +pool_t sieve_ast_pool(struct sieve_ast *ast) +{ + return ast->pool; +} + +struct sieve_script *sieve_ast_script(struct sieve_ast *ast) +{ + return ast->script; +} + +/* + * Extension support + */ + +void sieve_ast_extension_link(struct sieve_ast *ast, + const struct sieve_extension *ext, bool required) +{ + unsigned int i, ext_count; + const struct sieve_extension *const *extensions; + struct sieve_ast_extension_reg *reg; + + if (ext->id < 0) + return; + + /* Initialize registration */ + reg = array_idx_get_space(&ast->extensions, (unsigned int)ext->id); + i_assert(reg->ext == NULL || reg->ext == ext); + reg->ext = ext; + reg->required = reg->required || required; + + /* Prevent duplicates */ + extensions = array_get(&ast->linked_extensions, &ext_count); + for (i = 0; i < ext_count; i++) { + if (extensions[i] == ext) + return; + } + + /* Add extension */ + array_append(&ast->linked_extensions, &ext, 1); +} + +const struct sieve_extension * const * +sieve_ast_extensions_get(struct sieve_ast *ast, unsigned int *count_r) +{ + return array_get(&ast->linked_extensions, count_r); +} + +void sieve_ast_extension_register(struct sieve_ast *ast, + const struct sieve_extension *ext, + const struct sieve_ast_extension *ast_ext, + void *context) +{ + struct sieve_ast_extension_reg *reg; + + if (ext->id < 0) + return; + + /* Initialize registration */ + reg = array_idx_get_space(&ast->extensions, (unsigned int)ext->id); + i_assert(reg->ext == NULL || reg->ext == ext); + reg->ext = ext; + reg->ast_ext = ast_ext; + reg->context = context; +} + +void sieve_ast_extension_set_context(struct sieve_ast *ast, + const struct sieve_extension *ext, + void *context) +{ + struct sieve_ast_extension_reg *reg; + + if (ext->id < 0) + return; + + reg = array_idx_get_space(&ast->extensions, (unsigned int)ext->id); + reg->context = context; +} + +void *sieve_ast_extension_get_context(struct sieve_ast *ast, + const struct sieve_extension *ext) +{ + const struct sieve_ast_extension_reg *reg; + + if (ext->id < 0 || ext->id >= (int)array_count(&ast->extensions)) + return NULL; + + reg = array_idx(&ast->extensions, (unsigned int)ext->id); + + return reg->context; +} + +bool sieve_ast_extension_is_required +(struct sieve_ast *ast, const struct sieve_extension *ext) +{ + const struct sieve_ast_extension_reg *reg; + + i_assert(ext->id >= 0 && + ext->id < (int)array_count(&ast->extensions)); + + reg = array_idx(&ast->extensions, (unsigned int)ext->id); + return reg->required; +} + +/* + * AST list implementations + */ + +/* Very simplistic linked list implementation + FIXME: Merge with core + */ +#define __LIST_CREATE(pool, type) { \ + type *list = p_new(pool, type, 1); \ + list->head = NULL; \ + list->tail = NULL; \ + list->len = 0; \ + return list; \ + } + +#define __LIST_ADD(list, node) { \ + if (list->len + 1 < list->len) \ + return FALSE; \ + \ + node->next = NULL; \ + if (list->head == NULL) { \ + node->prev = NULL; \ + list->head = node; \ + list->tail = node; \ + } else { \ + list->tail->next = node; \ + node->prev = list->tail; \ + list->tail = node; \ + } \ + list->len++; \ + node->list = list; \ + return TRUE; \ + } + +#define __LIST_INSERT(list, before, node) { \ + if (list->len + 1 < list->len) \ + return FALSE; \ + \ + node->next = before; \ + if (list->head == before) { \ + node->prev = NULL; \ + list->head = node; \ + } else { \ + before->prev->next = node; \ + } \ + node->prev = before->prev; \ + before->prev = node; \ + list->len++; \ + node->list = list; \ + \ + return TRUE; \ + } + +#define __LIST_JOIN(list, node_type, items) { \ + node_type *node; \ + \ + if (list->len + items->len < list->len) \ + return FALSE; \ + \ + if (items->len == 0) \ + return TRUE; \ + \ + if (list->head == NULL) { \ + list->head = items->head; \ + list->tail = items->tail; \ + } else { \ + list->tail->next = items->head; \ + items->head->prev = list->tail; \ + list->tail = items->tail; \ + } \ + list->len += items->len; \ + \ + node = items->head; \ + while (node != NULL) { \ + node->list = list; \ + node = node->next; \ + } \ + return TRUE; \ + } + +#define __LIST_DETACH(first, node_type, count) { \ + node_type *last, *result; \ + unsigned int left; \ + \ + i_assert(first->list != NULL); \ + \ + left = count - 1; \ + last = first; \ + while (left > 0 && last->next != NULL) { \ + left--; \ + last = last->next; \ + } \ + \ + if (first->list->head == first) \ + first->list->head = last->next; \ + if (first->list->tail == last) \ + first->list->tail = first->prev; \ + \ + if (first->prev != NULL) \ + first->prev->next = last->next; \ + if (last->next != NULL) \ + last->next->prev = first->prev; \ + \ + first->list->len -= count - left; \ + \ + result = last->next; \ + first->prev = NULL; \ + last->next = NULL; \ + \ + return result; \ + } + +/* List of AST nodes */ + +static struct sieve_ast_list * +sieve_ast_list_create(pool_t pool) + __LIST_CREATE(pool, struct sieve_ast_list) + +static bool +sieve_ast_list_add(struct sieve_ast_list *list, struct sieve_ast_node *node) + __LIST_ADD(list, node) + +static struct sieve_ast_node * +sieve_ast_list_detach(struct sieve_ast_node *first, unsigned int count) + __LIST_DETACH(first, struct sieve_ast_node, count) + +/* List of argument AST nodes */ + +struct sieve_ast_arg_list *sieve_ast_arg_list_create(pool_t pool) + __LIST_CREATE(pool, struct sieve_ast_arg_list) + +bool sieve_ast_arg_list_add(struct sieve_ast_arg_list *list, + struct sieve_ast_argument *argument) + __LIST_ADD(list, argument) + +bool sieve_ast_arg_list_insert(struct sieve_ast_arg_list *list, + struct sieve_ast_argument *before, + struct sieve_ast_argument *argument) + __LIST_INSERT(list, before, argument) + +static bool +sieve_ast_arg_list_join(struct sieve_ast_arg_list *list, + struct sieve_ast_arg_list *items) + __LIST_JOIN(list, struct sieve_ast_argument, items) + +static struct sieve_ast_argument * +sieve_ast_arg_list_detach(struct sieve_ast_argument *first, + const unsigned int count) + __LIST_DETACH(first, struct sieve_ast_argument, count) + +void sieve_ast_arg_list_substitute(struct sieve_ast_arg_list *list, + struct sieve_ast_argument *argument, + struct sieve_ast_argument *replacement) +{ + if (list->head == argument) + list->head = replacement; + if (list->tail == argument) + list->tail = replacement; + + if (argument->prev != NULL) + argument->prev->next = replacement; + if (argument->next != NULL) + argument->next->prev = replacement; + + replacement->prev = argument->prev; + replacement->next = argument->next; + replacement->list = argument->list; + + argument->next = NULL; + argument->prev = NULL; +} + +/* + * AST node + */ + +static struct sieve_ast_node * +sieve_ast_node_create(struct sieve_ast *ast, struct sieve_ast_node *parent, + enum sieve_ast_type type, unsigned int source_line) +{ + struct sieve_ast_node *node = + p_new(ast->pool, struct sieve_ast_node, 1); + + node->ast = ast; + node->parent = parent; + node->type = type; + + node->prev = NULL; + node->next = NULL; + + node->arguments = NULL; + node->tests = NULL; + node->commands = NULL; + + node->test_list = FALSE; + node->block = FALSE; + + node->source_line = source_line; + + return node; +} + +static bool +sieve_ast_node_add_command(struct sieve_ast_node *node, + struct sieve_ast_node *command) +{ + i_assert(command->type == SAT_COMMAND && + (node->type == SAT_ROOT || node->type == SAT_COMMAND)); + + if (node->commands == NULL) + node->commands = sieve_ast_list_create(node->ast->pool); + + return sieve_ast_list_add(node->commands, command); +} + +static bool +sieve_ast_node_add_test(struct sieve_ast_node *node, + struct sieve_ast_node *test) +{ + i_assert(test->type == SAT_TEST && + (node->type == SAT_TEST || node->type == SAT_COMMAND)); + + if (node->tests == NULL) + node->tests = sieve_ast_list_create(node->ast->pool); + + return sieve_ast_list_add(node->tests, test); +} + +static bool +sieve_ast_node_add_argument(struct sieve_ast_node *node, + struct sieve_ast_argument *argument) +{ + i_assert(node->type == SAT_TEST || node->type == SAT_COMMAND); + + if (node->arguments == NULL) + node->arguments = sieve_ast_arg_list_create(node->ast->pool); + + return sieve_ast_arg_list_add(node->arguments, argument); +} + +struct sieve_ast_node *sieve_ast_node_detach(struct sieve_ast_node *first) +{ + return sieve_ast_list_detach(first, 1); +} + +const char *sieve_ast_type_name(enum sieve_ast_type ast_type) +{ + switch (ast_type) { + case SAT_NONE: + return "none"; + case SAT_ROOT: + return "ast root node"; + case SAT_COMMAND: + return "command"; + case SAT_TEST: + return "test"; + default: + return "??AST NODE??"; + } +} + +/* + * Argument AST node + */ + +struct sieve_ast_argument * +sieve_ast_argument_create(struct sieve_ast *ast, unsigned int source_line) +{ + struct sieve_ast_argument *arg = + p_new(ast->pool, struct sieve_ast_argument, 1); + + arg->ast = ast; + + arg->prev = NULL; + arg->next = NULL; + + arg->source_line = source_line; + + arg->argument = NULL; + + return arg; +} + +static void +sieve_ast_argument_substitute(struct sieve_ast_argument *argument, + struct sieve_ast_argument *replacement) +{ + sieve_ast_arg_list_substitute(argument->list, argument, replacement); +} + +struct sieve_ast_argument * +sieve_ast_argument_string_create_raw(struct sieve_ast *ast, string_t *str, + unsigned int source_line) +{ + struct sieve_ast_argument *argument = + sieve_ast_argument_create(ast, source_line); + + argument->type = SAAT_STRING; + argument->_value.str = str; + + return argument; +} + +struct sieve_ast_argument * +sieve_ast_argument_string_create(struct sieve_ast_node *node, + const string_t *str, unsigned int source_line) +{ + struct sieve_ast_argument *argument; + string_t *newstr; + + /* Allocate new internal string buffer */ + newstr = str_new(node->ast->pool, str_len(str)); + + /* Clone string */ + str_append_str(newstr, str); + + /* Create string argument */ + argument = sieve_ast_argument_string_create_raw( + node->ast, newstr, source_line); + + /* Add argument to command/test node */ + sieve_ast_node_add_argument(node, argument); + + return argument; +} + +struct sieve_ast_argument * +sieve_ast_argument_cstring_create(struct sieve_ast_node *node, const char *str, + unsigned int source_line) +{ + struct sieve_ast_argument *argument; + string_t *newstr; + + /* Allocate new internal string buffer */ + newstr = str_new(node->ast->pool, strlen(str)); + + /* Clone string */ + str_append(newstr, str); + + /* Create string argument */ + argument = sieve_ast_argument_string_create_raw( + node->ast, newstr, source_line); + + /* Add argument to command/test node */ + sieve_ast_node_add_argument(node, argument); + + return argument; +} + +void sieve_ast_argument_string_set(struct sieve_ast_argument *argument, + string_t *newstr) +{ + i_assert(argument->type == SAAT_STRING); + argument->_value.str = newstr; +} + +void sieve_ast_argument_string_setc(struct sieve_ast_argument *argument, + const char *newstr) +{ + i_assert(argument->type == SAAT_STRING); + + str_truncate(argument->_value.str, 0); + str_append(argument->_value.str, newstr); +} + +void sieve_ast_argument_number_substitute(struct sieve_ast_argument *argument, + sieve_number_t number) +{ + argument->type = SAAT_NUMBER; + argument->_value.number = number; +} + +struct sieve_ast_argument * +sieve_ast_argument_stringlist_create(struct sieve_ast_node *node, + unsigned int source_line) +{ + struct sieve_ast_argument *argument = + sieve_ast_argument_create(node->ast, source_line); + + argument->type = SAAT_STRING_LIST; + argument->_value.strlist = NULL; + + sieve_ast_node_add_argument(node, argument); + + return argument; +} + +struct sieve_ast_argument * +sieve_ast_argument_stringlist_substitute(struct sieve_ast_node *node, + struct sieve_ast_argument *arg) +{ + struct sieve_ast_argument *argument = + sieve_ast_argument_create(node->ast, arg->source_line); + + argument->type = SAAT_STRING_LIST; + argument->_value.strlist = NULL; + + sieve_ast_argument_substitute(arg, argument); + + return argument; +} + +static inline bool +_sieve_ast_stringlist_add_item(struct sieve_ast_argument *list, + struct sieve_ast_argument *item) +{ + i_assert(list->type == SAAT_STRING_LIST); + + if (list->_value.strlist == NULL) { + list->_value.strlist = + sieve_ast_arg_list_create(list->ast->pool); + } + + return sieve_ast_arg_list_add(list->_value.strlist, item); +} + +static bool +sieve_ast_stringlist_add_stringlist(struct sieve_ast_argument *list, + struct sieve_ast_argument *items) +{ + i_assert(list->type == SAAT_STRING_LIST); + i_assert(items->type == SAAT_STRING_LIST); + + if (list->_value.strlist == NULL) { + list->_value.strlist = + sieve_ast_arg_list_create(list->ast->pool); + } + + return sieve_ast_arg_list_join(list->_value.strlist, + items->_value.strlist); +} + +static bool +_sieve_ast_stringlist_add_str(struct sieve_ast_argument *list, string_t *str, + unsigned int source_line) +{ + struct sieve_ast_argument *stritem; + + stritem = sieve_ast_argument_create(list->ast, source_line); + stritem->type = SAAT_STRING; + stritem->_value.str = str; + + return _sieve_ast_stringlist_add_item(list, stritem); +} + +bool sieve_ast_stringlist_add(struct sieve_ast_argument *list, + const string_t *str, unsigned int source_line) +{ + string_t *copied_str = str_new(list->ast->pool, str_len(str)); + str_append_str(copied_str, str); + + return _sieve_ast_stringlist_add_str(list, copied_str, source_line); +} + +bool sieve_ast_stringlist_add_strc(struct sieve_ast_argument *list, + const char *str, unsigned int source_line) +{ + string_t *copied_str = str_new(list->ast->pool, strlen(str)); + str_append(copied_str, str); + + return _sieve_ast_stringlist_add_str(list, copied_str, source_line); +} + +struct sieve_ast_argument * +sieve_ast_argument_tag_create(struct sieve_ast_node *node, const char *tag, + unsigned int source_line) +{ + struct sieve_ast_argument *argument = + sieve_ast_argument_create(node->ast, source_line); + + argument->type = SAAT_TAG; + argument->_value.tag = p_strdup(node->ast->pool, tag); + + if (!sieve_ast_node_add_argument(node, argument)) + return NULL; + return argument; +} + +struct sieve_ast_argument * +sieve_ast_argument_tag_insert(struct sieve_ast_argument *before, + const char *tag, unsigned int source_line) +{ + struct sieve_ast_argument *argument = + sieve_ast_argument_create(before->ast, source_line); + + argument->type = SAAT_TAG; + argument->_value.tag = p_strdup(before->ast->pool, tag); + + if (!sieve_ast_arg_list_insert(before->list, before, argument)) + return NULL; + return argument; +} + +struct sieve_ast_argument * +sieve_ast_argument_number_create(struct sieve_ast_node *node, + sieve_number_t number, + unsigned int source_line) +{ + struct sieve_ast_argument *argument = + sieve_ast_argument_create(node->ast, source_line); + + argument->type = SAAT_NUMBER; + argument->_value.number = number; + + if (!sieve_ast_node_add_argument(node, argument)) + return NULL; + return argument; +} + +void sieve_ast_argument_number_set(struct sieve_ast_argument *argument, + sieve_number_t newnum) +{ + i_assert(argument->type == SAAT_NUMBER); + argument->_value.number = newnum; +} + +struct sieve_ast_argument * +sieve_ast_arguments_detach(struct sieve_ast_argument *first, + unsigned int count) +{ + return sieve_ast_arg_list_detach(first, count); +} + +bool sieve_ast_argument_attach(struct sieve_ast_node *node, + struct sieve_ast_argument *argument) +{ + return sieve_ast_node_add_argument(node, argument); +} + +const char *sieve_ast_argument_type_name(enum sieve_ast_argument_type arg_type) +{ + switch (arg_type) { + case SAAT_NONE: + return "none"; + case SAAT_STRING_LIST: + return "a string list"; + case SAAT_STRING: + return "a string"; + case SAAT_NUMBER: + return "a number"; + case SAAT_TAG: + return "a tag"; + default: + return "??ARGUMENT??"; + } +} + +/* Test AST node */ + +struct sieve_ast_node * +sieve_ast_test_create(struct sieve_ast_node *parent, const char *identifier, + unsigned int source_line) +{ + struct sieve_ast_node *test = sieve_ast_node_create( + parent->ast, parent, SAT_TEST, source_line); + + test->identifier = p_strdup(parent->ast->pool, identifier); + + if (!sieve_ast_node_add_test(parent, test)) + return NULL; + return test; +} + +/* Command AST node */ + +struct sieve_ast_node * +sieve_ast_command_create(struct sieve_ast_node *parent, const char *identifier, + unsigned int source_line) +{ + struct sieve_ast_node *command = sieve_ast_node_create( + parent->ast, parent, SAT_COMMAND, source_line); + + command->identifier = p_strdup(parent->ast->pool, identifier); + + if (!sieve_ast_node_add_command(parent, command)) + return NULL; + return command; +} + +/* + * Utility + */ + +int sieve_ast_stringlist_map( + struct sieve_ast_argument **listitem, void *context, + int (*map_function)(void *context, struct sieve_ast_argument *arg)) +{ + if (sieve_ast_argument_type(*listitem) == SAAT_STRING) { + /* Single string */ + return map_function(context, *listitem); + } else if (sieve_ast_argument_type(*listitem) == SAAT_STRING_LIST) { + int ret = 0; + + /* String list */ + *listitem = sieve_ast_strlist_first(*listitem); + + while (*listitem != NULL) { + if ((ret = map_function(context, *listitem)) <= 0) + return ret; + + *listitem = sieve_ast_strlist_next(*listitem); + } + + return ret; + } + + i_unreached(); + return -1; +} + +struct sieve_ast_argument * +sieve_ast_stringlist_join(struct sieve_ast_argument *list, + struct sieve_ast_argument *items) +{ + enum sieve_ast_argument_type list_type, items_type; + struct sieve_ast_argument *newlist; + + list_type = sieve_ast_argument_type(list); + items_type = sieve_ast_argument_type(items); + + switch (list_type) { + case SAAT_STRING: + switch (items_type) { + case SAAT_STRING: + newlist = sieve_ast_argument_create( + list->ast, list->source_line); + newlist->type = SAAT_STRING_LIST; + newlist->_value.strlist = NULL; + + sieve_ast_argument_substitute(list, newlist); + sieve_ast_arguments_detach(items, 1); + + if (!_sieve_ast_stringlist_add_item(newlist, list) || + !_sieve_ast_stringlist_add_item(newlist, items)) + return NULL; + return newlist; + case SAAT_STRING_LIST: + /* Adding stringlist to string; make them swith places + and add one to the other. + */ + sieve_ast_arguments_detach(items, 1); + sieve_ast_argument_substitute(list, items); + if (!_sieve_ast_stringlist_add_item(items, list)) + return NULL; + return list; + default: + i_unreached(); + } + break; + case SAAT_STRING_LIST: + switch (items_type) { + case SAAT_STRING: + /* Adding string to stringlist; straightforward add */ + sieve_ast_arguments_detach(items, 1); + if (!_sieve_ast_stringlist_add_item(list, items)) + return NULL; + return list; + case SAAT_STRING_LIST: + /* Adding stringlist to stringlist; perform actual join + */ + sieve_ast_arguments_detach(items, 1); + if (!sieve_ast_stringlist_add_stringlist(list, items)) + return NULL; + return list; + default: + i_unreached(); + } + break; + default: + i_unreached(); + } + return NULL; +} + +/* Debug */ + +/* Unparsing, currently implemented using plain printf()s */ + +static void sieve_ast_unparse_string(const string_t *strval) +{ + char *str = t_strdup_noconst(str_c((string_t *)strval)); + + if (strchr(str, '\n') != NULL && str[strlen(str)-1] == '\n') { + /* Print it as a multi-line string and do required dotstuffing + */ + char *spos = str; + char *epos = strchr(str, '\n'); + printf("text:\n"); + + while (epos != NULL) { + *epos = '\0'; + if (*spos == '.') + printf("."); + + printf("%s\n", spos); + + spos = epos+1; + epos = strchr(spos, '\n'); + } + if (*spos == '.') + printf("."); + + printf("%s\n.\n", spos); + } else { + /* Print it as a quoted string and escape " */ + char *spos = str; + char *epos = strchr(str, '"'); + printf("\""); + + while (epos != NULL) { + *epos = '\0'; + printf("%s\\\"", spos); + + spos = epos+1; + epos = strchr(spos, '"'); + } + + printf("%s\"", spos); + } +} + +static void +sieve_ast_unparse_argument(struct sieve_ast_argument *argument, int level); + +static void +sieve_ast_unparse_stringlist(struct sieve_ast_argument *strlist, int level) +{ + struct sieve_ast_argument *stritem; + + if (sieve_ast_strlist_count(strlist) > 1) { + int i; + + printf("[\n"); + + /* Create indent */ + for (i = 0; i < level+2; i++) + printf(" "); + + stritem = sieve_ast_strlist_first(strlist); + if (stritem != NULL) { + sieve_ast_unparse_string( + sieve_ast_strlist_str(stritem)); + + stritem = sieve_ast_strlist_next(stritem); + while (stritem != NULL) { + printf(",\n"); + for (i = 0; i < level+2; i++) + printf(" "); + sieve_ast_unparse_string( + sieve_ast_strlist_str(stritem)); + stritem = sieve_ast_strlist_next(stritem); + } + } + + printf(" ]"); + } else { + stritem = sieve_ast_strlist_first(strlist); + if (stritem != NULL) { + sieve_ast_unparse_string( + sieve_ast_strlist_str(stritem)); + } + } +} + +static void +sieve_ast_unparse_argument(struct sieve_ast_argument *argument, int level) +{ + switch (argument->type) { + case SAAT_STRING: + sieve_ast_unparse_string(sieve_ast_argument_str(argument)); + break; + case SAAT_STRING_LIST: + sieve_ast_unparse_stringlist(argument, level+1); + break; + case SAAT_NUMBER: + printf("%"SIEVE_PRI_NUMBER, + sieve_ast_argument_number(argument)); + break; + case SAAT_TAG: + printf(":%s", sieve_ast_argument_tag(argument)); + break; + default: + printf("??ARGUMENT??"); + break; + } +} + +static void sieve_ast_unparse_test(struct sieve_ast_node *node, int level); + +static void sieve_ast_unparse_tests(struct sieve_ast_node *node, int level) +{ + struct sieve_ast_node *test; + + if (sieve_ast_test_count(node) > 1) { + int i; + + printf(" (\n"); + + /* Create indent */ + for (i = 0; i < level+2; i++) + printf(" "); + + test = sieve_ast_test_first(node); + sieve_ast_unparse_test(test, level+1); + + test = sieve_ast_test_next(test); + while (test != NULL) { + printf(", \n"); + for (i = 0; i < level+2; i++) + printf(" "); + sieve_ast_unparse_test(test, level+1); + test = sieve_ast_test_next(test); + } + + printf(" )"); + } else { + test = sieve_ast_test_first(node); + if (test != NULL) + sieve_ast_unparse_test(test, level); + } +} + +static void sieve_ast_unparse_test(struct sieve_ast_node *node, int level) +{ + struct sieve_ast_argument *argument; + + printf(" %s", node->identifier); + + argument = sieve_ast_argument_first(node); + while (argument != NULL) { + printf(" "); + sieve_ast_unparse_argument(argument, level); + argument = sieve_ast_argument_next(argument); + } + + sieve_ast_unparse_tests(node, level); +} + +static void sieve_ast_unparse_command(struct sieve_ast_node *node, int level) +{ + struct sieve_ast_node *command; + struct sieve_ast_argument *argument; + + int i; + + /* Create indent */ + for (i = 0; i < level; i++) + printf(" "); + + printf("%s", node->identifier); + + argument = sieve_ast_argument_first(node); + while (argument != NULL) { + printf(" "); + sieve_ast_unparse_argument(argument, level); + argument = sieve_ast_argument_next(argument); + } + + sieve_ast_unparse_tests(node, level); + + command = sieve_ast_command_first(node); + if (command != NULL) { + printf(" {\n"); + + while (command != NULL) { + sieve_ast_unparse_command(command, level+1); + command = sieve_ast_command_next(command); + } + + for (i = 0; i < level; i++) + printf(" "); + printf("}\n"); + } else + printf(";\n"); +} + +void sieve_ast_unparse(struct sieve_ast *ast) +{ + struct sieve_ast_node *command; + + printf("Unparsing Abstract Syntax Tree:\n"); + + T_BEGIN { + command = sieve_ast_command_first(sieve_ast_root(ast)); + while (command != NULL) { + sieve_ast_unparse_command(command, 0); + command = sieve_ast_command_next(command); + } + } T_END; +} diff --git a/pigeonhole/src/lib-sieve/sieve-ast.h b/pigeonhole/src/lib-sieve/sieve-ast.h new file mode 100644 index 0000000..00f8a6b --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-ast.h @@ -0,0 +1,382 @@ +#ifndef SIEVE_AST_H +#define SIEVE_AST_H + +#include "lib.h" +#include "str.h" + +#include "sieve-common.h" +#include "sieve-error.h" + +/* + Abstract Syntax Tree (AST) structure: + + sieve_ast (root) + [*command] + | + +-- command: + | .... + +-- command: + | [identifier *argument *test *command] + | +-- argument: | \--> as from root + | | .... | + | +-- argument: V (continued below) + | | [number | tag | *string] + | . + . + + *test + +-- test: + | .... + +-- test: + | [identifier *argument *test] + | +-- argument: \--> as from the top + . | .... of this tree + +-- argument: + | [number | tag | *string] + . + + Tests and commands are defined using the same structure: sieve_ast_node. + However, arguments and string-lists are described using sieve_ast_argument. +*/ + +/* IMPORTANT NOTICE: Do not decorate the AST with objects other than those + allocated on the ast's pool or static const objects. Otherwise it is possible + that pointers in the tree become dangling which is highly undesirable. + */ + +/* + * Forward declarations + */ + +struct sieve_ast_list; +struct sieve_ast_arg_list; + +/* + * Types + */ + +enum sieve_ast_argument_type { + SAAT_NONE, + SAAT_NUMBER, + SAAT_STRING, + SAAT_STRING_LIST, + SAAT_TAG, +}; + +enum sieve_ast_type { + SAT_NONE, + SAT_ROOT, + SAT_COMMAND, + SAT_TEST, +}; + +/* + * AST Nodes + */ + +/* Argument node */ + +struct sieve_ast_argument { + enum sieve_ast_argument_type type; + + /* Back reference to the AST object */ + struct sieve_ast *ast; + + /* List related */ + struct sieve_ast_arg_list *list; + struct sieve_ast_argument *next; + struct sieve_ast_argument *prev; + + /* Parser-assigned data */ + + union { + string_t *str; + struct sieve_ast_arg_list *strlist; + const char *tag; + sieve_number_t number; + } _value; + + unsigned int source_line; + + /* Assigned during validation */ + + /* Argument associated with this ast element */ + struct sieve_argument *argument; + + /* Parameters to this (tag) argument */ + struct sieve_ast_argument *parameters; +}; + +struct sieve_ast_node { + enum sieve_ast_type type; + + /* Back reference to the AST object */ + struct sieve_ast *ast; + + /* Back reference to this node's parent */ + struct sieve_ast_node *parent; + + /* Linked list references */ + struct sieve_ast_list *list; + struct sieve_ast_node *next; + struct sieve_ast_node *prev; + + /* Commands (NULL if not allocated) */ + bool block; + struct sieve_ast_list *commands; + + /* Tests (NULL if not allocated)*/ + bool test_list; + struct sieve_ast_list *tests; + + /* Arguments (NULL if not allocated) */ + struct sieve_ast_arg_list *arguments; + + /* Identifier of command or test */ + const char *identifier; + + /* The location in the file where this command was started */ + unsigned int source_line; + + /* Assigned during validation */ + + /* Context */ + struct sieve_command *command; +}; + +/* + * AST node lists + */ + +struct sieve_ast_list { + struct sieve_ast_node *head; + struct sieve_ast_node *tail; + unsigned int len; +}; + +struct sieve_ast_arg_list { + struct sieve_ast_argument *head; + struct sieve_ast_argument *tail; + unsigned int len; +}; + +/* + * AST object + */ + +struct sieve_ast; + +struct sieve_ast *sieve_ast_create(struct sieve_script *script); +void sieve_ast_ref(struct sieve_ast *ast); +void sieve_ast_unref(struct sieve_ast **ast); + +struct sieve_ast_node *sieve_ast_root(struct sieve_ast *ast); +pool_t sieve_ast_pool(struct sieve_ast *ast); +struct sieve_script *sieve_ast_script(struct sieve_ast *ast); + +/* Extension support */ + +struct sieve_ast_extension { + const struct sieve_extension_def *ext; + + void (*free)(const struct sieve_extension *ext, struct sieve_ast *ast, + void *context); +}; + +void sieve_ast_extension_link(struct sieve_ast *ast, + const struct sieve_extension *ext, + bool required); +const struct sieve_extension * const * +sieve_ast_extensions_get(struct sieve_ast *ast, unsigned int *count_r); + +void sieve_ast_extension_register(struct sieve_ast *ast, + const struct sieve_extension *ext, + const struct sieve_ast_extension *ast_ext, + void *context); +void sieve_ast_extension_set_context(struct sieve_ast *ast, + const struct sieve_extension *ext, + void *context); +void *sieve_ast_extension_get_context(struct sieve_ast *ast, + const struct sieve_extension *ext); + +bool sieve_ast_extension_is_required(struct sieve_ast *ast, + const struct sieve_extension *ext); + +/* + * AST node manipulation + */ + +/* Command nodes */ + +struct sieve_ast_node * +sieve_ast_test_create(struct sieve_ast_node *parent, const char *identifier, + unsigned int source_line); +struct sieve_ast_node * +sieve_ast_command_create(struct sieve_ast_node *parent, const char *identifier, + unsigned int source_line); + +struct sieve_ast_node * +sieve_ast_node_detach(struct sieve_ast_node *first); + +const char *sieve_ast_type_name(enum sieve_ast_type ast_type); + +/* Argument nodes */ + +struct sieve_ast_argument * +sieve_ast_argument_create(struct sieve_ast *ast, unsigned int source_line); + +struct sieve_ast_arg_list *sieve_ast_arg_list_create(pool_t pool); +bool sieve_ast_arg_list_add(struct sieve_ast_arg_list *list, + struct sieve_ast_argument *argument); +bool sieve_ast_arg_list_insert(struct sieve_ast_arg_list *list, + struct sieve_ast_argument *before, + struct sieve_ast_argument *argument); +void sieve_ast_arg_list_substitute(struct sieve_ast_arg_list *list, + struct sieve_ast_argument *argument, + struct sieve_ast_argument *replacement); + +struct sieve_ast_argument * +sieve_ast_argument_string_create_raw(struct sieve_ast *ast, string_t *str, + unsigned int source_line); +struct sieve_ast_argument * +sieve_ast_argument_string_create(struct sieve_ast_node *node, + const string_t *str, unsigned int source_line); +struct sieve_ast_argument * +sieve_ast_argument_cstring_create(struct sieve_ast_node *node, const char *str, + unsigned int source_line); + +struct sieve_ast_argument * +sieve_ast_argument_tag_create(struct sieve_ast_node *node, const char *tag, + unsigned int source_line); + +struct sieve_ast_argument * +sieve_ast_argument_number_create(struct sieve_ast_node *node, + sieve_number_t number, + unsigned int source_line); + +void sieve_ast_argument_string_set(struct sieve_ast_argument *argument, + string_t *newstr); +void sieve_ast_argument_string_setc(struct sieve_ast_argument *argument, + const char *newstr); + +void sieve_ast_argument_number_set(struct sieve_ast_argument *argument, + sieve_number_t newnum); +void sieve_ast_argument_number_substitute(struct sieve_ast_argument *argument, + sieve_number_t number); + +struct sieve_ast_argument * +sieve_ast_argument_tag_insert(struct sieve_ast_argument *before, + const char *tag, unsigned int source_line); + +struct sieve_ast_argument * +sieve_ast_argument_stringlist_create(struct sieve_ast_node *node, + unsigned int source_line); +struct sieve_ast_argument * +sieve_ast_argument_stringlist_substitute(struct sieve_ast_node *node, + struct sieve_ast_argument *arg); + +struct sieve_ast_argument * +sieve_ast_arguments_detach(struct sieve_ast_argument *first, + unsigned int count); +bool sieve_ast_argument_attach(struct sieve_ast_node *node, + struct sieve_ast_argument *argument); + +const char *sieve_ast_argument_type_name(enum sieve_ast_argument_type arg_type); +#define sieve_ast_argument_name(argument) \ + sieve_ast_argument_type_name((argument)->type) + +bool sieve_ast_stringlist_add(struct sieve_ast_argument *list, + const string_t *str, unsigned int source_line); +bool sieve_ast_stringlist_add_strc(struct sieve_ast_argument *list, + const char *str, unsigned int source_line); + +/* + * Utility + */ + +int sieve_ast_stringlist_map( + struct sieve_ast_argument **listitem, void *context, + int (*map_function)(void *context, struct sieve_ast_argument *arg)); +struct sieve_ast_argument * +sieve_ast_stringlist_join(struct sieve_ast_argument *list, + struct sieve_ast_argument *items); + +/* + * AST access macros + */ + +/* Generic list access macros */ +#define __AST_LIST_FIRST(list) \ + ((list) == NULL ? NULL : (list)->head) +#define __AST_LIST_LAST(list) \ + ((list) == NULL ? NULL : (list)->tail) +#define __AST_LIST_COUNT(list) \ + ((list) == NULL || (list)->head == NULL ? 0 : (list)->len) +#define __AST_LIST_NEXT(item) ((item)->next) +#define __AST_LIST_PREV(item) ((item)->prev) + +#define __AST_NODE_LIST_FIRST(node, list) __AST_LIST_FIRST((node)->list) +#define __AST_NODE_LIST_LAST(node, list) __AST_LIST_LAST((node)->list) +#define __AST_NODE_LIST_COUNT(node, list) __AST_LIST_COUNT((node)->list) + +/* AST macros */ + +/* AST node macros */ +#define sieve_ast_node_pool(node) (sieve_ast_pool((node)->ast)) +#define sieve_ast_node_parent(node) ((node)->parent) +#define sieve_ast_node_prev(node) __AST_LIST_PREV(node) +#define sieve_ast_node_next(node) __AST_LIST_NEXT(node) +#define sieve_ast_node_type(node) ((node) == NULL ? SAT_NONE : (node)->type) +#define sieve_ast_node_line(node) ((node) == NULL ? 0 : (node)->source_line) + +/* AST command node macros */ +#define sieve_ast_command_first(node) __AST_NODE_LIST_FIRST(node, commands) +#define sieve_ast_command_count(node) __AST_NODE_LIST_COUNT(node, commands) +#define sieve_ast_command_prev(command) __AST_LIST_PREV(command) +#define sieve_ast_command_next(command) __AST_LIST_NEXT(command) + +/* Compare the identifier of the previous command */ +#define sieve_ast_prev_cmd_is(cmd, id) \ + ((cmd)->prev == NULL ? FALSE : \ + strncasecmp((cmd)->prev->identifier, id, sizeof(id)-1) == 0) + +/* AST test macros */ +#define sieve_ast_test_count(node) __AST_NODE_LIST_COUNT(node, tests) +#define sieve_ast_test_first(node) __AST_NODE_LIST_FIRST(node, tests) +#define sieve_ast_test_next(test) __AST_LIST_NEXT(test) + +/* AST argument macros */ +#define sieve_ast_argument_pool(node) (sieve_ast_pool((node)->ast)) +#define sieve_ast_argument_first(node) __AST_NODE_LIST_FIRST(node, arguments) +#define sieve_ast_argument_last(node) __AST_NODE_LIST_LAST(node, arguments) +#define sieve_ast_argument_count(node) __AST_NODE_LIST_COUNT(node, arguments) +#define sieve_ast_argument_prev(argument) __AST_LIST_PREV(argument) +#define sieve_ast_argument_next(argument) __AST_LIST_NEXT(argument) +#define sieve_ast_argument_type(argument) (argument)->type +#define sieve_ast_argument_line(argument) (argument)->source_line + +#define sieve_ast_argument_str(argument) ((argument)->_value.str) +#define sieve_ast_argument_strc(argument) (str_c((argument)->_value.str)) +#define sieve_ast_argument_tag(argument) ((argument)->_value.tag) +#define sieve_ast_argument_number(argument) ((argument)->_value.number) + +/* AST string list macros */ +// @UNSAFE: should check whether we are actually accessing a string list +#define sieve_ast_strlist_first(list) \ + __AST_NODE_LIST_FIRST(list, _value.strlist) +#define sieve_ast_strlist_last(list) \ + __AST_NODE_LIST_LAST(list, _value.strlist) +#define sieve_ast_strlist_count(list) \ + __AST_NODE_LIST_COUNT(list, _value.strlist) +#define sieve_ast_strlist_next(str) __AST_LIST_NEXT(str) +#define sieve_ast_strlist_prev(str) __AST_LIST_PREV(str) +#define sieve_ast_strlist_str(str) sieve_ast_argument_str(str) +#define sieve_ast_strlist_strc(str) sieve_ast_argument_strc(str) + +/* + * Debug + */ + +void sieve_ast_unparse(struct sieve_ast *ast); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-binary-code.c b/pigeonhole/src/lib-sieve/sieve-binary-code.c new file mode 100644 index 0000000..0d76ee0 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-binary-code.c @@ -0,0 +1,405 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "str-sanitize.h" +#include "mempool.h" +#include "buffer.h" +#include "hash.h" +#include "array.h" +#include "ostream.h" + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-extensions.h" +#include "sieve-code.h" +#include "sieve-script.h" + +#include "sieve-binary-private.h" + +/* + * Forward declarations + */ + +static inline sieve_size_t +sieve_binary_emit_dynamic_data(struct sieve_binary_block *sblock, + const void *data, size_t size); + +/* + * Emission functions + */ + +/* Low-level emission functions */ + +static inline void +_sieve_binary_emit_data(struct sieve_binary_block *sblock, + const void *data, sieve_size_t size) +{ + buffer_append(sblock->data, data, size); +} + +static inline void +_sieve_binary_emit_byte(struct sieve_binary_block *sblock, uint8_t byte) +{ + _sieve_binary_emit_data(sblock, &byte, 1); +} + +static inline void +_sieve_binary_update_data(struct sieve_binary_block *sblock, + sieve_size_t address, const void *data, + sieve_size_t size) +{ + buffer_write(sblock->data, address, data, size); +} + +sieve_size_t sieve_binary_emit_data(struct sieve_binary_block *sblock, + const void *data, sieve_size_t size) +{ + sieve_size_t address = _sieve_binary_block_get_size(sblock); + + _sieve_binary_emit_data(sblock, data, size); + + return address; +} + +sieve_size_t sieve_binary_emit_byte(struct sieve_binary_block *sblock, + uint8_t byte) +{ + sieve_size_t address = _sieve_binary_block_get_size(sblock); + + _sieve_binary_emit_data(sblock, &byte, 1); + + return address; +} + +void sieve_binary_update_data(struct sieve_binary_block *sblock, + sieve_size_t address, const void *data, + sieve_size_t size) +{ + _sieve_binary_update_data(sblock, address, data, size); +} + +/* Offset emission functions */ + +sieve_size_t sieve_binary_emit_offset(struct sieve_binary_block *sblock, + sieve_offset_t offset) +{ + sieve_size_t address = _sieve_binary_block_get_size(sblock); + uint8_t encoded[sizeof(offset)]; + int i; + + for (i = sizeof(offset)-1; i >= 0; i--) { + encoded[i] = (uint8_t)offset; + offset >>= 8; + } + + _sieve_binary_emit_data(sblock, encoded, sizeof(offset)); + + return address; +} + +void sieve_binary_resolve_offset(struct sieve_binary_block *sblock, + sieve_size_t address) +{ + sieve_size_t cur_address = _sieve_binary_block_get_size(sblock); + sieve_offset_t offset; + uint8_t encoded[sizeof(offset)]; + int i; + + i_assert(cur_address > address); + i_assert((cur_address - address) <= (sieve_offset_t)-1); + offset = cur_address - address; + for (i = sizeof(offset)-1; i >= 0; i--) { + encoded[i] = (uint8_t)offset; + offset >>= 8; + } + + _sieve_binary_update_data(sblock, address, encoded, sizeof(offset)); +} + +/* Literal emission */ + +sieve_size_t sieve_binary_emit_integer(struct sieve_binary_block *sblock, + sieve_number_t integer) +{ + sieve_size_t address = _sieve_binary_block_get_size(sblock); + uint8_t buffer[sizeof(sieve_number_t) + 1]; + int bufpos = sizeof(buffer) - 1; + + /* Encode last byte [0xxxxxxx]; msb == 0 marks the last byte */ + buffer[bufpos] = integer & 0x7F; + bufpos--; + + /* Encode first bytes [1xxxxxxx] */ + integer >>= 7; + while (integer > 0) { + buffer[bufpos] = (integer & 0x7F) | 0x80; + bufpos--; + integer >>= 7; + } + + /* Emit encoded integer */ + bufpos++; + _sieve_binary_emit_data(sblock, buffer + bufpos, sizeof(buffer) - bufpos); + + return address; +} + +static inline sieve_size_t +sieve_binary_emit_dynamic_data(struct sieve_binary_block *sblock, + const void *data, sieve_size_t size) +{ + sieve_size_t address = + sieve_binary_emit_integer(sblock, (sieve_number_t)size); + + _sieve_binary_emit_data(sblock, data, size); + + return address; +} + +sieve_size_t sieve_binary_emit_cstring(struct sieve_binary_block *sblock, + const char *str) +{ + sieve_size_t address = + sieve_binary_emit_dynamic_data(sblock, (void *)str, + (sieve_size_t)strlen(str)); + + _sieve_binary_emit_byte(sblock, 0); + return address; +} + +sieve_size_t sieve_binary_emit_string(struct sieve_binary_block *sblock, + const string_t *str) +{ + sieve_size_t address = + sieve_binary_emit_dynamic_data(sblock, (void *)str_data(str), + (sieve_size_t)str_len(str)); + + _sieve_binary_emit_byte(sblock, 0); + return address; +} + +/* + * Extension emission + */ + +sieve_size_t sieve_binary_emit_extension(struct sieve_binary_block *sblock, + const struct sieve_extension *ext, + unsigned int offset) +{ + sieve_size_t address = _sieve_binary_block_get_size(sblock); + struct sieve_binary_extension_reg *ereg = NULL; + + (void)sieve_binary_extension_register(sblock->sbin, ext, &ereg); + + i_assert(ereg != NULL); + + _sieve_binary_emit_byte(sblock, offset + ereg->index); + return address; +} + +void sieve_binary_emit_extension_object( + struct sieve_binary_block *sblock, + const struct sieve_extension_objects *objs, unsigned int code) +{ + if (objs->count > 1) + _sieve_binary_emit_byte(sblock, code); +} + +/* + * Code retrieval + */ + +#define ADDR_CODE_READ(block) \ + size_t _code_size; \ + const int8_t *_code = buffer_get_data((block)->data, &_code_size) + +#define ADDR_CODE_AT(address) \ + ((int8_t)(_code[*address])) +#define ADDR_DATA_AT(address) \ + ((uint8_t)(_code[*address])) +#define ADDR_POINTER(address) \ + ((const int8_t *)(&_code[*address])) + +#define ADDR_BYTES_LEFT(address) \ + ((*address) > _code_size ? 0 : ((_code_size) - (*address))) +#define ADDR_JUMP(address, offset) \ + (*address) += offset + +/* Literals */ + +bool sieve_binary_read_byte(struct sieve_binary_block *sblock, + sieve_size_t *address, unsigned int *byte_r) +{ + ADDR_CODE_READ(sblock); + + if (ADDR_BYTES_LEFT(address) >= 1) { + if (byte_r != NULL) + *byte_r = ADDR_DATA_AT(address); + ADDR_JUMP(address, 1); + + return TRUE; + } + + if (byte_r != NULL) + *byte_r = 0; + return FALSE; +} + +bool sieve_binary_read_code(struct sieve_binary_block *sblock, + sieve_size_t *address, signed int *code_r) +{ + ADDR_CODE_READ(sblock); + + if (ADDR_BYTES_LEFT(address) >= 1) { + if (code_r != NULL) + *code_r = ADDR_CODE_AT(address); + ADDR_JUMP(address, 1); + + return TRUE; + } + + if (code_r != NULL) + *code_r = 0; + return FALSE; +} + + +bool sieve_binary_read_offset(struct sieve_binary_block *sblock, + sieve_size_t *address, sieve_offset_t *offset_r) +{ + sieve_offset_t offs = 0; + ADDR_CODE_READ(sblock); + + if (ADDR_BYTES_LEFT(address) >= 4) { + int i; + + for (i = 0; i < 4; i++) { + offs = (offs << 8) + ADDR_DATA_AT(address); + ADDR_JUMP(address, 1); + } + + if (offset_r != NULL) + *offset_r = offs; + + return TRUE; + } + return FALSE; +} + +/* FIXME: might need negative numbers in the future */ +bool sieve_binary_read_integer(struct sieve_binary_block *sblock, + sieve_size_t *address, sieve_number_t *int_r) +{ + int bits = sizeof(sieve_number_t) * 8; + sieve_number_t integer = 0; + + ADDR_CODE_READ(sblock); + + if (ADDR_BYTES_LEFT(address) == 0) + return FALSE; + + /* Read first integer bytes [1xxxxxxx] */ + while ((ADDR_DATA_AT(address) & 0x80) > 0) { + if (ADDR_BYTES_LEFT(address) > 0 && bits > 0) { + integer |= ADDR_DATA_AT(address) & 0x7F; + ADDR_JUMP(address, 1); + + /* Each byte encodes 7 bits of the integer */ + integer <<= 7; + bits -= 7; + } else { + /* This is an error */ + return FALSE; + } + } + + /* Read last byte [0xxxxxxx] */ + integer |= ADDR_DATA_AT(address) & 0x7F; + ADDR_JUMP(address, 1); + + if (int_r != NULL) + *int_r = integer; + return TRUE; +} + +bool sieve_binary_read_string(struct sieve_binary_block *sblock, + sieve_size_t *address, string_t **str_r) +{ + unsigned int strlen = 0; + const char *strdata; + + ADDR_CODE_READ(sblock); + + if (!sieve_binary_read_unsigned(sblock, address, &strlen)) + return FALSE; + + if (strlen > ADDR_BYTES_LEFT(address)) + return FALSE; + + strdata = (const char *)ADDR_POINTER(address); + ADDR_JUMP(address, strlen); + + if (ADDR_CODE_AT(address) != 0) + return FALSE; + + if (str_r != NULL) + *str_r = t_str_new_const(strdata, strlen); + + ADDR_JUMP(address, 1); + + return TRUE; +} + +bool sieve_binary_read_extension(struct sieve_binary_block *sblock, + sieve_size_t *address, unsigned int *offset_r, + const struct sieve_extension **ext_r) +{ + unsigned int code; + unsigned int offset = *offset_r; + const struct sieve_extension *ext = NULL; + + ADDR_CODE_READ(sblock); + + if (ADDR_BYTES_LEFT(address) == 0) + return FALSE; + + *offset_r = code = ADDR_DATA_AT(address); + ADDR_JUMP(address, 1); + + if (code >= offset) { + ext = sieve_binary_extension_get_by_index(sblock->sbin, + (code - offset)); + if (ext == NULL) + return FALSE; + } + + if (ext_r != NULL) + *ext_r = ext; + return TRUE; +} + +const void * +sieve_binary_read_extension_object(struct sieve_binary_block *sblock, + sieve_size_t *address, + const struct sieve_extension_objects *objs) +{ + unsigned int code; + + ADDR_CODE_READ(sblock); + + if (objs->count == 0) + return NULL; + if (objs->count == 1) + return objs->objects; + if (ADDR_BYTES_LEFT(address) == 0) + return NULL; + + code = ADDR_DATA_AT(address); + ADDR_JUMP(address, 1); + + if (code >= objs->count) + return NULL; + return ((const void *const *)objs->objects)[code]; +} diff --git a/pigeonhole/src/lib-sieve/sieve-binary-debug.c b/pigeonhole/src/lib-sieve/sieve-binary-debug.c new file mode 100644 index 0000000..5e8b13a --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-binary-debug.c @@ -0,0 +1,276 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-code.h" + +#include "sieve-binary-private.h" + +/* Quick 'n dirty debug */ +#if 0 +#define debug_printf(...) printf ("lineinfo: " __VA_ARGS__) +#else +#define debug_printf(...) +#endif + +/* + * Opcodes + */ + +enum { + LINPROG_OP_COPY, + LINPROG_OP_ADVANCE_PC, + LINPROG_OP_ADVANCE_LINE, + LINPROG_OP_SET_COLUMN, + LINPROG_OP_SPECIAL_BASE +}; + +#define LINPROG_LINE_BASE 0 +#define LINPROG_LINE_RANGE 4 + +/* + * Lineinfo writer + */ + +struct sieve_binary_debug_writer { + struct sieve_binary_block *sblock; + + sieve_size_t address; + unsigned int line; + unsigned int column; +}; + +struct sieve_binary_debug_writer * +sieve_binary_debug_writer_init(struct sieve_binary_block *sblock) +{ + struct sieve_binary_debug_writer *dwriter; + + dwriter = i_new(struct sieve_binary_debug_writer, 1); + dwriter->sblock = sblock; + + return dwriter; +} + +void sieve_binary_debug_writer_deinit( + struct sieve_binary_debug_writer **dwriter) +{ + i_free(*dwriter); + *dwriter = NULL; +} + +void sieve_binary_debug_emit(struct sieve_binary_debug_writer *dwriter, + sieve_size_t code_address, unsigned int code_line, + unsigned int code_column) +{ + i_assert(code_address >= dwriter->address); + + struct sieve_binary_block *sblock = dwriter->sblock; + sieve_size_t address_inc = code_address - dwriter->address; + int line_inc = (code_line > dwriter->line ? + (int)(code_line - dwriter->line) : + -(int)(dwriter->line - code_line)); + unsigned int sp_opcode = 0; + + /* Check for applicability of special opcode */ + if (line_inc > 0 && + (LINPROG_LINE_BASE + LINPROG_LINE_RANGE - 1) >= line_inc) { + sp_opcode = LINPROG_OP_SPECIAL_BASE + + (line_inc - LINPROG_LINE_BASE) + + (LINPROG_LINE_RANGE * address_inc); + + if (sp_opcode > 255) + sp_opcode = 0; + } + + /* Update line and address */ + if (sp_opcode == 0) { + if (line_inc != 0) { + (void)sieve_binary_emit_byte(sblock, + LINPROG_OP_ADVANCE_LINE); + (void)sieve_binary_emit_unsigned( + sblock, (unsigned int)line_inc); + } + + if (address_inc > 0) { + (void)sieve_binary_emit_byte(sblock, + LINPROG_OP_ADVANCE_PC); + (void)sieve_binary_emit_unsigned(sblock, address_inc); + } + } else { + (void)sieve_binary_emit_byte(sblock, sp_opcode); + } + + /* Set column */ + if (dwriter->column != code_column) { + (void)sieve_binary_emit_byte(sblock, LINPROG_OP_SET_COLUMN); + (void)sieve_binary_emit_unsigned(sblock, code_column); + } + + /* Generate matrix row */ + (void)sieve_binary_emit_byte(sblock, LINPROG_OP_COPY); + + dwriter->address = code_address; + dwriter->line = code_line; + dwriter->column = code_column; +} + +/* + * Debug reader + */ + +struct sieve_binary_debug_reader { + struct sieve_binary_block *sblock; + + sieve_size_t address, last_address; + unsigned int line, last_line; + + unsigned int column; + + sieve_size_t state; +}; + +struct sieve_binary_debug_reader * +sieve_binary_debug_reader_init(struct sieve_binary_block *sblock) +{ + struct sieve_binary_debug_reader *dreader; + + dreader = i_new(struct sieve_binary_debug_reader, 1); + dreader->sblock = sblock; + + return dreader; +} + +void sieve_binary_debug_reader_deinit( + struct sieve_binary_debug_reader **dreader) +{ + i_free(*dreader); + *dreader = NULL; +} + +void sieve_binary_debug_reader_reset(struct sieve_binary_debug_reader *dreader) +{ + dreader->address = 0; + dreader->line = 0; + dreader->column = 0; + dreader->state = 0; +} + +unsigned int +sieve_binary_debug_read_line(struct sieve_binary_debug_reader *dreader, + sieve_size_t code_address) +{ + size_t linprog_size; + sieve_size_t address; + unsigned int line; + + if (code_address < dreader->last_address) + sieve_binary_debug_reader_reset(dreader); + + if (code_address >= dreader->last_address && + code_address < dreader->address) { + debug_printf("%08llx: NOOP [%08llx]\n", + (unsigned long long)dreader->state, + (unsigned long long)code_address); + return dreader->last_line; + } + + address = dreader->address; + line = dreader->line; + + debug_printf("%08llx: READ [%08llx]\n", + (unsigned long long)dreader->state, + (unsigned long long)code_address); + + linprog_size = sieve_binary_block_get_size(dreader->sblock); + while (dreader->state < linprog_size) { + unsigned int opcode; + unsigned int value; + int line_inc; + + if (sieve_binary_read_byte(dreader->sblock, + &dreader->state, &opcode)) { + switch (opcode) { + case LINPROG_OP_COPY: + debug_printf("%08llx: COPY ==> %08llx: %ld\n", + (unsigned long long)dreader->state, + (unsigned long long)address, line); + + dreader->last_address = dreader->address; + dreader->last_line = dreader->line; + + dreader->address = address; + dreader->line = line; + + if (code_address < address) + return dreader->last_line; + else if (code_address == address) + return dreader->line; + break; + case LINPROG_OP_ADVANCE_PC: + debug_printf("%08llx: ADV_PC\n", + (unsigned long long)dreader->state); + if (!sieve_binary_read_unsigned( + dreader->sblock, &dreader->state, + &value)) { + sieve_binary_debug_reader_reset(dreader); + return 0; + } + debug_printf(" : + %d\n", value); + address += value; + break; + case LINPROG_OP_ADVANCE_LINE: + debug_printf("%08llx: ADV_LINE\n", + (unsigned long long)dreader->state); + if (!sieve_binary_read_unsigned( + dreader->sblock, &dreader->state, + &value)) { + sieve_binary_debug_reader_reset(dreader); + return 0; + } + line_inc = (int)value; + debug_printf(" : + %d\n", line_inc); + line = (line_inc > 0 ? + line + (unsigned int)line_inc : + line - (unsigned int)-line_inc); + break; + case LINPROG_OP_SET_COLUMN: + debug_printf("%08llx: SET_COL\n", + (unsigned long long)dreader->state); + if (!sieve_binary_read_unsigned( + dreader->sblock, &dreader->state, + &value)) { + sieve_binary_debug_reader_reset(dreader); + return 0; + } + debug_printf(" : = %d\n", value); + dreader->column = value; + break; + default: + opcode -= LINPROG_OP_SPECIAL_BASE; + + address += (opcode / LINPROG_LINE_RANGE); + line += LINPROG_LINE_BASE + + (opcode % LINPROG_LINE_RANGE); + + debug_printf("%08llx: SPECIAL\n", + (unsigned long long)dreader->state); + debug_printf(" : +A %d +L %d\n", + (opcode / LINPROG_LINE_RANGE), + LINPROG_LINE_BASE + + (opcode % LINPROG_LINE_RANGE)); + break; + } + } else { + debug_printf("OPCODE READ FAILED\n"); + sieve_binary_debug_reader_reset(dreader); + return 0; + } + } + + return dreader->line; +} + diff --git a/pigeonhole/src/lib-sieve/sieve-binary-dumper.c b/pigeonhole/src/lib-sieve/sieve-binary-dumper.c new file mode 100644 index 0000000..3b3bb4b --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-binary-dumper.c @@ -0,0 +1,326 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "ostream.h" +#include "array.h" +#include "buffer.h" +#include "time-util.h" + +#include "sieve-common.h" +#include "sieve-extensions.h" +#include "sieve-dump.h" +#include "sieve-script.h" + +#include "sieve-binary-private.h" + +/* + * Binary dumper object + */ + +struct sieve_binary_dumper { + pool_t pool; + + /* Dumptime environment */ + struct sieve_dumptime_env dumpenv; +}; + +struct sieve_binary_dumper * +sieve_binary_dumper_create(struct sieve_binary *sbin) +{ + pool_t pool; + struct sieve_binary_dumper *dumper; + + pool = pool_alloconly_create("sieve_binary_dumper", 4096); + dumper = p_new(pool, struct sieve_binary_dumper, 1); + dumper->pool = pool; + dumper->dumpenv.dumper = dumper; + + dumper->dumpenv.sbin = sbin; + sieve_binary_ref(sbin); + + dumper->dumpenv.svinst = sieve_binary_svinst(sbin); + + return dumper; +} + +void sieve_binary_dumper_free(struct sieve_binary_dumper **dumper) +{ + sieve_binary_unref(&(*dumper)->dumpenv.sbin); + pool_unref(&((*dumper)->pool)); + + *dumper = NULL; +} + +pool_t sieve_binary_dumper_pool(struct sieve_binary_dumper *dumper) +{ + return dumper->pool; +} + +/* + * Formatted output + */ + +void sieve_binary_dumpf(const struct sieve_dumptime_env *denv, + const char *fmt, ...) +{ + string_t *outbuf = t_str_new(128); + va_list args; + + va_start(args, fmt); + str_vprintfa(outbuf, fmt, args); + va_end(args); + + o_stream_nsend(denv->stream, str_data(outbuf), str_len(outbuf)); +} + +void sieve_binary_dump_sectionf(const struct sieve_dumptime_env *denv, + const char *fmt, ...) +{ + string_t *outbuf = t_str_new(128); + va_list args; + + va_start(args, fmt); + str_printfa(outbuf, "\n* "); + str_vprintfa(outbuf, fmt, args); + str_printfa(outbuf, ":\n\n"); + va_end(args); + + o_stream_nsend(denv->stream, str_data(outbuf), str_len(outbuf)); +} + +/* + * Dumping the binary + */ + +bool sieve_binary_dumper_run(struct sieve_binary_dumper *dumper, + struct ostream *stream, bool verbose) +{ + struct sieve_binary *sbin = dumper->dumpenv.sbin; + struct sieve_script *script = sieve_binary_script(sbin); + struct sieve_dumptime_env *denv = &(dumper->dumpenv); + const struct sieve_binary_header *header = &sbin->header; + struct sieve_binary_block *sblock; + bool success = TRUE; + sieve_size_t offset; + int count, i; + + dumper->dumpenv.stream = stream; + + /* Dump header */ + + sieve_binary_dump_sectionf(denv, "Header"); + + sieve_binary_dumpf(denv, + "version = %"PRIu16".%"PRIu16"\n" + "flags = 0x%08"PRIx32"\n", + header->version_major, header->version_minor, + header->flags); + if (header->resource_usage.update_time != 0) { + time_t update_time = + (time_t)header->resource_usage.update_time; + sieve_binary_dumpf(denv, + "resource usage:\n" + " update time = %s\n" + " cpu time = %"PRIu32" ms\n", + t_strflocaltime("%Y-%m-%d %H:%M:%S", + update_time), + header->resource_usage.cpu_time_msecs); + } + + /* Dump list of binary blocks */ + + if (verbose) { + count = sieve_binary_block_count(sbin); + + sieve_binary_dump_sectionf(denv, "Binary blocks (count: %d)", + count); + + for (i = 0; i < count; i++) { + struct sieve_binary_block *sblock = + sieve_binary_block_get(sbin, i); + + sieve_binary_dumpf( + denv, "%3d: size: %zu bytes\n", + i, sieve_binary_block_get_size(sblock)); + } + } + + /* Dump script metadata */ + + sieve_binary_dump_sectionf(denv, "Script metadata (block: %d)", + SBIN_SYSBLOCK_SCRIPT_DATA); + sblock = sieve_binary_block_get(sbin, SBIN_SYSBLOCK_SCRIPT_DATA); + + T_BEGIN { + offset = 0; + success = sieve_script_binary_dump_metadata( + script, denv, sblock, &offset); + } T_END; + + if (!success) + return FALSE; + + /* Dump list of used extensions */ + + count = sieve_binary_extensions_count(sbin); + if (count > 0) { + sieve_binary_dump_sectionf( + denv, "Required extensions (block: %d)", + SBIN_SYSBLOCK_EXTENSIONS); + + for (i = 0; i < count; i++) { + const struct sieve_extension *ext = + sieve_binary_extension_get_by_index(sbin, i); + + sblock = sieve_binary_extension_get_block(sbin, ext); + + if (sblock == NULL) { + sieve_binary_dumpf( + denv, "%3d: %s (id: %d)\n", + i, sieve_extension_name(ext), ext->id); + } else { + sieve_binary_dumpf( + denv, "%3d: %s (id: %d; block: %d)\n", + i, sieve_extension_name(ext), ext->id, + sieve_binary_block_get_id(sblock)); + } + } + } + + /* Dump extension-specific elements of the binary */ + + count = sieve_binary_extensions_count(sbin); + if (count > 0) { + for (i = 0; i < count; i++) { + success = TRUE; + + T_BEGIN { + const struct sieve_extension *ext = + sieve_binary_extension_get_by_index( + sbin, i); + + if (ext->def != NULL && + ext->def->binary_dump != NULL) { + success = ext->def->binary_dump( + ext, denv); + } + } T_END; + + if (!success) + return FALSE; + } + } + + /* Dump main program */ + + sieve_binary_dump_sectionf(denv, "Main program (block: %d)", + SBIN_SYSBLOCK_MAIN_PROGRAM); + + dumper->dumpenv.sblock = + sieve_binary_block_get(sbin, SBIN_SYSBLOCK_MAIN_PROGRAM); + dumper->dumpenv.cdumper = sieve_code_dumper_create(&(dumper->dumpenv)); + + if (dumper->dumpenv.cdumper != NULL) { + sieve_code_dumper_run(dumper->dumpenv.cdumper); + + sieve_code_dumper_free(&dumper->dumpenv.cdumper); + } + + /* Finish with empty line */ + sieve_binary_dumpf(denv, "\n"); + + return TRUE; +} + +/* + * Hexdump production + */ + +void sieve_binary_dumper_hexdump(struct sieve_binary_dumper *dumper, + struct ostream *stream) +{ + struct sieve_binary *sbin = dumper->dumpenv.sbin; + struct sieve_dumptime_env *denv = &(dumper->dumpenv); + int count, i; + + dumper->dumpenv.stream = stream; + + count = sieve_binary_block_count(sbin); + + /* Block overview */ + + sieve_binary_dump_sectionf(denv, "Binary blocks (count: %d)", count); + + for (i = 0; i < count; i++) { + struct sieve_binary_block *sblock = + sieve_binary_block_get(sbin, i); + + sieve_binary_dumpf(denv, "%3d: size: %zu bytes\n", + i, sieve_binary_block_get_size(sblock)); + } + + /* Hexdump for each block */ + + for (i = 0; i < count; i++) { + struct sieve_binary_block *sblock = + sieve_binary_block_get(sbin, i); + buffer_t *blockbuf = sieve_binary_block_get_buffer(sblock); + string_t *line; + size_t data_size; + const unsigned char *data; + size_t offset; + + data = buffer_get_data(blockbuf, &data_size); + + // FIXME: calculate offset more nicely. + sieve_binary_dump_sectionf( + denv, "Block %d (%zu bytes, file offset %08llx)", i, data_size, + (unsigned long long int)sblock->offset + 8); + + line = t_str_new(128); + offset = 0; + while (offset < data_size) { + size_t len = (data_size - offset >= 16 ? + 16 : data_size - offset); + size_t b; + + str_printfa(line, "%08llx ", + (unsigned long long)offset); + + for (b = 0; b < len; b++) { + str_printfa(line, "%02x ", data[offset+b]); + if (b == 7) + str_append_c(line, ' '); + } + + if (len < 16) { + if (len <= 7) + str_append_c(line, ' '); + + for (b = len; b < 16; b++) + str_append(line, " "); + } + + str_append(line, " |"); + + for (b = 0; b < len; b++) { + const unsigned char c = data[offset+b]; + + if (c >= 32 && c <= 126) + str_append_c(line, (const char)c); + else + str_append_c(line, '.'); + } + + str_append(line, "|\n"); + o_stream_nsend(stream, str_data(line), str_len(line)); + str_truncate(line, 0); + offset += len; + } + + str_printfa(line, "%08llx\n", (unsigned long long)offset); + o_stream_nsend(stream, str_data(line), str_len(line)); + } +} diff --git a/pigeonhole/src/lib-sieve/sieve-binary-dumper.h b/pigeonhole/src/lib-sieve/sieve-binary-dumper.h new file mode 100644 index 0000000..4343190 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-binary-dumper.h @@ -0,0 +1,41 @@ +#ifndef SIEVE_BINARY_DUMPER_H +#define SIEVE_BINARY_DUMPER_H + +#include "sieve-common.h" + +/* + * Binary dumper object + */ + +struct sieve_binary_dumper; + +struct sieve_binary_dumper * +sieve_binary_dumper_create(struct sieve_binary *sbin); +void sieve_binary_dumper_free(struct sieve_binary_dumper **dumper); + +pool_t sieve_binary_dumper_pool(struct sieve_binary_dumper *dumper); + +/* + * Formatted output + */ + +void sieve_binary_dumpf(const struct sieve_dumptime_env *denv, + const char *fmt, ...) ATTR_FORMAT(2, 3); +void sieve_binary_dump_sectionf(const struct sieve_dumptime_env *denv, + const char *fmt, ...) ATTR_FORMAT(2, 3); + +/* + * Dumping the binary + */ + +bool sieve_binary_dumper_run(struct sieve_binary_dumper *dumper, + struct ostream *stream, bool verbose); + +/* + * Hexdump production + */ + +void sieve_binary_dumper_hexdump(struct sieve_binary_dumper *dumper, + struct ostream *stream); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-binary-file.c b/pigeonhole/src/lib-sieve/sieve-binary-file.c new file mode 100644 index 0000000..8eedbc2 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-binary-file.c @@ -0,0 +1,1042 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "str-sanitize.h" +#include "mempool.h" +#include "buffer.h" +#include "hash.h" +#include "array.h" +#include "ostream.h" +#include "eacces-error.h" +#include "safe-mkstemp.h" +#include "file-lock.h" + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-extensions.h" +#include "sieve-code.h" +#include "sieve-script.h" + +#include "sieve-binary-private.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> + +/* + * Macros + */ + +#define SIEVE_BINARY_MAGIC 0xcafebabe +#define SIEVE_BINARY_MAGIC_OTHER_ENDIAN 0xbebafeca + +#define SIEVE_BINARY_ALIGN(offset) \ + (((offset) + 3) & ~3U) +#define SIEVE_BINARY_ALIGN_PTR(ptr) \ + ((void *) SIEVE_BINARY_ALIGN(((size_t) ptr))) + +#define SIEVE_BINARY_PRE_HDR_SIZE_MAJOR 1 +#define SIEVE_BINARY_PRE_HDR_SIZE_MINOR 4 +#define SIEVE_BINARY_PRE_HDR_SIZE_HDR_SIZE 12 + +/* + * Header and record structures of the binary on disk + */ + +struct sieve_binary_block_index { + uint32_t id; + uint32_t size; + uint32_t offset; + uint32_t ext_id; +}; + +struct sieve_binary_block_header { + uint32_t id; + uint32_t size; +}; + +/* + * Header manipulation + */ + +static int +sieve_binary_file_read_header(struct sieve_binary *sbin, int fd, + struct sieve_binary_header *header_r, + enum sieve_error *error_r) +{ + struct sieve_binary_header header; + enum sieve_error error; + ssize_t rret; + + if (error_r == NULL) + error_r = &error; + *error_r = SIEVE_ERROR_NONE; + + rret = pread(fd, &header, sizeof(header), 0); + if (rret == 0) { + e_error(sbin->event, "read: " + "file is not large enough to contain the header"); + *error_r = SIEVE_ERROR_NOT_VALID; + return -1; + } else if (rret < 0) { + e_error(sbin->event, "read: " + "failed to read from binary: %m"); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } else if (rret != sizeof(header)) { + e_error(sbin->event, "read: " + "header read only partially %zd/%zu", + rret, sizeof(header)); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + + /* Check header validity */ + if (header.magic != SIEVE_BINARY_MAGIC) { + if (header.magic != SIEVE_BINARY_MAGIC_OTHER_ENDIAN) { + e_error(sbin->event, "read: " + "binary has corrupted header " + "(0x%08x) or it is not a Sieve binary", + header.magic); + } else { + e_error(sbin->event, "read: " + "binary stored with in different endian format " + "(automatically fixed when re-compiled)"); + } + *error_r = SIEVE_ERROR_NOT_VALID; + return -1; + } + /* Check binary version */ + if (header.version_major == SIEVE_BINARY_PRE_HDR_SIZE_MAJOR && + header.version_minor == SIEVE_BINARY_PRE_HDR_SIZE_MINOR) { + /* Old header without hdr_size; clear new fields */ + static const size_t old_header_size = + SIEVE_BINARY_PRE_HDR_SIZE_HDR_SIZE; + memset(PTR_OFFSET(&header, old_header_size), 0, + (sizeof(header) - old_header_size)); + header.hdr_size = old_header_size; + } else if (header.version_major != SIEVE_BINARY_VERSION_MAJOR) { + /* Binary is of different major version. Caller will have to + recompile */ + e_error(sbin->event, "read: " + "binary stored with different major version %d.%d " + "(!= %d.%d; automatically fixed when re-compiled)", + (int)header.version_major, (int)header.version_minor, + SIEVE_BINARY_VERSION_MAJOR, SIEVE_BINARY_VERSION_MINOR); + *error_r = SIEVE_ERROR_NOT_VALID; + return -1; + } else if (header.hdr_size < SIEVE_BINARY_BASE_HEADER_SIZE) { + /* Header size is smaller than base size */ + e_error(sbin->event, "read: " + "binary is corrupt: header size is too small"); + *error_r = SIEVE_ERROR_NOT_VALID; + return -1; + } + /* Check block content */ + if (header.blocks == 0) { + e_error(sbin->event, "read: " + "binary is corrupt: it contains no blocks"); + *error_r = SIEVE_ERROR_NOT_VALID; + return -1; + } + /* Valid */ + *header_r = header; + return 0; +} + +static int +sieve_binary_file_write_header(struct sieve_binary *sbin, int fd, + struct sieve_binary_header *header, + enum sieve_error *error_r) ATTR_NULL(4) +{ + ssize_t wret; + + wret = pwrite(fd, header, sizeof(*header), 0); + if (wret < 0) { + e_error(sbin->event, "update: " + "failed to write to binary: %m"); + if (error_r != NULL) + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } else if (wret != sizeof(*header)) { + e_error(sbin->event, "update: " + "header written partially %zd/%zu", + wret, sizeof(*header)); + if (error_r != NULL) + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + return 0; +} + +static void sieve_binary_file_update_header(struct sieve_binary *sbin) +{ + struct sieve_binary_header *header = &sbin->header; + struct sieve_resource_usage rusage; + + sieve_binary_get_resource_usage(sbin, &rusage); + + i_zero(&header->resource_usage); + if (HAS_ALL_BITS(header->flags, SIEVE_BINARY_FLAG_RESOURCE_LIMIT) || + sieve_resource_usage_is_high(sbin->svinst, &rusage)) { + header->resource_usage.update_time = ioloop_time; + header->resource_usage.cpu_time_msecs = rusage.cpu_time_msecs; + } + + sieve_resource_usage_init(&sbin->rusage); + sbin->rusage_updated = FALSE; + + (void)sieve_binary_check_resource_usage(sbin); +} + +/* + * Saving the binary to a file. + */ + +static inline bool +_save_skip(struct sieve_binary *sbin, struct ostream *stream, size_t size) +{ + if ((o_stream_seek(stream, stream->offset + size)) <= 0) { + e_error(sbin->event, "save: " + "failed to skip output stream to position " + "%"PRIuUOFF_T": %s", stream->offset + size, + strerror(stream->stream_errno)); + return FALSE; + } + + return TRUE; +} + +static inline bool +_save_skip_aligned(struct sieve_binary *sbin, struct ostream *stream, + size_t size, uoff_t *offset) +{ + uoff_t aligned_offset = SIEVE_BINARY_ALIGN(stream->offset); + + if ((o_stream_seek(stream, aligned_offset + size)) <= 0) { + e_error(sbin->event, "save: " + "failed to skip output stream to position " + "%"PRIuUOFF_T": %s", aligned_offset + size, + strerror(stream->stream_errno)); + return FALSE; + } + + if (offset != NULL) + *offset = aligned_offset; + return TRUE; +} + +/* FIXME: Is this even necessary for a file? */ +static bool +_save_full(struct sieve_binary *sbin, struct ostream *stream, + const void *data, size_t size) +{ + size_t bytes_left = size; + const void *pdata = data; + + while (bytes_left > 0) { + ssize_t ret; + + ret = o_stream_send(stream, pdata, bytes_left); + if (ret <= 0) { + e_error(sbin->event, "save: " + "failed to write %zu bytes " + "to output stream: %s", bytes_left, + strerror(stream->stream_errno)); + return FALSE; + } + + pdata = PTR_OFFSET(pdata, ret); + bytes_left -= ret; + } + + return TRUE; +} + +static bool +_save_aligned(struct sieve_binary *sbin, struct ostream *stream, + const void *data, size_t size, uoff_t *offset) +{ + uoff_t aligned_offset = SIEVE_BINARY_ALIGN(stream->offset); + + o_stream_cork(stream); + + /* Align the data by adding zeroes to the output stream */ + if (stream->offset < aligned_offset) { + if (!_save_skip(sbin, stream, + (aligned_offset - stream->offset))) + return FALSE; + } + + if (!_save_full(sbin, stream, data, size)) + return FALSE; + + o_stream_uncork(stream); + + if (offset != NULL) + *offset = aligned_offset; + return TRUE; +} + +static bool +_save_block(struct sieve_binary *sbin, struct ostream *stream, unsigned int id) +{ + struct sieve_binary_block_header block_header; + struct sieve_binary_block *block; + const void *data; + size_t size; + + block = sieve_binary_block_get(sbin, id); + if (block == NULL) + return FALSE; + + data = buffer_get_data(block->data, &size); + + block_header.id = id; + block_header.size = size; + + if (!_save_aligned(sbin, stream, &block_header, sizeof(block_header), + &block->offset)) + return FALSE; + + return _save_aligned(sbin, stream, data, size, NULL); +} + +static bool +_save_block_index_record(struct sieve_binary *sbin, struct ostream *stream, + unsigned int id) +{ + struct sieve_binary_block *block; + struct sieve_binary_block_index header; + + block = sieve_binary_block_get(sbin, id); + if (block == NULL) + return FALSE; + + header.id = id; + header.size = buffer_get_used_size(block->data); + header.ext_id = block->ext_index; + header.offset = block->offset; + + if (!_save_full(sbin, stream, &header, sizeof(header))) { + e_error(sbin->event, "save: " + "failed to save block index header %d", id); + return FALSE; + } + + return TRUE; +} + +static bool +sieve_binary_save_to_stream(struct sieve_binary *sbin, struct ostream *stream) +{ + struct sieve_binary_header *header = &sbin->header; + struct sieve_binary_block *ext_block; + unsigned int ext_count, blk_count, i; + uoff_t block_index; + + blk_count = sieve_binary_block_count(sbin); + + /* Create header */ + + header->magic = SIEVE_BINARY_MAGIC; + header->version_major = SIEVE_BINARY_VERSION_MAJOR; + header->version_minor = SIEVE_BINARY_VERSION_MINOR; + header->blocks = blk_count; + header->hdr_size = sizeof(*header); + + header->flags &= ENUM_NEGATE(SIEVE_BINARY_FLAG_RESOURCE_LIMIT); + sieve_binary_file_update_header(sbin); + + if (!_save_aligned(sbin, stream, header, sizeof(*header), NULL)) { + e_error(sbin->event, "save: failed to save header"); + return FALSE; + } + + /* Skip block index for now */ + + if (!_save_skip_aligned( + sbin, stream, + (sizeof(struct sieve_binary_block_index) * blk_count), + &block_index)) + return FALSE; + + /* Create block containing all used extensions */ + + ext_block = sieve_binary_block_get(sbin, SBIN_SYSBLOCK_EXTENSIONS); + i_assert(ext_block != NULL); + sieve_binary_block_clear(ext_block); + + ext_count = array_count(&sbin->linked_extensions); + sieve_binary_emit_unsigned(ext_block, ext_count); + + for (i = 0; i < ext_count; i++) { + struct sieve_binary_extension_reg * const *ext = + array_idx(&sbin->linked_extensions, i); + + sieve_binary_emit_cstring( + ext_block, sieve_extension_name((*ext)->extension)); + sieve_binary_emit_unsigned( + ext_block, sieve_extension_version((*ext)->extension)); + sieve_binary_emit_unsigned(ext_block, (*ext)->block_id); + } + + /* Save all blocks into the binary */ + + for (i = 0; i < blk_count; i++) { + if (!_save_block(sbin, stream, i)) + return FALSE; + } + + /* Create the block index */ + o_stream_seek(stream, block_index); + for (i = 0; i < blk_count; i++) { + if (!_save_block_index_record(sbin, stream, i)) + return FALSE; + } + + return TRUE; +} + +static int +sieve_binary_do_save(struct sieve_binary *sbin, const char *path, bool update, + mode_t save_mode, enum sieve_error *error_r) +{ + int result, fd; + string_t *temp_path; + struct ostream *stream; + struct sieve_binary_extension_reg *const *regs; + unsigned int ext_count, i; + + if (error_r != NULL) + *error_r = SIEVE_ERROR_NONE; + + /* Check whether saving is necessary */ + if (!update && sbin->path != NULL && strcmp(sbin->path, path) == 0) { + e_debug(sbin->event, "save: " + "not saving binary, because it is already stored"); + return 0; + } + + /* Open it as temp file first, as not to overwrite an existing just yet */ + temp_path = t_str_new(256); + str_append(temp_path, path); + str_append_c(temp_path, '.'); + fd = safe_mkstemp_hostpid(temp_path, save_mode, (uid_t)-1, (gid_t)-1); + if (fd < 0) { + if (errno == EACCES) { + e_error(sbin->event, "save: " + "failed to create temporary file: %s", + eacces_error_get_creating("open", + str_c(temp_path))); + if (error_r != NULL) + *error_r = SIEVE_ERROR_NO_PERMISSION; + } else { + e_error(sbin->event, "save: " + "failed to create temporary file: " + "open(%s) failed: %m", str_c(temp_path)); + if (error_r != NULL) + *error_r = SIEVE_ERROR_TEMP_FAILURE; + } + return -1; + } + + /* Signal all extensions that we're about to save the binary */ + regs = array_get(&sbin->extensions, &ext_count); + for (i = 0; i < ext_count; i++) { + const struct sieve_binary_extension *binext = regs[i]->binext; + + if (binext != NULL && binext->binary_pre_save != NULL && + !binext->binary_pre_save(regs[i]->extension, sbin, + regs[i]->context, error_r)) + return -1; + } + + /* Save binary */ + result = 1; + stream = o_stream_create_fd(fd, 0); + if (!sieve_binary_save_to_stream(sbin, stream)) { + result = -1; + if (error_r != NULL) + *error_r = SIEVE_ERROR_TEMP_FAILURE; + } + o_stream_destroy(&stream); + + /* Close saved binary */ + if (close(fd) < 0) { + e_error(sbin->event, "save: " + "failed to close temporary file: " + "close(fd=%s) failed: %m", str_c(temp_path)); + } + + /* Replace any original binary atomically */ + if (result > 0 && (rename(str_c(temp_path), path) < 0)) { + if (errno == EACCES) { + e_error(sbin->event, "save: " + "failed to save binary: %s", + eacces_error_get_creating("rename", path)); + if (error_r != NULL) + *error_r = SIEVE_ERROR_NO_PERMISSION; + } else { + e_error(sbin->event, "save: " + "failed to save binary: " + "rename(%s, %s) failed: %m", + str_c(temp_path), path); + if (error_r != NULL) + *error_r = SIEVE_ERROR_TEMP_FAILURE; + } + result = -1; + } + + if (result < 0) { + /* Get rid of temp output (if any) */ + if (unlink(str_c(temp_path)) < 0 && errno != ENOENT) { + e_error(sbin->event, "save: " + "failed to clean up after error: " + "unlink(%s) failed: %m", str_c(temp_path)); + } + } else { + if (sbin->path == NULL) + sbin->path = p_strdup(sbin->pool, path); + + /* Signal all extensions that we successfully saved the binary. + */ + regs = array_get(&sbin->extensions, &ext_count); + for (i = 0; i < ext_count; i++) { + const struct sieve_binary_extension *binext = + regs[i]->binext; + + if (binext != NULL && + binext->binary_post_save != NULL && + !binext->binary_post_save(regs[i]->extension, sbin, + regs[i]->context, + error_r)) { + result = -1; + break; + } + } + + if (result < 0 && unlink(path) < 0 && errno != ENOENT) { + e_error(sbin->event, "failed to clean up after error: " + "unlink(%s) failed: %m", path); + } + } + + return result; +} + +int sieve_binary_save(struct sieve_binary *sbin, const char *path, bool update, + mode_t save_mode, enum sieve_error *error_r) +{ + int ret; + + sieve_binary_update_event(sbin, path); + ret = sieve_binary_do_save(sbin, path, update, save_mode, error_r); + sieve_binary_update_event(sbin, NULL); + + return ret; +} + + +/* + * Binary file management + */ + +static int +sieve_binary_fd_open(struct sieve_binary *sbin, const char *path, + int open_flags, enum sieve_error *error_r) +{ + int fd; + + if (error_r != NULL) + *error_r = SIEVE_ERROR_NONE; + + fd = open(path, open_flags); + if (fd < 0) { + switch (errno) { + case ENOENT: + if (error_r != NULL) + *error_r = SIEVE_ERROR_NOT_FOUND; + break; + case EACCES: + e_error(sbin->event, "open: " + "failed to open: %s", + eacces_error_get("open", path)); + if (error_r != NULL) + *error_r = SIEVE_ERROR_NO_PERMISSION; + break; + default: + e_error(sbin->event, "open: " + "failed to open: open(%s) failed: %m", path); + if (error_r != NULL) + *error_r = SIEVE_ERROR_TEMP_FAILURE; + break; + } + return -1; + } + return fd; +} + +static int +sieve_binary_file_open(struct sieve_binary *sbin, const char *path, + struct sieve_binary_file **file_r, + enum sieve_error *error_r) +{ + int fd, ret = 0; + struct stat st; + + if (error_r != NULL) + *error_r = SIEVE_ERROR_NONE; + + fd = sieve_binary_fd_open(sbin, path, O_RDONLY, error_r); + if (fd < 0) + return -1; + + if (fstat(fd, &st) < 0) { + if (errno != ENOENT) + e_error(sbin->event, "open: fstat() failed: %m"); + ret = -1; + } + + if (ret == 0 && !S_ISREG(st.st_mode)) { + e_error(sbin->event, "open: " + "binary is not a regular file"); + ret = -1; + } + + if (ret < 0) { + if (close(fd) < 0) { + e_error(sbin->event, "open: " + "close() failed after error: %m"); + } + return -1; + } + + pool_t pool; + struct sieve_binary_file *file; + + pool = pool_alloconly_create("sieve_binary_file", 4096); + file = p_new(pool, struct sieve_binary_file, 1); + file->pool = pool; + file->path = p_strdup(pool, path); + file->fd = fd; + file->st = st; + file->sbin = sbin; + + *file_r = file; + return 0; +} + +void sieve_binary_file_close(struct sieve_binary_file **_file) +{ + struct sieve_binary_file *file = *_file; + + *_file = NULL; + if (file == NULL) + return; + + if (file->fd != -1) { + if (close(file->fd) < 0) { + e_error(file->sbin->event, "close: " + "failed to close: close() failed: %m"); + } + } + + pool_unref(&file->pool); +} + +static int +sieve_binary_file_read(struct sieve_binary_file *file, off_t *offset, + void *buffer, size_t size) +{ + struct sieve_binary *sbin = file->sbin; + int ret; + void *indata = buffer; + size_t insize = size; + + *offset = SIEVE_BINARY_ALIGN(*offset); + + /* Seek to the correct position */ + if (*offset != file->offset && + lseek(file->fd, *offset, SEEK_SET) == (off_t)-1) { + e_error(sbin->event, "read: " + "failed to seek(fd, %lld, SEEK_SET): %m", + (long long) *offset); + return -1; + } + + /* Read record into memory */ + while (insize > 0) { + ret = read(file->fd, indata, insize); + if (ret <= 0) { + if (ret == 0) { + e_error(sbin->event, "read: " + "binary is truncated " + "(more data expected)"); + } else { + e_error(sbin->event, "read: " + "failed to read from binary: %m"); + } + break; + } + + indata = PTR_OFFSET(indata, ret); + insize -= ret; + } + + if (insize != 0) { + /* Failed to read the whole requested record */ + return 0; + } + + *offset += size; + file->offset = *offset; + return 1; +} + +static const void * +sieve_binary_file_load_data(struct sieve_binary_file *file, + off_t *offset, size_t size) +{ + void *data = t_malloc_no0(size); + + if (sieve_binary_file_read(file, offset, data, size) > 0) + return data; + + return NULL; +} + +static buffer_t * +sieve_binary_file_load_buffer(struct sieve_binary_file *file, + off_t *offset, size_t size) +{ + buffer_t *buffer = buffer_create_dynamic(file->pool, size); + + if (sieve_binary_file_read(file, offset, + buffer_get_space_unsafe(buffer, 0, size), + size) > 0) + return buffer; + + return NULL; +} + +/* + * Load binary from a file + */ + +#define LOAD_HEADER(sbin, offset, header) \ + (header *)sieve_binary_file_load_data(sbin->file, offset, \ + sizeof(header)) + +bool sieve_binary_load_block(struct sieve_binary_block *sblock) +{ + struct sieve_binary *sbin = sblock->sbin; + unsigned int id = sblock->id; + off_t offset = sblock->offset; + const struct sieve_binary_block_header *header = + LOAD_HEADER(sbin, &offset, + const struct sieve_binary_block_header); + + if (header == NULL) { + e_error(sbin->event, "load: binary is corrupt: " + "failed to read header of block %d", id); + return FALSE; + } + + if (header->id != id) { + e_error(sbin->event, "load: binary is corrupt: " + "header of block %d has non-matching id %d", + id, header->id); + return FALSE; + } + + sblock->data = sieve_binary_file_load_buffer(sbin->file, &offset, + header->size); + if (sblock->data == NULL) { + e_error(sbin->event, "load: " + "failed to read block %d of binary (size=%d)", + id, header->size); + return FALSE; + } + + return TRUE; +} + +static bool +_read_block_index_record(struct sieve_binary *sbin, off_t *offset, + unsigned int id) +{ + const struct sieve_binary_block_index *record = + LOAD_HEADER(sbin, offset, + const struct sieve_binary_block_index); + struct sieve_binary_block *block; + + if (record == NULL) { + e_error(sbin->event, "open: binary is corrupt: " + "failed to load block index record %d", id); + return FALSE; + } + + if (record->id != id) { + e_error(sbin->event, "open: binary is corrupt: " + "block index record %d has unexpected id %d", + id, record->id); + return FALSE; + } + + block = sieve_binary_block_create_id(sbin, id); + block->ext_index = record->ext_id; + block->offset = record->offset; + + return TRUE; +} + +static int _read_extensions(struct sieve_binary_block *sblock) +{ + struct sieve_binary *sbin = sblock->sbin; + sieve_size_t offset = 0; + unsigned int i, count; + int result = 1; + + if (!sieve_binary_read_unsigned(sblock, &offset, &count)) + return -1; + + for (i = 0; result > 0 && i < count; i++) { + T_BEGIN { + string_t *extension; + const struct sieve_extension *ext; + unsigned int version; + + if (sieve_binary_read_string(sblock, &offset, + &extension)) { + ext = sieve_extension_get_by_name( + sbin->svinst, str_c(extension)); + + if (ext == NULL) { + e_error(sbin->event, "open: " + "binary requires unknown extension `%s'", + str_sanitize(str_c(extension), 128)); + result = 0; + } else { + struct sieve_binary_extension_reg *ereg = NULL; + + (void)sieve_binary_extension_register(sbin, ext, &ereg); + if (!sieve_binary_read_unsigned(sblock, &offset, &version) || + !sieve_binary_read_unsigned(sblock, &offset, &ereg->block_id)) { + result = -1; + } else if (!sieve_extension_version_is(ext, version)) { + e_debug(sbin->event, "open: " + "binary was compiled with different version " + "of the `%s' extension (compiled v%d, expected v%d;" + "automatically fixed when re-compiled)", + sieve_extension_name(ext), version, + sieve_extension_version(ext)); + result = 0; + } + } + } else { + result = -1; + } + } T_END; + } + + return result; +} + +static bool +_sieve_binary_open(struct sieve_binary *sbin, enum sieve_error *error_r) +{ + bool result = TRUE; + off_t offset = 0; + struct sieve_binary_block *ext_block; + unsigned int i; + int ret; + + /* Read header */ + + ret = sieve_binary_file_read_header(sbin, sbin->file->fd, + &sbin->header, error_r); + if (ret < 0) + return FALSE; + offset = sbin->header.hdr_size; + + /* Load block index */ + + for (i = 0; i < sbin->header.blocks && result; i++) { + T_BEGIN { + if (!_read_block_index_record(sbin, &offset, i)) + result = FALSE; + } T_END; + } + + if (!result) { + if (error_r != NULL) + *error_r = SIEVE_ERROR_NOT_VALID; + return FALSE; + } + + /* Load extensions used by this binary */ + + T_BEGIN { + ext_block = sieve_binary_block_get( + sbin, SBIN_SYSBLOCK_EXTENSIONS); + if (ext_block == NULL) { + result = FALSE; + } else if ((ret = _read_extensions(ext_block)) <= 0) { + if (ret < 0) { + e_error(sbin->event, "open: binary is corrupt: " + "failed to load extension block"); + } + result = FALSE; + } + } T_END; + + if (!result) { + if (error_r != NULL) + *error_r = SIEVE_ERROR_NOT_VALID; + return FALSE; + } + return TRUE; +} + +struct sieve_binary * +sieve_binary_open(struct sieve_instance *svinst, const char *path, + struct sieve_script *script, enum sieve_error *error_r) +{ + struct sieve_binary_extension_reg *const *regs; + unsigned int ext_count, i; + struct sieve_binary *sbin; + struct sieve_binary_file *file; + + i_assert(script == NULL || sieve_script_svinst(script) == svinst); + + /* Create binary object */ + sbin = sieve_binary_create(svinst, script); + sbin->path = p_strdup(sbin->pool, path); + + if (sieve_binary_file_open(sbin, path, &file, error_r) < 0) { + sieve_binary_unref(&sbin); + return NULL; + } + + sbin->file = file; + + event_set_append_log_prefix( + sbin->event, + t_strdup_printf("binary %s: ", path)); + + if (!_sieve_binary_open(sbin, error_r)) { + sieve_binary_unref(&sbin); + return NULL; + } + + sieve_binary_activate(sbin); + + /* Signal open event to extensions */ + regs = array_get(&sbin->extensions, &ext_count); + for (i = 0; i < ext_count; i++) { + const struct sieve_binary_extension *binext = regs[i]->binext; + + if (binext != NULL && binext->binary_open != NULL && + !binext->binary_open(regs[i]->extension, sbin, + regs[i]->context)) { + /* Extension thinks its corrupt */ + if (error_r != NULL) + *error_r = SIEVE_ERROR_NOT_VALID; + sieve_binary_unref(&sbin); + return NULL; + } + } + return sbin; +} + +int sieve_binary_check_executable(struct sieve_binary *sbin, + enum sieve_error *error_r, + const char **client_error_r) +{ + if (error_r != NULL) + *error_r = SIEVE_ERROR_NONE; + *client_error_r = NULL; + + if (HAS_ALL_BITS(sbin->header.flags, + SIEVE_BINARY_FLAG_RESOURCE_LIMIT)) { + e_debug(sbin->event, + "Binary execution is blocked: " + "Cumulative resource usage limit exceeded " + "(resource limit flag is set)"); + if (error_r != NULL) + *error_r = SIEVE_ERROR_RESOURCE_LIMIT; + *client_error_r = "cumulative resource usage limit exceeded"; + return 0; + } + return 1; +} + +/* + * Resource usage + */ + +static int +sieve_binary_file_do_update_resource_usage( + struct sieve_binary *sbin, int fd, enum sieve_error *error_r) +{ + struct sieve_binary_header *header = &sbin->header; + struct file_lock *lock; + const char *error; + int ret; + + struct file_lock_settings lock_set = { + .lock_method = FILE_LOCK_METHOD_FCNTL, + }; + ret = file_wait_lock(fd, sbin->path, F_WRLCK, &lock_set, + SIEVE_BINARY_FILE_LOCK_TIMEOUT, &lock, &error); + if (ret <= 0) { + e_error(sbin->event, "%s", error); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + + ret = sieve_binary_file_read_header(sbin, fd, header, error_r); + if (ret == 0) { + sieve_binary_file_update_header(sbin); + ret = sieve_binary_file_write_header(sbin, fd, header, error_r); + } + + file_lock_free(&lock); + + return ret; +} + +int sieve_binary_file_update_resource_usage(struct sieve_binary *sbin, + enum sieve_error *error_r) +{ + enum sieve_error error; + int fd, ret = 0; + + if (error_r == NULL) + error_r = &error; + *error_r = SIEVE_ERROR_NONE; + + sieve_binary_file_close(&sbin->file); + + if (sbin->path == NULL) + return 0; + if (sbin->header.version_major != SIEVE_BINARY_VERSION_MAJOR) + return sieve_binary_save(sbin, sbin->path, TRUE, 0600, error_r); + + fd = sieve_binary_fd_open(sbin, sbin->path, O_RDWR, error_r); + if (fd < 0) + return -1; + + ret = sieve_binary_file_do_update_resource_usage(sbin, fd, error_r); + + if (close(fd) < 0) { + e_error(sbin->event, "update: " + "failed to close: close() failed: %m"); + } + + return ret; +} diff --git a/pigeonhole/src/lib-sieve/sieve-binary-private.h b/pigeonhole/src/lib-sieve/sieve-binary-private.h new file mode 100644 index 0000000..73d2d75 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-binary-private.h @@ -0,0 +1,240 @@ +#ifndef SIEVE_BINARY_PRIVATE_H +#define SIEVE_BINARY_PRIVATE_H + +#include "sieve-common.h" +#include "sieve-binary.h" +#include "sieve-extensions.h" + +#include <sys/stat.h> + +#define SIEVE_BINARY_FILE_LOCK_TIMEOUT 10 + +/* + * Binary file + */ + +enum SIEVE_BINARY_FLAGS { + SIEVE_BINARY_FLAG_RESOURCE_LIMIT = BIT(0), +}; + +struct sieve_binary_header { + uint32_t magic; + uint16_t version_major; + uint16_t version_minor; + uint32_t blocks; + + uint32_t hdr_size; + uint32_t flags; + + struct { + uint64_t update_time; + uint32_t cpu_time_msecs; + } resource_usage; +}; + +struct sieve_binary_file { + pool_t pool; + const char *path; + struct sieve_binary *sbin; + + struct stat st; + int fd; + off_t offset; +}; + +void sieve_binary_file_close(struct sieve_binary_file **_file); + +/* + * Internal structures + */ + +/* Extension registration */ + +struct sieve_binary_extension_reg { + /* The identifier of the extension within this binary */ + int index; + + /* Global extension object */ + const struct sieve_extension *extension; + + /* Extension to the binary; typically used to manage extension-specific + blocks in the binary and as a means to get a binary_free notification + to release references held by extensions. + */ + const struct sieve_binary_extension *binext; + + /* Context data associated to the binary by this extension */ + void *context; + + /* Main block for this extension */ + unsigned int block_id; +}; + +/* Block */ + +struct sieve_binary_block { + struct sieve_binary *sbin; + unsigned int id; + int ext_index; + + buffer_t *data; + + uoff_t offset; +}; + +/* + * Binary object + */ + +struct sieve_binary { + pool_t pool; + int refcount; + struct sieve_instance *svinst; + struct event *event; + + struct sieve_script *script; + + struct sieve_binary_file *file; + struct sieve_binary_header header; + struct sieve_resource_usage rusage; + + /* When the binary is loaded into memory or when it is being constructed + by the generator, extensions can be associated to the binary. The + extensions array is a sequential list of all linked extensions. The + extension_index array is a mapping ext_id -> binary_extension. This + is used to obtain the index code associated with an extension for + this particular binary. The linked_extensions list all extensions + linked to this binary object other than the preloaded language + features implemented as 'extensions'. + + All arrays refer to the same extension registration objects. Upon + loading a binary, the 'require'd extensions will sometimes need to + associate context data to the binary object in memory. This is stored + in these registration objects as well. + */ + ARRAY(struct sieve_binary_extension_reg *) extensions; + ARRAY(struct sieve_binary_extension_reg *) extension_index; + ARRAY(struct sieve_binary_extension_reg *) linked_extensions; + + /* Attributes of a loaded binary */ + const char *path; + + /* Blocks */ + ARRAY(struct sieve_binary_block *) blocks; + + bool rusage_updated:1; +}; + +void sieve_binary_update_event(struct sieve_binary *sbin, const char *new_path) + ATTR_NULL(2); + +struct sieve_binary * +sieve_binary_create(struct sieve_instance *svinst, struct sieve_script *script); + +/* Blocks management */ + +static inline struct sieve_binary_block * +sieve_binary_block_index(struct sieve_binary *sbin, unsigned int id) +{ + struct sieve_binary_block * const *sblock; + + if (id >= array_count(&sbin->blocks)) + return NULL; + + sblock = array_idx(&sbin->blocks, id); + if (*sblock == NULL) + return NULL; + return *sblock; +} + +static inline size_t +_sieve_binary_block_get_size(const struct sieve_binary_block *sblock) +{ + return buffer_get_used_size(sblock->data); +} + +struct sieve_binary_block * +sieve_binary_block_create_id(struct sieve_binary *sbin, unsigned int id); + +buffer_t *sieve_binary_block_get_buffer(struct sieve_binary_block *sblock); + +/* Extension registration */ + +static inline struct sieve_binary_extension_reg * +sieve_binary_extension_create_reg(struct sieve_binary *sbin, + const struct sieve_extension *ext) +{ + int index = array_count(&sbin->extensions); + struct sieve_binary_extension_reg *ereg; + + if (ext->id < 0) + return NULL; + + ereg = p_new(sbin->pool, struct sieve_binary_extension_reg, 1); + ereg->index = index; + ereg->extension = ext; + + array_idx_set(&sbin->extensions, (unsigned int) index, &ereg); + array_idx_set(&sbin->extension_index, (unsigned int) ext->id, &ereg); + + return ereg; +} + +static inline struct sieve_binary_extension_reg * +sieve_binary_extension_get_reg(struct sieve_binary *sbin, + const struct sieve_extension *ext, + bool create) +{ + struct sieve_binary_extension_reg *reg = NULL; + + if (ext->id >= 0 && + ext->id < (int)array_count(&sbin->extension_index)) { + struct sieve_binary_extension_reg * const *ereg = + array_idx(&sbin->extension_index, + (unsigned int)ext->id); + + reg = *ereg; + } + + /* Register if not known */ + if (reg == NULL && create) + return sieve_binary_extension_create_reg(sbin, ext); + return reg; +} + +static inline int +sieve_binary_extension_register(struct sieve_binary *sbin, + const struct sieve_extension *ext, + struct sieve_binary_extension_reg **reg_r) +{ + struct sieve_binary_extension_reg *ereg; + + if ((ereg = sieve_binary_extension_get_reg(sbin, ext, FALSE)) == NULL) { + ereg = sieve_binary_extension_create_reg(sbin, ext); + + if (ereg == NULL) + return -1; + + array_append(&sbin->linked_extensions, &ereg, 1); + } + + if (reg_r != NULL) + *reg_r = ereg; + return ereg->index; +} + +/* Load/Save */ + +bool sieve_binary_load_block(struct sieve_binary_block *); + +/* + * Resource limits + */ + +bool sieve_binary_check_resource_usage(struct sieve_binary *sbin); + +int sieve_binary_file_update_resource_usage(struct sieve_binary *sbin, + enum sieve_error *error_r) + ATTR_NULL(2); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-binary.c b/pigeonhole/src/lib-sieve/sieve-binary.c new file mode 100644 index 0000000..06cf598 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-binary.c @@ -0,0 +1,606 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "str-sanitize.h" +#include "mempool.h" +#include "buffer.h" +#include "hash.h" +#include "array.h" +#include "ostream.h" +#include "eacces-error.h" +#include "safe-mkstemp.h" + +#include "sieve-error.h" +#include "sieve-extensions.h" +#include "sieve-code.h" +#include "sieve-script.h" + +#include "sieve-binary-private.h" + +/* + * Forward declarations + */ + +static inline struct sieve_binary_extension_reg * +sieve_binary_extension_get_reg(struct sieve_binary *sbin, + const struct sieve_extension *ext, bool create); + +static inline int +sieve_binary_extension_register(struct sieve_binary *sbin, + const struct sieve_extension *ext, + struct sieve_binary_extension_reg **reg); + +/* + * Binary object + */ + +void sieve_binary_update_event(struct sieve_binary *sbin, const char *new_path) +{ + if (new_path != NULL) { + event_set_append_log_prefix( + sbin->event, t_strdup_printf("binary %s: ", new_path)); + } else if (sbin->path != NULL) { + event_set_append_log_prefix( + sbin->event, + t_strdup_printf("binary %s: ", sbin->path)); + } else if (sbin->script != NULL) { + event_set_append_log_prefix( + sbin->event, + t_strdup_printf("binary %s: ", + sieve_script_name(sbin->script))); + } else { + event_set_append_log_prefix(sbin->event, "binary: "); + } +} + +struct sieve_binary * +sieve_binary_create(struct sieve_instance *svinst, struct sieve_script *script) +{ + pool_t pool; + struct sieve_binary *sbin; + const struct sieve_extension *const *ext_preloaded; + unsigned int i, ext_count; + + pool = pool_alloconly_create("sieve_binary", 16384); + sbin = p_new(pool, struct sieve_binary, 1); + sbin->pool = pool; + sbin->refcount = 1; + sbin->svinst = svinst; + + sbin->header.version_major = SIEVE_BINARY_VERSION_MAJOR; + sbin->header.version_minor = SIEVE_BINARY_VERSION_MINOR; + + sbin->script = script; + if (script != NULL) + sieve_script_ref(script); + + sbin->event = event_create(svinst->event); + + ext_count = sieve_extensions_get_count(svinst); + + p_array_init(&sbin->linked_extensions, pool, ext_count); + p_array_init(&sbin->extensions, pool, ext_count); + p_array_init(&sbin->extension_index, pool, ext_count); + + p_array_init(&sbin->blocks, pool, 16); + + /* Pre-load core language features implemented as 'extensions' */ + ext_preloaded = sieve_extensions_get_preloaded(svinst, &ext_count); + for (i = 0; i < ext_count; i++) { + const struct sieve_extension_def *ext_def = ext_preloaded[i]->def; + + if (ext_def != NULL && ext_def->binary_load != NULL) + (void)ext_def->binary_load(ext_preloaded[i], sbin); + } + + return sbin; +} + +struct sieve_binary *sieve_binary_create_new(struct sieve_script *script) +{ + struct sieve_binary *sbin = + sieve_binary_create(sieve_script_svinst(script), script); + struct sieve_binary_block *sblock; + unsigned int i; + + sieve_binary_update_event(sbin, NULL); + + /* Create script metadata block */ + sblock = sieve_binary_block_create(sbin); + sieve_script_binary_write_metadata(script, sblock); + + /* Create other system blocks */ + for (i = 1; i < SBIN_SYSBLOCK_LAST; i++) + (void)sieve_binary_block_create(sbin); + + return sbin; +} + +void sieve_binary_ref(struct sieve_binary *sbin) +{ + sbin->refcount++; +} + +static inline void sieve_binary_extensions_free(struct sieve_binary *sbin) +{ + struct sieve_binary_extension_reg *const *regs; + unsigned int ext_count, i; + + /* Cleanup binary extensions */ + regs = array_get(&sbin->extensions, &ext_count); + for (i = 0; i < ext_count; i++) { + const struct sieve_binary_extension *binext = regs[i]->binext; + + if (binext != NULL && binext->binary_free != NULL) { + binext->binary_free(regs[i]->extension, sbin, + regs[i]->context); + } + } +} + +static void sieve_binary_update_resource_usage(struct sieve_binary *sbin) +{ + enum sieve_error error; + + if (sbin->rusage_updated) + (void)sieve_binary_file_update_resource_usage(sbin, &error); + sbin->rusage_updated = FALSE; +} + +void sieve_binary_unref(struct sieve_binary **_sbin) +{ + struct sieve_binary *sbin = *_sbin; + + *_sbin = NULL; + if (sbin == NULL) + return; + + i_assert(sbin->refcount > 0); + if (--sbin->refcount != 0) + return; + + sieve_binary_file_close(&sbin->file); + sieve_binary_update_resource_usage(sbin); + sieve_binary_extensions_free(sbin); + + if (sbin->script != NULL) + sieve_script_unref(&sbin->script); + + event_unref(&sbin->event); + pool_unref(&sbin->pool); +} + +void sieve_binary_close(struct sieve_binary **_sbin) +{ + struct sieve_binary *sbin = *_sbin; + + *_sbin = NULL; + if (sbin == NULL) + return; + + sieve_binary_file_close(&sbin->file); + sieve_binary_update_resource_usage(sbin); + sieve_binary_unref(&sbin); +} + +/* + * Resource usage + */ + +void sieve_binary_get_resource_usage(struct sieve_binary *sbin, + struct sieve_resource_usage *rusage_r) +{ + struct sieve_binary_header *header = &sbin->header; + time_t update_time = header->resource_usage.update_time; + unsigned int timeout = sbin->svinst->resource_usage_timeout_secs; + + if (update_time != 0 && (ioloop_time - update_time) > timeout) + i_zero(&header->resource_usage); + + sieve_resource_usage_init(rusage_r); + rusage_r->cpu_time_msecs = header->resource_usage.cpu_time_msecs; + sieve_resource_usage_add(rusage_r, &sbin->rusage); +} + +bool sieve_binary_check_resource_usage(struct sieve_binary *sbin) +{ + struct sieve_binary_header *header = &sbin->header; + struct sieve_resource_usage rusage; + + sieve_binary_get_resource_usage(sbin, &rusage); + + if (sieve_resource_usage_is_excessive(sbin->svinst, &rusage)) { + header->flags |= SIEVE_BINARY_FLAG_RESOURCE_LIMIT; + return FALSE; + } + return TRUE; +} + +bool sieve_binary_record_resource_usage( + struct sieve_binary *sbin, const struct sieve_resource_usage *rusage) +{ + struct sieve_resource_usage rusage_total; + + if (sbin == NULL) + return TRUE; + if (!sieve_resource_usage_is_high(sbin->svinst, rusage)) + return TRUE; + + sieve_resource_usage_add(&sbin->rusage, rusage); + sbin->rusage_updated = TRUE; + + sieve_binary_get_resource_usage(sbin, &rusage_total); + + e_debug(sbin->event, "Updated cumulative resource usage: %s", + sieve_resource_usage_get_summary(&rusage_total)); + + return sieve_binary_check_resource_usage(sbin); +} + +void sieve_binary_set_resource_usage(struct sieve_binary *sbin, + const struct sieve_resource_usage *rusage) +{ + struct sieve_binary_header *header = &sbin->header; + + i_zero(&header->resource_usage); + sbin->rusage = *rusage; + sbin->rusage_updated = TRUE; + + (void)sieve_binary_check_resource_usage(sbin); +} + +/* + * Accessors + */ + +pool_t sieve_binary_pool(struct sieve_binary *sbin) +{ + return sbin->pool; +} + +struct sieve_script *sieve_binary_script(struct sieve_binary *sbin) +{ + return sbin->script; +} + +const char *sieve_binary_path(struct sieve_binary *sbin) +{ + return sbin->path; +} + +bool sieve_binary_saved(struct sieve_binary *sbin) +{ + return (sbin->path != NULL); +} + +bool sieve_binary_loaded(struct sieve_binary *sbin) +{ + return (sbin->file != NULL); +} + +const char *sieve_binary_source(struct sieve_binary *sbin) +{ + if (sbin->script != NULL && (sbin->path == NULL || sbin->file == NULL)) + return sieve_script_location(sbin->script); + + return sbin->path; +} + +struct sieve_instance *sieve_binary_svinst(struct sieve_binary *sbin) +{ + return sbin->svinst; +} + +time_t sieve_binary_mtime(struct sieve_binary *sbin) +{ + i_assert(sbin->file != NULL); + return sbin->file->st.st_mtime; +} + +const struct stat *sieve_binary_stat(struct sieve_binary *sbin) +{ + i_assert(sbin->file != NULL); + return &sbin->file->st; +} + +const char *sieve_binary_script_name(struct sieve_binary *sbin) +{ + return (sbin->script == NULL ? + NULL : sieve_script_name(sbin->script)); +} + +const char *sieve_binary_script_location(struct sieve_binary *sbin) +{ + return (sbin->script == NULL ? + NULL : sieve_script_location(sbin->script)); +} + +/* + * Utility + */ + +const char *sieve_binfile_from_name(const char *name) +{ + return t_strconcat(name, "."SIEVE_BINARY_FILEEXT, NULL); +} + +/* + * Block management + */ + +unsigned int sieve_binary_block_count(struct sieve_binary *sbin) +{ + return array_count(&sbin->blocks); +} + +struct sieve_binary_block *sieve_binary_block_create(struct sieve_binary *sbin) +{ + unsigned int id = sieve_binary_block_count(sbin); + struct sieve_binary_block *sblock; + + sblock = p_new(sbin->pool, struct sieve_binary_block, 1); + sblock->data = buffer_create_dynamic(sbin->pool, 64); + sblock->sbin = sbin; + sblock->id = id; + + array_append(&sbin->blocks, &sblock, 1); + + return sblock; +} + +struct sieve_binary_block * +sieve_binary_block_create_id(struct sieve_binary *sbin, unsigned int id) +{ + struct sieve_binary_block *sblock; + + sblock = p_new(sbin->pool, struct sieve_binary_block, 1); + + array_idx_set(&sbin->blocks, id, &sblock); + sblock->data = NULL; + sblock->sbin = sbin; + sblock->id = id; + + return sblock; +} + +static bool sieve_binary_block_fetch(struct sieve_binary_block *sblock) +{ + struct sieve_binary *sbin = sblock->sbin; + + if (sbin->file != NULL) { + /* Try to acces the block in the binary on disk (apparently we + were lazy) + */ + if (!sieve_binary_load_block(sblock) || sblock->data == NULL) + return FALSE; + } else { + sblock->data = buffer_create_dynamic(sbin->pool, 64); + return TRUE; + } + + return TRUE; +} + +struct sieve_binary_block * +sieve_binary_block_get(struct sieve_binary *sbin, unsigned int id) +{ + struct sieve_binary_block *sblock = sieve_binary_block_index(sbin, id); + + if (sblock == NULL) + return NULL; + + if (sblock->data == NULL && !sieve_binary_block_fetch(sblock)) + return NULL; + + return sblock; +} + +void sieve_binary_block_clear(struct sieve_binary_block *sblock) +{ + buffer_set_used_size(sblock->data, 0); +} + +buffer_t *sieve_binary_block_get_buffer(struct sieve_binary_block *sblock) +{ + if (sblock->data == NULL && !sieve_binary_block_fetch(sblock)) + return NULL; + + return sblock->data; +} + +struct sieve_binary * +sieve_binary_block_get_binary(const struct sieve_binary_block *sblock) +{ + return sblock->sbin; +} + +unsigned int sieve_binary_block_get_id(const struct sieve_binary_block *sblock) +{ + return sblock->id; +} + +size_t sieve_binary_block_get_size(const struct sieve_binary_block *sblock) +{ + return _sieve_binary_block_get_size(sblock); +} + +/* + * Up-to-date checking + */ + +bool sieve_binary_up_to_date(struct sieve_binary *sbin, + enum sieve_compile_flags cpflags) +{ + struct sieve_binary_extension_reg *const *regs; + struct sieve_binary_block *sblock; + sieve_size_t offset = 0; + unsigned int ext_count, i; + int ret; + + i_assert(sbin->file != NULL); + + sblock = sieve_binary_block_get(sbin, SBIN_SYSBLOCK_SCRIPT_DATA); + if (sblock == NULL || sbin->script == NULL) + return FALSE; + + if ((ret = sieve_script_binary_read_metadata(sbin->script, sblock, + &offset)) <= 0) { + if (ret < 0) { + e_debug(sbin->event, "up-to-date: " + "failed to read script metadata from binary"); + } else { + e_debug(sbin->event, "up-to-date: " + "script metadata indicates that binary is not up-to-date"); + } + return FALSE; + } + + regs = array_get(&sbin->extensions, &ext_count); + for (i = 0; i < ext_count; i++) { + const struct sieve_binary_extension *binext = regs[i]->binext; + + if (binext != NULL && binext->binary_up_to_date != NULL && + !binext->binary_up_to_date(regs[i]->extension, sbin, + regs[i]->context, cpflags)) { + e_debug(sbin->event, "up-to-date: " + "the %s extension indicates binary is not up-to-date", + sieve_extension_name(regs[i]->extension)); + return FALSE; + } + } + return TRUE; +} + +/* + * Activate the binary (after code generation) + */ + +void sieve_binary_activate(struct sieve_binary *sbin) +{ + struct sieve_binary_extension_reg *const *regs; + unsigned int i, ext_count; + + /* Load other extensions into binary */ + regs = array_get(&sbin->linked_extensions, &ext_count); + for (i = 0; i < ext_count; i++) { + const struct sieve_extension *ext = regs[i]->extension; + + if (ext != NULL && ext->def != NULL && + ext->def->binary_load != NULL) + ext->def->binary_load(ext, sbin); + } +} + +/* + * Extension handling + */ + +void sieve_binary_extension_set_context(struct sieve_binary *sbin, + const struct sieve_extension *ext, + void *context) +{ + struct sieve_binary_extension_reg *ereg = + sieve_binary_extension_get_reg(sbin, ext, TRUE); + + if (ereg != NULL) + ereg->context = context; +} + +const void * +sieve_binary_extension_get_context(struct sieve_binary *sbin, + const struct sieve_extension *ext) +{ + struct sieve_binary_extension_reg *ereg = + sieve_binary_extension_get_reg(sbin, ext, TRUE); + + if (ereg != NULL) + return ereg->context; + + return NULL; +} + +void sieve_binary_extension_set(struct sieve_binary *sbin, + const struct sieve_extension *ext, + const struct sieve_binary_extension *bext, + void *context) +{ + struct sieve_binary_extension_reg *ereg = + sieve_binary_extension_get_reg(sbin, ext, TRUE); + + if (ereg != NULL) { + ereg->binext = bext; + + if (context != NULL) + ereg->context = context; + } +} + +struct sieve_binary_block * +sieve_binary_extension_create_block(struct sieve_binary *sbin, + const struct sieve_extension *ext) +{ + struct sieve_binary_block *sblock; + struct sieve_binary_extension_reg *ereg = + sieve_binary_extension_get_reg(sbin, ext, TRUE); + + i_assert(ereg != NULL); + + sblock = sieve_binary_block_create(sbin); + + if (ereg->block_id < SBIN_SYSBLOCK_LAST) + ereg->block_id = sblock->id; + sblock->ext_index = ereg->index; + + return sblock; +} + +struct sieve_binary_block * +sieve_binary_extension_get_block(struct sieve_binary *sbin, + const struct sieve_extension *ext) +{ + struct sieve_binary_extension_reg *ereg = + sieve_binary_extension_get_reg(sbin, ext, TRUE); + + i_assert(ereg != NULL); + + if (ereg->block_id < SBIN_SYSBLOCK_LAST) + return NULL; + + return sieve_binary_block_get(sbin, ereg->block_id); +} + +int sieve_binary_extension_link(struct sieve_binary *sbin, + const struct sieve_extension *ext) +{ + return sieve_binary_extension_register(sbin, ext, NULL); +} + +const struct sieve_extension * +sieve_binary_extension_get_by_index(struct sieve_binary *sbin, int index) +{ + struct sieve_binary_extension_reg * const *ereg; + + if (index < (int)array_count(&sbin->extensions)) { + ereg = array_idx(&sbin->extensions, (unsigned int)index); + + return (*ereg)->extension; + } + + return NULL; +} + +int sieve_binary_extension_get_index(struct sieve_binary *sbin, + const struct sieve_extension *ext) +{ + struct sieve_binary_extension_reg *ereg = + sieve_binary_extension_get_reg(sbin, ext, FALSE); + + return (ereg == NULL ? -1 : ereg->index); +} + +int sieve_binary_extensions_count(struct sieve_binary *sbin) +{ + return (int)array_count(&sbin->extensions); +} diff --git a/pigeonhole/src/lib-sieve/sieve-binary.h b/pigeonhole/src/lib-sieve/sieve-binary.h new file mode 100644 index 0000000..0b8b66e --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-binary.h @@ -0,0 +1,291 @@ +#ifndef SIEVE_BINARY_H +#define SIEVE_BINARY_H + +#include "lib.h" + +#include "sieve-common.h" + +/* + * Config + */ + +#define SIEVE_BINARY_VERSION_MAJOR 2 +#define SIEVE_BINARY_VERSION_MINOR 0 + +#define SIEVE_BINARY_BASE_HEADER_SIZE 20 + +/* + * Binary object + */ + +struct sieve_binary; + +struct sieve_binary *sieve_binary_create_new(struct sieve_script *script); +void sieve_binary_ref(struct sieve_binary *sbin); +void sieve_binary_unref(struct sieve_binary **_sbin); + +void sieve_binary_close(struct sieve_binary **_sbin); + +/* + * Resource usage + */ + +void sieve_binary_get_resource_usage(struct sieve_binary *sbin, + struct sieve_resource_usage *rusage_r); +bool sieve_binary_record_resource_usage( + struct sieve_binary *sbin, const struct sieve_resource_usage *rusage) + ATTR_NULL(1); +void sieve_binary_set_resource_usage(struct sieve_binary *sbin, + const struct sieve_resource_usage *rusage); +/* + * Accessors + */ + +pool_t sieve_binary_pool(struct sieve_binary *sbin); +struct sieve_instance *sieve_binary_svinst(struct sieve_binary *sbin); +const char *sieve_binary_path(struct sieve_binary *sbin); +struct sieve_script *sieve_binary_script(struct sieve_binary *sbin); + +time_t sieve_binary_mtime(struct sieve_binary *sbin); +const struct stat *sieve_binary_stat(struct sieve_binary *sbin); + +const char *sieve_binary_script_name(struct sieve_binary *sbin); +const char *sieve_binary_script_location(struct sieve_binary *sbin); + +const char *sieve_binary_source(struct sieve_binary *sbin); +bool sieve_binary_loaded(struct sieve_binary *sbin); +bool sieve_binary_saved(struct sieve_binary *sbin); + +/* + * Utility + */ + +const char *sieve_binfile_from_name(const char *name); + +/* + * Activation after code generation + */ + +void sieve_binary_activate(struct sieve_binary *sbin); + +/* + * Saving the binary + */ + +int sieve_binary_save(struct sieve_binary *sbin, const char *path, bool update, + mode_t save_mode, enum sieve_error *error_r); + +/* + * Loading the binary + */ + +struct sieve_binary * +sieve_binary_open(struct sieve_instance *svinst, const char *path, + struct sieve_script *script, enum sieve_error *error_r); +bool sieve_binary_up_to_date(struct sieve_binary *sbin, + enum sieve_compile_flags cpflags); + +int sieve_binary_check_executable(struct sieve_binary *sbin, + enum sieve_error *error_r, + const char **client_error_r); + +/* + * Block management + */ + +enum sieve_binary_system_block { + SBIN_SYSBLOCK_SCRIPT_DATA, + SBIN_SYSBLOCK_EXTENSIONS, + SBIN_SYSBLOCK_MAIN_PROGRAM, + SBIN_SYSBLOCK_LAST +}; + +struct sieve_binary_block *sieve_binary_block_create(struct sieve_binary *sbin); + +unsigned int sieve_binary_block_count(struct sieve_binary *sbin); + +struct sieve_binary_block * +sieve_binary_block_get(struct sieve_binary *sbin, unsigned int id); + +void sieve_binary_block_clear(struct sieve_binary_block *sblock); + +size_t sieve_binary_block_get_size(const struct sieve_binary_block *sblock); + +struct sieve_binary * +sieve_binary_block_get_binary(const struct sieve_binary_block *sblock); + +unsigned int sieve_binary_block_get_id(const struct sieve_binary_block *sblock); + +/* + * Extension support + */ + +struct sieve_binary_extension { + const struct sieve_extension_def *extension; + + bool (*binary_pre_save)(const struct sieve_extension *ext, + struct sieve_binary *sbin, void *context, + enum sieve_error *error_r); + bool (*binary_post_save)(const struct sieve_extension *ext, + struct sieve_binary *sbin, void *context, + enum sieve_error *error_r); + bool (*binary_open)(const struct sieve_extension *ext, + struct sieve_binary *sbin, void *context); + + void (*binary_free)(const struct sieve_extension *ext, + struct sieve_binary *sbin, void *context); + + bool (*binary_up_to_date)(const struct sieve_extension *ext, + struct sieve_binary *sbin, void *context, + enum sieve_compile_flags cpflags); +}; + +void sieve_binary_extension_set_context(struct sieve_binary *sbin, + const struct sieve_extension *ext, + void *context); +const void * +sieve_binary_extension_get_context(struct sieve_binary *sbin, + const struct sieve_extension *ext); + +void sieve_binary_extension_set(struct sieve_binary *sbin, + const struct sieve_extension *ext, + const struct sieve_binary_extension *bext, + void *context); + +struct sieve_binary_block * +sieve_binary_extension_create_block(struct sieve_binary *sbin, + const struct sieve_extension *ext); +struct sieve_binary_block * +sieve_binary_extension_get_block(struct sieve_binary *sbin, + const struct sieve_extension *ext); + +int sieve_binary_extension_link(struct sieve_binary *sbin, + const struct sieve_extension *ext); +const struct sieve_extension * +sieve_binary_extension_get_by_index(struct sieve_binary *sbin, int index); +int sieve_binary_extension_get_index(struct sieve_binary *sbin, + const struct sieve_extension *ext); +int sieve_binary_extensions_count(struct sieve_binary *sbin); + +/* + * Code emission + */ + +/* Low-level emission functions */ + +sieve_size_t sieve_binary_emit_data(struct sieve_binary_block *sblock, + const void *data, sieve_size_t size); +sieve_size_t sieve_binary_emit_byte(struct sieve_binary_block *sblock, + uint8_t byte); +void sieve_binary_update_data(struct sieve_binary_block *sblock, + sieve_size_t address, const void *data, + sieve_size_t size); + +/* Offset emission functions */ + +sieve_size_t sieve_binary_emit_offset(struct sieve_binary_block *sblock, + sieve_offset_t offset); +void sieve_binary_resolve_offset(struct sieve_binary_block *sblock, + sieve_size_t address); + +/* Literal emission functions */ + +sieve_size_t sieve_binary_emit_integer(struct sieve_binary_block *sblock, + sieve_number_t integer); +sieve_size_t sieve_binary_emit_string(struct sieve_binary_block *sblock, + const string_t *str); +sieve_size_t sieve_binary_emit_cstring(struct sieve_binary_block *sblock, + const char *str); + +static inline sieve_size_t +sieve_binary_emit_unsigned(struct sieve_binary_block *sblock, + unsigned int count) +{ + return sieve_binary_emit_integer(sblock, count); +} + +/* Extension emission functions */ + +sieve_size_t sieve_binary_emit_extension(struct sieve_binary_block *sblock, + const struct sieve_extension *ext, + unsigned int offset); +void sieve_binary_emit_extension_object( + struct sieve_binary_block *sblock, + const struct sieve_extension_objects *objs, unsigned int code); + +/* + * Code retrieval + */ + +/* Literals */ + +bool sieve_binary_read_byte(struct sieve_binary_block *sblock, + sieve_size_t *address, unsigned int *byte_r) + ATTR_NULL(3); +bool sieve_binary_read_code(struct sieve_binary_block *sblock, + sieve_size_t *address, signed int *code_r) + ATTR_NULL(3); +bool sieve_binary_read_offset(struct sieve_binary_block *sblock, + sieve_size_t *address, sieve_offset_t *offset_r) + ATTR_NULL(3); +bool sieve_binary_read_integer(struct sieve_binary_block *sblock, + sieve_size_t *address, sieve_number_t *int_r) + ATTR_NULL(3); +bool sieve_binary_read_string(struct sieve_binary_block *sblock, + sieve_size_t *address, string_t **str_r) + ATTR_NULL(3); + +static inline bool ATTR_NULL(3) +sieve_binary_read_unsigned(struct sieve_binary_block *sblock, + sieve_size_t *address, unsigned int *count_r) +{ + sieve_number_t integer = 0; + + if (!sieve_binary_read_integer(sblock, address, &integer)) + return FALSE; + if (count_r != NULL) + *count_r = integer; + return TRUE; +} + +/* Extensions */ + +bool sieve_binary_read_extension(struct sieve_binary_block *sblock, + sieve_size_t *address, unsigned int *offset_r, + const struct sieve_extension **ext_r); +const void * +sieve_binary_read_extension_object(struct sieve_binary_block *sblock, + sieve_size_t *address, + const struct sieve_extension_objects *objs); + +/* + * Debug info + */ + +/* Writer */ + +struct sieve_binary_debug_writer; + +struct sieve_binary_debug_writer * +sieve_binary_debug_writer_init(struct sieve_binary_block *sblock); +void sieve_binary_debug_writer_deinit( + struct sieve_binary_debug_writer **dwriter); + +void sieve_binary_debug_emit(struct sieve_binary_debug_writer *dwriter, + sieve_size_t code_address, unsigned int code_line, + unsigned int code_column); + +/* Reader */ + +struct sieve_binary_debug_reader * +sieve_binary_debug_reader_init(struct sieve_binary_block *sblock); +void sieve_binary_debug_reader_deinit( + struct sieve_binary_debug_reader **dreader); + +void sieve_binary_debug_reader_reset(struct sieve_binary_debug_reader *dreader); + +unsigned int +sieve_binary_debug_read_line(struct sieve_binary_debug_reader *dreader, + sieve_size_t code_address); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-code-dumper.c b/pigeonhole/src/lib-sieve/sieve-code-dumper.c new file mode 100644 index 0000000..17e2768 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-code-dumper.c @@ -0,0 +1,351 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include <stdio.h> +#include <string.h> + +#include "lib.h" +#include "str.h" +#include "mempool.h" +#include "ostream.h" + +#include "sieve-common.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-code.h" +#include "sieve-actions.h" +#include "sieve-generator.h" +#include "sieve-binary.h" +#include "sieve-result.h" +#include "sieve-comparators.h" + +#include "sieve-dump.h" + +/* + * Code dumper extension + */ + +struct sieve_code_dumper_extension_reg { + const struct sieve_code_dumper_extension *cdmpext; + const struct sieve_extension *ext; + void *context; +}; + +struct sieve_code_dumper { + pool_t pool; + + /* Dump status */ + struct sieve_operation oprtn; + sieve_size_t mark_address; + unsigned int mark_line; + unsigned int mark_last_line; + unsigned int indent; + + /* Dump environment */ + struct sieve_dumptime_env *dumpenv; + + struct sieve_binary_debug_reader *dreader; + + ARRAY(struct sieve_code_dumper_extension_reg) extensions; +}; + +struct sieve_code_dumper *sieve_code_dumper_create +(struct sieve_dumptime_env *denv) +{ + pool_t pool; + struct sieve_code_dumper *cdumper; + + pool = pool_alloconly_create("sieve_code_dumper", 4096); + cdumper = p_new(pool, struct sieve_code_dumper, 1); + cdumper->pool = pool; + cdumper->dumpenv = denv; + + /* Setup storage for extension contexts */ + p_array_init(&cdumper->extensions, pool, + sieve_extensions_get_count(denv->svinst)); + + return cdumper; +} + +void sieve_code_dumper_free(struct sieve_code_dumper **_cdumper) +{ + struct sieve_code_dumper *cdumper = *_cdumper; + const struct sieve_code_dumper_extension_reg *eregs; + unsigned int count, i; + + sieve_binary_debug_reader_deinit(&cdumper->dreader); + + /* Signal registered extensions that the dumper is being destroyed */ + eregs = array_get(&cdumper->extensions, &count); + for ( i = 0; i < count; i++ ) { + if ( eregs[i].cdmpext != NULL && eregs[i].cdmpext->free != NULL ) + eregs[i].cdmpext->free(cdumper, eregs[i].context); + } + + pool_unref(&cdumper->pool); + *_cdumper = NULL; +} + +pool_t sieve_code_dumper_pool(struct sieve_code_dumper *cdumper) +{ + return cdumper->pool; +} + +/* EXtension support */ + +void sieve_dump_extension_register +(struct sieve_code_dumper *cdumper, const struct sieve_extension *ext, + const struct sieve_code_dumper_extension *cdmpext, void *context) +{ + struct sieve_code_dumper_extension_reg *reg; + + if ( ext->id < 0 ) return; + + reg = array_idx_get_space(&cdumper->extensions, (unsigned int) ext->id); + reg->cdmpext = cdmpext; + reg->ext = ext; + reg->context = context; +} + +void sieve_dump_extension_set_context +(struct sieve_code_dumper *cdumper, const struct sieve_extension *ext, + void *context) +{ + struct sieve_code_dumper_extension_reg *reg; + + if ( ext->id < 0 ) return; + + reg = array_idx_get_space(&cdumper->extensions, (unsigned int) ext->id); + reg->context = context; +} + +void *sieve_dump_extension_get_context +(struct sieve_code_dumper *cdumper, const struct sieve_extension *ext) +{ + const struct sieve_code_dumper_extension_reg *reg; + + if ( ext->id < 0 || ext->id >= (int) array_count(&cdumper->extensions) ) + return NULL; + + reg = array_idx(&cdumper->extensions, (unsigned int) ext->id); + + return reg->context; +} + +/* Dump functions */ + +void sieve_code_dumpf +(const struct sieve_dumptime_env *denv, const char *fmt, ...) +{ + struct sieve_code_dumper *cdumper = denv->cdumper; + unsigned tab = cdumper->indent; + + string_t *outbuf = t_str_new(128); + va_list args; + + va_start(args, fmt); + str_printfa(outbuf, "%08llx: ", (unsigned long long) cdumper->mark_address); + + if ( cdumper->mark_line > 0 && (cdumper->indent == 0 || + cdumper->mark_line != cdumper->mark_last_line) ) { + str_printfa(outbuf, "%4u: ", cdumper->mark_line); + cdumper->mark_last_line = cdumper->mark_line; + } else { + str_append(outbuf, " "); + } + + while ( tab > 0 ) { + str_append(outbuf, " "); + tab--; + } + + str_vprintfa(outbuf, fmt, args); + str_append_c(outbuf, '\n'); + va_end(args); + + o_stream_nsend(denv->stream, str_data(outbuf), str_len(outbuf)); +} + +static inline void sieve_code_line_mark +(const struct sieve_dumptime_env *denv, sieve_size_t location) +{ + if ( denv->cdumper->dreader != NULL ) { + denv->cdumper->mark_line = sieve_binary_debug_read_line + (denv->cdumper->dreader, location); + } +} + +void sieve_code_mark(const struct sieve_dumptime_env *denv) +{ + denv->cdumper->mark_address = denv->offset; + sieve_code_line_mark(denv, denv->offset); +} + +void sieve_code_mark_specific +(const struct sieve_dumptime_env *denv, sieve_size_t location) +{ + denv->cdumper->mark_address = location; + sieve_code_line_mark(denv, location); +} + +void sieve_code_descend(const struct sieve_dumptime_env *denv) +{ + denv->cdumper->indent++; +} + +void sieve_code_ascend(const struct sieve_dumptime_env *denv) +{ + if ( denv->cdumper->indent > 0 ) + denv->cdumper->indent--; +} + +/* Code Dump */ + +static bool sieve_code_dumper_print_operation +(struct sieve_code_dumper *cdumper) +{ + struct sieve_dumptime_env *denv = cdumper->dumpenv; + struct sieve_operation *oprtn = &(cdumper->oprtn); + sieve_size_t *address = &(denv->offset); + + /* Mark start address of operation */ + cdumper->indent = 0; + cdumper->mark_address = *address; + + sieve_code_line_mark(denv, *address); + + /* Read operation */ + if ( sieve_operation_read(denv->sblock, address, oprtn) ) { + const struct sieve_operation_def *opdef = oprtn->def; + + if ( opdef->dump != NULL ) + return opdef->dump(denv, address); + else if ( opdef->mnemonic != NULL ) + sieve_code_dumpf(denv, "%s", opdef->mnemonic); + else + return FALSE; + + return TRUE; + } + + sieve_code_dumpf(denv, "Failed to read opcode."); + return FALSE; +} + +static bool sieve_code_dumper_print_extension +(struct sieve_code_dumper *cdumper) +{ + struct sieve_dumptime_env *denv = cdumper->dumpenv; + sieve_size_t *address = &(denv->offset); + struct sieve_binary_block *sblock = denv->sblock; + unsigned int code = 0, deferred; + const struct sieve_extension *ext; + + sieve_code_mark(denv); + + if ( !sieve_binary_read_extension + (sblock, address, &code, &ext) || + !sieve_binary_read_byte + (sblock, address, &deferred) ) { + return FALSE; + } + + if ( ext->def == NULL) { + sieve_code_dumpf(denv, "[undefined]"); + + } else { + sieve_code_dumpf(denv, "%s%s", + sieve_extension_name(ext), + (deferred > 0 ? " (deferred)" : "")); + + if (ext->def->code_dump != NULL ) { + sieve_code_descend(denv); + if ( !ext->def->code_dump(ext, denv, address) ) + return FALSE; + sieve_code_ascend(denv); + } + } + return TRUE; +} + +void sieve_code_dumper_run(struct sieve_code_dumper *cdumper) +{ + struct sieve_dumptime_env *denv = cdumper->dumpenv; + struct sieve_binary *sbin = denv->sbin; + struct sieve_binary_block *sblock = denv->sblock; + unsigned int debug_block_id, ext_count; + bool success; + sieve_size_t *address; + + denv->offset = 0; + denv->oprtn = &(cdumper->oprtn); + address = &(denv->offset); + + /* Heading */ + o_stream_nsend_str(denv->stream, "Address Line Code\n"); + + /* Load debug block */ + sieve_code_mark(denv); + + if ( sieve_binary_read_unsigned(sblock, address, &debug_block_id) ) { + struct sieve_binary_block *debug_block = + sieve_binary_block_get(sbin, debug_block_id); + + if ( debug_block == NULL ) { + sieve_code_dumpf(denv, "Invalid id %d for debug block.", debug_block_id); + return; + } else { + /* Initialize debug reader */ + cdumper->dreader = sieve_binary_debug_reader_init(debug_block); + + /* Dump block id */ + sieve_code_dumpf(denv, "DEBUG BLOCK: %d", debug_block_id); + } + } else { + sieve_code_dumpf(denv, "Binary code header is corrupt."); + return; + } + + /* Load and dump extensions listed in code */ + sieve_code_mark(denv); + + success = TRUE; + if ( sieve_binary_read_unsigned(sblock, address, &ext_count) ) { + unsigned int i; + + sieve_code_dumpf(denv, "EXTENSIONS [%d]:", ext_count); + sieve_code_descend(denv); + + for ( i = 0; success && (i < ext_count); i++ ) { + T_BEGIN { + success = success && + sieve_code_dumper_print_extension(cdumper); + } T_END; + } + + sieve_code_ascend(denv); + } else + success = FALSE; + + if ( !success ) { + sieve_code_dumpf(denv, "Binary code header is corrupt."); + return; + } + + while ( *address < sieve_binary_block_get_size(sblock) ) { + + T_BEGIN { + success = sieve_code_dumper_print_operation(cdumper); + } T_END; + + if ( !success ) { + sieve_code_dumpf(denv, "Binary is corrupt."); + return; + } + } + + /* Mark end of the binary */ + cdumper->indent = 0; + cdumper->mark_address = sieve_binary_block_get_size(sblock); + sieve_code_dumpf(denv, "[End of code]"); +} diff --git a/pigeonhole/src/lib-sieve/sieve-code-dumper.h b/pigeonhole/src/lib-sieve/sieve-code-dumper.h new file mode 100644 index 0000000..651bd11 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-code-dumper.h @@ -0,0 +1,55 @@ +#ifndef SIEVE_CODE_DUMPER_H +#define SIEVE_CODE_DUMPER_H + +#include "sieve-common.h" + +struct sieve_code_dumper; + +struct sieve_code_dumper *sieve_code_dumper_create + (struct sieve_dumptime_env *denv); +void sieve_code_dumper_free + (struct sieve_code_dumper **_dumper); +pool_t sieve_code_dumper_pool + (struct sieve_code_dumper *dumper); + +/* + * Extension support + */ + +struct sieve_code_dumper_extension { + const struct sieve_extension_def *ext; + + void (*free)(struct sieve_code_dumper *dumper, void *context); +}; + +void sieve_dump_extension_register +(struct sieve_code_dumper *dumper, const struct sieve_extension *ext, + const struct sieve_code_dumper_extension *dump_ext, void *context); +void sieve_dump_extension_set_context + (struct sieve_code_dumper *dumper, const struct sieve_extension *ext, + void *context); +void *sieve_dump_extension_get_context + (struct sieve_code_dumper *dumper, const struct sieve_extension *ext); + +/* Dump functions */ + +void sieve_code_dumpf + (const struct sieve_dumptime_env *denv, const char *fmt, ...) + ATTR_FORMAT(2, 3); + +void sieve_code_mark(const struct sieve_dumptime_env *denv); +void sieve_code_mark_specific + (const struct sieve_dumptime_env *denv, sieve_size_t location); +void sieve_code_descend(const struct sieve_dumptime_env *denv); +void sieve_code_ascend(const struct sieve_dumptime_env *denv); + +/* Operations and operands */ + +bool sieve_code_dumper_print_optional_operands + (const struct sieve_dumptime_env *denv, sieve_size_t *address); + +/* Code dump (debugging purposes) */ + +void sieve_code_dumper_run(struct sieve_code_dumper *dumper); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-code.c b/pigeonhole/src/lib-sieve/sieve-code.c new file mode 100644 index 0000000..82bd7f1 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-code.c @@ -0,0 +1,1169 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "str-sanitize.h" + +#include "sieve-common.h" +#include "sieve-limits.h" +#include "sieve-extensions.h" +#include "sieve-stringlist.h" +#include "sieve-actions.h" +#include "sieve-binary.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "sieve-code.h" + +#include <stdio.h> + +/* + * Code stringlist + */ + +/* Forward declarations */ + +static int sieve_code_stringlist_next_item + (struct sieve_stringlist *_strlist, string_t **str_r); +static void sieve_code_stringlist_reset + (struct sieve_stringlist *_strlist); +static int sieve_code_stringlist_get_length + (struct sieve_stringlist *_strlist); + +/* Coded stringlist object */ + +struct sieve_code_stringlist { + struct sieve_stringlist strlist; + + sieve_size_t start_address; + sieve_size_t end_address; + sieve_size_t current_offset; + int length; + int index; +}; + +static struct sieve_stringlist *sieve_code_stringlist_create +(const struct sieve_runtime_env *renv, + sieve_size_t start_address, unsigned int length, sieve_size_t end) +{ + struct sieve_code_stringlist *strlist; + + if ( end > sieve_binary_block_get_size(renv->sblock) ) + return NULL; + + strlist = t_new(struct sieve_code_stringlist, 1); + strlist->strlist.runenv = renv; + strlist->strlist.exec_status = SIEVE_EXEC_OK; + strlist->strlist.next_item = sieve_code_stringlist_next_item; + strlist->strlist.reset = sieve_code_stringlist_reset; + strlist->strlist.get_length = sieve_code_stringlist_get_length; + strlist->start_address = start_address; + strlist->current_offset = start_address; + strlist->end_address = end; + strlist->length = length; + strlist->index = 0; + + return &strlist->strlist; +} + +/* Stringlist implementation */ + +static int sieve_code_stringlist_next_item +(struct sieve_stringlist *_strlist, string_t **str_r) +{ + struct sieve_code_stringlist *strlist = + (struct sieve_code_stringlist *) _strlist; + sieve_size_t address; + *str_r = NULL; + int ret; + + /* Check for end of list */ + if ( strlist->index >= strlist->length ) + return 0; + + /* Read next item */ + address = strlist->current_offset; + if ( (ret=sieve_opr_string_read(_strlist->runenv, &address, NULL, str_r)) + == SIEVE_EXEC_OK ) { + strlist->index++; + strlist->current_offset = address; + return 1; + } + + _strlist->exec_status = ret; + return -1; +} + +static void sieve_code_stringlist_reset +(struct sieve_stringlist *_strlist) +{ + struct sieve_code_stringlist *strlist = + (struct sieve_code_stringlist *) _strlist; + + strlist->current_offset = strlist->start_address; + strlist->index = 0; +} + +static int sieve_code_stringlist_get_length +(struct sieve_stringlist *_strlist) +{ + struct sieve_code_stringlist *strlist = + (struct sieve_code_stringlist *) _strlist; + + return strlist->length; +} + +static bool sieve_code_stringlist_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address, + unsigned int length, sieve_size_t end, const char *field_name) +{ + unsigned int i; + + if ( end > sieve_binary_block_get_size(denv->sblock) ) + return FALSE; + + if ( field_name != NULL ) + sieve_code_dumpf(denv, "%s: STRLIST [%u] (end: %08llx)", + field_name, length, (unsigned long long) end); + else + sieve_code_dumpf(denv, "STRLIST [%u] (end: %08llx)", + length, (unsigned long long) end); + + sieve_code_descend(denv); + + for ( i = 0; i < length; i++ ) { + bool success = TRUE; + + T_BEGIN { + success = sieve_opr_string_dump(denv, address, NULL); + } T_END; + + if ( !success || *address > end ) + return FALSE; + } + + if ( *address != end ) return FALSE; + + sieve_code_ascend(denv); + + return TRUE; +} + +/* + * Core operands + */ + +extern const struct sieve_operand_def comparator_operand; +extern const struct sieve_operand_def match_type_operand; +extern const struct sieve_operand_def address_part_operand; + +const struct sieve_operand_def *sieve_operands[] = { + &omitted_operand, /* SIEVE_OPERAND_OPTIONAL */ + &number_operand, + &string_operand, + &stringlist_operand, + &comparator_operand, + &match_type_operand, + &address_part_operand, + &catenated_string_operand +}; + +const unsigned int sieve_operand_count = + N_ELEMENTS(sieve_operands); + +/* + * Operand functions + */ + +sieve_size_t sieve_operand_emit +(struct sieve_binary_block *sblock, const struct sieve_extension *ext, + const struct sieve_operand_def *opr_def) +{ + sieve_size_t address; + + if ( ext != NULL ) { + address = sieve_binary_emit_extension + (sblock, ext, sieve_operand_count); + + sieve_binary_emit_extension_object + (sblock, &opr_def->ext_def->operands, opr_def->code); + + return address; + } + + return sieve_binary_emit_byte(sblock, opr_def->code); +} + +bool sieve_operand_read +(struct sieve_binary_block *sblock, sieve_size_t *address, + const char *field_name, struct sieve_operand *operand) +{ + unsigned int code = sieve_operand_count; + + operand->address = *address; + operand->field_name = field_name; + operand->ext = NULL; + operand->def = NULL; + + if ( !sieve_binary_read_extension(sblock, address, &code, &operand->ext) ) + return FALSE; + + if ( operand->ext == NULL ) { + if ( code < sieve_operand_count ) + operand->def = sieve_operands[code]; + + return ( operand->def != NULL ); + } + + if ( operand->ext->def == NULL ) + return FALSE; + + operand->def = (const struct sieve_operand_def *) + sieve_binary_read_extension_object(sblock, address, + &operand->ext->def->operands); + + return ( operand->def != NULL ); +} + +/* + * Optional operand + */ + +int sieve_opr_optional_next +(struct sieve_binary_block *sblock, sieve_size_t *address, signed int *opt_code) +{ + /* Start of optional operand block */ + if ( *opt_code == 0 ) { + sieve_size_t tmp_addr = *address; + unsigned int op; + + if ( !sieve_binary_read_byte(sblock, &tmp_addr, &op) || + op != SIEVE_OPERAND_OPTIONAL ) + return 0; + + *address = tmp_addr; + } + + /* Read optional operand code */ + if ( !sieve_binary_read_code(sblock, address, opt_code) ) + return -1; + + /* Return 0 at end of list */ + return ( *opt_code != 0 ? 1 : 0 ); +} + +/* + * Operand definitions + */ + +/* Omitted */ + +const struct sieve_operand_class omitted_class = + { "OMITTED" }; + +const struct sieve_operand_def omitted_operand = { + .name = "@OMITTED", + .code = SIEVE_OPERAND_OPTIONAL, + .class = &omitted_class +}; + +/* Number */ + +static bool opr_number_dump + (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, + sieve_size_t *address); +static int opr_number_read + (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + sieve_size_t *address, sieve_number_t *number_r); + +const struct sieve_opr_number_interface number_interface = { + opr_number_dump, + opr_number_read +}; + +const struct sieve_operand_class number_class = + { "number" }; + +const struct sieve_operand_def number_operand = { + .name = "@number", + .code = SIEVE_OPERAND_NUMBER, + .class = &number_class, + .interface = &number_interface +}; + +/* String */ + +static bool opr_string_dump + (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, + sieve_size_t *address); +static int opr_string_read + (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + sieve_size_t *address, string_t **str_r); + +const struct sieve_opr_string_interface string_interface ={ + opr_string_dump, + opr_string_read +}; + +const struct sieve_operand_class string_class = + { "string" }; + +const struct sieve_operand_def string_operand = { + .name = "@string", + .code = SIEVE_OPERAND_STRING, + .class = &string_class, + .interface = &string_interface +}; + +/* String List */ + +static bool opr_stringlist_dump + (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, + sieve_size_t *address); +static int opr_stringlist_read + (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + sieve_size_t *address, struct sieve_stringlist **strlist_r); + +const struct sieve_opr_stringlist_interface stringlist_interface = { + opr_stringlist_dump, + opr_stringlist_read +}; + +const struct sieve_operand_class stringlist_class = + { "string-list" }; + +const struct sieve_operand_def stringlist_operand = { + .name = "@string-list", + .code = SIEVE_OPERAND_STRING_LIST, + .class = &stringlist_class, + .interface = &stringlist_interface +}; + +/* Catenated String */ + +static bool opr_catenated_string_dump + (const struct sieve_dumptime_env *denv, const struct sieve_operand *operand, + sieve_size_t *address); +static int opr_catenated_string_read + (const struct sieve_runtime_env *renv, const struct sieve_operand *operand, + sieve_size_t *address, string_t **str); + +const struct sieve_opr_string_interface catenated_string_interface = { + opr_catenated_string_dump, + opr_catenated_string_read +}; + +const struct sieve_operand_def catenated_string_operand = { + .name = "@catenated-string", + .code = SIEVE_OPERAND_CATENATED_STRING, + .class = &string_class, + .interface = &catenated_string_interface +}; + +/* + * Operand implementations + */ + +/* Omitted */ + +void sieve_opr_omitted_emit(struct sieve_binary_block *sblock) +{ + (void) sieve_operand_emit(sblock, NULL, &omitted_operand); +} + +/* Number */ + +void sieve_opr_number_emit +(struct sieve_binary_block *sblock, sieve_number_t number) +{ + (void) sieve_operand_emit(sblock, NULL, &number_operand); + (void) sieve_binary_emit_integer(sblock, number); +} + +bool sieve_opr_number_dump_data +(const struct sieve_dumptime_env *denv, struct sieve_operand *oprnd, + sieve_size_t *address, const char *field_name) +{ + const struct sieve_opr_number_interface *intf; + + oprnd->field_name = field_name; + + if ( !sieve_operand_is_number(oprnd) ) + return FALSE; + + intf = (const struct sieve_opr_number_interface *) oprnd->def->interface; + + if ( intf->dump == NULL ) + return FALSE; + + return intf->dump(denv, oprnd, address); +} + +bool sieve_opr_number_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address, + const char *field_name) +{ + struct sieve_operand operand; + + sieve_code_mark(denv); + + if ( !sieve_operand_read(denv->sblock, address, field_name, &operand) ) + return FALSE; + + return sieve_opr_number_dump_data(denv, &operand, address, field_name); +} + +int sieve_opr_number_read_data +(const struct sieve_runtime_env *renv, struct sieve_operand *oprnd, + sieve_size_t *address, const char *field_name, sieve_number_t *number_r) +{ + const struct sieve_opr_number_interface *intf; + + oprnd->field_name = field_name; + + if ( !sieve_operand_is_number(oprnd) ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "expected number operand but found %s", sieve_operand_name(oprnd)); + return SIEVE_EXEC_BIN_CORRUPT; + } + + intf = (const struct sieve_opr_number_interface *) oprnd->def->interface; + + if ( intf->read == NULL ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "number operand not implemented"); + return SIEVE_EXEC_FAILURE; + } + + return intf->read(renv, oprnd, address, number_r); +} + +int sieve_opr_number_read +(const struct sieve_runtime_env *renv, sieve_size_t *address, + const char *field_name, sieve_number_t *number_r) +{ + struct sieve_operand operand; + int ret; + + if ( (ret=sieve_operand_runtime_read(renv, address, field_name, &operand)) + <= 0) + return ret; + + return sieve_opr_number_read_data + (renv, &operand, address, field_name, number_r); +} + +static bool opr_number_dump +(const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, + sieve_size_t *address) +{ + sieve_number_t number = 0; + + if (sieve_binary_read_integer(denv->sblock, address, &number) ) { + if ( oprnd->field_name != NULL ) + sieve_code_dumpf(denv, "%s: NUM %llu", oprnd->field_name, + (unsigned long long) number); + else + sieve_code_dumpf(denv, "NUM %llu", (unsigned long long) number); + + return TRUE; + } + + return FALSE; +} + +static int opr_number_read +(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + sieve_size_t *address, sieve_number_t *number_r) +{ + if ( !sieve_binary_read_integer(renv->sblock, address, number_r) ) { + sieve_runtime_trace_operand_error(renv, oprnd, "invalid number operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + return SIEVE_EXEC_OK; +} + +/* String */ + +void sieve_opr_string_emit(struct sieve_binary_block *sblock, string_t *str) +{ + (void) sieve_operand_emit(sblock, NULL, &string_operand); + (void) sieve_binary_emit_string(sblock, str); +} + +bool sieve_opr_string_dump_data +(const struct sieve_dumptime_env *denv, struct sieve_operand *oprnd, + sieve_size_t *address, const char *field_name) +{ + const struct sieve_opr_string_interface *intf; + + oprnd->field_name = field_name; + + if ( !sieve_operand_is_string(oprnd) ) { + sieve_code_dumpf(denv, "ERROR: INVALID STRING OPERAND %s", + sieve_operand_name(oprnd)); + return FALSE; + } + + intf = (const struct sieve_opr_string_interface *) oprnd->def->interface; + + if ( intf->dump == NULL ) { + sieve_code_dumpf(denv, "ERROR: DUMP STRING OPERAND"); + return FALSE; + } + + return intf->dump(denv, oprnd, address); +} + +bool sieve_opr_string_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address, + const char *field_name) +{ + struct sieve_operand operand; + + sieve_code_mark(denv); + + if ( !sieve_operand_read(denv->sblock, address, field_name, &operand) ) { + sieve_code_dumpf(denv, "ERROR: INVALID OPERAND"); + return FALSE; + } + + return sieve_opr_string_dump_data(denv, &operand, address, field_name); +} + +bool sieve_opr_string_dump_ex +(const struct sieve_dumptime_env *denv, sieve_size_t *address, + const char *field_name, const char *omitted_value) +{ + struct sieve_operand operand; + + sieve_code_mark(denv); + if ( !sieve_operand_read(denv->sblock, address, field_name, &operand) ) { + sieve_code_dumpf(denv, "ERROR: INVALID OPERAND"); + return FALSE; + } + + if ( omitted_value != NULL && sieve_operand_is_omitted(&operand) ) { + if ( *omitted_value != '\0' ) + sieve_code_dumpf(denv, "%s: %s", field_name, omitted_value); + return TRUE; + } + + return sieve_opr_string_dump_data(denv, &operand, address, field_name); +} + +int sieve_opr_string_read_data +(const struct sieve_runtime_env *renv, struct sieve_operand *oprnd, + sieve_size_t *address, const char *field_name, string_t **str_r) +{ + const struct sieve_opr_string_interface *intf; + + oprnd->field_name = field_name; + + if ( !sieve_operand_is_string(oprnd) ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "expected string operand but found %s", sieve_operand_name(oprnd)); + return SIEVE_EXEC_BIN_CORRUPT; + } + + intf = (const struct sieve_opr_string_interface *) oprnd->def->interface; + + if ( intf->read == NULL ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "string operand not implemented"); + return SIEVE_EXEC_FAILURE; + } + + return intf->read(renv, oprnd, address, str_r); +} + +int sieve_opr_string_read +(const struct sieve_runtime_env *renv, sieve_size_t *address, + const char *field_name, string_t **str_r) +{ + struct sieve_operand operand; + int ret; + + if ( (ret=sieve_operand_runtime_read(renv, address, field_name, &operand)) + <= 0 ) + return ret; + + return sieve_opr_string_read_data(renv, &operand, address, field_name, str_r); +} + +int sieve_opr_string_read_ex +(const struct sieve_runtime_env *renv, sieve_size_t *address, + const char *field_name, bool optional, string_t **str_r, bool *literal_r) +{ + struct sieve_operand operand; + int ret; + + if ( (ret=sieve_operand_runtime_read(renv, address, field_name, &operand)) + <= 0 ) + return ret; + + if ( optional && sieve_operand_is_omitted(&operand) ) { + *str_r = NULL; + return 1; + } + + if ( literal_r != NULL ) + *literal_r = sieve_operand_is_string_literal(&operand); + + return sieve_opr_string_read_data(renv, &operand, address, field_name, str_r); +} + +static void _dump_string +(const struct sieve_dumptime_env *denv, string_t *str, + const char *field_name) +{ + if ( str_len(str) > 80 ) { + if ( field_name != NULL ) + sieve_code_dumpf(denv, "%s: STR[%ld] \"%s", + field_name, (long) str_len(str), str_sanitize(str_c(str), 80)); + else + sieve_code_dumpf(denv, "STR[%ld] \"%s", + (long) str_len(str), str_sanitize(str_c(str), 80)); + } else { + if ( field_name != NULL ) + sieve_code_dumpf(denv, "%s: STR[%ld] \"%s\"", + field_name, (long) str_len(str), str_sanitize(str_c(str), 80)); + else + sieve_code_dumpf(denv, "STR[%ld] \"%s\"", + (long) str_len(str), str_sanitize(str_c(str), 80)); + } +} + +bool opr_string_dump +(const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, + sieve_size_t *address) +{ + string_t *str; + + if ( sieve_binary_read_string(denv->sblock, address, &str) ) { + _dump_string(denv, str, oprnd->field_name); + + return TRUE; + } + + return FALSE; +} + +static int opr_string_read +(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + sieve_size_t *address, string_t **str_r) +{ + if ( !sieve_binary_read_string(renv->sblock, address, str_r) ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "invalid string operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + return SIEVE_EXEC_OK; +} + +/* String list */ + +void sieve_opr_stringlist_emit_start +(struct sieve_binary_block *sblock, unsigned int listlen, void **context) +{ + sieve_size_t *end_offset = t_new(sieve_size_t, 1); + + /* Emit byte identifying the type of operand */ + (void) sieve_operand_emit(sblock, NULL, &stringlist_operand); + + /* Give the interpreter an easy way to skip over this string list */ + *end_offset = sieve_binary_emit_offset(sblock, 0); + *context = (void *) end_offset; + + /* Emit the length of the list */ + (void) sieve_binary_emit_unsigned(sblock, listlen); +} + +void sieve_opr_stringlist_emit_item +(struct sieve_binary_block *sblock, void *context ATTR_UNUSED, string_t *item) +{ + (void) sieve_opr_string_emit(sblock, item); +} + +void sieve_opr_stringlist_emit_end +(struct sieve_binary_block *sblock, void *context) +{ + sieve_size_t *end_offset = (sieve_size_t *) context; + + (void) sieve_binary_resolve_offset(sblock, *end_offset); +} + +bool sieve_opr_stringlist_dump_data +(const struct sieve_dumptime_env *denv, struct sieve_operand *oprnd, + sieve_size_t *address, const char *field_name) +{ + if ( oprnd == NULL || oprnd->def == NULL ) + return FALSE; + + oprnd->field_name = field_name; + + if ( oprnd->def->class == &stringlist_class ) { + const struct sieve_opr_stringlist_interface *intf = + (const struct sieve_opr_stringlist_interface *) oprnd->def->interface; + + if ( intf->dump == NULL ) + return FALSE; + + return intf->dump(denv, oprnd, address); + } else if ( oprnd->def->class == &string_class ) { + const struct sieve_opr_string_interface *intf = + (const struct sieve_opr_string_interface *) oprnd->def->interface; + + if ( intf->dump == NULL ) + return FALSE; + + return intf->dump(denv, oprnd, address); + } + + return FALSE; +} + +bool sieve_opr_stringlist_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address, + const char *field_name) +{ + struct sieve_operand operand; + + sieve_code_mark(denv); + + if ( !sieve_operand_read(denv->sblock, address, field_name, &operand) ) { + return FALSE; + } + + return sieve_opr_stringlist_dump_data(denv, &operand, address, field_name); +} + +bool sieve_opr_stringlist_dump_ex +(const struct sieve_dumptime_env *denv, sieve_size_t *address, + const char *field_name, const char *omitted_value) +{ + struct sieve_operand operand; + + sieve_code_mark(denv); + + if ( !sieve_operand_read(denv->sblock, address, field_name, &operand) ) { + return FALSE; + } + + if ( omitted_value != NULL && sieve_operand_is_omitted(&operand) ) { + if ( *omitted_value != '\0' ) + sieve_code_dumpf(denv, "%s: %s", field_name, omitted_value); + return TRUE; + } + + return sieve_opr_stringlist_dump_data(denv, &operand, address, field_name); +} + +int sieve_opr_stringlist_read_data +(const struct sieve_runtime_env *renv, struct sieve_operand *oprnd, + sieve_size_t *address, const char *field_name, + struct sieve_stringlist **strlist_r) +{ + if ( oprnd == NULL || oprnd->def == NULL ) + return SIEVE_EXEC_FAILURE; + + oprnd->field_name = field_name; + + if ( oprnd->def->class == &stringlist_class ) { + const struct sieve_opr_stringlist_interface *intf = + (const struct sieve_opr_stringlist_interface *) oprnd->def->interface; + int ret; + + if ( intf->read == NULL ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "stringlist operand not implemented"); + return SIEVE_EXEC_FAILURE; + } + + if ( (ret=intf->read(renv, oprnd, address, strlist_r)) <= 0 ) + return ret; + + return SIEVE_EXEC_OK; + } else if ( oprnd->def->class == &string_class ) { + /* Special case, accept single string as string list as well. */ + const struct sieve_opr_string_interface *intf = + (const struct sieve_opr_string_interface *) oprnd->def->interface; + int ret; + + if ( intf->read == NULL ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "stringlist string operand not implemented"); + return SIEVE_EXEC_FAILURE; + } + + if ( strlist_r == NULL ) { + if ( (ret=intf->read(renv, oprnd, address, NULL)) <= 0 ) + return ret; + } else { + string_t *stritem; + if ( (ret=intf->read(renv, oprnd, address, &stritem)) <= 0 ) + return ret; + + *strlist_r = sieve_single_stringlist_create + (renv, stritem, FALSE); + } + return SIEVE_EXEC_OK; + } + + sieve_runtime_trace_operand_error(renv, oprnd, + "expected stringlist or string operand but found %s", + sieve_operand_name(oprnd)); + return SIEVE_EXEC_BIN_CORRUPT; +} + +int sieve_opr_stringlist_read +(const struct sieve_runtime_env *renv, sieve_size_t *address, + const char *field_name, struct sieve_stringlist **strlist_r) +{ + struct sieve_operand operand; + int ret; + + if ( (ret=sieve_operand_runtime_read(renv, address, field_name, &operand)) + <= 0 ) + return ret; + + return sieve_opr_stringlist_read_data + (renv, &operand, address, field_name, strlist_r); +} + +int sieve_opr_stringlist_read_ex +(const struct sieve_runtime_env *renv, sieve_size_t *address, + const char *field_name, bool optional, struct sieve_stringlist **strlist_r) +{ + struct sieve_operand operand; + int ret; + + if ( (ret=sieve_operand_runtime_read(renv, address, field_name, &operand)) + <= 0 ) + return ret; + + if ( optional && sieve_operand_is_omitted(&operand) ) { + *strlist_r = NULL; + return 1; + } + + return sieve_opr_stringlist_read_data + (renv, &operand, address, field_name, strlist_r); +} + +static bool opr_stringlist_dump +(const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, + sieve_size_t *address) +{ + sieve_size_t pc = *address; + sieve_size_t end; + unsigned int length = 0; + sieve_offset_t end_offset; + + if ( !sieve_binary_read_offset(denv->sblock, address, &end_offset) ) + return FALSE; + + end = pc + end_offset; + + if ( !sieve_binary_read_unsigned(denv->sblock, address, &length) ) + return FALSE; + + return sieve_code_stringlist_dump + (denv, address, length, end, oprnd->field_name); +} + +static int opr_stringlist_read +(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + sieve_size_t *address, struct sieve_stringlist **strlist_r) +{ + sieve_size_t pc = *address; + sieve_size_t end; + unsigned int length = 0; + sieve_offset_t end_offset; + + if ( !sieve_binary_read_offset(renv->sblock, address, &end_offset) ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "stringlist corrupt: invalid end offset"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + end = pc + end_offset; + + if ( !sieve_binary_read_unsigned(renv->sblock, address, &length) ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "stringlist corrupt: invalid length data"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if ( strlist_r != NULL ) + *strlist_r = sieve_code_stringlist_create + (renv, *address, (unsigned int) length, end); + + /* Skip over the string list for now */ + *address = end; + + return SIEVE_EXEC_OK; +} + +/* Catenated String */ + +void sieve_opr_catenated_string_emit +(struct sieve_binary_block *sblock, unsigned int elements) +{ + (void) sieve_operand_emit(sblock, NULL, &catenated_string_operand); + (void) sieve_binary_emit_unsigned(sblock, elements); +} + +static bool opr_catenated_string_dump +(const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, + sieve_size_t *address) +{ + unsigned int elements = 0; + unsigned int i; + + if ( !sieve_binary_read_unsigned(denv->sblock, address, &elements) ) + return FALSE; + + if ( oprnd->field_name != NULL ) + sieve_code_dumpf(denv, "%s: CAT-STR [%ld]:", + oprnd->field_name, (long) elements); + else + sieve_code_dumpf(denv, "CAT-STR [%ld]:", (long) elements); + + sieve_code_descend(denv); + for ( i = 0; i < (unsigned int) elements; i++ ) { + if ( !sieve_opr_string_dump(denv, address, NULL) ) + return FALSE; + } + sieve_code_ascend(denv); + + return TRUE; +} + +static int opr_catenated_string_read +(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + sieve_size_t *address, string_t **str) +{ + unsigned int elements = 0; + unsigned int i; + int ret; + + if ( !sieve_binary_read_unsigned(renv->sblock, address, &elements) ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "catenated string corrupt: invalid element count data"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + /* Parameter str can be NULL if we are requested to only skip and not + * actually read the argument. + */ + if ( str == NULL ) { + for ( i = 0; i < (unsigned int) elements; i++ ) { + if ( (ret=sieve_opr_string_read(renv, address, NULL, NULL)) <= 0 ) + return ret; + } + } else { + string_t *strelm; + string_t **elm = &strelm; + + *str = t_str_new(128); + for ( i = 0; i < (unsigned int) elements; i++ ) { + + if ( (ret=sieve_opr_string_read(renv, address, NULL, elm)) <= 0 ) + return ret; + + if ( elm != NULL ) { + str_append_str(*str, strelm); + + if ( str_len(*str) > SIEVE_MAX_STRING_LEN ) { + str_truncate(*str, SIEVE_MAX_STRING_LEN); + elm = NULL; + } + } + } + } + + return SIEVE_EXEC_OK; +} + +/* + * Core operations + */ + +/* Forward declarations */ + +static bool opc_jmp_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); + +static int opc_jmp_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); +static int opc_jmptrue_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); +static int opc_jmpfalse_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +/* Operation objects defined in this file */ + +const struct sieve_operation_def sieve_jmp_operation = { + .mnemonic = "JMP", + .code = SIEVE_OPERATION_JMP, + .dump = opc_jmp_dump, + .execute = opc_jmp_execute +}; + +const struct sieve_operation_def sieve_jmptrue_operation = { + .mnemonic = "JMPTRUE", + .code = SIEVE_OPERATION_JMPTRUE, + .dump = opc_jmp_dump, + .execute = opc_jmptrue_execute +}; + +const struct sieve_operation_def sieve_jmpfalse_operation = { + .mnemonic = "JMPFALSE", + .code = SIEVE_OPERATION_JMPFALSE, + .dump = opc_jmp_dump, + .execute = opc_jmpfalse_execute +}; + +/* Operation objects defined in other files */ + +extern const struct sieve_operation_def cmd_stop_operation; +extern const struct sieve_operation_def cmd_keep_operation; +extern const struct sieve_operation_def cmd_discard_operation; +extern const struct sieve_operation_def cmd_redirect_operation; + +extern const struct sieve_operation_def tst_address_operation; +extern const struct sieve_operation_def tst_header_operation; +extern const struct sieve_operation_def tst_exists_operation; +extern const struct sieve_operation_def tst_size_over_operation; +extern const struct sieve_operation_def tst_size_under_operation; + +const struct sieve_operation_def *sieve_operations[] = { + NULL, + + &sieve_jmp_operation, + &sieve_jmptrue_operation, + &sieve_jmpfalse_operation, + + &cmd_stop_operation, + &cmd_keep_operation, + &cmd_discard_operation, + &cmd_redirect_operation, + + &tst_address_operation, + &tst_header_operation, + &tst_exists_operation, + &tst_size_over_operation, + &tst_size_under_operation +}; + +const unsigned int sieve_operation_count = + N_ELEMENTS(sieve_operations); + +/* + * Operation functions + */ + +sieve_size_t sieve_operation_emit +(struct sieve_binary_block *sblock, const struct sieve_extension *ext, + const struct sieve_operation_def *op_def) +{ + sieve_size_t address; + + if ( ext != NULL ) { + i_assert( op_def->ext_def != NULL ); + address = sieve_binary_emit_extension + (sblock, ext, sieve_operation_count); + + sieve_binary_emit_extension_object + (sblock, &op_def->ext_def->operations, op_def->code); + return address; + } + + i_assert( op_def->ext_def == NULL ); + return sieve_binary_emit_byte(sblock, op_def->code); +} + +bool sieve_operation_read +(struct sieve_binary_block *sblock, sieve_size_t *address, + struct sieve_operation *oprtn) +{ + unsigned int code = sieve_operation_count; + + oprtn->address = *address; + oprtn->def = NULL; + oprtn->ext = NULL; + + if ( !sieve_binary_read_extension(sblock, address, &code, &oprtn->ext) ) + return FALSE; + + if ( oprtn->ext == NULL ) { + if ( code < sieve_operation_count ) { + oprtn->def = sieve_operations[code]; + } + + return ( oprtn->def != NULL ); + } + + oprtn->def = (const struct sieve_operation_def *) + sieve_binary_read_extension_object(sblock, address, + &oprtn->ext->def->operations); + + return ( oprtn->def != NULL ); +} + +/* + * Jump operations + */ + +/* Code dump */ + +static bool opc_jmp_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + const struct sieve_operation *oprtn = denv->oprtn; + unsigned int pc = *address; + sieve_offset_t offset; + + if ( sieve_binary_read_offset(denv->sblock, address, &offset) ) + sieve_code_dumpf(denv, "%s %d [%08x]", + sieve_operation_mnemonic(oprtn), offset, pc + offset); + else + return FALSE; + + return TRUE; +} + +/* Code execution */ + +static int opc_jmp_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address ATTR_UNUSED) +{ + return sieve_interpreter_program_jump(renv->interp, TRUE, FALSE); +} + +static int opc_jmptrue_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address ATTR_UNUSED) +{ + bool result = sieve_interpreter_get_test_result(renv->interp); + + sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "jump if result is true"); + sieve_runtime_trace_descend(renv); + + return sieve_interpreter_program_jump(renv->interp, result, FALSE); +} + +static int opc_jmpfalse_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address ATTR_UNUSED) +{ + bool result = sieve_interpreter_get_test_result(renv->interp); + + sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "jump if result is false"); + sieve_runtime_trace_descend(renv); + + return sieve_interpreter_program_jump(renv->interp, !result, FALSE); +} diff --git a/pigeonhole/src/lib-sieve/sieve-code.h b/pigeonhole/src/lib-sieve/sieve-code.h new file mode 100644 index 0000000..00bf68b --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-code.h @@ -0,0 +1,351 @@ +#ifndef SIEVE_CODE_H +#define SIEVE_CODE_H + +#include "lib.h" +#include "buffer.h" +#include "mempool.h" +#include "array.h" + +#include "sieve-common.h" +#include "sieve-runtime.h" +#include "sieve-runtime-trace.h" +#include "sieve-dump.h" + +/* + * Operand object + */ + +struct sieve_operand_class { + const char *name; +}; + +struct sieve_operand_def { + const char *name; + + const struct sieve_extension_def *ext_def; + unsigned int code; + + const struct sieve_operand_class *class; + const void *interface; +}; + +struct sieve_operand { + const struct sieve_operand_def *def; + const struct sieve_extension *ext; + sieve_size_t address; + const char *field_name; +}; + +#define sieve_operand_name(opr) \ + ( (opr)->def == NULL ? "(NULL)" : (opr)->def->name ) +#define sieve_operand_is(opr, definition) \ + ( (opr)->def == &(definition) ) + +sieve_size_t sieve_operand_emit + (struct sieve_binary_block *sblock, const struct sieve_extension *ext, + const struct sieve_operand_def *oprnd); +bool sieve_operand_read + (struct sieve_binary_block *sblock, sieve_size_t *address, + const char *field_name, struct sieve_operand *oprnd); + +static inline int sieve_operand_runtime_read +(const struct sieve_runtime_env *renv, sieve_size_t *address, + const char *field_name, struct sieve_operand *operand) +{ + if ( !sieve_operand_read(renv->sblock, address, field_name, operand) ) { + sieve_runtime_trace_operand_error(renv, operand, "invalid operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + return SIEVE_EXEC_OK; +} + +/* + * Optional operands + */ + +int sieve_opr_optional_next +(struct sieve_binary_block *sblock, sieve_size_t *address, + signed int *opt_code); + +static inline int sieve_opr_optional_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address, + signed int *opt_code) +{ + sieve_size_t pc = *address; + int ret; + + if ( (ret=sieve_opr_optional_next(denv->sblock, address, opt_code)) <= 0 ) + return ret; + + sieve_code_mark_specific(denv, pc); + return ret; +} + +static inline int sieve_opr_optional_read +(const struct sieve_runtime_env *renv, sieve_size_t *address, + signed int *opt_code) +{ + int ret; + + if ( (ret=sieve_opr_optional_next(renv->sblock, address, opt_code)) < 0 ) + sieve_runtime_trace_error(renv, "invalid optional operand code"); + + return ret; +} + +/* + * Core operands + */ + +/* Operand codes */ + +enum sieve_core_operand { + SIEVE_OPERAND_OPTIONAL = 0x00, + SIEVE_OPERAND_NUMBER, + SIEVE_OPERAND_STRING, + SIEVE_OPERAND_STRING_LIST, + SIEVE_OPERAND_COMPARATOR, + SIEVE_OPERAND_MATCH_TYPE, + SIEVE_OPERAND_ADDRESS_PART, + SIEVE_OPERAND_CATENATED_STRING, + + SIEVE_OPERAND_CUSTOM +}; + +/* Operand classes */ + +extern const struct sieve_operand_class number_class; +extern const struct sieve_operand_class string_class; +extern const struct sieve_operand_class stringlist_class; + +/* Operand objects */ + +extern const struct sieve_operand_def omitted_operand; +extern const struct sieve_operand_def number_operand; +extern const struct sieve_operand_def string_operand; +extern const struct sieve_operand_def stringlist_operand; +extern const struct sieve_operand_def catenated_string_operand; + +extern const struct sieve_operand_def *sieve_operands[]; +extern const unsigned int sieve_operand_count; + +/* Operand object interfaces */ + +struct sieve_opr_number_interface { + bool (*dump) + (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, + sieve_size_t *address); + int (*read) + (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + sieve_size_t *address, sieve_number_t *number_r); +}; + +struct sieve_opr_string_interface { + bool (*dump) + (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, + sieve_size_t *address); + int (*read) + (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + sieve_size_t *address, string_t **str_r); +}; + +struct sieve_opr_stringlist_interface { + bool (*dump) + (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, + sieve_size_t *address); + int (*read) + (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + sieve_size_t *address, struct sieve_stringlist **strlist_r); +}; + +/* + * Core operand functions + */ + +/* Omitted */ + +void sieve_opr_omitted_emit(struct sieve_binary_block *sblock); + +static inline bool sieve_operand_is_omitted +(const struct sieve_operand *operand) +{ + return ( operand != NULL && operand->def != NULL && + operand->def == &omitted_operand ); +} + +/* Number */ + +void sieve_opr_number_emit + (struct sieve_binary_block *sblock, sieve_number_t number); +bool sieve_opr_number_dump_data + (const struct sieve_dumptime_env *denv, struct sieve_operand *operand, + sieve_size_t *address, const char *field_name); +bool sieve_opr_number_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address, + const char *field_name); +int sieve_opr_number_read_data + (const struct sieve_runtime_env *renv, struct sieve_operand *operand, + sieve_size_t *address, const char *field_name, sieve_number_t *number_r); +int sieve_opr_number_read + (const struct sieve_runtime_env *renv, sieve_size_t *address, + const char *field_name, sieve_number_t *number_r); + +static inline bool sieve_operand_is_number +(const struct sieve_operand *operand) +{ + return ( operand != NULL && operand->def != NULL && + operand->def->class == &number_class ); +} + +/* String */ + +void sieve_opr_string_emit + (struct sieve_binary_block *sblock, string_t *str); +bool sieve_opr_string_dump_data + (const struct sieve_dumptime_env *denv, struct sieve_operand *operand, + sieve_size_t *address, const char *field_name); +bool sieve_opr_string_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address, + const char *field_name); +bool sieve_opr_string_dump_ex + (const struct sieve_dumptime_env *denv, sieve_size_t *address, + const char *field_name, const char *omitted_value); +int sieve_opr_string_read_data + (const struct sieve_runtime_env *renv, struct sieve_operand *operand, + sieve_size_t *address, const char *field_name, string_t **str_r); +int sieve_opr_string_read + (const struct sieve_runtime_env *renv, sieve_size_t *address, + const char *field_name, string_t **str_r); +int sieve_opr_string_read_ex + (const struct sieve_runtime_env *renv, sieve_size_t *address, + const char *field_name, bool optional, string_t **str_r, bool *literal_r); + +static inline bool sieve_operand_is_string +(const struct sieve_operand *operand) +{ + return ( operand != NULL && operand->def != NULL && + operand->def->class == &string_class ); +} + +static inline bool sieve_operand_is_string_literal +(const struct sieve_operand *operand) +{ + return ( operand != NULL && sieve_operand_is(operand, string_operand) ); +} + +/* String list */ + +void sieve_opr_stringlist_emit_start + (struct sieve_binary_block *sblock, unsigned int listlen, void **context); +void sieve_opr_stringlist_emit_item + (struct sieve_binary_block *sblock, void *context ATTR_UNUSED, + string_t *item); +void sieve_opr_stringlist_emit_end + (struct sieve_binary_block *sblock, void *context); +bool sieve_opr_stringlist_dump_data + (const struct sieve_dumptime_env *denv, struct sieve_operand *operand, + sieve_size_t *address, const char *field_name); +bool sieve_opr_stringlist_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address, + const char *field_name); +bool sieve_opr_stringlist_dump_ex + (const struct sieve_dumptime_env *denv, sieve_size_t *address, + const char *field_name, const char *omitted_value); +int sieve_opr_stringlist_read_data + (const struct sieve_runtime_env *renv, struct sieve_operand *operand, + sieve_size_t *address, const char *field_name, + struct sieve_stringlist **strlist_r); +int sieve_opr_stringlist_read + (const struct sieve_runtime_env *renv, sieve_size_t *address, + const char *field_name, struct sieve_stringlist **strlist_r); +int sieve_opr_stringlist_read_ex + (const struct sieve_runtime_env *renv, sieve_size_t *address, + const char *field_name, bool optional, struct sieve_stringlist **strlist_r); + +static inline bool sieve_operand_is_stringlist +(const struct sieve_operand *operand) +{ + return ( operand != NULL && operand->def != NULL && + (operand->def->class == &stringlist_class || + operand->def->class == &string_class) ); +} + +/* Catenated string */ + +void sieve_opr_catenated_string_emit + (struct sieve_binary_block *sblock, unsigned int elements); + +/* + * Operation object + */ + +struct sieve_operation_def { + const char *mnemonic; + + const struct sieve_extension_def *ext_def; + unsigned int code; + + bool (*dump) + (const struct sieve_dumptime_env *denv, sieve_size_t *address); + int (*execute) + (const struct sieve_runtime_env *renv, sieve_size_t *address); +}; + +struct sieve_operation { + const struct sieve_operation_def *def; + const struct sieve_extension *ext; + + sieve_size_t address; +}; + +#define sieve_operation_is(oprtn, definition) \ + ( (oprtn)->def == &(definition) ) +#define sieve_operation_mnemonic(oprtn) \ + ( (oprtn)->def == NULL ? "(NULL)" : (oprtn)->def->mnemonic ) + +sieve_size_t sieve_operation_emit + (struct sieve_binary_block *sblock, const struct sieve_extension *ext, + const struct sieve_operation_def *op_def); +bool sieve_operation_read + (struct sieve_binary_block *sblock, sieve_size_t *address, + struct sieve_operation *oprtn); +const char *sieve_operation_read_string + (struct sieve_binary_block *sblock, sieve_size_t *address); + +/* + * Core operations + */ + +/* Opcodes */ + +enum sieve_operation_code { + SIEVE_OPERATION_INVALID, + SIEVE_OPERATION_JMP, + SIEVE_OPERATION_JMPTRUE, + SIEVE_OPERATION_JMPFALSE, + + SIEVE_OPERATION_STOP, + SIEVE_OPERATION_KEEP, + SIEVE_OPERATION_DISCARD, + SIEVE_OPERATION_REDIRECT, + + SIEVE_OPERATION_ADDRESS, + SIEVE_OPERATION_HEADER, + SIEVE_OPERATION_EXISTS, + SIEVE_OPERATION_SIZE_OVER, + SIEVE_OPERATION_SIZE_UNDER, + + SIEVE_OPERATION_CUSTOM +}; + +/* Operation objects */ + +extern const struct sieve_operation_def sieve_jmp_operation; +extern const struct sieve_operation_def sieve_jmptrue_operation; +extern const struct sieve_operation_def sieve_jmpfalse_operation; + +extern const struct sieve_operation_def *sieve_operations[]; +extern const unsigned int sieve_operations_count; + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-commands.c b/pigeonhole/src/lib-sieve/sieve-commands.c new file mode 100644 index 0000000..324b66d --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-commands.c @@ -0,0 +1,403 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "str-sanitize.h" + +#include "rfc2822.h" + +#include "sieve-common.h" +#include "sieve-ast.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-binary.h" +#include "sieve-commands.h" +#include "sieve-code.h" +#include "sieve-interpreter.h" + +/* + * Literal arguments + */ + +/* Forward declarations */ + +static bool arg_number_generate + (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *context); +static bool arg_string_generate + (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *context); +static bool arg_string_list_validate + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *context); +static bool arg_string_list_generate + (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *context); + +/* Argument objects */ + +const struct sieve_argument_def number_argument = { + .identifier = "@number", + .generate = arg_number_generate +}; + +const struct sieve_argument_def string_argument = { + .identifier = "@string", + .generate = arg_string_generate +}; + +const struct sieve_argument_def string_list_argument = { + .identifier = "@string-list", + .validate = arg_string_list_validate, + .generate = arg_string_list_generate +}; + +/* Argument implementations */ + +static bool arg_number_generate +(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *cmd ATTR_UNUSED) +{ + sieve_opr_number_emit(cgenv->sblock, sieve_ast_argument_number(arg)); + + return TRUE; +} + +static bool arg_string_generate +(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *cmd ATTR_UNUSED) +{ + sieve_opr_string_emit(cgenv->sblock, sieve_ast_argument_str(arg)); + + return TRUE; +} + +static bool arg_string_list_validate +(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *stritem; + + stritem = sieve_ast_strlist_first(*arg); + while ( stritem != NULL ) { + if ( !sieve_validator_argument_activate(valdtr, cmd, stritem, FALSE) ) + return FALSE; + + stritem = sieve_ast_strlist_next(stritem); + } + + return TRUE; +} + +static bool emit_string_list_operand +(const struct sieve_codegen_env *cgenv, const struct sieve_ast_argument *strlist, + struct sieve_command *cmd) +{ + void *list_context; + struct sieve_ast_argument *stritem; + + sieve_opr_stringlist_emit_start + (cgenv->sblock, sieve_ast_strlist_count(strlist), &list_context); + + stritem = sieve_ast_strlist_first(strlist); + while ( stritem != NULL ) { + if ( !sieve_generate_argument(cgenv, stritem, cmd) ) + return FALSE; + + stritem = sieve_ast_strlist_next(stritem); + } + + sieve_opr_stringlist_emit_end(cgenv->sblock, list_context); + + return TRUE; +} + +static bool arg_string_list_generate +(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *cmd) +{ + if ( sieve_ast_argument_type(arg) == SAAT_STRING ) { + return ( sieve_generate_argument(cgenv, arg, cmd) ); + + } else if ( sieve_ast_argument_type(arg) == SAAT_STRING_LIST ) { + bool result = TRUE; + + if ( sieve_ast_strlist_count(arg) == 1 ) + return ( sieve_generate_argument + (cgenv, sieve_ast_strlist_first(arg), cmd) ); + else { + T_BEGIN { + result=emit_string_list_operand(cgenv, arg, cmd); + } T_END; + } + + return result; + } + + return FALSE; +} + +/* + * Abstract arguments + * + * (Generated by processing and not by parsing the grammar) + */ + +/* Catenated string */ + +struct sieve_arg_catenated_string { + struct sieve_ast_arg_list *str_parts; +}; + +struct sieve_arg_catenated_string *sieve_arg_catenated_string_create +(struct sieve_ast_argument *orig_arg) +{ + pool_t pool = sieve_ast_pool(orig_arg->ast); + struct sieve_ast_arg_list *arglist; + struct sieve_arg_catenated_string *catstr; + + arglist = sieve_ast_arg_list_create(pool); + + catstr = p_new(pool, struct sieve_arg_catenated_string, 1); + catstr->str_parts = arglist; + (orig_arg)->argument->data = (void *) catstr; + + return catstr; +} + +void sieve_arg_catenated_string_add_element +(struct sieve_arg_catenated_string *catstr, + struct sieve_ast_argument *element) +{ + sieve_ast_arg_list_add(catstr->str_parts, element); +} + +#define _cat_string_first(catstr) __AST_LIST_FIRST((catstr)->str_parts) +#define _cat_string_count(catstr) __AST_LIST_COUNT((catstr)->str_parts) +#define _cat_string_next(item) __AST_LIST_NEXT(item) + +bool sieve_arg_catenated_string_generate +(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *cmd) +{ + struct sieve_arg_catenated_string *catstr = + (struct sieve_arg_catenated_string *) arg->argument->data; + struct sieve_ast_argument *strpart; + + if ( _cat_string_count(catstr) == 1 ) + sieve_generate_argument(cgenv, _cat_string_first(catstr), cmd); + else { + sieve_opr_catenated_string_emit(cgenv->sblock, _cat_string_count(catstr)); + + strpart = _cat_string_first(catstr); + while ( strpart != NULL ) { + if ( !sieve_generate_argument(cgenv, strpart, cmd) ) + return FALSE; + + strpart = _cat_string_next(strpart); + } + } + + return TRUE; +} + +/* + * Argument creation + */ + +struct sieve_argument *sieve_argument_create +(struct sieve_ast *ast, const struct sieve_argument_def *def, + const struct sieve_extension *ext, int id_code) +{ + struct sieve_argument *arg; + pool_t pool; + + pool = sieve_ast_pool(ast); + arg = p_new(pool, struct sieve_argument, 1); + arg->def = def; + arg->ext = ext; + arg->id_code = id_code; + + return arg; +} + +/* + * Core tests and commands + */ + +const struct sieve_command_def *sieve_core_tests[] = { + &tst_false, &tst_true, + &tst_not, &tst_anyof, &tst_allof, + &tst_address, &tst_header, &tst_exists, &tst_size +}; + +const unsigned int sieve_core_tests_count = N_ELEMENTS(sieve_core_tests); + +const struct sieve_command_def *sieve_core_commands[] = { + &cmd_require, + &cmd_stop, &cmd_if, &cmd_elsif, &cmd_else, + &cmd_keep, &cmd_discard, &cmd_redirect +}; + +const unsigned int sieve_core_commands_count = N_ELEMENTS(sieve_core_commands); + +/* + * Command context + */ + +struct sieve_command *sieve_command_prev +(struct sieve_command *cmd) +{ + struct sieve_ast_node *node = sieve_ast_node_prev(cmd->ast_node); + + if ( node != NULL ) { + return node->command; + } + + return NULL; +} + +struct sieve_command *sieve_command_parent +(struct sieve_command *cmd) +{ + struct sieve_ast_node *node = sieve_ast_node_parent(cmd->ast_node); + + return ( node != NULL ? node->command : NULL ); +} + +struct sieve_command *sieve_command_create +(struct sieve_ast_node *cmd_node, const struct sieve_extension *ext, + const struct sieve_command_def *cmd_def, + struct sieve_command_registration *cmd_reg) +{ + struct sieve_command *cmd; + + cmd = p_new(sieve_ast_node_pool(cmd_node), struct sieve_command, 1); + + cmd->ast_node = cmd_node; + cmd->def = cmd_def; + cmd->ext = ext; + cmd->reg = cmd_reg; + + cmd->block_exit_command = NULL; + + return cmd; +} + +const char *sieve_command_def_type_name +(const struct sieve_command_def *cmd_def) +{ + switch ( cmd_def->type ) { + case SCT_NONE: return "command of unspecified type (bug)"; + case SCT_TEST: return "test"; + case SCT_COMMAND: return "command"; + case SCT_HYBRID: return "command or test"; + default: + break; + } + return "??COMMAND-TYPE??"; +} + +const char *sieve_command_type_name + (const struct sieve_command *cmd) +{ + switch ( cmd->def->type ) { + case SCT_NONE: return "command of unspecified type (bug)"; + case SCT_TEST: return "test"; + case SCT_COMMAND: return "command"; + case SCT_HYBRID: + if ( cmd->ast_node->type == SAT_TEST ) + return "test"; + return "command"; + default: + break; + } + return "??COMMAND-TYPE??"; +} + +struct sieve_ast_argument *sieve_command_add_dynamic_tag +(struct sieve_command *cmd, const struct sieve_extension *ext, + const struct sieve_argument_def *tag, int id_code) +{ + struct sieve_ast_argument *arg; + + if ( cmd->first_positional != NULL ) + arg = sieve_ast_argument_tag_insert + (cmd->first_positional, tag->identifier, cmd->ast_node->source_line); + else + arg = sieve_ast_argument_tag_create + (cmd->ast_node, tag->identifier, cmd->ast_node->source_line); + + arg->argument = sieve_argument_create(cmd->ast_node->ast, tag, ext, id_code); + + return arg; +} + +struct sieve_ast_argument *sieve_command_find_argument +(struct sieve_command *cmd, const struct sieve_argument_def *arg_def) +{ + struct sieve_ast_argument *arg = sieve_ast_argument_first(cmd->ast_node); + + /* Visit tagged and optional arguments */ + while ( arg != NULL ) { + if ( arg->argument != NULL && arg->argument->def == arg_def ) + return arg; + + arg = sieve_ast_argument_next(arg); + } + + return arg; +} + +/* Use this function with caution. The command commits to exiting the block. + * When it for some reason does not, the interpretation will break later on, + * because exiting jumps are not generated when they would otherwise be + * necessary. + */ +void sieve_command_exit_block_unconditionally + (struct sieve_command *cmd) +{ + struct sieve_command *parent = sieve_command_parent(cmd); + + /* Only the first unconditional exit is of importance */ + if ( parent != NULL && parent->block_exit_command == NULL ) + parent->block_exit_command = cmd; +} + +bool sieve_command_block_exits_unconditionally + (struct sieve_command *cmd) +{ + return ( cmd->block_exit_command != NULL ); +} + +/* + * Command utility functions + */ + +/* NOTE: this may be moved */ + +static int _verify_header_name_item +(void *context, struct sieve_ast_argument *header) +{ + struct sieve_validator *valdtr = (struct sieve_validator *) context; + string_t *name = sieve_ast_argument_str(header); + + if ( sieve_argument_is_string_literal(header) && + !rfc2822_header_field_name_verify(str_c(name), str_len(name)) ) { + sieve_argument_validate_warning + (valdtr, header, "specified header field name '%s' is invalid", + str_sanitize(str_c(name), 80)); + + return 0; + } + + return 1; +} + +bool sieve_command_verify_headers_argument +(struct sieve_validator *valdtr, struct sieve_ast_argument *headers) +{ + return ( sieve_ast_stringlist_map + (&headers, (void *) valdtr, _verify_header_name_item) >= 0 ); +} diff --git a/pigeonhole/src/lib-sieve/sieve-commands.h b/pigeonhole/src/lib-sieve/sieve-commands.h new file mode 100644 index 0000000..f9b83e3 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-commands.h @@ -0,0 +1,286 @@ +#ifndef SIEVE_COMMANDS_H +#define SIEVE_COMMANDS_H + +#include "lib.h" + +#include "sieve-common.h" +#include "sieve-ast.h" + +/* + * Argument definition + */ + +enum sieve_argument_flag { + /* More than one of this (type of) tagged argument is allowed */ + SIEVE_ARGUMENT_FLAG_MULTIPLE = (1 << 0) +}; + +struct sieve_argument_def { + const char *identifier; + enum sieve_argument_flag flags; + + bool (*is_instance_of) + (struct sieve_validator *valdtr, struct sieve_command *cmd, + const struct sieve_extension *ext, const char *identifier, void **data); + + bool (*validate) + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd); + bool (*validate_context) + (struct sieve_validator *valdtr, struct sieve_ast_argument *arg, + struct sieve_command *cmd); + bool (*validate_persistent) + (struct sieve_validator *valdtr, struct sieve_command *cmd, + const struct sieve_extension *ext); + + bool (*generate) + (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *cmd); +}; + +/* + * Argument instance + */ + +struct sieve_argument { + const struct sieve_argument_def *def; + const struct sieve_extension *ext; + int id_code; + + /* Context data */ + void *data; +}; + +#define sieve_argument_is(ast_arg, definition) \ + ( (ast_arg)->argument->def == &(definition) ) +#define sieve_argument_ext(ast_arg) \ + ( (ast_arg)->argument->ext ) +#define sieve_argument_identifier(ast_arg) \ + ( (ast_arg)->argument->def->identifier ) + +/* Utility macros */ + +#define sieve_argument_is_string_literal(arg) \ + ( (arg)->argument->def == &string_argument ) + +/* Error handling */ + +#define sieve_argument_validate_error(validator, arg_node, ...) \ + sieve_validator_error(validator, \ + ((arg_node) == NULL ? 0 : (arg_node)->source_line), \ + __VA_ARGS__) +#define sieve_argument_validate_warning(validator, arg_node, ...) \ + sieve_validator_warning(validator, \ + ((arg_node) == NULL ? 0 : (arg_node)->source_line), \ + __VA_ARGS__) + +#define sieve_argument_generate_error(gentr, arg_node, ...) \ + sieve_generator_error(gentr, \ + ((arg_node) == NULL ? 0 : (arg_node)->source_line), \ + __VA_ARGS__) +#define sieve_argument_generate_warning(gentr, arg_node, ...) \ + sieve_generator_warning(gentr, \ + ((arg_node) == NULL ? 0 : (arg_node)->source_line), \ + __VA_ARGS__) + +/* Argument API */ + +struct sieve_argument *sieve_argument_create + (struct sieve_ast *ast, const struct sieve_argument_def *def, + const struct sieve_extension *ext, int id_code); + +/* Literal arguments */ + +extern const struct sieve_argument_def number_argument; +extern const struct sieve_argument_def string_argument; +extern const struct sieve_argument_def string_list_argument; + +/* Catenated string argument */ + +bool sieve_arg_catenated_string_generate + (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *context); + +struct sieve_arg_catenated_string; + +struct sieve_arg_catenated_string *sieve_arg_catenated_string_create + (struct sieve_ast_argument *orig_arg); +void sieve_arg_catenated_string_add_element + (struct sieve_arg_catenated_string *strdata, + struct sieve_ast_argument *element); + +/* + * Command definition + */ + +enum sieve_command_type { + SCT_NONE, + SCT_COMMAND, + SCT_TEST, + SCT_HYBRID +}; + +struct sieve_command_def { + const char *identifier; + enum sieve_command_type type; + + /* High-level command syntax */ + int positional_args; + int subtests; + bool block_allowed; + bool block_required; + + bool (*registered) + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); + bool (*pre_validate) + (struct sieve_validator *valdtr, struct sieve_command *cmd); + bool (*validate) + (struct sieve_validator *valdtr, struct sieve_command *cmd); + bool (*validate_const) + (struct sieve_validator *valdtr, struct sieve_command *cmd, + int *const_current, int const_next); + bool (*generate) + (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd); + bool (*control_generate) + (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd, + struct sieve_jumplist *jumps, bool jump_true); +}; + +/* + * Command instance + */ + +struct sieve_command { + const struct sieve_command_def *def; + const struct sieve_extension *ext; + + /* The registration of this command in the validator (sieve-validator.h) */ + struct sieve_command_registration *reg; + + /* The ast node of this command */ + struct sieve_ast_node *ast_node; + + /* First positional argument, found during argument validation */ + struct sieve_ast_argument *first_positional; + + /* The child ast node that unconditionally exits this command's block */ + struct sieve_command *block_exit_command; + + /* Context data*/ + void *data; +}; + +#define sieve_command_is(cmd, definition) \ + ( (cmd)->def == &(definition) ) +#define sieve_command_identifier(cmd) \ + ( (cmd)->def->identifier ) + +#define sieve_commands_equal(cmd1, cmd2) \ + ( (cmd1) != NULL && (cmd2) != NULL && (cmd1)->def == (cmd2)->def ) + +/* Context API */ + +struct sieve_command *sieve_command_create + (struct sieve_ast_node *cmd_node, const struct sieve_extension *ext, + const struct sieve_command_def *cmd_def, + struct sieve_command_registration *cmd_reg); + +const char *sieve_command_def_type_name + (const struct sieve_command_def *cmd_def); +const char *sieve_command_type_name + (const struct sieve_command *cmd); + +struct sieve_command *sieve_command_prev + (struct sieve_command *cmd); +struct sieve_command *sieve_command_parent + (struct sieve_command *cmd); + +struct sieve_ast_argument *sieve_command_add_dynamic_tag + (struct sieve_command *cmd, const struct sieve_extension *ext, + const struct sieve_argument_def *tag, int id_code); +struct sieve_ast_argument *sieve_command_find_argument + (struct sieve_command *cmd, const struct sieve_argument_def *argument); + +void sieve_command_exit_block_unconditionally + (struct sieve_command *cmd); +bool sieve_command_block_exits_unconditionally + (struct sieve_command *cmd); + +/* Error handling */ + +#define sieve_command_validate_error(validator, context, ...) \ + sieve_validator_error(validator, \ + ((context) == NULL ? 0 : (context)->ast_node->source_line), \ + __VA_ARGS__) +#define sieve_command_validate_warning(validator, context, ...) \ + sieve_validator_warning(validator, \ + ((context) == NULL ? 0 : (context)->ast_node->source_line), \ + __VA_ARGS__) + +#define sieve_command_generate_error(gentr, context, ...) \ + sieve_generator_error(gentr, \ + ((context) == NULL ? 0 : (context)->ast_node->source_line), \ + __VA_ARGS__) +#define sieve_command_generate_warning(gentr, context, ...) \ + sieve_generator_warning(gentr, \ + ((context) == NULL ? 0 : (context)->ast_node->source_line), \ + __VA_ARGS__) + +/* Utility macros */ + +#define sieve_command_pool(context) \ + sieve_ast_node_pool((context)->ast_node) + +#define sieve_command_source_line(context) \ + (context)->ast_node->source_line + +#define sieve_command_first_argument(context) \ + sieve_ast_argument_first((context)->ast_node) + +#define sieve_command_is_toplevel(context) \ + ( sieve_ast_node_type(sieve_ast_node_parent((context)->ast_node)) == SAT_ROOT ) +#define sieve_command_is_first(context) \ + ( sieve_ast_node_prev((context)->ast_node) == NULL ) + +/* + * Core commands + */ + +extern const struct sieve_command_def cmd_require; +extern const struct sieve_command_def cmd_stop; +extern const struct sieve_command_def cmd_if; +extern const struct sieve_command_def cmd_elsif; +extern const struct sieve_command_def cmd_else; +extern const struct sieve_command_def cmd_redirect; +extern const struct sieve_command_def cmd_keep; +extern const struct sieve_command_def cmd_discard; + +extern const struct sieve_command_def *sieve_core_commands[]; +extern const unsigned int sieve_core_commands_count; + +/* + * Core tests + */ + +extern const struct sieve_command_def tst_true; +extern const struct sieve_command_def tst_false; +extern const struct sieve_command_def tst_not; +extern const struct sieve_command_def tst_anyof; +extern const struct sieve_command_def tst_allof; +extern const struct sieve_command_def tst_address; +extern const struct sieve_command_def tst_header; +extern const struct sieve_command_def tst_exists; +extern const struct sieve_command_def tst_size; + +extern const struct sieve_command_def *sieve_core_tests[]; +extern const unsigned int sieve_core_tests_count; + +/* + * Command utility functions + */ + +bool sieve_command_verify_headers_argument +(struct sieve_validator *valdtr, struct sieve_ast_argument *headers); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-common.h b/pigeonhole/src/lib-sieve/sieve-common.h new file mode 100644 index 0000000..e79fb4d --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-common.h @@ -0,0 +1,240 @@ +#ifndef SIEVE_COMMON_H +#define SIEVE_COMMON_H + +#include "lib.h" + +#include "sieve.h" + +#include <sys/types.h> + +/* + * Types + */ + +typedef size_t sieve_size_t; +typedef uint32_t sieve_offset_t; +typedef uint64_t sieve_number_t; + +#define SIEVE_MAX_NUMBER ((sieve_number_t)-1) +#define SIEVE_PRI_NUMBER PRIu64 + +/* + * Forward declarations + */ + +/* sieve-error.h */ +struct sieve_error_handler; + +/* sieve-ast.h */ +enum sieve_ast_argument_type; + +struct sieve_ast; +struct sieve_ast_node; +struct sieve_ast_argument; + +/* sieve-commands.h */ +struct sieve_argument; +struct sieve_argument_def; +struct sieve_command; +struct sieve_command_def; +struct sieve_command_context; +struct sieve_command_registration; + +/* sieve-stringlist.h */ +struct sieve_stringlist; + +/* sieve-code.h */ +struct sieve_operation_extension; + +/* sieve-lexer.h */ +struct sieve_lexer; + +/* sieve-parser.h */ +struct sieve_parser; + +/* sieve-validator.h */ +struct sieve_validator; + +/* sieve-generator.h */ +struct sieve_jumplist; +struct sieve_generator; +struct sieve_codegen_env; + +/* sieve-runtime.h */ +struct sieve_runtime_env; + +/* sieve-interpreter.h */ +struct sieve_interpreter; + +/* sieve-dump.h */ +struct sieve_dumptime_env; + +/* sieve-binary-dumper.h */ +struct sieve_binary_dumper; + +/* sieve-code-dumper.h */ +struct sieve_code_dumper; + +/* sieve-extension.h */ +struct sieve_extension; +struct sieve_extension_def; +struct sieve_extension_objects; + +/* sieve-code.h */ +struct sieve_operand; +struct sieve_operand_def; +struct sieve_operand_class; +struct sieve_operation; +struct sieve_coded_stringlist; + +/* sieve-binary.h */ +struct sieve_binary; +struct sieve_binary_block; +struct sieve_binary_debug_writer; +struct sieve_binary_debug_reader; + +/* sieve-execute.h */ +struct sieve_execute; + +/* sieve-objects.h */ +struct sieve_object_def; +struct sieve_object; + +/* sieve-comparator.h */ +struct sieve_comparator; + +/* sieve-match-types.h */ +struct sieve_match_type; + +/* sieve-match.h */ +struct sieve_match_context; + +/* sieve-address.h */ +struct sieve_address_list; + +/* sieve-address-parts.h */ +struct sieve_address_part_def; +struct sieve_address_part; + +/* sieve-result.h */ +struct sieve_result; +struct sieve_side_effects_list; +struct sieve_result_print_env; + +/* sieve-actions.h */ +struct sieve_action_exec_env; +struct sieve_action; +struct sieve_action_def; +struct sieve_side_effect; +struct sieve_side_effect_def; + +/* sieve-script.h */ +struct sieve_script; +struct sieve_script_sequence; + +/* sieve-storage.h */ +struct sieve_storage_class_registry; +struct sieve_storage; + +/* sieve-message.h */ +struct sieve_message_context; +struct sieve_message_override; +struct sieve_message_override_def; + +/* sieve-plugins.h */ +struct sieve_plugin; + +/* sieve.c */ +struct sieve_ast *sieve_parse + (struct sieve_script *script, struct sieve_error_handler *ehandler, + enum sieve_error *error_r); +bool sieve_validate + (struct sieve_ast *ast, struct sieve_error_handler *ehandler, + enum sieve_compile_flags flags, enum sieve_error *error_r); + +/* + * Parent category + */ + +extern struct event_category event_category_sieve; + +/* + * Sieve engine instance + */ + +#include "sieve-address-source.h" + +struct sieve_instance { + /* Main engine pool */ + pool_t pool; + + /* System environment */ + const char *hostname; + const char *domainname; + const char *base_dir; + const char *temp_dir; + + /* User environment */ + const char *username; + const char *home_dir; + + /* Flags */ + enum sieve_flag flags; + + /* Callbacks */ + const struct sieve_callbacks *callbacks; + void *context; + + /* Logging, events, and debug */ + struct event *event; + bool debug; + + /* Extension registry */ + struct sieve_extension_registry *ext_reg; + + /* Storage class registry */ + struct sieve_storage_class_registry *storage_reg; + + /* Plugin modules */ + struct sieve_plugin *plugins; + enum sieve_env_location env_location; + enum sieve_delivery_phase delivery_phase; + + /* Settings */ + size_t max_script_size; + unsigned int max_actions; + unsigned int max_redirects; + unsigned int max_cpu_time_secs; + unsigned int resource_usage_timeout_secs; + const struct smtp_address *user_email, *user_email_implicit; + struct sieve_address_source redirect_from; + unsigned int redirect_duplicate_period; +}; + +/* + * Script trace log + */ + +void sieve_trace_log_write_line + (struct sieve_trace_log *trace_log, const string_t *line) + ATTR_NULL(2); + +/* + * User e-mail address + */ + +const struct smtp_address *sieve_get_user_email + (struct sieve_instance *svinst); + +/* + * Postmaster address + */ + +const struct message_address * +sieve_get_postmaster(const struct sieve_script_env *senv); +const struct smtp_address * +sieve_get_postmaster_smtp(const struct sieve_script_env *senv); +const char * +sieve_get_postmaster_address(const struct sieve_script_env *senv); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-comparators.c b/pigeonhole/src/lib-sieve/sieve-comparators.c new file mode 100644 index 0000000..56c33ab --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-comparators.c @@ -0,0 +1,260 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str-sanitize.h" +#include "hash.h" +#include "array.h" + +#include "sieve-extensions.h" +#include "sieve-code.h" +#include "sieve-commands.h" +#include "sieve-binary.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "sieve-comparators.h" + +#include <string.h> +#include <stdio.h> + +/* + * Core comparators + */ + +const struct sieve_comparator_def *sieve_core_comparators[] = { + &i_octet_comparator, &i_ascii_casemap_comparator +}; + +const unsigned int sieve_core_comparators_count = + N_ELEMENTS(sieve_core_comparators); + +/* + * Comparator 'extension' + */ + +static bool cmp_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); + +const struct sieve_extension_def comparator_extension = { + .name = "@comparators", + .validator_load = cmp_validator_load +}; + +/* + * Validator context: + * name-based comparator registry. + */ + +static struct sieve_validator_object_registry *_get_object_registry +(struct sieve_validator *valdtr) +{ + struct sieve_instance *svinst; + const struct sieve_extension *mcht_ext; + + svinst = sieve_validator_svinst(valdtr); + mcht_ext = sieve_get_comparator_extension(svinst); + return sieve_validator_object_registry_get(valdtr, mcht_ext); +} + +void sieve_comparator_register +(struct sieve_validator *valdtr, const struct sieve_extension *ext, + const struct sieve_comparator_def *cmp) +{ + struct sieve_validator_object_registry *regs = _get_object_registry(valdtr); + + sieve_validator_object_registry_add(regs, ext, &cmp->obj_def); +} + +static struct sieve_comparator *sieve_comparator_create +(struct sieve_validator *valdtr, struct sieve_command *cmd, + const char *identifier) +{ + struct sieve_validator_object_registry *regs = _get_object_registry(valdtr); + struct sieve_object object; + struct sieve_comparator *cmp; + + if ( !sieve_validator_object_registry_find(regs, identifier, &object) ) + return NULL; + + cmp = p_new(sieve_command_pool(cmd), struct sieve_comparator, 1); + cmp->object = object; + cmp->def = (const struct sieve_comparator_def *) object.def; + + return cmp; +} + +bool cmp_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + struct sieve_validator_object_registry *regs = + sieve_validator_object_registry_init(valdtr, ext); + unsigned int i; + + /* Register core comparators */ + for ( i = 0; i < sieve_core_comparators_count; i++ ) { + sieve_validator_object_registry_add + (regs, NULL, &(sieve_core_comparators[i]->obj_def)); + } + + return TRUE; +} + +/* + * Comparator tagged argument + */ + +/* Forward declarations */ + +static bool tag_comparator_validate + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd); +static bool tag_comparator_generate + (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *cmd); + +/* Argument object */ + +const struct sieve_argument_def comparator_tag = { + .identifier = "comparator", + .validate = tag_comparator_validate, + .generate = tag_comparator_generate +}; + +/* Argument implementation */ + +static bool tag_comparator_validate +(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + const struct sieve_comparator *cmp; + + /* Skip tag */ + *arg = sieve_ast_argument_next(*arg); + + /* Check syntax: + * ":comparator" <comparator-name: string> + */ + if ( !sieve_validate_tag_parameter + (valdtr, cmd, tag, *arg, NULL, 0, SAAT_STRING, FALSE) ) { + return FALSE; + } + + /* FIXME: We can currently only handle string literal argument, so + * variables are not allowed. + */ + if ( !sieve_argument_is_string_literal(*arg) ) { + sieve_argument_validate_error(valdtr, *arg, + "this Sieve implementation currently only supports " + "a literal string argument for the :comparator tag"); + return FALSE; + } + + /* Get comparator from registry */ + cmp = sieve_comparator_create(valdtr, cmd, sieve_ast_argument_strc(*arg)); + + if ( cmp == NULL ) { + sieve_argument_validate_error(valdtr, *arg, + "unknown comparator '%s'", + str_sanitize(sieve_ast_argument_strc(*arg),80)); + + return FALSE; + } + + /* String argument not needed during code generation, so detach it from + * argument list + */ + *arg = sieve_ast_arguments_detach(*arg, 1); + + /* Store comparator in context */ + tag->argument->data = (void *) cmp; + + return TRUE; +} + +static bool tag_comparator_generate +(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *cmd ATTR_UNUSED) +{ + const struct sieve_comparator *cmp = + (const struct sieve_comparator *) arg->argument->data; + + sieve_opr_comparator_emit(cgenv->sblock, cmp); + + return TRUE; +} + +/* Functions to enable and evaluate comparator tag for commands */ + +void sieve_comparators_link_tag +(struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg, + int id_code) +{ + struct sieve_instance *svinst; + const struct sieve_extension *mcht_ext; + + svinst = sieve_validator_svinst(valdtr); + mcht_ext = sieve_get_comparator_extension(svinst); + + sieve_validator_register_tag + (valdtr, cmd_reg, mcht_ext, &comparator_tag, id_code); +} + +bool sieve_comparator_tag_is +(struct sieve_ast_argument *tag, const struct sieve_comparator_def *cmp_def) +{ + const struct sieve_comparator *cmp; + + if ( !sieve_argument_is(tag, comparator_tag) ) + return FALSE; + + cmp = (const struct sieve_comparator *) tag->argument->data; + + return ( cmp->def == cmp_def ); +} + +const struct sieve_comparator *sieve_comparator_tag_get +(struct sieve_ast_argument *tag) +{ + if ( !sieve_argument_is(tag, comparator_tag) ) + return NULL; + + + return (const struct sieve_comparator *) tag->argument->data; +} + +/* + * Comparator coding + */ + +const struct sieve_operand_class sieve_comparator_operand_class = + { "comparator" }; + +static const struct sieve_extension_objects core_comparators = + SIEVE_EXT_DEFINE_COMPARATORS(sieve_core_comparators); + +const struct sieve_operand_def comparator_operand = { + .name = "comparator", + .code = SIEVE_OPERAND_COMPARATOR, + .class = &sieve_comparator_operand_class, + .interface = &core_comparators +}; + +/* + * Trivial/Common comparator method implementations + */ + +bool sieve_comparator_octet_skip + (const struct sieve_comparator *cmp ATTR_UNUSED, + const char **val, const char *val_end) +{ + if ( *val < val_end ) { + (*val)++; + return TRUE; + } + + return FALSE; +} diff --git a/pigeonhole/src/lib-sieve/sieve-comparators.h b/pigeonhole/src/lib-sieve/sieve-comparators.h new file mode 100644 index 0000000..fdfb9ee --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-comparators.h @@ -0,0 +1,153 @@ +#ifndef SIEVE_COMPARATORS_H +#define SIEVE_COMPARATORS_H + +#include "sieve-common.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-objects.h" +#include "sieve-code.h" + +/* + * Core comparators + */ + +enum sieve_comparator_code { + SIEVE_COMPARATOR_I_OCTET, + SIEVE_COMPARATOR_I_ASCII_CASEMAP, + SIEVE_COMPARATOR_CUSTOM +}; + +extern const struct sieve_comparator_def i_octet_comparator; +extern const struct sieve_comparator_def i_ascii_casemap_comparator; + +/* + * Comparator flags + */ + +enum sieve_comparator_flags { + SIEVE_COMPARATOR_FLAG_ORDERING = (1 << 0), + SIEVE_COMPARATOR_FLAG_EQUALITY = (1 << 1), + SIEVE_COMPARATOR_FLAG_PREFIX_MATCH = (1 << 2), + SIEVE_COMPARATOR_FLAG_SUBSTRING_MATCH = (1 << 3), +}; + +/* + * Comparator definition + */ + +struct sieve_comparator_def { + struct sieve_object_def obj_def; + + unsigned int flags; + + /* Equality and ordering */ + + int (*compare)(const struct sieve_comparator *cmp, + const char *val1, size_t val1_size, + const char *val2, size_t val2_size); + + /* Prefix and substring match */ + + bool (*char_match)(const struct sieve_comparator *cmp, + const char **val, const char *val_end, + const char **key, const char *key_end); + bool (*char_skip)(const struct sieve_comparator *cmp, + const char **val, const char *val_end); +}; + +/* + * Comparator instance + */ + +struct sieve_comparator { + struct sieve_object object; + + const struct sieve_comparator_def *def; +}; + +#define SIEVE_COMPARATOR_DEFAULT(definition) \ + { SIEVE_OBJECT_DEFAULT(definition), &(definition) } + +#define sieve_comparator_name(cmp) \ + ( (cmp)->object.def->identifier ) +#define sieve_comparator_is(cmp, definition) \ + ( (cmp)->def == &(definition) ) + +static inline const struct sieve_comparator *sieve_comparator_copy +(pool_t pool, const struct sieve_comparator *cmp_orig) +{ + struct sieve_comparator *cmp = p_new(pool, struct sieve_comparator, 1); + + *cmp = *cmp_orig; + + return cmp; +} + +/* + * Comparator tagged argument + */ + +extern const struct sieve_argument_def comparator_tag; + +static inline bool sieve_argument_is_comparator +(struct sieve_ast_argument *arg) +{ + return ( arg->argument != NULL && + (arg->argument->def == &comparator_tag) ); +} + +void sieve_comparators_link_tag + (struct sieve_validator *validator, + struct sieve_command_registration *cmd_reg, int id_code); +bool sieve_comparator_tag_is + (struct sieve_ast_argument *tag, const struct sieve_comparator_def *cmp); +const struct sieve_comparator *sieve_comparator_tag_get + (struct sieve_ast_argument *tag); + +void sieve_comparator_register + (struct sieve_validator *validator, const struct sieve_extension *ext, + const struct sieve_comparator_def *cmp); + +/* + * Comparator operand + */ + +#define SIEVE_EXT_DEFINE_COMPARATOR(OP) SIEVE_EXT_DEFINE_OBJECT(OP) +#define SIEVE_EXT_DEFINE_COMPARATORS(OPS) SIEVE_EXT_DEFINE_OBJECTS(OPS) + +extern const struct sieve_operand_class sieve_comparator_operand_class; +extern const struct sieve_operand_def comparator_operand; + +static inline void sieve_opr_comparator_emit +(struct sieve_binary_block *sblock, const struct sieve_comparator *cmp) +{ + sieve_opr_object_emit(sblock, cmp->object.ext, cmp->object.def); +} +static inline bool sieve_opr_comparator_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + return sieve_opr_object_dump + (denv, &sieve_comparator_operand_class, address, NULL); +} + +static inline int sieve_opr_comparator_read +(const struct sieve_runtime_env *renv, sieve_size_t *address, + struct sieve_comparator *cmp) +{ + if ( !sieve_opr_object_read + (renv, &sieve_comparator_operand_class, address, &cmp->object) ) + return SIEVE_EXEC_BIN_CORRUPT; + + cmp->def = (const struct sieve_comparator_def *) cmp->object.def; + return SIEVE_EXEC_OK; +} + +/* + * Trivial/Common comparator method implementations + */ + +bool sieve_comparator_octet_skip + (const struct sieve_comparator *cmp ATTR_UNUSED, + const char **val, const char *val_end); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-config.h b/pigeonhole/src/lib-sieve/sieve-config.h new file mode 100644 index 0000000..a0fed7c --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-config.h @@ -0,0 +1,16 @@ +#ifndef SIEVE_CONFIG_H +#define SIEVE_CONFIG_H + +#include "pigeonhole-config.h" +#include "pigeonhole-version.h" + +#define SIEVE_IMPLEMENTATION PIGEONHOLE_NAME " Sieve " PIGEONHOLE_VERSION_FULL + +#define SIEVE_SCRIPT_FILEEXT "sieve" +#define SIEVE_BINARY_FILEEXT "svbin" + +#define DEFAULT_ENVELOPE_SENDER "MAILER-DAEMON" + +#define DEFAULT_REDIRECT_DUPLICATE_PERIOD (3600 * 12) + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-dump.h b/pigeonhole/src/lib-sieve/sieve-dump.h new file mode 100644 index 0000000..d704877 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-dump.h @@ -0,0 +1,30 @@ +#ifndef SIEVE_DUMP_H +#define SIEVE_DUMP_H + +#include "sieve-common.h" +#include "sieve-code-dumper.h" +#include "sieve-binary-dumper.h" + +/* + * Dumptime environment + */ + +struct sieve_dumptime_env { + /* Dumpers */ + struct sieve_instance *svinst; + struct sieve_binary_dumper *dumper; + struct sieve_code_dumper *cdumper; + + /* Binary */ + struct sieve_binary *sbin; + struct sieve_binary_block *sblock; + + /* Code position */ + const struct sieve_operation *oprtn; + sieve_size_t offset; + + /* Output stream */ + struct ostream *stream; +}; + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-error-private.h b/pigeonhole/src/lib-sieve/sieve-error-private.h new file mode 100644 index 0000000..e0172b2 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-error-private.h @@ -0,0 +1,67 @@ +#ifndef SIEVE_ERROR_PRIVATE_H +#define SIEVE_ERROR_PRIVATE_H + +#include "sieve-error.h" + +/* + * Initialization + */ + +void sieve_errors_init(struct sieve_instance *svinst); +void sieve_errors_deinit(struct sieve_instance *svinst); + +/* + * Error handler object + */ + +struct sieve_error_handler { + pool_t pool; + int refcount; + + struct sieve_instance *svinst; + + unsigned int max_errors; + + unsigned int errors; + unsigned int warnings; + + void (*log)(struct sieve_error_handler *ehandler, + const struct sieve_error_params *params, + enum sieve_error_flags flags, + const char *message); + + void (*free)(struct sieve_error_handler *ehandler); + + bool master_log:1; /* this logs through master log facility */ + bool log_info:1; /* handle or discard info log */ + bool log_debug:1; /* handle or discard debug log */ +}; + +void sieve_error_handler_init(struct sieve_error_handler *ehandler, + struct sieve_instance *svinst, pool_t pool, + unsigned int max_errors); + +/* + * Direct handler calls + */ + +void sieve_direct_logv(struct sieve_instance *svinst, + struct sieve_error_handler *ehandler, + const struct sieve_error_params *params, + enum sieve_error_flags flags, + const char *fmt, va_list args) ATTR_FORMAT(5, 0); + +static inline void ATTR_FORMAT(5, 6) +sieve_direct_log(struct sieve_instance *svinst, + struct sieve_error_handler *ehandler, + const struct sieve_error_params *params, + enum sieve_error_flags flags, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + sieve_direct_logv(svinst, ehandler, params, flags, fmt, args); + va_end(args); +} + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-error.c b/pigeonhole/src/lib-sieve/sieve-error.c new file mode 100644 index 0000000..aae13a5 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-error.c @@ -0,0 +1,1064 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "array.h" +#include "ostream.h" +#include "var-expand.h" +#include "eacces-error.h" + +#include "sieve-common.h" +#include "sieve-script.h" +#include "sieve-error-private.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <ctype.h> + +/* + * Definitions + */ + +#define CRITICAL_MSG \ + "internal error occurred: refer to server log for more information." +#define CRITICAL_MSG_STAMP CRITICAL_MSG " [%Y-%m-%d %H:%M:%S]" + +/* Logfile error handler will rotate log when it exceeds 10k bytes */ +#define LOGFILE_MAX_SIZE (10 * 1024) + +/* + * Utility + */ + +const char * +sieve_error_script_location(const struct sieve_script *script, + unsigned int source_line) +{ + const char *sname; + + sname = (script == NULL ? NULL : sieve_script_name(script)); + + if (sname == NULL || *sname == '\0') { + if (source_line == 0) + return NULL; + + return t_strdup_printf("line %d", source_line); + } + + if (source_line == 0) + return sname; + + return t_strdup_printf("%s: line %d", sname, source_line); +} + +const char *sieve_error_from_external(const char *msg) +{ + char *new_msg; + + if (msg == NULL || *msg == '\0') + return msg; + + new_msg = t_strdup_noconst(msg); + new_msg[0] = i_tolower(new_msg[0]); + + return new_msg; +} + +/* + * Initialization + */ + +void sieve_errors_init(struct sieve_instance *svinst ATTR_UNUSED) +{ + /* nothing */ +} + +void sieve_errors_deinit(struct sieve_instance *svinst ATTR_UNUSED) +{ + /* nothing */ +} + +/* + * Direct handler calls + */ + +static void +sieve_direct_master_log(struct sieve_instance *svinst, + const struct sieve_error_params *params, + const char *message) +{ + struct event_log_params event_params = { + .log_type = params->log_type, + .source_filename = params->csrc.filename, + .source_linenum = params->csrc.linenum, + + .base_event = svinst->event, + }; + struct event *event = (params->event != NULL ? + params->event : svinst->event); + + if (params->location != NULL && *params->location != '\0') { + event_params.base_send_prefix = + t_strconcat(params->location, ": ", NULL); + } + + event_log(event, &event_params, "%s", message); +} + +void sieve_direct_logv(struct sieve_instance *svinst, + struct sieve_error_handler *ehandler, + const struct sieve_error_params *params, + enum sieve_error_flags flags, + const char *fmt, va_list args) +{ + struct event_log_params event_params = { + .log_type = params->log_type, + .source_filename = params->csrc.filename, + .source_linenum = params->csrc.linenum, + .base_event = svinst->event, + .base_str_out = NULL, + .no_send = TRUE, + }; + struct event *event = (params->event != NULL ? + params->event : svinst->event); + bool event_log = FALSE, ehandler_log = FALSE; + + if (ehandler != NULL) { + switch (params->log_type) { + case LOG_TYPE_ERROR: + ehandler_log = sieve_errors_more_allowed(ehandler); + break; + case LOG_TYPE_WARNING: + ehandler_log = TRUE; + break; + case LOG_TYPE_INFO: + ehandler_log = ehandler->log_info; + break; + case LOG_TYPE_DEBUG: + ehandler_log = ehandler->log_debug; + break; + case LOG_TYPE_FATAL: + case LOG_TYPE_PANIC: + case LOG_TYPE_COUNT: + case LOG_TYPE_OPTION: + i_unreached(); + } + } + + if (ehandler != NULL && ehandler->master_log) { + event_log = ehandler_log; + ehandler_log = FALSE; + } + if ((flags & SIEVE_ERROR_FLAG_GLOBAL) != 0) { + event_log = TRUE; + if ((flags & SIEVE_ERROR_FLAG_GLOBAL_MAX_INFO) != 0 && + params->log_type > LOG_TYPE_INFO) + event_params.log_type = LOG_TYPE_INFO; + } + + if (event_log) { + event_params.no_send = FALSE; + if (params->location != NULL && *params->location != '\0') { + event_params.base_send_prefix = + t_strconcat(params->location, ": ", NULL); + } + } + if (ehandler_log) { + if (ehandler->log == NULL) + ehandler_log = FALSE; + else + event_params.base_str_out = t_str_new(128); + } + + if (event_log || ehandler_log) + event_logv(event, &event_params, fmt, args); + + if (ehandler_log) { + ehandler->log(ehandler, params, flags, + str_c(event_params.base_str_out)); + } + + if (ehandler != NULL && ehandler->pool != NULL) { + switch (params->log_type) { + case LOG_TYPE_ERROR: + ehandler->errors++; + break; + case LOG_TYPE_WARNING: + ehandler->warnings++; + break; + default: + break; + } + } +} + +/* + * User errors + */ + +void sieve_global_logv(struct sieve_instance *svinst, + struct sieve_error_handler *ehandler, + const struct sieve_error_params *params, + const char *fmt, va_list args) +{ + sieve_direct_logv(svinst, ehandler, params, + SIEVE_ERROR_FLAG_GLOBAL, fmt, args); +} + +void sieve_global_info_logv(struct sieve_instance *svinst, + struct sieve_error_handler *ehandler, + const struct sieve_error_params *params, + const char *fmt, va_list args) +{ + sieve_direct_logv(svinst, ehandler, params, + (SIEVE_ERROR_FLAG_GLOBAL | + SIEVE_ERROR_FLAG_GLOBAL_MAX_INFO), fmt, args); +} + +#undef sieve_global_error +void sieve_global_error(struct sieve_instance *svinst, + struct sieve_error_handler *ehandler, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, const char *fmt, ...) +{ + struct sieve_error_params params = { + .log_type = LOG_TYPE_ERROR, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + .location = location, + }; + va_list args; + va_start(args, fmt); + + T_BEGIN { + sieve_global_logv(svinst, ehandler, ¶ms, fmt, args); + } T_END; + + va_end(args); +} + +#undef sieve_global_warning +void sieve_global_warning(struct sieve_instance *svinst, + struct sieve_error_handler *ehandler, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, const char *fmt, ...) +{ + struct sieve_error_params params = { + .log_type = LOG_TYPE_WARNING, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + .location = location, + }; + va_list args; + va_start(args, fmt); + + T_BEGIN { + sieve_global_logv(svinst, ehandler, ¶ms, fmt, args); + } T_END; + + va_end(args); +} + +#undef sieve_global_info +void sieve_global_info(struct sieve_instance *svinst, + struct sieve_error_handler *ehandler, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, const char *fmt, ...) +{ + struct sieve_error_params params = { + .log_type = LOG_TYPE_INFO, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + .location = location, + }; + va_list args; + va_start(args, fmt); + + T_BEGIN { + sieve_global_logv(svinst, ehandler, ¶ms, fmt, args); + } T_END; + + va_end(args); +} + +#undef sieve_global_info_error +void sieve_global_info_error(struct sieve_instance *svinst, + struct sieve_error_handler *ehandler, + const char *csrc_filename, + unsigned int csrc_linenum, + const char *location, const char *fmt, ...) +{ + struct sieve_error_params params = { + .log_type = LOG_TYPE_ERROR, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + .location = location, + }; + va_list args; + va_start(args, fmt); + + T_BEGIN { + sieve_global_info_logv(svinst, ehandler, ¶ms, fmt, args); + } T_END; + + va_end(args); +} + +#undef sieve_global_info_warning +void sieve_global_info_warning(struct sieve_instance *svinst, + struct sieve_error_handler *ehandler, + const char *csrc_filename, + unsigned int csrc_linenum, + const char *location, const char *fmt, ...) +{ + struct sieve_error_params params = { + .log_type = LOG_TYPE_WARNING, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + .location = location, + }; + va_list args; + va_start(args, fmt); + + T_BEGIN { + sieve_global_info_logv(svinst, ehandler, ¶ms, fmt, args); + } T_END; + + va_end(args); +} + +/* + * Default (user) error functions + */ + +void sieve_internal_error_params(struct sieve_error_handler *ehandler, + const struct sieve_error_params *params, + const char *user_prefix) +{ + char str[256]; + const char *msg; + struct tm *tm; + + if (ehandler == NULL || ehandler->master_log) + return; + + tm = localtime(&ioloop_time); + msg = (strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ? + str : CRITICAL_MSG); + + if (user_prefix == NULL || *user_prefix == '\0') { + sieve_direct_log(ehandler->svinst, ehandler, params, 0, + "%s", msg); + } else { + sieve_direct_log(ehandler->svinst, ehandler, params, 0, + "%s: %s", user_prefix, msg); + } +} + +#undef sieve_internal_error +void sieve_internal_error(struct sieve_error_handler *ehandler, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, const char *user_prefix) + +{ + struct sieve_error_params params = { + .log_type = LOG_TYPE_ERROR, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + .location = location, + }; + + sieve_internal_error_params(ehandler, ¶ms, user_prefix); +} + +void sieve_logv(struct sieve_error_handler *ehandler, + const struct sieve_error_params *params, + const char *fmt, va_list args) +{ + if (ehandler == NULL) return; + + sieve_direct_logv(ehandler->svinst, ehandler, params, 0, fmt, args); +} + +void sieve_event_logv(struct sieve_instance *svinst, + struct sieve_error_handler *ehandler, + struct event *event, enum log_type log_type, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, enum sieve_error_flags flags, + const char *fmt, va_list args) +{ + struct sieve_error_params params = { + .log_type = log_type, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + .event = event, + .location = location, + }; + + T_BEGIN { + sieve_direct_logv(svinst, ehandler, ¶ms, flags, fmt, args); + } T_END; +} + + +#undef sieve_event_log +void sieve_event_log(struct sieve_instance *svinst, + struct sieve_error_handler *ehandler, + struct event *event, enum log_type log_type, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, enum sieve_error_flags flags, + const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + + sieve_event_logv(svinst, ehandler, event, log_type, csrc_filename, + csrc_linenum, location, flags, fmt, args); + + va_end(args); +} + +void sieve_criticalv(struct sieve_instance *svinst, + struct sieve_error_handler *ehandler, + const struct sieve_error_params *params, + const char *user_prefix, const char *fmt, va_list args) +{ + struct sieve_error_params new_params = *params; + + new_params.log_type = LOG_TYPE_ERROR; + + sieve_direct_master_log(svinst, &new_params, + t_strdup_vprintf(fmt, args)); + sieve_internal_error_params(ehandler, &new_params, user_prefix); +} + +#undef sieve_error +void sieve_error(struct sieve_error_handler *ehandler, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, const char *fmt, ...) +{ + struct sieve_error_params params = { + .log_type = LOG_TYPE_ERROR, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + .location = location, + }; + va_list args; + va_start(args, fmt); + + T_BEGIN { + sieve_logv(ehandler, ¶ms, fmt, args); + } T_END; + + va_end(args); +} + +#undef sieve_warning +void sieve_warning(struct sieve_error_handler *ehandler, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, const char *fmt, ...) +{ + struct sieve_error_params params = { + .log_type = LOG_TYPE_WARNING, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + .location = location, + }; + va_list args; + va_start(args, fmt); + + T_BEGIN { + sieve_logv(ehandler, ¶ms, fmt, args); + } T_END; + + va_end(args); +} + +#undef sieve_info +void sieve_info(struct sieve_error_handler *ehandler, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, const char *fmt, ...) +{ + struct sieve_error_params params = { + .log_type = LOG_TYPE_INFO, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + .location = location, + }; + va_list args; + va_start(args, fmt); + + T_BEGIN { + sieve_logv(ehandler, ¶ms, fmt, args); + } T_END; + + va_end(args); +} + +#undef sieve_debug +void sieve_debug(struct sieve_error_handler *ehandler, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, const char *fmt, ...) +{ + struct sieve_error_params params = { + .log_type = LOG_TYPE_DEBUG, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + .location = location, + }; + va_list args; + va_start(args, fmt); + + T_BEGIN { + sieve_logv(ehandler, ¶ms, fmt, args); + } T_END; + + va_end(args); +} + +#undef sieve_critical +void sieve_critical(struct sieve_instance *svinst, + struct sieve_error_handler *ehandler, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, const char *user_prefix, + const char *fmt, ...) +{ + struct sieve_error_params params = { + .log_type = LOG_TYPE_ERROR, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + .location = location, + }; + va_list args; + + va_start(args, fmt); + + T_BEGIN { + sieve_criticalv(svinst, ehandler, ¶ms, user_prefix, + fmt, args); + } T_END; + + va_end(args); +} + +/* + * Error statistics + */ + +unsigned int sieve_get_errors(struct sieve_error_handler *ehandler) +{ + if (ehandler == NULL || ehandler->pool == NULL) + return 0; + + return ehandler->errors; +} + +unsigned int sieve_get_warnings(struct sieve_error_handler *ehandler) +{ + if (ehandler == NULL || ehandler->pool == NULL) + return 0; + + return ehandler->warnings; +} + +bool sieve_errors_more_allowed(struct sieve_error_handler *ehandler) +{ + if (ehandler == NULL || ehandler->pool == NULL) + return TRUE; + + return (ehandler->max_errors == 0 || + ehandler->errors < ehandler->max_errors); +} + +/* + * Error handler configuration + */ + +void sieve_error_handler_accept_infolog(struct sieve_error_handler *ehandler, + bool enable) +{ + ehandler->log_info = enable; +} + +void sieve_error_handler_accept_debuglog(struct sieve_error_handler *ehandler, + bool enable) +{ + ehandler->log_debug = enable; +} + +/* + * Error handler init + */ + +void sieve_error_handler_init(struct sieve_error_handler *ehandler, + struct sieve_instance *svinst, + pool_t pool, unsigned int max_errors) +{ + ehandler->pool = pool; + ehandler->svinst = svinst; + ehandler->refcount = 1; + ehandler->max_errors = max_errors; + + ehandler->errors = 0; + ehandler->warnings = 0; +} + +void sieve_error_handler_ref(struct sieve_error_handler *ehandler) +{ + if (ehandler == NULL || ehandler->pool == NULL) + return; + + ehandler->refcount++; +} + +void sieve_error_handler_unref(struct sieve_error_handler **ehandler) +{ + if (*ehandler == NULL || (*ehandler)->pool == NULL) + return; + + i_assert((*ehandler)->refcount > 0); + + if (--(*ehandler)->refcount != 0) + return; + + if ((*ehandler)->free != NULL) + (*ehandler)->free(*ehandler); + + pool_unref(&((*ehandler)->pool)); + *ehandler = NULL; +} + +void sieve_error_handler_reset(struct sieve_error_handler *ehandler) +{ + if (ehandler == NULL || ehandler->pool == NULL) + return; + + ehandler->errors = 0; + ehandler->warnings = 0; +} + +/* + * Error params utility + */ + +static void +sieve_error_params_add_prefix(struct sieve_error_handler *ehandler ATTR_UNUSED, + const struct sieve_error_params *params, + string_t *prefix) +{ + if (params->location != NULL && *params->location != '\0') { + str_append(prefix, params->location); + str_append(prefix, ": "); + } + + switch (params->log_type) { + case LOG_TYPE_ERROR: + str_append(prefix, "error: "); + break; + case LOG_TYPE_WARNING: + str_append(prefix, "warning: "); + break; + case LOG_TYPE_INFO: + str_append(prefix, "info: "); + break; + case LOG_TYPE_DEBUG: + str_append(prefix, "debug: "); + break; + default: + i_unreached(); + } +} + +/* + * Master/System error handler + * + * - Output errors directly to Dovecot master log + */ + +struct sieve_error_handler * +sieve_master_ehandler_create(struct sieve_instance *svinst, + unsigned int max_errors) +{ + struct sieve_error_handler *ehandler; + pool_t pool; + + pool = pool_alloconly_create("master_error_handler", 256); + ehandler = p_new(pool, struct sieve_error_handler, 1); + sieve_error_handler_init(ehandler, svinst, pool, max_errors); + ehandler->master_log = TRUE; + ehandler->log_debug = svinst->debug; + + return ehandler; +} + +/* + * STDERR error handler + * + * - Output errors directly to stderror + */ + +static void +sieve_stderr_log(struct sieve_error_handler *ehandler, + const struct sieve_error_params *params, + enum sieve_error_flags flags ATTR_UNUSED, + const char *message) +{ + string_t *prefix = t_str_new(64); + + sieve_error_params_add_prefix(ehandler, params, prefix); + + fprintf(stderr, "%s%s.\n", str_c(prefix), message); +} + +struct sieve_error_handler * +sieve_stderr_ehandler_create(struct sieve_instance *svinst, + unsigned int max_errors) +{ + pool_t pool; + struct sieve_error_handler *ehandler; + + /* Pool is not strictly necessary, but other handler types will need + * a pool, so this one will have one too. + */ + pool = pool_alloconly_create("stderr_error_handler", + sizeof(struct sieve_error_handler)); + ehandler = p_new(pool, struct sieve_error_handler, 1); + sieve_error_handler_init(ehandler, svinst, pool, max_errors); + + ehandler->log = sieve_stderr_log; + + return ehandler; +} + +/* String buffer error handler + * + * - Output errors to a string buffer + */ + +struct sieve_strbuf_ehandler { + struct sieve_error_handler handler; + + string_t *errors; + bool crlf; +}; + +static void +sieve_strbuf_log(struct sieve_error_handler *ehandler, + const struct sieve_error_params *params, + enum sieve_error_flags flags ATTR_UNUSED, const char *message) +{ + struct sieve_strbuf_ehandler *handler = + (struct sieve_strbuf_ehandler *) ehandler; + + sieve_error_params_add_prefix(ehandler, params, handler->errors); + str_append(handler->errors, message); + + if (!handler->crlf) + str_append(handler->errors, ".\n"); + else + str_append(handler->errors, ".\r\n"); +} + +struct sieve_error_handler * +sieve_strbuf_ehandler_create(struct sieve_instance *svinst, string_t *strbuf, + bool crlf, unsigned int max_errors) +{ + pool_t pool; + struct sieve_strbuf_ehandler *ehandler; + + pool = pool_alloconly_create("strbuf_error_handler", 256); + ehandler = p_new(pool, struct sieve_strbuf_ehandler, 1); + ehandler->errors = strbuf; + + sieve_error_handler_init(&ehandler->handler, svinst, pool, max_errors); + + ehandler->handler.log = sieve_strbuf_log; + ehandler->crlf = crlf; + + return &(ehandler->handler); +} + +/* + * Logfile error handler + * + * - Output errors to a log file + */ + +struct sieve_logfile_ehandler { + struct sieve_error_handler handler; + + const char *logfile; + bool started; + int fd; + struct ostream *stream; +}; + +static void +sieve_logfile_write(struct sieve_logfile_ehandler *ehandler, + const struct sieve_error_params *params, + const char *message) +{ + string_t *outbuf; + ssize_t ret = 0, remain; + const char *data; + + if (ehandler->stream == NULL) + return; + + T_BEGIN { + outbuf = t_str_new(256); + sieve_error_params_add_prefix(&ehandler->handler, + params, outbuf); + str_append(outbuf, message); + str_append(outbuf, ".\n"); + + remain = str_len(outbuf); + data = (const char *) str_data(outbuf); + + while (remain > 0) { + if ((ret = o_stream_send(ehandler->stream, + data, remain)) < 0) + break; + + remain -= ret; + data += ret; + } + } T_END; + + if (ret < 0) { + e_error(ehandler->handler.svinst->event, + "o_stream_send() failed on logfile %s: %m", + ehandler->logfile); + } +} + +inline static void ATTR_FORMAT(5, 6) +sieve_logfile_printf(struct sieve_logfile_ehandler *ehandler, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, const char *fmt, ...) +{ + struct sieve_error_params params = { + .log_type = LOG_TYPE_INFO, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + .location = location, + }; + va_list args; + va_start(args, fmt); + + sieve_logfile_write(ehandler, ¶ms, t_strdup_vprintf(fmt, args)); + + va_end(args); +} + +static void sieve_logfile_start(struct sieve_logfile_ehandler *ehandler) +{ + struct sieve_instance *svinst = ehandler->handler.svinst; + struct ostream *ostream = NULL; + struct stat st; + struct tm *tm; + char buf[256]; + time_t now; + int fd; + + /* Open the logfile */ + + fd = open(ehandler->logfile, O_CREAT | O_APPEND | O_WRONLY, 0600); + if (fd == -1) { + if (errno == EACCES) { + e_error(svinst->event, + "failed to open logfile " + "(LOGGING TO STDERR): %s", + eacces_error_get_creating("open", + ehandler->logfile)); + } else { + e_error(svinst->event, "failed to open logfile " + "(LOGGING TO STDERR): " + "open(%s) failed: %m", ehandler->logfile); + } + fd = STDERR_FILENO; + } else { + /* fd_close_on_exec(fd, TRUE); Necessary? */ + + /* Stat the log file to obtain size information */ + if (fstat(fd, &st) != 0) { + e_error(svinst->event, "failed to stat logfile " + "(logging to STDERR): " + "fstat(fd=%s) failed: %m", ehandler->logfile); + + if (close(fd) < 0) { + e_error(svinst->event, + "failed to close logfile after error: " + "close(fd=%s) failed: %m", + ehandler->logfile); + } + + fd = STDERR_FILENO; + } + + /* Rotate log when it has grown too large */ + if (st.st_size >= LOGFILE_MAX_SIZE) { + const char *rotated; + + /* Close open file */ + if (close(fd) < 0) { + e_error(svinst->event, + "failed to close logfile: " + "close(fd=%s) failed: %m", + ehandler->logfile); + } + + /* Rotate logfile */ + rotated = t_strconcat(ehandler->logfile, ".0", NULL); + if (rename(ehandler->logfile, rotated) < 0 && + errno != ENOENT) { + if (errno == EACCES) { + const char *target = + t_strconcat(ehandler->logfile, + ", ", rotated, NULL); + e_error(svinst->event, + "failed to rotate logfile: %s", + eacces_error_get_creating( + "rename", target)); + } else { + e_error(svinst->event, + "failed to rotate logfile: " + "rename(%s, %s) failed: %m", + ehandler->logfile, rotated); + } + } + + /* Open clean logfile (overwrites existing if rename() failed earlier) */ + fd = open(ehandler->logfile, + O_CREAT | O_APPEND | O_WRONLY | O_TRUNC, 0600); + if (fd == -1) { + if (errno == EACCES) { + e_error(svinst->event, + "failed to open logfile " + "(LOGGING TO STDERR): %s", + eacces_error_get_creating( + "open", ehandler->logfile)); + } else { + e_error(svinst->event, + "failed to open logfile " + "(LOGGING TO STDERR): " + "open(%s) failed: %m", + ehandler->logfile); + } + fd = STDERR_FILENO; + } + } + } + + ostream = o_stream_create_fd(fd, 0); + if (ostream == NULL) { + /* Can't we do anything else in this most awkward situation? */ + e_error(svinst->event, + "failed to open log stream on open file: " + "o_stream_create_fd(fd=%s) failed " + "(non-critical messages are not logged!)", + ehandler->logfile); + } + + ehandler->fd = fd; + ehandler->stream = ostream; + ehandler->started = TRUE; + + if (ostream != NULL) { + now = time(NULL); + tm = localtime(&now); + + if (strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %z", tm) > 0) { + sieve_logfile_printf(ehandler, __FILE__, __LINE__, + "sieve", "started log at %s", buf); + } + } +} + +static void +sieve_logfile_log(struct sieve_error_handler *ehandler, + const struct sieve_error_params *params, + enum sieve_error_flags flags ATTR_UNUSED, + const char *message) +{ + struct sieve_logfile_ehandler *handler = + (struct sieve_logfile_ehandler *) ehandler; + + if (!handler->started) + sieve_logfile_start(handler); + + sieve_logfile_write(handler, params, message); +} + +static void sieve_logfile_free(struct sieve_error_handler *ehandler) +{ + struct sieve_logfile_ehandler *handler = + (struct sieve_logfile_ehandler *) ehandler; + + if (handler->stream != NULL) { + o_stream_destroy(&(handler->stream)); + if (handler->fd != STDERR_FILENO) { + if (close(handler->fd) < 0) { + e_error(ehandler->svinst->event, + "failed to close logfile: " + "close(fd=%s) failed: %m", + handler->logfile); + } + } + } +} + +struct sieve_error_handler * +sieve_logfile_ehandler_create(struct sieve_instance *svinst, + const char *logfile, unsigned int max_errors) +{ + pool_t pool; + struct sieve_logfile_ehandler *ehandler; + + pool = pool_alloconly_create("logfile_error_handler", 512); + ehandler = p_new(pool, struct sieve_logfile_ehandler, 1); + sieve_error_handler_init(&ehandler->handler, svinst, pool, max_errors); + + ehandler->handler.log = sieve_logfile_log; + ehandler->handler.free = sieve_logfile_free; + + /* Don't open logfile until something is actually logged. + * Let's not pullute the sieve directory with useless logfiles. + */ + ehandler->logfile = p_strdup(pool, logfile); + ehandler->started = FALSE; + ehandler->stream = NULL; + ehandler->fd = -1; + + return &(ehandler->handler); +} diff --git a/pigeonhole/src/lib-sieve/sieve-error.h b/pigeonhole/src/lib-sieve/sieve-error.h new file mode 100644 index 0000000..83b26b7 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-error.h @@ -0,0 +1,235 @@ +#ifndef SIEVE_ERROR_H +#define SIEVE_ERROR_H + +#include "lib.h" +#include "compat.h" + +#include <stdarg.h> + +/* + * Forward declarations + */ + +struct var_expand_table; + +struct sieve_instance; +struct sieve_script; +struct sieve_error_handler; + +/* + * Types + */ + +enum sieve_error_flags { + SIEVE_ERROR_FLAG_GLOBAL = (1 << 0), + SIEVE_ERROR_FLAG_GLOBAL_MAX_INFO = (1 << 1), +}; + +struct sieve_error_params { + enum log_type log_type; + struct event *event; + + /* Location log command in C source code */ + struct { + const char *filename; + unsigned int linenum; + } csrc; + + /* Location in Sieve source script */ + const char *location; +}; + +/* + * Utility + */ + +/* Converts external messages to a style that better matches Sieve user errors + */ +const char *sieve_error_from_external(const char *msg); + +/* + * Global (user+system) errors + */ + +void sieve_global_logv(struct sieve_instance *svinst, + struct sieve_error_handler *ehandler, + const struct sieve_error_params *params, + const char *fmt, va_list args) ATTR_FORMAT(4, 0); +void sieve_global_info_logv(struct sieve_instance *svinst, + struct sieve_error_handler *ehandler, + const struct sieve_error_params *params, + const char *fmt, va_list args) ATTR_FORMAT(4, 0); + +void sieve_global_error(struct sieve_instance *svinst, + struct sieve_error_handler *ehandler, + const char *csrc_filename, + unsigned int csrc_linenum, + const char *location, const char *fmt, ...) + ATTR_FORMAT(6, 7); +#define sieve_global_error(svinst, ehandler, ...) \ + sieve_global_error(svinst, ehandler, __FILE__, __LINE__, __VA_ARGS__) +void sieve_global_warning(struct sieve_instance *svinst, + struct sieve_error_handler *ehandler, + const char *csrc_filename, + unsigned int csrc_linenum, + const char *location, const char *fmt, ...) + ATTR_FORMAT(6, 7); +#define sieve_global_warning(svinst, ehandler, ...) \ + sieve_global_warning(svinst, ehandler, __FILE__, __LINE__, __VA_ARGS__) +void sieve_global_info(struct sieve_instance *svinst, + struct sieve_error_handler *ehandler, + const char *csrc_filename, + unsigned int csrc_linenum, + const char *location, const char *fmt, ...) + ATTR_FORMAT(6, 7); +#define sieve_global_info(svinst, ehandler, ...) \ + sieve_global_info(svinst, ehandler, __FILE__, __LINE__, __VA_ARGS__) +void sieve_global_info_error(struct sieve_instance *svinst, + struct sieve_error_handler *ehandler, + const char *csrc_filename, + unsigned int csrc_linenum, + const char *location, const char *fmt, ...) + ATTR_FORMAT(6, 7); +#define sieve_global_info_error(svinst, ehandler, ...) \ + sieve_global_info_error(svinst, ehandler, __FILE__, __LINE__, \ + __VA_ARGS__) +void sieve_global_info_warning(struct sieve_instance *svinst, + struct sieve_error_handler *ehandler, + const char *csrc_filename, + unsigned int csrc_linenum, + const char *location, const char *fmt, ...) + ATTR_FORMAT(6, 7); +#define sieve_global_info_warning(svinst, ehandler, ...) \ + sieve_global_info_warning(svinst, ehandler, __FILE__, __LINE__, \ + __VA_ARGS__) + +/* + * Main (user) error functions + */ + +/* For these functions it is the responsibility of the caller to + * manage the datastack. + */ + +const char * +sieve_error_script_location(const struct sieve_script *script, + unsigned int source_line); + +void sieve_logv(struct sieve_error_handler *ehandler, + const struct sieve_error_params *params, + const char *fmt, va_list args) ATTR_FORMAT(3, 0); + +void sieve_event_logv(struct sieve_instance *svinst, + struct sieve_error_handler *ehandler, + struct event *event, enum log_type log_type, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, enum sieve_error_flags flags, + const char *fmt, va_list args) ATTR_FORMAT(9, 0); +void sieve_event_log(struct sieve_instance *svinst, + struct sieve_error_handler *ehandler, + struct event *event, enum log_type log_type, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, enum sieve_error_flags flags, + const char *fmt, ...) ATTR_FORMAT(9, 10); +#define sieve_event_log(svinst, ehandler, event, log_type, ...) \ + sieve_event_log(svinst, ehandler, event, log_type, __FILE__, __LINE__, \ + __VA_ARGS__) + +void sieve_criticalv(struct sieve_instance *svinst, + struct sieve_error_handler *ehandler, + const struct sieve_error_params *params, + const char *user_prefix, const char *fmt, va_list args) + ATTR_FORMAT(5, 0); + +void sieve_error(struct sieve_error_handler *ehandler, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, const char *fmt, ...) ATTR_FORMAT(5, 6); +#define sieve_error(ehandler, ...) \ + sieve_error(ehandler, __FILE__, __LINE__, __VA_ARGS__) +void sieve_warning(struct sieve_error_handler *ehandler, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, const char *fmt, ...) + ATTR_FORMAT(5, 6); +#define sieve_warning(ehandler, ...) \ + sieve_warning(ehandler, __FILE__, __LINE__, __VA_ARGS__) +void sieve_info(struct sieve_error_handler *ehandler, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, const char *fmt, ...) ATTR_FORMAT(5, 6); +#define sieve_info(ehandler, ...) \ + sieve_info(ehandler, __FILE__, __LINE__, __VA_ARGS__) +void sieve_debug(struct sieve_error_handler *ehandler, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, const char *fmt, ...) ATTR_FORMAT(5, 6); +#define sieve_debug(ehandler, ...) \ + sieve_debug(ehandler, __FILE__, __LINE__, __VA_ARGS__) +void sieve_critical(struct sieve_instance *svinst, + struct sieve_error_handler *ehandler, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, const char *user_prefix, + const char *fmt, ...) ATTR_FORMAT(7, 8); +#define sieve_critical(svinst, ehandler, ...) \ + sieve_critical(svinst, ehandler, __FILE__, __LINE__, __VA_ARGS__) + + +void sieve_internal_error_params(struct sieve_error_handler *ehandler, + const struct sieve_error_params *params, + const char *user_prefix); +void sieve_internal_error(struct sieve_error_handler *ehandler, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, const char *user_prefix) + ATTR_NULL(1, 4, 5); +#define sieve_internal_error(ehandler, ...) \ + sieve_internal_error(ehandler, __FILE__, __LINE__, __VA_ARGS__) + +/* + * Error handler configuration + */ + +void sieve_error_handler_accept_infolog(struct sieve_error_handler *ehandler, + bool enable); +void sieve_error_handler_accept_debuglog(struct sieve_error_handler *ehandler, + bool enable); + +/* + * Error handler statistics + */ + +unsigned int sieve_get_errors(struct sieve_error_handler *ehandler); +unsigned int sieve_get_warnings(struct sieve_error_handler *ehandler); + +bool sieve_errors_more_allowed(struct sieve_error_handler *ehandler); + +/* + * Error handler object + */ + +void sieve_error_handler_ref(struct sieve_error_handler *ehandler); +void sieve_error_handler_unref(struct sieve_error_handler **ehandler); + +void sieve_error_handler_reset(struct sieve_error_handler *ehandler); + +/* + * Error handlers + */ + +/* Write errors to dovecot master log */ +struct sieve_error_handler * +sieve_master_ehandler_create(struct sieve_instance *svinst, + unsigned int max_errors); + +/* Write errors to stderr */ +struct sieve_error_handler * +sieve_stderr_ehandler_create(struct sieve_instance *svinst, + unsigned int max_errors); + +/* Write errors into a string buffer */ +struct sieve_error_handler * +sieve_strbuf_ehandler_create(struct sieve_instance *svinst, string_t *strbuf, + bool crlf, unsigned int max_errors); + +/* Write errors to a logfile */ +struct sieve_error_handler * +sieve_logfile_ehandler_create(struct sieve_instance *svinst, + const char *logfile, unsigned int max_errors); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-execute.c b/pigeonhole/src/lib-sieve/sieve-execute.c new file mode 100644 index 0000000..a395cc6 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-execute.c @@ -0,0 +1,162 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" + +#include "sieve-execute.h" + +struct sieve_execute_state { + void *dup_trans; +}; + +struct event_category event_category_sieve_execute = { + .parent = &event_category_sieve, + .name = "sieve-execute", +}; + +static struct sieve_execute_state * +sieve_execute_state_create(struct sieve_execute_env *eenv) +{ + return p_new(eenv->pool, struct sieve_execute_state, 1); +} + +static void +sieve_execute_state_free(struct sieve_execute_state **_estate, + struct sieve_execute_env *eenv) +{ + struct sieve_execute_state *estate = *_estate; + const struct sieve_script_env *senv = eenv->scriptenv; + + *_estate = NULL; + + if (senv->duplicate_transaction_rollback != NULL) + senv->duplicate_transaction_rollback(&estate->dup_trans); +} + +void sieve_execute_init(struct sieve_execute_env *eenv, + struct sieve_instance *svinst, pool_t pool, + const struct sieve_message_data *msgdata, + const struct sieve_script_env *senv, + enum sieve_execute_flags flags) +{ + i_zero(eenv); + eenv->svinst = svinst; + eenv->pool = pool; + eenv->flags = flags; + eenv->msgdata = msgdata; + eenv->scriptenv = senv; + + pool_ref(pool); + eenv->event = event_create(svinst->event); + event_add_category(eenv->event, &event_category_sieve_execute); + event_add_str(eenv->event, "message_id", msgdata->id); + if ((flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0) { + /* Make sure important envelope fields are available */ + event_add_str(eenv->event, "mail_from", + smtp_address_encode(msgdata->envelope.mail_from)); + event_add_str(eenv->event, "rcpt_to", + smtp_address_encode(msgdata->envelope.rcpt_to)); + } + + eenv->state = sieve_execute_state_create(eenv); + + eenv->exec_status = senv->exec_status; + if (eenv->exec_status == NULL) + eenv->exec_status = p_new(pool, struct sieve_exec_status, 1); + else + i_zero(eenv->exec_status); +} + +void sieve_execute_finish(struct sieve_execute_env *eenv, int status) +{ + const struct sieve_script_env *senv = eenv->scriptenv; + + if (status == SIEVE_EXEC_OK) { + if (senv->duplicate_transaction_commit != NULL) { + senv->duplicate_transaction_commit( + &eenv->state->dup_trans); + } + } else { + if (senv->duplicate_transaction_rollback != NULL) { + senv->duplicate_transaction_rollback( + &eenv->state->dup_trans); + } + } +} + +void sieve_execute_deinit(struct sieve_execute_env *eenv) +{ + sieve_execute_state_free(&eenv->state, eenv); + event_unref(&eenv->event); + pool_unref(&eenv->pool); +} + +/* + * Checking for duplicates + */ + +static void * +sieve_execute_get_dup_transaction(const struct sieve_execute_env *eenv) +{ + const struct sieve_script_env *senv = eenv->scriptenv; + + if (senv->duplicate_transaction_begin == NULL) + return NULL; + if (eenv->state->dup_trans == NULL) { + eenv->state->dup_trans = + senv->duplicate_transaction_begin(senv); + } + return eenv->state->dup_trans; +} + +bool sieve_execute_duplicate_check_available( + const struct sieve_execute_env *eenv) +{ + const struct sieve_script_env *senv = eenv->scriptenv; + + return (senv->duplicate_transaction_begin != NULL); +} + +int sieve_execute_duplicate_check(const struct sieve_execute_env *eenv, + const void *id, size_t id_size, + bool *duplicate_r) +{ + const struct sieve_script_env *senv = eenv->scriptenv; + void *dup_trans = sieve_execute_get_dup_transaction(eenv); + int ret; + + *duplicate_r = FALSE; + + if (senv->duplicate_check == NULL) + return SIEVE_EXEC_OK; + + e_debug(eenv->svinst->event, "Check duplicate ID"); + + ret = senv->duplicate_check(dup_trans, senv, id, id_size); + switch (ret) { + case SIEVE_DUPLICATE_CHECK_RESULT_EXISTS: + *duplicate_r = TRUE; + break; + case SIEVE_DUPLICATE_CHECK_RESULT_NOT_FOUND: + break; + case SIEVE_DUPLICATE_CHECK_RESULT_FAILURE: + return SIEVE_EXEC_FAILURE; + case SIEVE_DUPLICATE_CHECK_RESULT_TEMP_FAILURE: + return SIEVE_EXEC_TEMP_FAILURE; + } + return SIEVE_EXEC_OK; +} + +void sieve_execute_duplicate_mark(const struct sieve_execute_env *eenv, + const void *id, size_t id_size, time_t time) +{ + const struct sieve_script_env *senv = eenv->scriptenv; + void *dup_trans = sieve_execute_get_dup_transaction(eenv); + + if (senv->duplicate_mark == NULL) + return; + + e_debug(eenv->svinst->event, "Mark ID as duplicate"); + + senv->duplicate_mark(dup_trans, senv, id, id_size, time); +} diff --git a/pigeonhole/src/lib-sieve/sieve-execute.h b/pigeonhole/src/lib-sieve/sieve-execute.h new file mode 100644 index 0000000..8af182b --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-execute.h @@ -0,0 +1,42 @@ +#ifndef SIEVE_EXECUTE_H +#define SIEVE_EXECUTE_H + +#include "sieve-common.h" + +struct sieve_execute_state; + +struct sieve_execute_env { + struct sieve_instance *svinst; + pool_t pool; + + enum sieve_execute_flags flags; + struct event *event; + + const struct sieve_message_data *msgdata; + const struct sieve_script_env *scriptenv; + + struct sieve_execute_state *state; + struct sieve_exec_status *exec_status; +}; + +void sieve_execute_init(struct sieve_execute_env *eenv, + struct sieve_instance *svinst, pool_t pool, + const struct sieve_message_data *msgdata, + const struct sieve_script_env *senv, + enum sieve_execute_flags flags); +void sieve_execute_finish(struct sieve_execute_env *eenv, int status); +void sieve_execute_deinit(struct sieve_execute_env *eenv); + +/* + * Checking for duplicates + */ + +bool sieve_execute_duplicate_check_available( + const struct sieve_execute_env *eenv); +int sieve_execute_duplicate_check(const struct sieve_execute_env *eenv, + const void *id, size_t id_size, + bool *duplicate_r); +void sieve_execute_duplicate_mark(const struct sieve_execute_env *eenv, + const void *id, size_t id_size, time_t time); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-extensions.c b/pigeonhole/src/lib-sieve/sieve-extensions.c new file mode 100644 index 0000000..a1cb810 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-extensions.c @@ -0,0 +1,879 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "mempool.h" +#include "hash.h" +#include "array.h" + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-settings.h" +#include "sieve-extensions.h" + +/* + * Forward declarations + */ + +static void sieve_extension_registry_init(struct sieve_instance *svinst); +static void sieve_extension_registry_deinit(struct sieve_instance *svinst); + +static void sieve_capability_registry_init(struct sieve_instance *svinst); +static void sieve_capability_registry_deinit(struct sieve_instance *svinst); + +static struct sieve_extension *_sieve_extension_register + (struct sieve_instance *svinst, const struct sieve_extension_def *extdef, + bool load, bool required); + +/* + * Instance global context + */ + +struct sieve_extension_registry { + ARRAY(struct sieve_extension *) extensions; + HASH_TABLE(const char *, struct sieve_extension *) extension_index; + HASH_TABLE(const char *, struct sieve_capability_registration *) capabilities_index; + + /* Core language 'extensions' */ + const struct sieve_extension *comparator_extension; + const struct sieve_extension *match_type_extension; + const struct sieve_extension *address_part_extension; + + /* Preloaded extensions */ + ARRAY(const struct sieve_extension *) preloaded_extensions; +}; + +/* + * Pre-loaded 'extensions' + */ + +extern const struct sieve_extension_def comparator_extension; +extern const struct sieve_extension_def match_type_extension; +extern const struct sieve_extension_def address_part_extension; + +/* + * Dummy extensions + */ + +/* FIXME: This is stupid. Define a comparator-* extension and be done with it */ + +const struct sieve_extension_def comparator_i_octet_extension = { + .name = "comparator-i;octet", +}; + +const struct sieve_extension_def comparator_i_ascii_casemap_extension = { + .name = "comparator-i;ascii-casemap", +}; + +/* + * List of native extensions + */ + +/* Dummy extensions */ + +extern const struct sieve_extension_def comparator_i_octet_extension; +extern const struct sieve_extension_def comparator_i_ascii_casemap_extension; + +const struct sieve_extension_def *sieve_dummy_extensions[] = { + &comparator_i_octet_extension, &comparator_i_ascii_casemap_extension +}; + +const unsigned int sieve_dummy_extensions_count = + N_ELEMENTS(sieve_dummy_extensions); + +/* Core */ + +extern const struct sieve_extension_def fileinto_extension; +extern const struct sieve_extension_def reject_extension; +extern const struct sieve_extension_def envelope_extension; +extern const struct sieve_extension_def encoded_character_extension; + +extern const struct sieve_extension_def vacation_extension; +extern const struct sieve_extension_def subaddress_extension; +extern const struct sieve_extension_def comparator_i_ascii_numeric_extension; +extern const struct sieve_extension_def relational_extension; +extern const struct sieve_extension_def regex_extension; +extern const struct sieve_extension_def imap4flags_extension; +extern const struct sieve_extension_def copy_extension; +extern const struct sieve_extension_def include_extension; +extern const struct sieve_extension_def body_extension; +extern const struct sieve_extension_def variables_extension; +extern const struct sieve_extension_def enotify_extension; +extern const struct sieve_extension_def environment_extension; +extern const struct sieve_extension_def mailbox_extension; +extern const struct sieve_extension_def date_extension; +extern const struct sieve_extension_def index_extension; +extern const struct sieve_extension_def ihave_extension; +extern const struct sieve_extension_def duplicate_extension; +extern const struct sieve_extension_def mime_extension; +extern const struct sieve_extension_def foreverypart_extension; +extern const struct sieve_extension_def extracttext_extension; +extern const struct sieve_extension_def mboxmetadata_extension; +extern const struct sieve_extension_def servermetadata_extension; + +const struct sieve_extension_def *sieve_core_extensions[] = { + /* Core extensions */ + &fileinto_extension, &reject_extension, &envelope_extension, + &encoded_character_extension, + + /* 'Plugins' */ + &vacation_extension, &subaddress_extension, + &comparator_i_ascii_numeric_extension, + &relational_extension, ®ex_extension, &imap4flags_extension, + ©_extension, &include_extension, &body_extension, + &variables_extension, &enotify_extension, &environment_extension, + &mailbox_extension, &date_extension, &index_extension, &ihave_extension, + &duplicate_extension, &mime_extension, &foreverypart_extension, + &extracttext_extension +}; + +const unsigned int sieve_core_extensions_count = + N_ELEMENTS(sieve_core_extensions); + +/* Extra; + * These are not enabled by default, e.g. because explicit configuration is + * necessary to make these useful. + */ + +extern const struct sieve_extension_def vacation_seconds_extension; +extern const struct sieve_extension_def spamtest_extension; +extern const struct sieve_extension_def spamtestplus_extension; +extern const struct sieve_extension_def virustest_extension; +extern const struct sieve_extension_def editheader_extension; +extern const struct sieve_extension_def special_use_extension; + +extern const struct sieve_extension_def vnd_debug_extension; +extern const struct sieve_extension_def vnd_environment_extension; +extern const struct sieve_extension_def vnd_report_extension; + +const struct sieve_extension_def *sieve_extra_extensions[] = { + &vacation_seconds_extension, &spamtest_extension, &spamtestplus_extension, + &virustest_extension, &editheader_extension, + &mboxmetadata_extension, &servermetadata_extension, + &special_use_extension, + + /* vnd.dovecot. */ + &vnd_debug_extension, &vnd_environment_extension, &vnd_report_extension +}; + +const unsigned int sieve_extra_extensions_count = + N_ELEMENTS(sieve_extra_extensions); + +/* + * Deprecated extensions + */ + +extern const struct sieve_extension_def imapflags_extension; +extern const struct sieve_extension_def notify_extension; +extern const struct sieve_extension_def vnd_duplicate_extension; + +const struct sieve_extension_def *sieve_deprecated_extensions[] = { + &imapflags_extension, + ¬ify_extension, + &vnd_duplicate_extension +}; + +const unsigned int sieve_deprecated_extensions_count = + N_ELEMENTS(sieve_deprecated_extensions); + +/* + * Unfinished extensions + */ + +#ifdef HAVE_SIEVE_UNFINISHED + +extern const struct sieve_extension_def ereject_extension; + +const struct sieve_extension_def *sieve_unfinished_extensions[] = { + &ereject_extension +}; + +const unsigned int sieve_unfinished_extensions_count = + N_ELEMENTS(sieve_unfinished_extensions); + +#endif /* HAVE_SIEVE_UNFINISHED */ + +/* + * Extensions init/deinit + */ + +bool sieve_extensions_init(struct sieve_instance *svinst) +{ + unsigned int i; + struct sieve_extension_registry *ext_reg = + p_new(svinst->pool, struct sieve_extension_registry, 1); + struct sieve_extension *ext; + + svinst->ext_reg = ext_reg; + + sieve_extension_registry_init(svinst); + sieve_capability_registry_init(svinst); + + /* Preloaded 'extensions' */ + ext_reg->comparator_extension = + sieve_extension_register(svinst, &comparator_extension, TRUE); + ext_reg->match_type_extension = + sieve_extension_register(svinst, &match_type_extension, TRUE); + ext_reg->address_part_extension = + sieve_extension_register(svinst, &address_part_extension, TRUE); + + p_array_init(&ext_reg->preloaded_extensions, svinst->pool, 5); + array_append(&ext_reg->preloaded_extensions, + &ext_reg->comparator_extension, 1); + array_append(&ext_reg->preloaded_extensions, + &ext_reg->match_type_extension, 1); + array_append(&ext_reg->preloaded_extensions, + &ext_reg->address_part_extension, 1); + + /* Pre-load dummy extensions */ + for ( i = 0; i < sieve_dummy_extensions_count; i++ ) { + if ( (ext=_sieve_extension_register + (svinst, sieve_dummy_extensions[i], TRUE, FALSE)) == NULL ) + return FALSE; + + ext->dummy = TRUE; + } + + /* Pre-load core extensions */ + for ( i = 0; i < sieve_core_extensions_count; i++ ) { + if ( sieve_extension_register + (svinst, sieve_core_extensions[i], TRUE) == NULL ) + return FALSE; + } + + /* Pre-load extra extensions */ + for ( i = 0; i < sieve_extra_extensions_count; i++ ) { + if ( sieve_extension_register + (svinst, sieve_extra_extensions[i], FALSE) == NULL ) + return FALSE; + } + + /* Register deprecated extensions */ + for ( i = 0; i < sieve_deprecated_extensions_count; i++ ) { + if ( sieve_extension_register + (svinst, sieve_deprecated_extensions[i], FALSE) == NULL ) + return FALSE; + } + +#ifdef HAVE_SIEVE_UNFINISHED + /* Register unfinished extensions */ + for ( i = 0; i < sieve_unfinished_extensions_count; i++ ) { + if ( sieve_extension_register + (svinst, sieve_unfinished_extensions[i], FALSE) == NULL ) + return FALSE; + } +#endif + + /* More extensions can be added through plugins */ + + return TRUE; +} + +void sieve_extensions_configure(struct sieve_instance *svinst) +{ + const char *extensions; + + /* Apply sieve_extensions configuration */ + + if ( (extensions=sieve_setting_get + (svinst, "sieve_extensions")) != NULL ) + sieve_extensions_set_string(svinst, extensions, FALSE, FALSE); + + /* Apply sieve_global_extensions configuration */ + + if ( (extensions=sieve_setting_get + (svinst, "sieve_global_extensions")) != NULL ) + sieve_extensions_set_string(svinst, extensions, TRUE, FALSE); + + /* Apply sieve_implicit_extensions configuration */ + + if ( (extensions=sieve_setting_get + (svinst, "sieve_implicit_extensions")) != NULL ) + sieve_extensions_set_string(svinst, extensions, FALSE, TRUE); +} + +void sieve_extensions_deinit(struct sieve_instance *svinst) +{ + sieve_extension_registry_deinit(svinst); + sieve_capability_registry_deinit(svinst); +} + +/* + * Pre-loaded extensions + */ + +const struct sieve_extension *const *sieve_extensions_get_preloaded +(struct sieve_instance *svinst, unsigned int *count_r) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + + return array_get(&ext_reg->preloaded_extensions, count_r); +} + +/* + * Extension registry + */ + +static bool _sieve_extension_load(struct sieve_extension *ext) +{ + /* Call load handler */ + if ( ext->def != NULL && ext->def->load != NULL && + !ext->def->load(ext, &ext->context) ) { + e_error(ext->svinst->event, + "failed to load '%s' extension support.", + ext->def->name); + return FALSE; + } + + return TRUE; +} + +static void _sieve_extension_unload(struct sieve_extension *ext) +{ + /* Call unload handler */ + if ( ext->def != NULL && ext->def->unload != NULL ) + ext->def->unload(ext); + ext->context = NULL; +} + +static void sieve_extension_registry_init(struct sieve_instance *svinst) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + + p_array_init(&ext_reg->extensions, svinst->pool, 50); + hash_table_create + (&ext_reg->extension_index, default_pool, 0, str_hash, strcmp); +} + +static void sieve_extension_registry_deinit(struct sieve_instance *svinst) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + struct sieve_extension * const *exts; + unsigned int i, ext_count; + + if ( !hash_table_is_created(ext_reg->extension_index) ) return; + + exts = array_get_modifiable(&ext_reg->extensions, &ext_count); + for ( i = 0; i < ext_count; i++ ) { + _sieve_extension_unload(exts[i]); + } + + hash_table_destroy(&ext_reg->extension_index); +} + +bool sieve_extension_reload(const struct sieve_extension *ext) +{ + struct sieve_extension_registry *ext_reg = ext->svinst->ext_reg; + struct sieve_extension * const *mod_ext; + int ext_id = ext->id; + + /* Let's not just cast the 'const' away */ + if ( ext_id >= 0 && ext_id < (int) array_count(&ext_reg->extensions) ) { + mod_ext = array_idx(&ext_reg->extensions, ext_id); + + return _sieve_extension_load(*mod_ext); + } + + return FALSE; +} + +static struct sieve_extension *sieve_extension_lookup +(struct sieve_instance *svinst, const char *name) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + + return hash_table_lookup(ext_reg->extension_index, name); +} + +static struct sieve_extension *sieve_extension_alloc +(struct sieve_instance *svinst, + const struct sieve_extension_def *extdef) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + struct sieve_extension *ext, **extr; + int ext_id; + + ext_id = (int)array_count(&ext_reg->extensions); + + /* Add extension to the registry */ + extr = array_append_space(&ext_reg->extensions); + *extr = ext = p_new(svinst->pool, struct sieve_extension, 1); + ext->id = ext_id; + ext->def = extdef; + ext->svinst = svinst; + return ext; +} + +static struct sieve_extension *_sieve_extension_register +(struct sieve_instance *svinst, const struct sieve_extension_def *extdef, + bool load, bool required) +{ + struct sieve_extension *ext; + + ext = sieve_extension_lookup(svinst, extdef->name); + + /* Register extension if it is not registered already */ + if ( ext == NULL ) { + ext = sieve_extension_alloc(svinst, extdef); + hash_table_insert + (svinst->ext_reg->extension_index, extdef->name, ext); + + } else if ( ext->overridden ) { + /* Create a dummy */ + ext = sieve_extension_alloc(svinst, extdef); + + } else { + /* Re-register it if it were previously unregistered + * (not going to happen) + */ + i_assert( ext->def == NULL || ext->def == extdef ); + ext->def = extdef; + } + + /* Enable extension */ + if ( load || required ) { + ext->enabled = ( ext->enabled || load ); + + /* Call load handler if extension was not loaded already */ + if ( !ext->loaded ) { + if ( !_sieve_extension_load(ext) ) + return NULL; + } + + ext->loaded = TRUE; + } + + ext->required = ( ext->required || required ); + + return ext; +} + +const struct sieve_extension *sieve_extension_register +(struct sieve_instance *svinst, const struct sieve_extension_def *extdef, + bool load) +{ + return _sieve_extension_register(svinst, extdef, load, FALSE); +} + +void sieve_extension_unregister(const struct sieve_extension *ext) +{ + struct sieve_extension_registry *ext_reg = ext->svinst->ext_reg; + struct sieve_extension * const *mod_ext; + int ext_id = ext->id; + + if ( ext_id >= 0 && ext_id < (int) array_count(&ext_reg->extensions) ) { + mod_ext = array_idx(&ext_reg->extensions, ext_id); + + sieve_extension_capabilities_unregister(*mod_ext); + _sieve_extension_unload(*mod_ext); + (*mod_ext)->loaded = FALSE; + (*mod_ext)->enabled = FALSE; + (*mod_ext)->def = NULL; + } +} + +const struct sieve_extension *sieve_extension_replace +(struct sieve_instance *svinst, const struct sieve_extension_def *extdef, + bool load) +{ + struct sieve_extension *ext; + + ext = sieve_extension_lookup(svinst, extdef->name); + if (ext != NULL) + sieve_extension_unregister(ext); + return sieve_extension_register(svinst, extdef, load); +} + +const struct sieve_extension *sieve_extension_require +(struct sieve_instance *svinst, const struct sieve_extension_def *extdef, + bool load) +{ + return _sieve_extension_register(svinst, extdef, load, TRUE); +} + +void sieve_extension_override +(struct sieve_instance *svinst, const char *name, + const struct sieve_extension *ext) +{ + struct sieve_extension_registry *ext_reg = ext->svinst->ext_reg; + struct sieve_extension * const *mod_ext; + struct sieve_extension *old_ext; + + old_ext = sieve_extension_lookup(svinst, name); + if (old_ext == ext) + return; + i_assert( old_ext == NULL || !old_ext->overridden ); + + i_assert( ext->id >= 0 && + ext->id < (int) array_count(&ext_reg->extensions) ); + mod_ext = array_idx(&ext_reg->extensions, ext->id); + + hash_table_update + (ext_reg->extension_index, name, *mod_ext); + if ( old_ext != NULL ) + old_ext->overridden = TRUE; +} + +unsigned int sieve_extensions_get_count(struct sieve_instance *svinst) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + + return array_count(&ext_reg->extensions); +} + +const struct sieve_extension *const * +sieve_extensions_get_all(struct sieve_instance *svinst, + unsigned int *count_r) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + + return (const struct sieve_extension *const *) + array_get(&ext_reg->extensions, count_r); +} + +const struct sieve_extension *sieve_extension_get_by_id +(struct sieve_instance *svinst, unsigned int ext_id) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + struct sieve_extension * const *ext; + + if ( ext_id < array_count(&ext_reg->extensions) ) { + ext = array_idx(&ext_reg->extensions, ext_id); + + if ( (*ext)->def != NULL && ((*ext)->enabled || (*ext)->required) ) + return *ext; + } + + return NULL; +} + +const struct sieve_extension *sieve_extension_get_by_name +(struct sieve_instance *svinst, const char *name) +{ + const struct sieve_extension *ext; + + if ( *name == '@' ) + return NULL; + + if ( strlen(name) > 128 ) + return NULL; + + ext = sieve_extension_lookup(svinst, name); + if ( ext == NULL || ext->def == NULL || (!ext->enabled && !ext->required)) + return NULL; + + return ext; +} + +static inline bool _sieve_extension_listable(const struct sieve_extension *ext) +{ + return ( ext->enabled && ext->def != NULL && *(ext->def->name) != '@' + && !ext->dummy && !ext->global && !ext->overridden); +} + +const char *sieve_extensions_get_string(struct sieve_instance *svinst) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + string_t *extstr = t_str_new(256); + struct sieve_extension * const *exts; + unsigned int i, ext_count; + + exts = array_get(&ext_reg->extensions, &ext_count); + + if ( ext_count > 0 ) { + i = 0; + + /* Find first listable extension */ + while ( i < ext_count && !_sieve_extension_listable(exts[i]) ) + i++; + + if ( i < ext_count ) { + /* Add first to string */ + str_append(extstr, exts[i]->def->name); + i++; + + /* Add others */ + for ( ; i < ext_count; i++ ) { + if ( _sieve_extension_listable(exts[i]) ) { + str_append_c(extstr, ' '); + str_append(extstr, exts[i]->def->name); + } + } + } + } + + return str_c(extstr); +} + +static void sieve_extension_set_enabled +(struct sieve_extension *ext, bool enabled) +{ + if ( enabled ) { + ext->enabled = TRUE; + + if ( !ext->loaded ) { + (void)_sieve_extension_load(ext); + } + + ext->loaded = TRUE; + } else { + ext->enabled = FALSE; + } +} + +static void sieve_extension_set_global +(struct sieve_extension *ext, bool enabled) +{ + if ( enabled ) { + sieve_extension_set_enabled(ext, TRUE); + ext->global = TRUE; + } else { + ext->global = FALSE; + } +} + +static void sieve_extension_set_implicit +(struct sieve_extension *ext, bool enabled) +{ + if ( enabled ) { + sieve_extension_set_enabled(ext, TRUE); + ext->implicit = TRUE; + } else { + ext->implicit = FALSE; + } +} + +void sieve_extensions_set_string +(struct sieve_instance *svinst, const char *ext_string, + bool global, bool implicit) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + ARRAY(const struct sieve_extension *) enabled_extensions; + ARRAY(const struct sieve_extension *) disabled_extensions; + const struct sieve_extension *const *ext_enabled; + const struct sieve_extension *const *ext_disabled; + struct sieve_extension **exts; + const char **ext_names; + unsigned int i, ext_count, ena_count, dis_count; + bool relative = FALSE; + + if ( ext_string == NULL ) { + if ( global || implicit ) return; + + /* Enable all */ + exts = array_get_modifiable(&ext_reg->extensions, &ext_count); + + for ( i = 0; i < ext_count; i++ ) + sieve_extension_set_enabled(exts[i], TRUE); + + return; + } + + T_BEGIN { + t_array_init(&enabled_extensions, array_count(&ext_reg->extensions)); + t_array_init(&disabled_extensions, array_count(&ext_reg->extensions)); + + ext_names = t_strsplit_spaces(ext_string, " \t"); + + while ( *ext_names != NULL ) { + const char *name = *ext_names; + + ext_names++; + + if ( *name != '\0' ) { + const struct sieve_extension *ext; + char op = '\0'; /* No add/remove operation */ + + if ( *name == '+' /* Add to existing config */ + || *name == '-' ) { /* Remove from existing config */ + op = *name++; + relative = TRUE; + } + + if ( *name == '@' ) + ext = NULL; + else + ext = hash_table_lookup(ext_reg->extension_index, name); + + if ( ext == NULL || ext->def == NULL ) { + e_warning(svinst->event, + "ignored unknown extension '%s' while configuring " + "available extensions", name); + continue; + } + + if ( op == '-' ) + array_append(&disabled_extensions, &ext, 1); + else + array_append(&enabled_extensions, &ext, 1); + } + } + + exts = array_get_modifiable(&ext_reg->extensions, &ext_count); + ext_enabled = array_get(&enabled_extensions, &ena_count); + ext_disabled = array_get(&disabled_extensions, &dis_count); + + /* Set new extension status */ + + for ( i = 0; i < ext_count; i++ ) { + unsigned int j; + bool enabled = FALSE; + + if ( exts[i]->id < 0 || exts[i]->def == NULL || + *(exts[i]->def->name) == '@' ) { + continue; + } + + /* If extensions are specified relative to the default set, + * we first need to check which ones are disabled + */ + + if ( relative ) { + if ( global ) + enabled = exts[i]->global; + else if ( implicit ) + enabled = exts[i]->implicit; + else + enabled = exts[i]->enabled; + + if ( enabled ) { + /* Disable if explicitly disabled */ + for ( j = 0; j < dis_count; j++ ) { + if ( ext_disabled[j]->def == exts[i]->def ) { + enabled = FALSE; + break; + } + } + } + } + + /* Enable if listed with '+' or no prefix */ + + for ( j = 0; j < ena_count; j++ ) { + if ( ext_enabled[j]->def == exts[i]->def ) { + enabled = TRUE; + break; + } + } + + /* Perform actual activation/deactivation */ + if ( global ) { + sieve_extension_set_global(exts[i], enabled); + } else if ( implicit ) { + sieve_extension_set_implicit(exts[i], enabled); + } else { + sieve_extension_set_enabled(exts[i], enabled); + } + } + } T_END; +} + +const struct sieve_extension *sieve_get_match_type_extension + (struct sieve_instance *svinst) +{ + return svinst->ext_reg->match_type_extension; +} + +const struct sieve_extension *sieve_get_comparator_extension + (struct sieve_instance *svinst) +{ + return svinst->ext_reg->comparator_extension; +} + +const struct sieve_extension *sieve_get_address_part_extension + (struct sieve_instance *svinst) +{ + return svinst->ext_reg->address_part_extension; +} + +void sieve_enable_debug_extension(struct sieve_instance *svinst) +{ + (void) sieve_extension_register(svinst, &vnd_debug_extension, TRUE); +} + +/* + * Extension capabilities + */ + +struct sieve_capability_registration { + const struct sieve_extension *ext; + const struct sieve_extension_capabilities *capabilities; +}; + +void sieve_capability_registry_init(struct sieve_instance *svinst) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + + hash_table_create + (&ext_reg->capabilities_index, default_pool, 0, str_hash, strcmp); +} + +void sieve_capability_registry_deinit(struct sieve_instance *svinst) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + + if ( !hash_table_is_created(ext_reg->capabilities_index) ) return; + + hash_table_destroy(&svinst->ext_reg->capabilities_index); +} + +void sieve_extension_capabilities_register +(const struct sieve_extension *ext, + const struct sieve_extension_capabilities *cap) +{ + struct sieve_instance *svinst = ext->svinst; + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + struct sieve_capability_registration *reg; + + reg = hash_table_lookup(ext_reg->capabilities_index, cap->name); + if (reg != NULL) { + /* Already registered */ + return; + } + + reg = p_new(svinst->pool, struct sieve_capability_registration, 1); + reg->ext = ext; + reg->capabilities = cap; + + hash_table_insert(ext_reg->capabilities_index, cap->name, reg); +} + +void sieve_extension_capabilities_unregister +(const struct sieve_extension *ext) +{ + struct sieve_extension_registry *ext_reg = ext->svinst->ext_reg; + struct hash_iterate_context *hictx; + const char *name; + struct sieve_capability_registration *reg; + + hictx = hash_table_iterate_init(ext_reg->capabilities_index); + while ( hash_table_iterate(hictx, ext_reg->capabilities_index, &name, ®) ) { + if ( reg->ext == ext ) + hash_table_remove(ext_reg->capabilities_index, name); + } + hash_table_iterate_deinit(&hictx); +} + +const char *sieve_extension_capabilities_get_string +(struct sieve_instance *svinst, const char *cap_name) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + const struct sieve_capability_registration *cap_reg = + hash_table_lookup(ext_reg->capabilities_index, cap_name); + const struct sieve_extension_capabilities *cap; + + if ( cap_reg == NULL || cap_reg->capabilities == NULL ) + return NULL; + + cap = cap_reg->capabilities; + + if ( cap->get_string == NULL || !cap_reg->ext->enabled ) + return NULL; + + return cap->get_string(cap_reg->ext); +} + + + + diff --git a/pigeonhole/src/lib-sieve/sieve-extensions.h b/pigeonhole/src/lib-sieve/sieve-extensions.h new file mode 100644 index 0000000..961cf41 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-extensions.h @@ -0,0 +1,191 @@ +#ifndef SIEVE_EXTENSIONS_H +#define SIEVE_EXTENSIONS_H + +#include "lib.h" +#include "sieve-common.h" + +/* + * Per-extension object registry + */ + +struct sieve_extension_objects { + const void *objects; + unsigned int count; +}; + +/* + * Extension definition + */ + +struct sieve_extension_def { + const char *name; + + /* Version */ + unsigned int version; + + /* Registration */ + bool (*load)(const struct sieve_extension *ext, void **context); + void (*unload)(const struct sieve_extension *ext); + + /* Compilation */ + bool (*validator_load) + (const struct sieve_extension *ext, struct sieve_validator *validator); + bool (*generator_load) + (const struct sieve_extension *ext, const struct sieve_codegen_env *cgenv); + bool (*interpreter_load) + (const struct sieve_extension *ext, const struct sieve_runtime_env *renv, + sieve_size_t *address); + bool (*binary_load) + (const struct sieve_extension *ext, struct sieve_binary *binary); + + /* Code dump */ + bool (*binary_dump) + (const struct sieve_extension *ext, struct sieve_dumptime_env *denv); + bool (*code_dump) + (const struct sieve_extension *ext, const struct sieve_dumptime_env *denv, + sieve_size_t *address); + + /* Objects */ + struct sieve_extension_objects operations; + struct sieve_extension_objects operands; +}; + +/* Defining opcodes and operands */ + +#define SIEVE_EXT_DEFINE_NO_OBJECTS \ + { NULL, 0 } +#define SIEVE_EXT_DEFINE_OBJECT(OBJ) \ + { &OBJ, 1 } +#define SIEVE_EXT_DEFINE_OBJECTS(OBJS) \ + { OBJS, N_ELEMENTS(OBJS) } + +#define SIEVE_EXT_GET_OBJECTS_COUNT(ext, field) \ + ext->field->count; + +#define SIEVE_EXT_DEFINE_NO_OPERATIONS \ + .operations = SIEVE_EXT_DEFINE_NO_OBJECTS +#define SIEVE_EXT_DEFINE_OPERATION(OP) \ + .operations = SIEVE_EXT_DEFINE_OBJECT(OP) +#define SIEVE_EXT_DEFINE_OPERATIONS(OPS) \ + .operations = SIEVE_EXT_DEFINE_OBJECTS(OPS) + +#define SIEVE_EXT_DEFINE_NO_OPERANDS \ + .operands = SIEVE_EXT_DEFINE_NO_OBJECTS +#define SIEVE_EXT_DEFINE_OPERAND(OP) \ + .operands = SIEVE_EXT_DEFINE_OBJECT(OP) +#define SIEVE_EXT_DEFINE_OPERANDS(OPS) \ + .operands = SIEVE_EXT_DEFINE_OBJECTS(OPS) + +/* + * Extension instance + */ + +struct sieve_extension { + const struct sieve_extension_def *def; + int id; + + struct sieve_instance *svinst; + void *context; + + bool required:1; + bool loaded:1; + bool enabled:1; + bool dummy:1; + bool global:1; + bool implicit:1; + bool overridden:1; +}; + +#define sieve_extension_is(ext, definition) \ + ( (ext)->def == &(definition) ) +#define sieve_extension_name(ext) \ + ((ext)->def->name) +#define sieve_extension_name_is(ext, _name) \ + ( strcmp((ext)->def->name, (_name)) == 0 ) +#define sieve_extension_version(ext) \ + ((ext)->def->version) +#define sieve_extension_version_is(ext, _version) \ + ((ext)->def->version == (_version)) + +/* + * Extensions init/deinit + */ + +bool sieve_extensions_init(struct sieve_instance *svinst); +void sieve_extensions_configure(struct sieve_instance *svinst); +void sieve_extensions_deinit(struct sieve_instance *svinst); + +/* + * Pre-loaded extensions + */ + +const struct sieve_extension *const *sieve_extensions_get_preloaded + (struct sieve_instance *svinst, unsigned int *count_r); + +/* + * Extension registry + */ + +const struct sieve_extension *sieve_extension_register + (struct sieve_instance *svinst, const struct sieve_extension_def *extension, + bool load); +const struct sieve_extension *sieve_extension_require + (struct sieve_instance *svinst, const struct sieve_extension_def *extension, + bool load); +bool sieve_extension_reload(const struct sieve_extension *ext); + +void sieve_extension_unregister(const struct sieve_extension *ext); + +const struct sieve_extension *sieve_extension_replace + (struct sieve_instance *svinst, + const struct sieve_extension_def *extdef, + bool load); +void sieve_extension_override + (struct sieve_instance *svinst, const char *name, + const struct sieve_extension *ext); + +unsigned int sieve_extensions_get_count(struct sieve_instance *svinst); +const struct sieve_extension *const * +sieve_extensions_get_all(struct sieve_instance *svinst, + unsigned int *count_r); + +const struct sieve_extension *sieve_extension_get_by_id + (struct sieve_instance *svinst, unsigned int ext_id); +const struct sieve_extension *sieve_extension_get_by_name + (struct sieve_instance *svinst, const char *name); + +const char *sieve_extensions_get_string + (struct sieve_instance *svinst); +void sieve_extensions_set_string + (struct sieve_instance *svinst, const char *ext_string, + bool global, bool implicit); + +const struct sieve_extension *sieve_get_match_type_extension + (struct sieve_instance *svinst); +const struct sieve_extension *sieve_get_comparator_extension + (struct sieve_instance *svinst); +const struct sieve_extension *sieve_get_address_part_extension + (struct sieve_instance *svinst); + +void sieve_enable_debug_extension(struct sieve_instance *svinst); + +/* + * Capability registries + */ + +struct sieve_extension_capabilities { + const char *name; + + const char *(*get_string)(const struct sieve_extension *ext); +}; + +void sieve_extension_capabilities_register + (const struct sieve_extension *ext, + const struct sieve_extension_capabilities *cap); +void sieve_extension_capabilities_unregister + (const struct sieve_extension *ext); + +const char *sieve_extension_capabilities_get_string + (struct sieve_instance *svinst, const char *cap_name); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-generator.c b/pigeonhole/src/lib-sieve/sieve-generator.c new file mode 100644 index 0000000..ebc06ed --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-generator.c @@ -0,0 +1,578 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "mempool.h" + +#include "sieve-common.h" +#include "sieve-script.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-code.h" +#include "sieve-binary.h" + +#include "sieve-generator.h" + +/* + * Jump list + */ + +struct sieve_jumplist * +sieve_jumplist_create(pool_t pool, struct sieve_binary_block *sblock) +{ + struct sieve_jumplist *jlist; + + jlist = p_new(pool, struct sieve_jumplist, 1); + jlist->block = sblock; + p_array_init(&jlist->jumps, pool, 4); + + return jlist; +} + +void sieve_jumplist_init_temp(struct sieve_jumplist *jlist, + struct sieve_binary_block *sblock) +{ + jlist->block = sblock; + t_array_init(&jlist->jumps, 4); +} + +void sieve_jumplist_reset(struct sieve_jumplist *jlist) +{ + array_clear(&jlist->jumps); +} + +void sieve_jumplist_add(struct sieve_jumplist *jlist, sieve_size_t jump) +{ + array_append(&jlist->jumps, &jump, 1); +} + +void sieve_jumplist_resolve(struct sieve_jumplist *jlist) +{ + unsigned int i; + + for (i = 0; i < array_count(&jlist->jumps); i++) { + const sieve_size_t *jump = array_idx(&jlist->jumps, i); + + sieve_binary_resolve_offset(jlist->block, *jump); + } +} + +/* + * Code Generator + */ + +struct sieve_generator { + pool_t pool; + + struct sieve_instance *instance; + + struct sieve_error_handler *ehandler; + + struct sieve_codegen_env genenv; + struct sieve_binary_debug_writer *dwriter; + + ARRAY(void *) ext_contexts; +}; + +struct sieve_generator * +sieve_generator_create(struct sieve_ast *ast, + struct sieve_error_handler *ehandler, + enum sieve_compile_flags flags) +{ + pool_t pool; + struct sieve_generator *gentr; + struct sieve_script *script; + struct sieve_instance *svinst; + + pool = pool_alloconly_create("sieve_generator", 4096); + gentr = p_new(pool, struct sieve_generator, 1); + gentr->pool = pool; + + gentr->ehandler = ehandler; + sieve_error_handler_ref(ehandler); + + gentr->genenv.gentr = gentr; + gentr->genenv.flags = flags; + gentr->genenv.ast = ast; + sieve_ast_ref(ast); + + script = sieve_ast_script(ast); + svinst = sieve_script_svinst(script); + + gentr->genenv.script = script; + gentr->genenv.svinst = svinst; + + /* Setup storage for extension contexts */ + p_array_init(&gentr->ext_contexts, pool, + sieve_extensions_get_count(svinst)); + + return gentr; +} + +void sieve_generator_free(struct sieve_generator **gentr) +{ + sieve_ast_unref(&(*gentr)->genenv.ast); + + sieve_error_handler_unref(&(*gentr)->ehandler); + sieve_binary_debug_writer_deinit(&(*gentr)->dwriter); + + sieve_binary_unref(&(*gentr)->genenv.sbin); + + pool_unref(&((*gentr)->pool)); + + *gentr = NULL; +} + +/* + * Accessors + */ + +struct sieve_error_handler * +sieve_generator_error_handler(struct sieve_generator *gentr) +{ + return gentr->ehandler; +} + +pool_t sieve_generator_pool(struct sieve_generator *gentr) +{ + return gentr->pool; +} + +struct sieve_script *sieve_generator_script(struct sieve_generator *gentr) +{ + return gentr->genenv.script; +} + +struct sieve_binary *sieve_generator_get_binary(struct sieve_generator *gentr) +{ + return gentr->genenv.sbin; +} + +struct sieve_binary_block * +sieve_generator_get_block(struct sieve_generator *gentr) +{ + return gentr->genenv.sblock; +} + +/* + * Extension support + */ + +void sieve_generator_extension_set_context(struct sieve_generator *gentr, + const struct sieve_extension *ext, + void *context) +{ + if (ext->id < 0) + return; + + array_idx_set(&gentr->ext_contexts, (unsigned int) ext->id, &context); +} + +const void * +sieve_generator_extension_get_context(struct sieve_generator *gentr, + const struct sieve_extension *ext) +{ + void * const *ctx; + + if (ext->id < 0 || ext->id >= (int) array_count(&gentr->ext_contexts)) + return NULL; + + ctx = array_idx(&gentr->ext_contexts, (unsigned int) ext->id); + + return *ctx; +} + +/* + * Code generation API + */ + +static void +sieve_generate_debug_from_ast_node(const struct sieve_codegen_env *cgenv, + struct sieve_ast_node *ast_node) +{ + sieve_size_t address = sieve_binary_block_get_size(cgenv->sblock); + unsigned int line = sieve_ast_node_line(ast_node); + + sieve_binary_debug_emit(cgenv->gentr->dwriter, address, line, 0); +} + +static void +sieve_generate_debug_from_ast_argument(const struct sieve_codegen_env *cgenv, + struct sieve_ast_argument *ast_arg) +{ + sieve_size_t address = sieve_binary_block_get_size(cgenv->sblock); + unsigned int line = sieve_ast_argument_line(ast_arg); + + sieve_binary_debug_emit(cgenv->gentr->dwriter, address, line, 0); +} + +bool sieve_generate_argument(const struct sieve_codegen_env *cgenv, + struct sieve_ast_argument *arg, + struct sieve_command *cmd) +{ + const struct sieve_argument_def *arg_def; + + if (arg->argument == NULL || arg->argument->def == NULL) + return FALSE; + + arg_def = arg->argument->def; + + if (arg_def->generate == NULL) + return TRUE; + + sieve_generate_debug_from_ast_argument(cgenv, arg); + + return arg_def->generate(cgenv, arg, cmd); +} + +bool sieve_generate_arguments(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd, + struct sieve_ast_argument **last_arg_r) +{ + enum { ARG_START, ARG_OPTIONAL, ARG_POSITIONAL } state = ARG_START; + struct sieve_ast_argument *arg = + sieve_ast_argument_first(cmd->ast_node); + + /* Generate all arguments with assigned generator function */ + + while (arg != NULL) { + const struct sieve_argument *argument; + const struct sieve_argument_def *arg_def; + + if (arg->argument == NULL || arg->argument->def == NULL) + return FALSE; + + argument = arg->argument; + arg_def = argument->def; + + switch (state) { + case ARG_START: + if (argument->id_code == 0) + state = ARG_POSITIONAL; + else { + /* Mark start of optional operands with 0 + operand identifier */ + sieve_binary_emit_byte(cgenv->sblock, + SIEVE_OPERAND_OPTIONAL); + + /* Emit argument id for optional operand */ + sieve_binary_emit_byte( + cgenv->sblock, + (unsigned char)argument->id_code); + + state = ARG_OPTIONAL; + } + break; + case ARG_OPTIONAL: + if (argument->id_code == 0) + state = ARG_POSITIONAL; + + /* Emit argument id for optional operand (0 marks the + end of the optionals) */ + sieve_binary_emit_byte( + cgenv->sblock, + (unsigned char)argument->id_code); + break; + case ARG_POSITIONAL: + if (argument->id_code != 0) + return FALSE; + break; + } + + /* Call the generation function for the argument */ + if (arg_def->generate != NULL) { + sieve_generate_debug_from_ast_argument(cgenv, arg); + + if (!arg_def->generate(cgenv, arg, cmd)) + return FALSE; + } else if (state == ARG_POSITIONAL) { + break; + } + + arg = sieve_ast_argument_next(arg); + } + + /* Mark end of optional list if it is still open */ + if (state == ARG_OPTIONAL) + sieve_binary_emit_byte(cgenv->sblock, 0); + + if (last_arg_r != NULL) + *last_arg_r = arg; + + return TRUE; +} + +bool sieve_generate_argument_parameters(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd, + struct sieve_ast_argument *arg) +{ + struct sieve_ast_argument *param = arg->parameters; + + /* Generate all parameters with assigned generator function */ + + while (param != NULL) { + if (param->argument != NULL && param->argument->def != NULL) { + const struct sieve_argument_def *parameter = + param->argument->def; + + /* Call the generation function for the parameter */ + if (parameter->generate != NULL) { + sieve_generate_debug_from_ast_argument( + cgenv, param); + + if (!parameter->generate(cgenv, param, cmd)) + return FALSE; + } + } + + param = sieve_ast_argument_next(param); + } + + return TRUE; +} + +bool sieve_generate_test(const struct sieve_codegen_env *cgenv, + struct sieve_ast_node *tst_node, + struct sieve_jumplist *jlist, bool jump_true) +{ + struct sieve_command *test; + const struct sieve_command_def *tst_def; + + i_assert(tst_node->command != NULL && tst_node->command->def != NULL); + + test = tst_node->command; + tst_def = test->def; + + if (tst_def->control_generate != NULL) { + sieve_generate_debug_from_ast_node(cgenv, tst_node); + + if (tst_def->control_generate(cgenv, test, jlist, jump_true)) + return TRUE; + + return FALSE; + } + + if (tst_def->generate != NULL) { + sieve_generate_debug_from_ast_node(cgenv, tst_node); + + if (tst_def->generate(cgenv, test)) { + + if (jump_true) { + sieve_operation_emit(cgenv->sblock, NULL, + &sieve_jmptrue_operation); + } else { + sieve_operation_emit(cgenv->sblock, NULL, + &sieve_jmpfalse_operation); + } + sieve_jumplist_add( + jlist, + sieve_binary_emit_offset(cgenv->sblock, 0)); + return TRUE; + } + + return FALSE; + } + + return TRUE; +} + +static bool +sieve_generate_command(const struct sieve_codegen_env *cgenv, + struct sieve_ast_node *cmd_node) +{ + struct sieve_command *command; + const struct sieve_command_def *cmd_def; + + i_assert(cmd_node->command != NULL && cmd_node->command->def != NULL); + + command = cmd_node->command; + cmd_def = command->def; + + if (cmd_def->generate != NULL) { + sieve_generate_debug_from_ast_node(cgenv, cmd_node); + + return cmd_def->generate(cgenv, command); + } + + return TRUE; +} + +bool sieve_generate_block(const struct sieve_codegen_env *cgenv, + struct sieve_ast_node *block) +{ + bool result = TRUE; + struct sieve_ast_node *cmd_node; + + T_BEGIN { + cmd_node = sieve_ast_command_first(block); + while (result && cmd_node != NULL) { + result = sieve_generate_command(cgenv, cmd_node); + cmd_node = sieve_ast_command_next(cmd_node); + } + } T_END; + + return result; +} + +struct sieve_binary * +sieve_generator_run(struct sieve_generator *gentr, + struct sieve_binary_block **sblock_r) +{ + bool topmost = (sblock_r == NULL || *sblock_r == NULL); + struct sieve_binary *sbin; + struct sieve_binary_block *sblock, *debug_block; + const struct sieve_extension *const *extensions; + unsigned int i, ext_count; + bool result = TRUE; + + /* Initialize */ + + if (topmost) { + sbin = sieve_binary_create_new( + sieve_ast_script(gentr->genenv.ast)); + sblock = sieve_binary_block_get( + sbin, SBIN_SYSBLOCK_MAIN_PROGRAM); + } else { + sblock = *sblock_r; + sbin = sieve_binary_block_get_binary(sblock); + } + + i_assert(sbin != NULL); + + gentr->genenv.sbin = sbin; + gentr->genenv.sblock = sblock; + sieve_binary_ref(gentr->genenv.sbin); + + /* Create debug block */ + debug_block = sieve_binary_block_create(sbin); + gentr->dwriter = sieve_binary_debug_writer_init(debug_block); + (void)sieve_binary_emit_unsigned( + sblock, sieve_binary_block_get_id(debug_block)); + + /* Load extensions linked to the AST and emit a list in code */ + extensions = sieve_ast_extensions_get(gentr->genenv.ast, &ext_count); + (void) sieve_binary_emit_unsigned(sblock, ext_count); + for (i = 0; i < ext_count; i++) { + const struct sieve_extension *ext = extensions[i]; + bool deferred; + + /* Link to binary */ + (void)sieve_binary_extension_link(sbin, ext); + + /* Emit */ + sieve_binary_emit_extension(sblock, ext, 0); + + /* Emit deferred flag */ + deferred = !sieve_ast_extension_is_required( + gentr->genenv.ast, ext); + sieve_binary_emit_byte(sblock, (deferred ? 1 : 0)); + + /* Load */ + if (ext->def != NULL && ext->def->generator_load != NULL && + !ext->def->generator_load(ext, &gentr->genenv)) + result = FALSE; + } + + /* Generate code */ + + if (result) { + if (!sieve_generate_block(&gentr->genenv, + sieve_ast_root(gentr->genenv.ast))) { + result = FALSE; + } else if (topmost) { + sieve_binary_activate(sbin); + } + } + + /* Cleanup */ + + sieve_binary_unref(&gentr->genenv.sbin); + gentr->genenv.sblock = NULL; + + if (!result) { + if (topmost) { + sieve_binary_unref(&sbin); + if (sblock_r != NULL) + *sblock_r = NULL; + } + sbin = NULL; + } else { + if (sblock_r != NULL) + *sblock_r = sblock; + } + + return sbin; +} + +/* + * Error handling + */ + +#undef sieve_generator_error +void sieve_generator_error(struct sieve_generator *gentr, + const char *csrc_filename, unsigned int csrc_linenum, + unsigned int source_line, const char *fmt, ...) +{ + struct sieve_error_params params = { + .log_type = LOG_TYPE_ERROR, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + }; + va_list args; + + params.location = + sieve_error_script_location(gentr->genenv.script, source_line); + + va_start(args, fmt); + sieve_logv(gentr->ehandler, ¶ms, fmt, args); + va_end(args); +} + +#undef sieve_generator_warning +void sieve_generator_warning(struct sieve_generator *gentr, + const char *csrc_filename, + unsigned int csrc_linenum, + unsigned int source_line, const char *fmt, ...) +{ + struct sieve_error_params params = { + .log_type = LOG_TYPE_WARNING, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + }; + va_list args; + + params.location = + sieve_error_script_location(gentr->genenv.script, source_line); + + va_start(args, fmt); + sieve_logv(gentr->ehandler, ¶ms, fmt, args); + va_end(args); +} + +#undef sieve_generator_critical +void sieve_generator_critical(struct sieve_generator *gentr, + const char *csrc_filename, + unsigned int csrc_linenum, + unsigned int source_line, const char *fmt, ...) +{ + struct sieve_error_params params = { + .log_type = LOG_TYPE_ERROR, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + }; + va_list args; + + params.location = + sieve_error_script_location(gentr->genenv.script, source_line); + + va_start(args, fmt); + sieve_criticalv(gentr->genenv.svinst, gentr->ehandler, ¶ms, + "Code generation failed", fmt, args); + va_end(args); +} diff --git a/pigeonhole/src/lib-sieve/sieve-generator.h b/pigeonhole/src/lib-sieve/sieve-generator.h new file mode 100644 index 0000000..fab2023 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-generator.h @@ -0,0 +1,121 @@ +#ifndef SIEVE_GENERATOR_H +#define SIEVE_GENERATOR_H + +#include "sieve-common.h" + +/* + * Code generator + */ + +struct sieve_generator; + +struct sieve_codegen_env { + struct sieve_generator *gentr; + + struct sieve_instance *svinst; + enum sieve_compile_flags flags; + + struct sieve_script *script; + struct sieve_ast *ast; + + struct sieve_binary *sbin; + struct sieve_binary_block *sblock; +}; + +struct sieve_generator * +sieve_generator_create(struct sieve_ast *ast, + struct sieve_error_handler *ehandler, + enum sieve_compile_flags flags); +void sieve_generator_free(struct sieve_generator **generator); + +/* + * Accessors + */ + +struct sieve_error_handler * +sieve_generator_error_handler(struct sieve_generator *gentr); +pool_t sieve_generator_pool(struct sieve_generator *gentr); +struct sieve_script *sieve_generator_script(struct sieve_generator *gentr); +struct sieve_binary *sieve_generator_get_binary(struct sieve_generator *gentr); +struct sieve_binary_block * +sieve_generator_get_block(struct sieve_generator *gentr); + +/* + * Extension support + */ + +void sieve_generator_extension_set_context(struct sieve_generator *gentr, + const struct sieve_extension *ext, + void *context); +const void * +sieve_generator_extension_get_context(struct sieve_generator *gentr, + const struct sieve_extension *ext); + +/* + * Jump list + */ + +struct sieve_jumplist { + pool_t pool; + struct sieve_binary_block *block; + ARRAY(sieve_size_t) jumps; +}; + +struct sieve_jumplist * +sieve_jumplist_create(pool_t pool, struct sieve_binary_block *sblock); +void sieve_jumplist_init_temp(struct sieve_jumplist *jlist, + struct sieve_binary_block *sblock); +void sieve_jumplist_reset(struct sieve_jumplist *jlist); +void sieve_jumplist_add(struct sieve_jumplist *jlist, sieve_size_t jump); +void sieve_jumplist_resolve(struct sieve_jumplist *jlist); + +/* + * Code generation API + */ + +bool sieve_generate_argument(const struct sieve_codegen_env *cgenv, + struct sieve_ast_argument *arg, + struct sieve_command *cmd); +bool sieve_generate_arguments(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd, + struct sieve_ast_argument **last_arg_r); +bool sieve_generate_argument_parameters(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd, + struct sieve_ast_argument *arg); + +bool sieve_generate_block(const struct sieve_codegen_env *cgenv, + struct sieve_ast_node *block); +bool sieve_generate_test(const struct sieve_codegen_env *cgenv, + struct sieve_ast_node *tst_node, + struct sieve_jumplist *jlist, bool jump_true); +struct sieve_binary * +sieve_generator_run(struct sieve_generator *gentr, + struct sieve_binary_block **sblock_r); + +/* + * Error handling + */ + +void sieve_generator_error(struct sieve_generator *gentr, + const char *csrc_filename, unsigned int csrc_linenum, + unsigned int source_line, const char *fmt, ...) + ATTR_FORMAT(5, 6); +#define sieve_generator_error(gentr, ...) \ + sieve_generator_error(gentr, __FILE__, __LINE__, __VA_ARGS__) +void sieve_generator_warning(struct sieve_generator *gentr, + const char *csrc_filename, + unsigned int csrc_linenum, + unsigned int source_line, const char *fmt, ...) + ATTR_FORMAT(5, 6); +#define sieve_generator_warning(gentr, ...) \ + sieve_generator_warning(gentr, __FILE__, __LINE__, __VA_ARGS__) +void sieve_generator_critical(struct sieve_generator *gentr, + const char *csrc_filename, + unsigned int csrc_linenum, + unsigned int source_line, const char *fmt, ...) + ATTR_FORMAT(5, 6); +#define sieve_generator_critical(gentr, ...) \ + sieve_generator_critical(gentr, __FILE__, __LINE__, __VA_ARGS__) + +#endif + diff --git a/pigeonhole/src/lib-sieve/sieve-interpreter.c b/pigeonhole/src/lib-sieve/sieve-interpreter.c new file mode 100644 index 0000000..274e142 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-interpreter.c @@ -0,0 +1,1196 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "ostream.h" +#include "mempool.h" +#include "array.h" +#include "hash.h" +#include "cpu-limit.h" +#include "mail-storage.h" + +#include "sieve-common.h" +#include "sieve-limits.h" +#include "sieve-script.h" +#include "sieve-error.h" +#include "sieve-extensions.h" +#include "sieve-message.h" +#include "sieve-commands.h" +#include "sieve-code.h" +#include "sieve-actions.h" +#include "sieve-generator.h" +#include "sieve-binary.h" +#include "sieve-result.h" +#include "sieve-comparators.h" +#include "sieve-runtime-trace.h" + +#include "sieve-interpreter.h" + +#include <string.h> + +static struct event_category event_category_sieve_runtime = { + .parent = &event_category_sieve, + .name = "sieve-runtime", +}; + +/* + * Interpreter extension + */ + +struct sieve_interpreter_extension_reg { + const struct sieve_interpreter_extension *intext; + const struct sieve_extension *ext; + + void *context; + + bool deferred:1; + bool started:1; +}; + +/* + * Code loop + */ + +struct sieve_interpreter_loop { + unsigned int level; + sieve_size_t begin, end; + const struct sieve_extension_def *ext_def; + pool_t pool; + void *context; +}; + +/* + * Interpreter + */ + +struct sieve_interpreter { + pool_t pool; + struct sieve_interpreter *parent; + + /* Runtime data for extensions */ + ARRAY(struct sieve_interpreter_extension_reg) extensions; + + sieve_size_t reset_vector; + + /* Execution status */ + sieve_size_t pc; /* Program counter */ + + /* Loop stack */ + ARRAY(struct sieve_interpreter_loop) loop_stack; + sieve_size_t loop_limit; + unsigned int parent_loop_level; + + /* Runtime environment */ + struct sieve_runtime_env runenv; + struct sieve_runtime_trace trace; + struct sieve_resource_usage rusage; + + /* Current operation */ + struct sieve_operation oprtn; + + /* Location information */ + struct sieve_binary_debug_reader *dreader; + unsigned int command_line; + + bool running:1; /* Interpreter is running + (may be interrupted) */ + bool interrupted:1; /* Interpreter interrupt requested */ + bool test_result:1; /* Result of previous test command */ +}; + +static struct sieve_interpreter * +_sieve_interpreter_create(struct sieve_binary *sbin, + struct sieve_binary_block *sblock, + struct sieve_script *script, + struct sieve_interpreter *parent, + const struct sieve_execute_env *eenv, + struct sieve_error_handler *ehandler) ATTR_NULL(3, 4) +{ + const struct sieve_script_env *senv = eenv->scriptenv; + unsigned int i, ext_count; + struct sieve_interpreter *interp; + pool_t pool; + struct sieve_instance *svinst; + const struct sieve_extension *const *ext_preloaded; + unsigned int debug_block_id; + sieve_size_t *address; + bool success = TRUE; + + pool = pool_alloconly_create("sieve_interpreter", 4096); + interp = p_new(pool, struct sieve_interpreter, 1); + interp->parent = parent; + interp->pool = pool; + + interp->runenv.ehandler = ehandler; + sieve_error_handler_ref(ehandler); + + interp->runenv.exec_env = eenv; + interp->runenv.interp = interp; + interp->runenv.oprtn = &interp->oprtn; + interp->runenv.sbin = sbin; + interp->runenv.sblock = sblock; + sieve_binary_ref(sbin); + + interp->runenv.event = event_create(eenv->event); + event_add_category(interp->runenv.event, &event_category_sieve_runtime); + event_add_str(interp->runenv.event, "script_name", + sieve_binary_script_name(sbin)); + event_add_str(interp->runenv.event, "script_location", + sieve_binary_script_location(sbin)); + event_add_str(interp->runenv.event, "binary_path", + sieve_binary_path(sbin)); + + svinst = sieve_binary_svinst(sbin); + + if (senv->trace_log != NULL) { + interp->trace.log = senv->trace_log; + interp->trace.config = senv->trace_config; + interp->trace.indent = 0; + interp->runenv.trace = &interp->trace; + } + + if (script == NULL) + interp->runenv.script = sieve_binary_script(sbin); + else + interp->runenv.script = script; + + interp->runenv.pc = 0; + address = &(interp->runenv.pc); + + sieve_runtime_trace_begin(&(interp->runenv)); + + p_array_init(&interp->extensions, pool, + sieve_extensions_get_count(svinst)); + + interp->parent_loop_level = 0; + if (parent != NULL && array_is_created(&parent->loop_stack)) { + interp->parent_loop_level = parent->parent_loop_level + + array_count(&parent->loop_stack); + } + + /* Pre-load core language features implemented as 'extensions' */ + ext_preloaded = sieve_extensions_get_preloaded(svinst, &ext_count); + for (i = 0; i < ext_count; i++) { + const struct sieve_extension_def *ext_def = + ext_preloaded[i]->def; + + if (ext_def != NULL && ext_def->interpreter_load != NULL) { + (void)ext_def->interpreter_load(ext_preloaded[i], + &interp->runenv, + address); + } + } + + /* Load debug block */ + if (sieve_binary_read_unsigned(sblock, address, &debug_block_id)) { + struct sieve_binary_block *debug_block = + sieve_binary_block_get(sbin, debug_block_id); + + if (debug_block == NULL) { + sieve_runtime_trace_error(&interp->runenv, + "invalid id for debug block"); + success = FALSE; + } else { + /* Initialize debug reader */ + interp->dreader = + sieve_binary_debug_reader_init(debug_block); + } + } + + /* Load other extensions listed in code */ + if (success && sieve_binary_read_unsigned(sblock, address, + &ext_count)) { + + for (i = 0; i < ext_count; i++) { + unsigned int code = 0, deferred; + struct sieve_interpreter_extension_reg *reg; + const struct sieve_extension *ext; + + if (!sieve_binary_read_extension(sblock, address, + &code, &ext) || + !sieve_binary_read_byte(sblock, address, + &deferred)) { + success = FALSE; + break; + } + + if (deferred > 0 && ext->id >= 0) { + reg = array_idx_get_space( + &interp->extensions, + (unsigned int)ext->id); + reg->deferred = TRUE; + } + + if (ext->def != NULL) { + if (ext->global && + (eenv->flags & SIEVE_EXECUTE_FLAG_NOGLOBAL) != 0) { + sieve_runtime_error(&interp->runenv, NULL, + "failed to enable extension `%s': " + "its use is restricted to global scripts", + sieve_extension_name(ext)); + success = FALSE; + break; + } + + if (ext->def->interpreter_load != NULL && + !ext->def->interpreter_load(ext, &interp->runenv, + address)) { + success = FALSE; + break; + } + } + } + } else { + success = FALSE; + } + + if (!success) { + sieve_interpreter_free(&interp); + interp = NULL; + } else { + interp->reset_vector = *address; + } + + return interp; +} + +struct sieve_interpreter * +sieve_interpreter_create(struct sieve_binary *sbin, + struct sieve_interpreter *parent, + const struct sieve_execute_env *eenv, + struct sieve_error_handler *ehandler) +{ + struct sieve_binary_block *sblock; + + if ((sblock = sieve_binary_block_get( + sbin, SBIN_SYSBLOCK_MAIN_PROGRAM)) == NULL) + return NULL; + + return _sieve_interpreter_create(sbin, sblock, NULL, parent, eenv, + ehandler); +} + +struct sieve_interpreter * +sieve_interpreter_create_for_block(struct sieve_binary_block *sblock, + struct sieve_script *script, + struct sieve_interpreter *parent, + const struct sieve_execute_env *eenv, + struct sieve_error_handler *ehandler) +{ + if (sblock == NULL) return NULL; + + return _sieve_interpreter_create(sieve_binary_block_get_binary(sblock), + sblock, script, parent, eenv, + ehandler); +} + +void sieve_interpreter_free(struct sieve_interpreter **_interp) +{ + struct sieve_interpreter *interp = *_interp; + struct sieve_runtime_env *renv = &interp->runenv; + const struct sieve_interpreter_extension_reg *eregs; + struct sieve_interpreter_loop *loops; + unsigned int count, i; + + if (interp->running) { + struct event_passthrough *e = + event_create_passthrough(interp->runenv.event)-> + set_name("sieve_runtime_script_finished")-> + add_str("error", "Aborted"); + e_debug(e->event(), "Aborted running script `%s'", + sieve_binary_source(interp->runenv.sbin)); + + interp->running = FALSE; + } + + if (array_is_created(&interp->loop_stack)) { + loops = array_get_modifiable(&interp->loop_stack, &count); + for (i = 0; i < count; i++) + pool_unref(&loops[i].pool); + } + + interp->trace.indent = 0; + sieve_runtime_trace_end(renv); + + /* Signal registered extensions that the interpreter is being destroyed */ + eregs = array_get(&interp->extensions, &count); + for (i = 0; i < count; i++) { + if (eregs[i].intext != NULL && eregs[i].intext->free != NULL) { + eregs[i].intext->free(eregs[i].ext, interp, + eregs[i].context); + } + } + + sieve_binary_debug_reader_deinit(&interp->dreader); + sieve_binary_unref(&renv->sbin); + sieve_error_handler_unref(&renv->ehandler); + event_unref(&renv->event); + + pool_unref(&interp->pool); + *_interp = NULL; +} + +/* + * Accessors + */ + +pool_t sieve_interpreter_pool(struct sieve_interpreter *interp) +{ + return interp->pool; +} + +struct sieve_interpreter * +sieve_interpreter_get_parent(struct sieve_interpreter *interp) +{ + return interp->parent; +} + +struct sieve_script *sieve_interpreter_script(struct sieve_interpreter *interp) +{ + return interp->runenv.script; +} + +struct sieve_error_handler * +sieve_interpreter_get_error_handler(struct sieve_interpreter *interp) +{ + return interp->runenv.ehandler; +} + +struct sieve_instance * +sieve_interpreter_svinst(struct sieve_interpreter *interp) +{ + return interp->runenv.exec_env->svinst; +} + +/* Do not use this function for normal sieve extensions. This is intended for + * the testsuite only. + */ +void sieve_interpreter_set_result(struct sieve_interpreter *interp, + struct sieve_result *result) +{ + sieve_result_unref(&interp->runenv.result); + interp->runenv.result = result; + interp->runenv.msgctx = sieve_result_get_message_context(result); + sieve_result_ref(result); +} + +/* + * Source location + */ + +unsigned int +sieve_runtime_get_source_location(const struct sieve_runtime_env *renv, + sieve_size_t code_address) +{ + struct sieve_interpreter *interp = renv->interp; + + if (interp->dreader == NULL) + return 0; + + if (interp->command_line == 0) { + interp->command_line = + sieve_binary_debug_read_line(interp->dreader, + renv->oprtn->address); + } + + return sieve_binary_debug_read_line(interp->dreader, code_address); +} + +unsigned int +sieve_runtime_get_command_location(const struct sieve_runtime_env *renv) +{ + struct sieve_interpreter *interp = renv->interp; + + if (interp->dreader == NULL) + return 0; + + if (interp->command_line == 0) { + interp->command_line = + sieve_binary_debug_read_line(interp->dreader, + renv->oprtn->address); + } + + return interp->command_line; +} + +const char * +sieve_runtime_get_full_command_location(const struct sieve_runtime_env *renv) +{ + return sieve_error_script_location( + renv->script, sieve_runtime_get_command_location(renv)); +} + +/* + * Extension support + */ + +void sieve_interpreter_extension_register( + struct sieve_interpreter *interp, const struct sieve_extension *ext, + const struct sieve_interpreter_extension *intext, void *context) +{ + struct sieve_interpreter_extension_reg *reg; + + if (ext->id < 0) + return; + + reg = array_idx_get_space(&interp->extensions, (unsigned int) ext->id); + reg->intext = intext; + reg->ext = ext; + reg->context = context; +} + +void sieve_interpreter_extension_set_context(struct sieve_interpreter *interp, + const struct sieve_extension *ext, + void *context) +{ + struct sieve_interpreter_extension_reg *reg; + + if (ext->id < 0) + return; + + reg = array_idx_get_space(&interp->extensions, (unsigned int) ext->id); + reg->context = context; +} + +void *sieve_interpreter_extension_get_context(struct sieve_interpreter *interp, + const struct sieve_extension *ext) +{ + const struct sieve_interpreter_extension_reg *reg; + + if (ext->id < 0 || ext->id >= (int) array_count(&interp->extensions)) + return NULL; + + reg = array_idx(&interp->extensions, (unsigned int) ext->id); + + return reg->context; +} + +int sieve_interpreter_extension_start(struct sieve_interpreter *interp, + const struct sieve_extension *ext) +{ + struct sieve_interpreter_extension_reg *reg; + int ret; + + i_assert(ext->id >= 0); + + if (ext->id >= (int) array_count(&interp->extensions)) + return SIEVE_EXEC_OK; + + reg = array_idx_modifiable(&interp->extensions, (unsigned int)ext->id); + + if (!reg->deferred) + return SIEVE_EXEC_OK; + reg->deferred = FALSE; + reg->started = TRUE; + + if (reg->intext != NULL && reg->intext->run != NULL && + (ret = reg->intext->run(ext, &interp->runenv, + reg->context, TRUE)) <= 0) + return ret; + return SIEVE_EXEC_OK; +} + +/* + * Loop handling + */ + +int sieve_interpreter_loop_start(struct sieve_interpreter *interp, + sieve_size_t loop_end, + const struct sieve_extension_def *ext_def, + struct sieve_interpreter_loop **loop_r) +{ + const struct sieve_runtime_env *renv = &interp->runenv; + struct sieve_interpreter_loop *loop; + + i_assert(loop_end > interp->runenv.pc); + + /* Check supplied end offset */ + if (loop_end > sieve_binary_block_get_size(renv->sblock)) { + sieve_runtime_trace_error(renv, "loop end offset out of range"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + /* Trace */ + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS)) { + unsigned int line = + sieve_runtime_get_source_location(renv, loop_end); + + if (sieve_runtime_trace_hasflag(renv, SIEVE_TRFLG_ADDRESSES)) { + sieve_runtime_trace(renv, 0, + "loop ends at line %d [%08llx]", + line, + (long long unsigned int) loop_end); + } else { + sieve_runtime_trace(renv, 0, + "loop ends at line %d", line); + } + } + + /* Check loop nesting limit */ + if (!array_is_created(&interp->loop_stack)) + p_array_init(&interp->loop_stack, interp->pool, 8); + if ((interp->parent_loop_level + + array_count(&interp->loop_stack)) >= SIEVE_MAX_LOOP_DEPTH) { + /* Should normally be caught at compile time */ + sieve_runtime_error(renv, NULL, + "new program loop exceeds " + "the nesting limit (<= %u levels)", + SIEVE_MAX_LOOP_DEPTH); + return SIEVE_EXEC_FAILURE; + } + + /* Create new loop */ + loop = array_append_space(&interp->loop_stack); + loop->level = array_count(&interp->loop_stack)-1; + loop->ext_def = ext_def; + loop->begin = interp->runenv.pc; + loop->end = loop_end; + loop->pool = pool_alloconly_create("sieve_interpreter", 128); + + /* Set new loop limit */ + interp->loop_limit = loop_end; + + *loop_r = loop; + return SIEVE_EXEC_OK; +} + +struct sieve_interpreter_loop * +sieve_interpreter_loop_get(struct sieve_interpreter *interp, + sieve_size_t loop_end, + const struct sieve_extension_def *ext_def) +{ + struct sieve_interpreter_loop *loops; + unsigned int count, i; + + if (!array_is_created(&interp->loop_stack)) + return NULL; + + loops = array_get_modifiable(&interp->loop_stack, &count); + for (i = count; i > 0; i--) { + /* We're really making sure our loop matches */ + if (loops[i-1].end == loop_end && + loops[i-1].ext_def == ext_def) + return &loops[i-1]; + } + return NULL; +} + +int sieve_interpreter_loop_next(struct sieve_interpreter *interp, + struct sieve_interpreter_loop *loop, + sieve_size_t loop_begin) +{ + const struct sieve_runtime_env *renv = &interp->runenv; + struct sieve_interpreter_loop *loops; + unsigned int count; + + /* Trace */ + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS)) { + unsigned int line = + sieve_runtime_get_source_location(renv, loop_begin); + + if (sieve_runtime_trace_hasflag(renv, SIEVE_TRFLG_ADDRESSES)) { + sieve_runtime_trace(renv, 0, + "looping back to line %d [%08llx]", + line, + (long long unsigned int) loop_begin); + } else { + sieve_runtime_trace(renv, 0, + "looping back to line %d", line); + } + } + + /* Check the code for corruption */ + if (loop->begin != loop_begin) { + sieve_runtime_trace_error(renv, "loop begin offset invalid"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + /* Check invariants */ + i_assert(array_is_created(&interp->loop_stack)); + loops = array_get_modifiable(&interp->loop_stack, &count); + i_assert(&loops[count-1] == loop); + + /* Return to beginning */ + interp->runenv.pc = loop_begin; + return SIEVE_EXEC_OK; +} + +int sieve_interpreter_loop_break(struct sieve_interpreter *interp, + struct sieve_interpreter_loop *loop) +{ + const struct sieve_runtime_env *renv = &interp->runenv; + struct sieve_interpreter_loop *loops; + sieve_size_t loop_end = loop->end; + unsigned int count, i; + + /* Find the loop */ + i_assert(array_is_created(&interp->loop_stack)); + loops = array_get_modifiable(&interp->loop_stack, &count); + i_assert(count > 0); + + i = count; + do { + pool_unref(&loops[i-1].pool); + i--; + } while (i > 0 && &loops[i] != loop); + i_assert(&loops[i] == loop); + + /* Set new loop limit */ + if (i > 0) + interp->loop_limit = loops[i].end; + else + interp->loop_limit = 0; + + /* Delete it and all deeper loops */ + array_delete(&interp->loop_stack, i, count - i); + + /* Trace */ + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS)) { + unsigned int jmp_line = + sieve_runtime_get_source_location(renv, loop_end); + + if (sieve_runtime_trace_hasflag(renv, SIEVE_TRFLG_ADDRESSES)) { + sieve_runtime_trace(renv, 0, + "exiting loops at line %d [%08llx]", + jmp_line, + (long long unsigned int) loop_end); + } else { + sieve_runtime_trace(renv, 0, + "exiting loops at line %d", + jmp_line); + } + } + + /* Exit loop */ + interp->runenv.pc = loop->end; + return SIEVE_EXEC_OK; +} + +static int +sieve_interpreter_loop_break_out(struct sieve_interpreter *interp, + sieve_size_t target) +{ + struct sieve_interpreter_loop *loops; + unsigned int count, i; + + if (!array_is_created(&interp->loop_stack)) + return SIEVE_EXEC_OK; + + loops = array_get_modifiable(&interp->loop_stack, &count); + for (i = count; i > 0; i--) { + /* We're really making sure our loop matches */ + if (loops[i-1].end > target) + break; + } + if (i == count) + return SIEVE_EXEC_OK; + + return sieve_interpreter_loop_break(interp, &loops[i]); +} + +struct sieve_interpreter_loop * +sieve_interpreter_loop_get_local(struct sieve_interpreter *interp, + struct sieve_interpreter_loop *loop, + const struct sieve_extension_def *ext_def) +{ + struct sieve_interpreter_loop *loops; + unsigned int count, i; + + if (!array_is_created(&interp->loop_stack)) + return NULL; + + loops = array_get_modifiable(&interp->loop_stack, &count); + i_assert(loop == NULL || loop->level < count); + + for (i = (loop == NULL ? count : loop->level); i > 0; i--) { + if (ext_def == NULL || loops[i-1].ext_def == ext_def) + return &loops[i-1]; + } + return NULL; +} + +struct sieve_interpreter_loop * +sieve_interpreter_loop_get_global(struct sieve_interpreter *interp, + struct sieve_interpreter_loop *loop, + const struct sieve_extension_def *ext_def) +{ + struct sieve_interpreter_loop *result; + + while (interp != NULL) { + result = sieve_interpreter_loop_get_local(interp, loop, + ext_def); + if (result != NULL) + return result; + interp = interp->parent; + } + return NULL; +} + +pool_t sieve_interpreter_loop_get_pool(struct sieve_interpreter_loop *loop) +{ + return loop->pool; +} + +void *sieve_interpreter_loop_get_context(struct sieve_interpreter_loop *loop) +{ + return loop->context; +} + +void sieve_interpreter_loop_set_context(struct sieve_interpreter_loop *loop, + void *context) +{ + loop->context = context; +} + +/* + * Program flow + */ + +void sieve_interpreter_reset(struct sieve_interpreter *interp) +{ + interp->runenv.pc = interp->reset_vector; + interp->interrupted = FALSE; + interp->test_result = FALSE; + interp->runenv.result = NULL; +} + +void sieve_interpreter_interrupt(struct sieve_interpreter *interp) +{ + interp->interrupted = TRUE; +} + +sieve_size_t sieve_interpreter_program_counter(struct sieve_interpreter *interp) +{ + return interp->runenv.pc; +} + +static int +sieve_interpreter_check_program_jump(struct sieve_interpreter *interp, + sieve_size_t jmp_target, bool break_loops) +{ + const struct sieve_runtime_env *renv = &interp->runenv; + sieve_size_t loop_limit = (break_loops ? 0 : interp->loop_limit); + + if (jmp_target == 0 || + jmp_target > sieve_binary_block_get_size(renv->sblock) || + (loop_limit > 0 && jmp_target >= loop_limit)) { + if (interp->loop_limit != 0) { + sieve_runtime_trace_error( + renv, "jump target crosses loop boundary"); + } else { + sieve_runtime_trace_error( + renv, "jump target out of range"); + } + return SIEVE_EXEC_BIN_CORRUPT; + } + return SIEVE_EXEC_OK; +} + +static int +sieve_interpreter_do_program_jump(struct sieve_interpreter *interp, + sieve_size_t jmp_target, bool break_loops) +{ + const struct sieve_runtime_env *renv = &interp->runenv; + sieve_size_t *address = &(interp->runenv.pc); + int ret; + + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS)) { + unsigned int jmp_line = + sieve_runtime_get_source_location(renv, jmp_target); + + if (sieve_runtime_trace_hasflag(renv, SIEVE_TRFLG_ADDRESSES)) { + sieve_runtime_trace(renv, 0, "jumping to line %d [%08llx]", + jmp_line, + (long long unsigned int)jmp_target); + } else { + sieve_runtime_trace(renv, 0, "jumping to line %d", + jmp_line); + } + } + + if (break_loops && + (ret = sieve_interpreter_loop_break_out(interp, + jmp_target)) <= 0) + return ret; + + *address = jmp_target; + return SIEVE_EXEC_OK; +} + +int sieve_interpreter_program_jump_to(struct sieve_interpreter *interp, + sieve_size_t jmp_target, + bool break_loops) +{ + int ret; + + ret = sieve_interpreter_check_program_jump(interp, jmp_target, + break_loops); + if (ret <= 0) + return ret; + + return sieve_interpreter_do_program_jump( + interp, jmp_target, break_loops); +} + +int sieve_interpreter_program_jump(struct sieve_interpreter *interp, + bool jump, bool break_loops) +{ + const struct sieve_runtime_env *renv = &interp->runenv; + sieve_size_t *address = &(interp->runenv.pc); + sieve_size_t jmp_start = *address, jmp_target; + sieve_offset_t jmp_offset; + int ret; + + if (!sieve_binary_read_offset(renv->sblock, address, &jmp_offset)) { + sieve_runtime_trace_error(renv, "invalid jump offset"); + return SIEVE_EXEC_BIN_CORRUPT; + } + jmp_target = jmp_start + jmp_offset; + + ret = sieve_interpreter_check_program_jump(interp, jmp_target, + break_loops); + if (ret <= 0) + return ret; + + if (!jump) { + sieve_runtime_trace(renv, 0, "not jumping"); + return SIEVE_EXEC_OK; + } + + return sieve_interpreter_do_program_jump( + interp, jmp_target, break_loops); +} + +/* + * Test results + */ + +void sieve_interpreter_set_test_result(struct sieve_interpreter *interp, + bool result) +{ + interp->test_result = result; +} + +bool sieve_interpreter_get_test_result(struct sieve_interpreter *interp) +{ + return interp->test_result; +} + +/* + * Code execute + */ + +static int sieve_interpreter_operation_execute(struct sieve_interpreter *interp) +{ + struct sieve_operation *oprtn = &(interp->oprtn); + sieve_size_t *address = &(interp->runenv.pc); + + sieve_runtime_trace_toplevel(&interp->runenv); + + /* Read the operation */ + if (sieve_operation_read(interp->runenv.sblock, address, oprtn)) { + const struct sieve_operation_def *op = oprtn->def; + int result = SIEVE_EXEC_OK; + + /* Reset cached command location */ + interp->command_line = 0; + + /* Execute the operation */ + if (op->execute != NULL) { /* Noop ? */ + T_BEGIN { + result = op->execute(&(interp->runenv), + address); + } T_END; + } else { + sieve_runtime_trace(&interp->runenv, + SIEVE_TRLVL_COMMANDS, + "OP: %s (NOOP)", + sieve_operation_mnemonic(oprtn)); + } + + return result; + } + + /* Binary corrupt */ + sieve_runtime_trace_error(&interp->runenv, + "Encountered invalid operation"); + return SIEVE_EXEC_BIN_CORRUPT; +} + +int sieve_interpreter_continue(struct sieve_interpreter *interp, + bool *interrupted) +{ + const struct sieve_runtime_env *renv = &interp->runenv; + const struct sieve_execute_env *eenv = renv->exec_env; + struct cpu_limit *climit = NULL; + sieve_size_t *address = &(interp->runenv.pc); + struct sieve_instance *svinst = eenv->svinst; + struct sieve_exec_status *exec_status = eenv->exec_status; + struct sieve_resource_usage rusage; + int ret = SIEVE_EXEC_OK; + + sieve_result_ref(renv->result); + interp->interrupted = FALSE; + + if (interrupted != NULL) + *interrupted = FALSE; + + if (svinst->max_cpu_time_secs > 0) { + climit = cpu_limit_init(svinst->max_cpu_time_secs, + CPU_LIMIT_TYPE_USER); + } + + while (ret == SIEVE_EXEC_OK && !interp->interrupted && + *address < sieve_binary_block_get_size(renv->sblock)) { + if (climit != NULL && cpu_limit_exceeded(climit)) { + sieve_runtime_error( + renv, NULL, + "execution exceeded CPU time limit"); + ret = SIEVE_EXEC_RESOURCE_LIMIT; + break; + } + if (interp->loop_limit != 0 && *address > interp->loop_limit) { + sieve_runtime_trace_error( + renv, "program crossed loop boundary"); + ret = SIEVE_EXEC_BIN_CORRUPT; + break; + } + + ret = sieve_interpreter_operation_execute(interp); + } + + if (climit != NULL) { + sieve_resource_usage_init(&rusage); + rusage.cpu_time_msecs = + cpu_limit_get_usage_msecs(climit, CPU_LIMIT_TYPE_USER); + sieve_resource_usage_add(&interp->rusage, &rusage); + + cpu_limit_deinit(&climit); + } + + if (ret != SIEVE_EXEC_OK) { + sieve_runtime_trace(&interp->runenv, SIEVE_TRLVL_NONE, + "[[EXECUTION ABORTED]]"); + } + + if (interrupted != NULL) + *interrupted = interp->interrupted; + + if (!interp->interrupted) { + exec_status->resource_usage = interp->rusage; + + struct event_passthrough *e = + event_create_passthrough(interp->runenv.event)-> + set_name("sieve_runtime_script_finished"); + switch (ret) { + case SIEVE_EXEC_OK: + break; + case SIEVE_EXEC_FAILURE: + e->add_str("error", "Failed"); + break; + case SIEVE_EXEC_TEMP_FAILURE: + e->add_str("error", "Failed temporarily"); + break; + case SIEVE_EXEC_BIN_CORRUPT: + e->add_str("error", "Binary corrupt"); + break; + case SIEVE_EXEC_RESOURCE_LIMIT: + e->add_str("error", "Resource limit exceeded"); + break; + case SIEVE_EXEC_KEEP_FAILED: + /* Not supposed to occur at runtime */ + i_unreached(); + } + e_debug(e->event(), "Finished running script `%s' " + "(status=%s, resource usage: %s)", + sieve_binary_source(interp->runenv.sbin), + sieve_execution_exitcode_to_str(ret), + sieve_resource_usage_get_summary(&interp->rusage)); + interp->running = FALSE; + } + + sieve_result_unref(&interp->runenv.result); + return ret; +} + +int sieve_interpreter_start(struct sieve_interpreter *interp, + struct sieve_result *result, bool *interrupted) +{ + struct sieve_interpreter_extension_reg *eregs; + unsigned int ext_count, i; + int ret; + + struct event_passthrough *e = + event_create_passthrough(interp->runenv.event)-> + set_name("sieve_runtime_script_started"); + e_debug(e->event(), "Started running script `%s'", + sieve_binary_source(interp->runenv.sbin)); + + interp->running = TRUE; + interp->runenv.result = result; + interp->runenv.msgctx = sieve_result_get_message_context(result); + + sieve_resource_usage_init(&interp->rusage); + + /* Signal registered extensions that the interpreter is being run */ + eregs = array_get_modifiable(&interp->extensions, &ext_count); + for (i = 0; i < ext_count; i++) { + if (!eregs[i].deferred) { + eregs[i].started = TRUE; + if (eregs[i].intext != NULL && + eregs[i].intext->run != NULL && + (ret = eregs[i].intext->run( + eregs[i].ext, &interp->runenv, + eregs[i].context, FALSE)) <= 0) + return ret; + } + } + + return sieve_interpreter_continue(interp, interrupted); +} + +int sieve_interpreter_run(struct sieve_interpreter *interp, + struct sieve_result *result) +{ + sieve_interpreter_reset(interp); + + return sieve_interpreter_start(interp, result, NULL); +} + +/* + * Error handling + */ + +static inline void ATTR_FORMAT(3, 0) +sieve_runtime_logv(const struct sieve_runtime_env *renv, + const struct sieve_error_params *params, + const char *fmt, va_list args) +{ + struct sieve_error_params new_params = *params; + + new_params.event = renv->event; + T_BEGIN { + if (new_params.location == NULL) { + new_params.location = + sieve_runtime_get_full_command_location(renv); + } + + sieve_logv(renv->ehandler, params, fmt, args); + } T_END; +} + +#undef sieve_runtime_error +void sieve_runtime_error(const struct sieve_runtime_env *renv, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, const char *fmt, ...) +{ + struct sieve_error_params params = { + .log_type = LOG_TYPE_ERROR, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + .location = location, + }; + va_list args; + + va_start(args, fmt); + sieve_runtime_logv(renv, ¶ms, fmt, args); + va_end(args); +} + +#undef sieve_runtime_warning +void sieve_runtime_warning(const struct sieve_runtime_env *renv, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, const char *fmt, ...) +{ + struct sieve_error_params params = { + .log_type = LOG_TYPE_WARNING, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + .location = location, + }; + va_list args; + + va_start(args, fmt); + sieve_runtime_logv(renv, ¶ms, fmt, args); + va_end(args); +} + +#undef sieve_runtime_log +void sieve_runtime_log(const struct sieve_runtime_env *renv, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, const char *fmt, ...) +{ + struct sieve_error_params params = { + .log_type = LOG_TYPE_INFO, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + .location = location, + }; + va_list args; + + va_start(args, fmt); + sieve_runtime_logv(renv, ¶ms, fmt, args); + va_end(args); +} + +#undef sieve_runtime_critical +void sieve_runtime_critical(const struct sieve_runtime_env *renv, + const char *csrc_filename, + unsigned int csrc_linenum, + const char *location, const char *user_prefix, + const char *fmt, ...) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + struct sieve_error_params params = { + .log_type = LOG_TYPE_ERROR, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + .location = location, + }; + va_list args; + + va_start(args, fmt); + + params.event = renv->event; + T_BEGIN { + if (params.location == NULL) { + params.location = + sieve_runtime_get_full_command_location(renv); + } + + sieve_criticalv(eenv->svinst, renv->ehandler, ¶ms, + user_prefix, fmt, args); + } T_END; + + va_end(args); +} + +#undef sieve_runtime_mail_error +int sieve_runtime_mail_error(const struct sieve_runtime_env *renv, + struct mail *mail, + const char *csrc_filename, + unsigned int csrc_linenum, + const char *fmt, ...) +{ + const char *error_msg, *user_prefix; + va_list args; + + error_msg = mailbox_get_last_internal_error(mail->box, NULL); + + va_start(args, fmt); + user_prefix = t_strdup_vprintf(fmt, args); + sieve_runtime_critical(renv, csrc_filename, csrc_linenum, + NULL, user_prefix, "%s: %s", + user_prefix, error_msg); + va_end(args); + + return SIEVE_EXEC_TEMP_FAILURE; +} diff --git a/pigeonhole/src/lib-sieve/sieve-interpreter.h b/pigeonhole/src/lib-sieve/sieve-interpreter.h new file mode 100644 index 0000000..ec38029 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-interpreter.h @@ -0,0 +1,204 @@ +#ifndef SIEVE_INTERPRETER_H +#define SIEVE_INTERPRETER_H + +#include "lib.h" +#include "array.h" +#include "buffer.h" + +#include "sieve-common.h" +#include "sieve-runtime.h" + +/* + * Interpreter + */ + +struct sieve_interpreter * +sieve_interpreter_create(struct sieve_binary *sbin, + struct sieve_interpreter *parent, + const struct sieve_execute_env *eenv, + struct sieve_error_handler *ehandler) ATTR_NULL(2); +struct sieve_interpreter * +sieve_interpreter_create_for_block(struct sieve_binary_block *sblock, + struct sieve_script *script, + struct sieve_interpreter *parent, + const struct sieve_execute_env *eenv, + struct sieve_error_handler *ehandler) + ATTR_NULL(3); +void sieve_interpreter_free(struct sieve_interpreter **_interp); + +/* + * Accessors + */ + +pool_t sieve_interpreter_pool(struct sieve_interpreter *interp); +struct sieve_interpreter * +sieve_interpreter_get_parent(struct sieve_interpreter *interp); +struct sieve_script *sieve_interpreter_script(struct sieve_interpreter *interp); +struct sieve_error_handler * +sieve_interpreter_get_error_handler(struct sieve_interpreter *interp); +struct sieve_instance * +sieve_interpreter_svinst(struct sieve_interpreter *interp); + +/* Do not use this function for normal sieve extensions. This is intended for + * the testsuite only. + */ +void sieve_interpreter_set_result(struct sieve_interpreter *interp, + struct sieve_result *result); + +/* + * Loop handling + */ + +struct sieve_interpreter_loop; + +int sieve_interpreter_loop_start(struct sieve_interpreter *interp, + sieve_size_t loop_end, + const struct sieve_extension_def *ext_def, + struct sieve_interpreter_loop **loop_r); +struct sieve_interpreter_loop * +sieve_interpreter_loop_get(struct sieve_interpreter *interp, + sieve_size_t loop_end, + const struct sieve_extension_def *ext_def); +int sieve_interpreter_loop_next(struct sieve_interpreter *interp, + struct sieve_interpreter_loop *loop, + sieve_size_t loop_begin); +int sieve_interpreter_loop_break(struct sieve_interpreter *interp, + struct sieve_interpreter_loop *loop); + +struct sieve_interpreter_loop * +sieve_interpreter_loop_get_local(struct sieve_interpreter *interp, + struct sieve_interpreter_loop *loop, + const struct sieve_extension_def *ext_def) + ATTR_NULL(2, 3); +struct sieve_interpreter_loop * +sieve_interpreter_loop_get_global(struct sieve_interpreter *interp, + struct sieve_interpreter_loop *loop, + const struct sieve_extension_def *ext_def) + ATTR_NULL(2, 3); + +pool_t sieve_interpreter_loop_get_pool(struct sieve_interpreter_loop *loop) + ATTR_PURE; +void *sieve_interpreter_loop_get_context(struct sieve_interpreter_loop *loop) + ATTR_PURE; +void sieve_interpreter_loop_set_context(struct sieve_interpreter_loop *loop, + void *context); + +/* + * Program flow + */ + +void sieve_interpreter_reset(struct sieve_interpreter *interp); +void sieve_interpreter_interrupt(struct sieve_interpreter *interp); +sieve_size_t +sieve_interpreter_program_counter(struct sieve_interpreter *interp); + +int sieve_interpreter_program_jump_to(struct sieve_interpreter *interp, + sieve_size_t jmp_target, + bool break_loops); +int sieve_interpreter_program_jump(struct sieve_interpreter *interp, bool jump, + bool break_loops); + +/* + * Test results + */ + +void sieve_interpreter_set_test_result(struct sieve_interpreter *interp, + bool result); +bool sieve_interpreter_get_test_result(struct sieve_interpreter *interp); + +/* + * Source location + */ + +unsigned int +sieve_runtime_get_source_location(const struct sieve_runtime_env *renv, + sieve_size_t code_address); + +unsigned int +sieve_runtime_get_command_location(const struct sieve_runtime_env *renv); +const char * +sieve_runtime_get_full_command_location(const struct sieve_runtime_env *renv); + +/* + * Extension support + */ + +struct sieve_interpreter_extension { + const struct sieve_extension_def *ext_def; + + int (*run)(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv, + void *context, bool deferred); + void (*free)(const struct sieve_extension *ext, + struct sieve_interpreter *interp, void *context); +}; + +void sieve_interpreter_extension_register( + struct sieve_interpreter *interp, const struct sieve_extension *ext, + const struct sieve_interpreter_extension *intext, void *context); +void sieve_interpreter_extension_set_context( + struct sieve_interpreter *interp, const struct sieve_extension *ext, + void *context); +void *sieve_interpreter_extension_get_context( + struct sieve_interpreter *interp, const struct sieve_extension *ext); + +int sieve_interpreter_extension_start(struct sieve_interpreter *interp, + const struct sieve_extension *ext); + +/* + * Opcodes and operands + */ + +int sieve_interpreter_handle_optional_operands( + const struct sieve_runtime_env *renv, sieve_size_t *address, + struct sieve_side_effects_list **list); + +/* + * Code execute + */ + +int sieve_interpreter_continue(struct sieve_interpreter *interp, + bool *interrupted); +int sieve_interpreter_start(struct sieve_interpreter *interp, + struct sieve_result *result, bool *interrupted); +int sieve_interpreter_run(struct sieve_interpreter *interp, + struct sieve_result *result); + +/* + * Error handling + */ + +void sieve_runtime_error(const struct sieve_runtime_env *renv, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, const char *fmt, ...) + ATTR_FORMAT(5, 6); +#define sieve_runtime_error(renv, ...) \ + sieve_runtime_error(renv, __FILE__, __LINE__, __VA_ARGS__) +void sieve_runtime_warning(const struct sieve_runtime_env *renv, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, const char *fmt, ...) + ATTR_FORMAT(5, 6); +#define sieve_runtime_warning(renv, ...) \ + sieve_runtime_warning(renv, __FILE__, __LINE__, __VA_ARGS__) +void sieve_runtime_log(const struct sieve_runtime_env *renv, + const char *csrc_filename, unsigned int csrc_linenum, + const char *location, const char *fmt, ...) + ATTR_FORMAT(5, 6); +#define sieve_runtime_log(renv, ...) \ + sieve_runtime_log(renv, __FILE__, __LINE__, __VA_ARGS__) +void sieve_runtime_critical(const struct sieve_runtime_env *renv, + const char *csrc_filename, + unsigned int csrc_linenum, + const char *location, const char *user_prefix, + const char *fmt, ...) ATTR_FORMAT(6, 7); +#define sieve_runtime_critical(renv, ...) \ + sieve_runtime_critical(renv, __FILE__, __LINE__, __VA_ARGS__) +int sieve_runtime_mail_error(const struct sieve_runtime_env *renv, + struct mail *mail, + const char *csrc_filename, + unsigned int csrc_linenum, + const char *fmt, ...) ATTR_FORMAT(5, 6); +#define sieve_runtime_mail_error(renv, mail, ...) \ + sieve_runtime_mail_error(renv, mail, __FILE__, __LINE__, __VA_ARGS__) + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-lexer.c b/pigeonhole/src/lib-sieve/sieve-lexer.c new file mode 100644 index 0000000..a6968a9 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-lexer.c @@ -0,0 +1,930 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "compat.h" +#include "str.h" +#include "str-sanitize.h" +#include "istream.h" + +#include "sieve-common.h" +#include "sieve-limits.h" +#include "sieve-error.h" +#include "sieve-script.h" + +#include "sieve-lexer.h" + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <ctype.h> + +/* + * Useful macros + */ + +#define DIGIT_VAL(c) (c - '0') + +/* + * Lexer object + */ + +struct sieve_lexical_scanner { + pool_t pool; + struct sieve_instance *svinst; + + struct sieve_script *script; + struct istream *input; + + struct sieve_error_handler *ehandler; + + /* Currently scanned data */ + const unsigned char *buffer; + size_t buffer_size; + size_t buffer_pos; + + struct sieve_lexer lexer; + + int current_line; +}; + +const struct sieve_lexer * +sieve_lexer_create(struct sieve_script *script, + struct sieve_error_handler *ehandler, + enum sieve_error *error_r) +{ + struct sieve_lexical_scanner *scanner; + struct sieve_instance *svinst = sieve_script_svinst(script); + struct istream *stream; + const struct stat *st; + + /* Open script as stream */ + if (sieve_script_get_stream(script, &stream, error_r) < 0) + return NULL; + + /* Check script size */ + if (i_stream_stat(stream, TRUE, &st) >= 0 && st->st_size > 0 && + svinst->max_script_size > 0 && + (uoff_t)st->st_size > svinst->max_script_size) { + sieve_error(ehandler, sieve_script_name(script), + "sieve script is too large (max %zu bytes)", + svinst->max_script_size); + if (error_r != NULL) + *error_r = SIEVE_ERROR_NOT_POSSIBLE; + return NULL; + } + + scanner = i_new(struct sieve_lexical_scanner, 1); + scanner->lexer.scanner = scanner; + + scanner->ehandler = ehandler; + sieve_error_handler_ref(ehandler); + + scanner->input = stream; + i_stream_ref(scanner->input); + + scanner->script = script; + sieve_script_ref(script); + + scanner->buffer = NULL; + scanner->buffer_size = 0; + scanner->buffer_pos = 0; + + scanner->lexer.token_type = STT_NONE; + scanner->lexer.token_str_value = str_new(default_pool, 256); + scanner->lexer.token_int_value = 0; + scanner->lexer.token_line = 1; + + scanner->current_line = 1; + + return &scanner->lexer; +} + +void sieve_lexer_free(const struct sieve_lexer **_lexer) +{ + const struct sieve_lexer *lexer = *_lexer; + struct sieve_lexical_scanner *scanner = lexer->scanner; + + i_stream_unref(&scanner->input); + sieve_script_unref(&scanner->script); + sieve_error_handler_unref(&scanner->ehandler); + str_free(&scanner->lexer.token_str_value); + + i_free(scanner); + *_lexer = NULL; +} + +/* + * Internal error handling + */ + +inline static void ATTR_FORMAT(4, 5) +sieve_lexer_error(const struct sieve_lexer *lexer, + const char *csrc_filename, unsigned int csrc_linenum, + const char *fmt, ...) +{ + struct sieve_lexical_scanner *scanner = lexer->scanner; + struct sieve_error_params params = { + .log_type = LOG_TYPE_ERROR, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + }; + va_list args; + + va_start(args, fmt); + + T_BEGIN { + params.location = + sieve_error_script_location(scanner->script, + scanner->current_line); + sieve_logv(scanner->ehandler, ¶ms, fmt, args); + } T_END; + + va_end(args); +} +#define sieve_lexer_error(lexer, ...) \ + sieve_lexer_error(lexer, __FILE__, __LINE__, __VA_ARGS__) + +inline static void ATTR_FORMAT(4, 5) +sieve_lexer_warning(const struct sieve_lexer *lexer, + const char *csrc_filename, unsigned int csrc_linenum, + const char *fmt, ...) +{ + struct sieve_lexical_scanner *scanner = lexer->scanner; + struct sieve_error_params params = { + .log_type = LOG_TYPE_WARNING, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + }; + va_list args; + + va_start(args, fmt); + + T_BEGIN { + params.location = + sieve_error_script_location(scanner->script, + scanner->current_line); + sieve_logv(scanner->ehandler, ¶ms, fmt, args); + } T_END; + + va_end(args); +} +#define sieve_lexer_warning(lexer, ...) \ + sieve_lexer_warning(lexer, __FILE__, __LINE__, __VA_ARGS__) + +const char *sieve_lexer_token_description(const struct sieve_lexer *lexer) +{ + switch (lexer->token_type) { + case STT_NONE: + return "no token (bug)"; + case STT_WHITESPACE: + return "whitespace (bug)"; + case STT_EOF: + return "end of file"; + + case STT_NUMBER: + return "number"; + case STT_IDENTIFIER: + return "identifier"; + case STT_TAG: + return "tag"; + case STT_STRING: + return "string"; + + case STT_RBRACKET: + return "')'"; + case STT_LBRACKET: + return "'('"; + case STT_RCURLY: + return "'}'"; + case STT_LCURLY: + return "'{'"; + case STT_RSQUARE: + return "']'"; + case STT_LSQUARE: + return "'['"; + case STT_SEMICOLON: + return "';'"; + case STT_COMMA: + return "','"; + + case STT_SLASH: + return "'/'"; + case STT_COLON: + return "':'"; + + case STT_GARBAGE: + return "unknown characters"; + case STT_ERROR: + return "error token (bug)"; + } + + return "unknown token (bug)"; +} + +/* + * Debug + */ + +void sieve_lexer_token_print(const struct sieve_lexer *lexer) +{ + switch (lexer->token_type) { + case STT_NONE: + printf("??NONE?? "); + break; + case STT_WHITESPACE: + printf("??WHITESPACE?? "); + break; + case STT_EOF: + printf("EOF\n"); + break; + + case STT_NUMBER: + printf("NUMBER "); + break; + case STT_IDENTIFIER: + printf("IDENTIFIER "); + break; + case STT_TAG: + printf("TAG "); + break; + case STT_STRING: + printf("STRING "); + break; + + case STT_RBRACKET: + printf(") "); + break; + case STT_LBRACKET: + printf("( "); + break; + case STT_RCURLY: + printf("}\n"); + break; + case STT_LCURLY: + printf("{\n"); + break; + case STT_RSQUARE: + printf("] "); + break; + case STT_LSQUARE: + printf("[ "); + break; + case STT_SEMICOLON: + printf(";\n"); + break; + case STT_COMMA: + printf(", "); + break; + + case STT_SLASH: + printf("/ "); + break; + case STT_COLON: + printf(": "); + break; + + case STT_GARBAGE: + printf(">>GARBAGE<<"); + break; + case STT_ERROR: + printf(">>ERROR<<"); + break; + default: + printf("UNKNOWN "); + break; + } +} + +/* + * Lexical scanning + */ + +static void sieve_lexer_shift(struct sieve_lexical_scanner *scanner) +{ + if (scanner->buffer_size > 0 && + scanner->buffer[scanner->buffer_pos] == '\n') + scanner->current_line++; + + if (scanner->buffer_size > 0 && + scanner->buffer_pos + 1 < scanner->buffer_size) + scanner->buffer_pos++; + else { + if (scanner->buffer_size > 0) + i_stream_skip(scanner->input, scanner->buffer_size); + + scanner->buffer = i_stream_get_data(scanner->input, + &scanner->buffer_size); + + if (scanner->buffer_size == 0 && + i_stream_read(scanner->input) > 0) { + scanner->buffer = i_stream_get_data( + scanner->input, &scanner->buffer_size); + } + + scanner->buffer_pos = 0; + } +} + +static inline int sieve_lexer_curchar(struct sieve_lexical_scanner *scanner) +{ + if (scanner->buffer_size == 0) + return -1; + + return scanner->buffer[scanner->buffer_pos]; +} + +static inline const char *_char_sanitize(int ch) +{ + if (ch > 31 && ch < 127) + return t_strdup_printf("'%c'", ch); + + return t_strdup_printf("0x%02x", ch); +} + +static bool sieve_lexer_scan_number(struct sieve_lexical_scanner *scanner) +{ + struct sieve_lexer *lexer = &scanner->lexer; + uintmax_t value; + string_t *str; + bool overflow = FALSE; + + str_truncate(lexer->token_str_value,0); + str = lexer->token_str_value; + + while (i_isdigit(sieve_lexer_curchar(scanner))) { + str_append_c(str, sieve_lexer_curchar(scanner)); + sieve_lexer_shift(scanner); + } + + if (str_to_uintmax(str_c(str), &value) < 0 || + value > (sieve_number_t)-1) { + overflow = TRUE; + } else { + switch (sieve_lexer_curchar(scanner)) { + case 'k': + case 'K': /* Kilo */ + if (value > (SIEVE_MAX_NUMBER >> 10)) + overflow = TRUE; + else + value = value << 10; + sieve_lexer_shift(scanner); + break; + case 'm': + case 'M': /* Mega */ + if (value > (SIEVE_MAX_NUMBER >> 20)) + overflow = TRUE; + else + value = value << 20; + sieve_lexer_shift(scanner); + break; + case 'g': + case 'G': /* Giga */ + if (value > (SIEVE_MAX_NUMBER >> 30)) + overflow = TRUE; + else + value = value << 30; + sieve_lexer_shift(scanner); + break; + default: + /* Next token */ + break; + } + } + + /* Check for integer overflow */ + if (overflow) { + sieve_lexer_error(lexer, + "number exceeds integer limits (max %llu)", + (long long) SIEVE_MAX_NUMBER); + lexer->token_type = STT_ERROR; + return FALSE; + } + + lexer->token_type = STT_NUMBER; + lexer->token_int_value = (sieve_number_t)value; + return TRUE; + +} + +static bool +sieve_lexer_scan_hash_comment(struct sieve_lexical_scanner *scanner) +{ + struct sieve_lexer *lexer = &scanner->lexer; + + while (sieve_lexer_curchar(scanner) != '\n') { + switch(sieve_lexer_curchar(scanner)) { + case -1: + if (!scanner->input->eof) { + lexer->token_type = STT_ERROR; + return FALSE; + } + sieve_lexer_warning(lexer, + "no newline (CRLF) at end of hash comment at end of file"); + lexer->token_type = STT_WHITESPACE; + return TRUE; + case '\0': + sieve_lexer_error(lexer, + "encountered NUL character in hash comment"); + lexer->token_type = STT_ERROR; + return FALSE; + default: + break; + } + + /* Stray CR is ignored */ + sieve_lexer_shift(scanner); + } + + sieve_lexer_shift(scanner); + + lexer->token_type = STT_WHITESPACE; + return TRUE; +} + +/* sieve_lexer_scan_raw_token: + * Scans valid tokens and whitespace + */ +static bool +sieve_lexer_scan_raw_token(struct sieve_lexical_scanner *scanner) +{ + struct sieve_lexer *lexer = &scanner->lexer; + string_t *str; + int ret; + + /* Read first character */ + if (lexer->token_type == STT_NONE) { + if ((ret = i_stream_read(scanner->input)) < 0) { + i_assert(ret != -2); + if (!scanner->input->eof) { + lexer->token_type = STT_ERROR; + return FALSE; + } + } + sieve_lexer_shift(scanner); + } + + lexer->token_line = scanner->current_line; + + switch (sieve_lexer_curchar(scanner)) { + + /* whitespace */ + + // hash-comment = ( "#" *CHAR-NOT-CRLF CRLF ) + case '#': + sieve_lexer_shift(scanner); + return sieve_lexer_scan_hash_comment(scanner); + + // bracket-comment = "/*" *(CHAR-NOT-STAR / ("*" CHAR-NOT-SLASH)) "*/" + // ;; No */ allowed inside a comment. + // ;; (No * is allowed unless it is the last character, + // ;; or unless it is followed by a character that isn't a + // ;; slash.) + case '/': + sieve_lexer_shift(scanner); + + if (sieve_lexer_curchar(scanner) == '*') { + sieve_lexer_shift(scanner); + + while (TRUE) { + switch (sieve_lexer_curchar(scanner)) { + case -1: + if (scanner->input->eof) { + sieve_lexer_error(lexer, + "end of file before end of bracket comment " + "('/* ... */') " + "started at line %d", + lexer->token_line); + } + lexer->token_type = STT_ERROR; + return FALSE; + case '*': + sieve_lexer_shift(scanner); + + if (sieve_lexer_curchar(scanner) == '/') { + sieve_lexer_shift(scanner); + + lexer->token_type = STT_WHITESPACE; + return TRUE; + + } else if (sieve_lexer_curchar(scanner) == -1) { + sieve_lexer_error(lexer, + "end of file before end of bracket comment " + "('/* ... */') " + "started at line %d", + lexer->token_line); + lexer->token_type = STT_ERROR; + return FALSE; + } + break; + case '\0': + sieve_lexer_error(lexer, + "encountered NUL character in bracket comment"); + lexer->token_type = STT_ERROR; + return FALSE; + default: + sieve_lexer_shift(scanner); + } + } + + i_unreached(); + return FALSE; + } + + lexer->token_type = STT_SLASH; + return TRUE; + + // comment = bracket-comment / hash-comment + // white-space = 1*(SP / CRLF / HTAB) / comment + case '\t': + case '\r': + case '\n': + case ' ': + sieve_lexer_shift(scanner); + + while (sieve_lexer_curchar(scanner) == '\t' || + sieve_lexer_curchar(scanner) == '\r' || + sieve_lexer_curchar(scanner) == '\n' || + sieve_lexer_curchar(scanner) == ' ') { + + sieve_lexer_shift(scanner); + } + + lexer->token_type = STT_WHITESPACE; + return TRUE; + + /* quoted-string */ + case '"': + sieve_lexer_shift(scanner); + + str_truncate(lexer->token_str_value, 0); + str = lexer->token_str_value; + + while (sieve_lexer_curchar(scanner) != '"') { + if (sieve_lexer_curchar(scanner) == '\\') + sieve_lexer_shift(scanner); + + switch (sieve_lexer_curchar(scanner)) { + + /* End of file */ + case -1: + if (scanner->input->eof) { + sieve_lexer_error(lexer, + "end of file before end of quoted string " + "started at line %d", lexer->token_line); + } + lexer->token_type = STT_ERROR; + return FALSE; + + /* NUL character */ + case '\0': + sieve_lexer_error(lexer, + "encountered NUL character in quoted string " + "started at line %d", lexer->token_line); + lexer->token_type = STT_ERROR; + return FALSE; + + /* CR .. check for LF */ + case '\r': + sieve_lexer_shift(scanner); + + if (sieve_lexer_curchar(scanner) != '\n') { + sieve_lexer_error(lexer, + "found stray carriage-return (CR) character " + "in quoted string started at line %d", + lexer->token_line); + lexer->token_type = STT_ERROR; + return FALSE; + } + + if (str_len(str) <= SIEVE_MAX_STRING_LEN) + str_append(str, "\r\n"); + break; + + /* Loose LF is allowed (non-standard) and converted to CRLF */ + case '\n': + if (str_len(str) <= SIEVE_MAX_STRING_LEN) + str_append(str, "\r\n"); + break; + + /* Other characters */ + default: + if (str_len(str) <= SIEVE_MAX_STRING_LEN) + str_append_c(str, sieve_lexer_curchar(scanner)); + } + + sieve_lexer_shift(scanner); + } + + sieve_lexer_shift(scanner); + + if (str_len(str) > SIEVE_MAX_STRING_LEN) { + sieve_lexer_error(lexer, + "quoted string started at line %d is too long " + "(longer than %llu bytes)", lexer->token_line, + (long long) SIEVE_MAX_STRING_LEN); + lexer->token_type = STT_ERROR; + return FALSE; + } + + lexer->token_type = STT_STRING; + return TRUE; + + /* single character tokens */ + case ']': + sieve_lexer_shift(scanner); + lexer->token_type = STT_RSQUARE; + return TRUE; + case '[': + sieve_lexer_shift(scanner); + lexer->token_type = STT_LSQUARE; + return TRUE; + case '}': + sieve_lexer_shift(scanner); + lexer->token_type = STT_RCURLY; + return TRUE; + case '{': + sieve_lexer_shift(scanner); + lexer->token_type = STT_LCURLY; + return TRUE; + case ')': + sieve_lexer_shift(scanner); + lexer->token_type = STT_RBRACKET; + return TRUE; + case '(': + sieve_lexer_shift(scanner); + lexer->token_type = STT_LBRACKET; + return TRUE; + case ';': + sieve_lexer_shift(scanner); + lexer->token_type = STT_SEMICOLON; + return TRUE; + case ',': + sieve_lexer_shift(scanner); + lexer->token_type = STT_COMMA; + return TRUE; + + /* EOF */ + case -1: + if (!scanner->input->eof) { + lexer->token_type = STT_ERROR; + return FALSE; + } + lexer->token_type = STT_EOF; + return TRUE; + + default: + /* number */ + if (i_isdigit(sieve_lexer_curchar(scanner))) { + return sieve_lexer_scan_number(scanner); + + /* identifier / tag */ + } else if (i_isalpha(sieve_lexer_curchar(scanner)) || + sieve_lexer_curchar(scanner) == '_' || + sieve_lexer_curchar(scanner) == ':') { + + enum sieve_token_type type = STT_IDENTIFIER; + str_truncate(lexer->token_str_value,0); + str = lexer->token_str_value; + + /* If it starts with a ':' it is a tag and not an + identifier */ + if (sieve_lexer_curchar(scanner) == ':') { + sieve_lexer_shift(scanner); // discard colon + type = STT_TAG; + + /* First character still can't be a DIGIT */ + if (i_isalpha(sieve_lexer_curchar(scanner)) || + sieve_lexer_curchar(scanner) == '_') { + str_append_c(str, sieve_lexer_curchar(scanner)); + sieve_lexer_shift(scanner); + } else { + /* Hmm, otherwise it is just a spurious + colon */ + lexer->token_type = STT_COLON; + return TRUE; + } + } else { + str_append_c(str, sieve_lexer_curchar(scanner)); + sieve_lexer_shift(scanner); + } + + /* Scan the rest of the identifier */ + while (i_isalnum(sieve_lexer_curchar(scanner)) || + sieve_lexer_curchar(scanner) == '_') { + + if (str_len(str) <= SIEVE_MAX_IDENTIFIER_LEN) { + str_append_c(str, sieve_lexer_curchar(scanner)); + } + sieve_lexer_shift(scanner); + } + + /* Is this in fact a multiline text string ? */ + if (sieve_lexer_curchar(scanner) == ':' && + type == STT_IDENTIFIER && str_len(str) == 4 && + strncasecmp(str_c(str), "text", 4) == 0) { + sieve_lexer_shift(scanner); // discard colon + + /* Discard SP and HTAB whitespace */ + while (sieve_lexer_curchar(scanner) == ' ' || + sieve_lexer_curchar(scanner) == '\t') + sieve_lexer_shift(scanner); + + /* Discard hash comment or handle single CRLF */ + if (sieve_lexer_curchar(scanner) == '\r') + sieve_lexer_shift(scanner); + switch (sieve_lexer_curchar(scanner)) { + case '#': + if (!sieve_lexer_scan_hash_comment(scanner)) + return FALSE; + if (scanner->input->eof) { + sieve_lexer_error(lexer, + "end of file before end of multi-line string"); + lexer->token_type = STT_ERROR; + return FALSE; + } else if (scanner->input->stream_errno != 0) { + lexer->token_type = STT_ERROR; + return FALSE; + } + break; + case '\n': + sieve_lexer_shift(scanner); + break; + case -1: + if (scanner->input->eof) { + sieve_lexer_error(lexer, + "end of file before end of multi-line string"); + } + lexer->token_type = STT_ERROR; + return FALSE; + default: + sieve_lexer_error(lexer, + "invalid character %s after 'text:' in multiline string", + _char_sanitize(sieve_lexer_curchar(scanner))); + lexer->token_type = STT_ERROR; + return FALSE; + } + + /* Start over */ + str_truncate(str, 0); + + /* Parse literal lines */ + while (TRUE) { + bool cr_shifted = FALSE; + + /* Remove dot-stuffing or detect end of text */ + if (sieve_lexer_curchar(scanner) == '.') { + sieve_lexer_shift(scanner); + + /* Check for CR.. */ + if (sieve_lexer_curchar(scanner) == '\r') { + sieve_lexer_shift(scanner); + cr_shifted = TRUE; + } + + /* ..LF */ + if (sieve_lexer_curchar(scanner) == '\n') { + sieve_lexer_shift(scanner); + + /* End of multi-line string */ + + /* Check whether length limit was violated */ + if (str_len(str) > SIEVE_MAX_STRING_LEN) { + sieve_lexer_error(lexer, + "multi-line string started at line %d is too long " + "(longer than %llu bytes)", lexer->token_line, + (long long) SIEVE_MAX_STRING_LEN); + lexer->token_type = STT_ERROR; + return FALSE; + } + + lexer->token_type = STT_STRING; + return TRUE; + } else if (cr_shifted) { + /* Seen CR, but no LF */ + if (sieve_lexer_curchar(scanner) != -1 || + !scanner->input->eof) { + sieve_lexer_error(lexer, + "found stray carriage-return (CR) character " + "in multi-line string started at line %d", + lexer->token_line); + } + lexer->token_type = STT_ERROR; + return FALSE; + } + + /* Handle dot-stuffing */ + if (str_len(str) <= SIEVE_MAX_STRING_LEN) + str_append_c(str, '.'); + if (sieve_lexer_curchar(scanner) == '.') + sieve_lexer_shift(scanner); + } + + /* Scan the rest of the line */ + while (sieve_lexer_curchar(scanner) != '\n' && + sieve_lexer_curchar(scanner) != '\r') { + + switch (sieve_lexer_curchar(scanner)) { + case -1: + if (scanner->input->eof) { + sieve_lexer_error(lexer, + "end of file before end of multi-line string"); + } + lexer->token_type = STT_ERROR; + return FALSE; + case '\0': + sieve_lexer_error(lexer, + "encountered NUL character in quoted string " + "started at line %d", lexer->token_line); + lexer->token_type = STT_ERROR; + return FALSE; + default: + if (str_len(str) <= SIEVE_MAX_STRING_LEN) + str_append_c(str, sieve_lexer_curchar(scanner)); + } + + sieve_lexer_shift(scanner); + } + + /* If exited loop due to CR, skip it */ + if (sieve_lexer_curchar(scanner) == '\r') + sieve_lexer_shift(scanner); + + /* Now we must see an LF */ + if (sieve_lexer_curchar(scanner) != '\n') { + if (sieve_lexer_curchar(scanner) != -1 || + !scanner->input->eof) { + sieve_lexer_error(lexer, + "found stray carriage-return (CR) character " + "in multi-line string started at line %d", + lexer->token_line); + } + lexer->token_type = STT_ERROR; + return FALSE; + } + + if (str_len(str) <= SIEVE_MAX_STRING_LEN) + str_append(str, "\r\n"); + + sieve_lexer_shift(scanner); + } + + i_unreached(); + lexer->token_type = STT_ERROR; + return FALSE; + } + + if (str_len(str) > SIEVE_MAX_IDENTIFIER_LEN) { + sieve_lexer_error(lexer, + "encountered impossibly long %s%s'", + (type == STT_TAG ? "tag identifier ':" : + "identifier '"), + str_sanitize(str_c(str), + SIEVE_MAX_IDENTIFIER_LEN)); + lexer->token_type = STT_ERROR; + return FALSE; + } + + lexer->token_type = type; + return TRUE; + } + + /* Error (unknown character and EOF handled already) */ + if (lexer->token_type != STT_GARBAGE) { + sieve_lexer_error(lexer, + "unexpected character(s) starting with %s", + _char_sanitize(sieve_lexer_curchar(scanner))); + } + sieve_lexer_shift(scanner); + lexer->token_type = STT_GARBAGE; + return FALSE; + } +} + +void sieve_lexer_skip_token(const struct sieve_lexer *lexer) +{ + /* Scan token while skipping whitespace */ + do { + struct sieve_lexical_scanner *scanner = lexer->scanner; + + if (!sieve_lexer_scan_raw_token(scanner)) { + if (!scanner->input->eof && + scanner->input->stream_errno != 0) { + sieve_critical(scanner->svinst, scanner->ehandler, + sieve_error_script_location(scanner->script, + scanner->current_line), + "error reading script", + "error reading script during lexical analysis: %s", + i_stream_get_error(scanner->input)); + } + return; + } + } while (lexer->token_type == STT_WHITESPACE); +} + diff --git a/pigeonhole/src/lib-sieve/sieve-lexer.h b/pigeonhole/src/lib-sieve/sieve-lexer.h new file mode 100644 index 0000000..95451cc --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-lexer.h @@ -0,0 +1,119 @@ +#ifndef SIEVE_LEXER_H +#define SIEVE_LEXER_H + +#include "lib.h" +#include "str.h" + +#include "sieve-common.h" + +enum sieve_token_type { + STT_NONE, + STT_WHITESPACE, + STT_EOF, + + STT_NUMBER, + STT_IDENTIFIER, + STT_TAG, + STT_STRING, + + STT_RBRACKET, + STT_LBRACKET, + STT_RCURLY, + STT_LCURLY, + STT_RSQUARE, + STT_LSQUARE, + STT_SEMICOLON, + STT_COMMA, + + /* These are currently not used in the lexical specification, but a + token is assigned to these to generate proper error messages (these + are technically not garbage and possibly part of mistyped but + otherwise valid tokens). + */ + STT_SLASH, + STT_COLON, + + /* Error tokens */ + STT_GARBAGE, /* Error reporting deferred to parser */ + STT_ERROR /* Lexer is responsible for error, parser won't report + additional errors */ +}; + +/* + * Lexer object + */ + +struct sieve_lexical_scanner; + +struct sieve_lexer { + struct sieve_lexical_scanner *scanner; + + enum sieve_token_type token_type; + string_t *token_str_value; + sieve_number_t token_int_value; + + int token_line; +}; + +const struct sieve_lexer * +sieve_lexer_create(struct sieve_script *script, + struct sieve_error_handler *ehandler, + enum sieve_error *error_r); +void sieve_lexer_free(const struct sieve_lexer **lexer); + +/* + * Scanning + */ + +void sieve_lexer_skip_token(const struct sieve_lexer *lexer); + +/* + * Token access + */ + +static inline enum sieve_token_type +sieve_lexer_token_type(const struct sieve_lexer *lexer) +{ + return lexer->token_type; +} + +static inline const string_t * +sieve_lexer_token_str(const struct sieve_lexer *lexer) +{ + i_assert(lexer->token_type == STT_STRING); + + return lexer->token_str_value; +} + +static inline const char * +sieve_lexer_token_ident(const struct sieve_lexer *lexer) +{ + i_assert(lexer->token_type == STT_TAG || + lexer->token_type == STT_IDENTIFIER); + + return str_c(lexer->token_str_value); +} + +static inline sieve_number_t +sieve_lexer_token_int(const struct sieve_lexer *lexer) +{ + i_assert(lexer->token_type == STT_NUMBER); + + return lexer->token_int_value; +} + +static inline bool sieve_lexer_eof(const struct sieve_lexer *lexer) +{ + return lexer->token_type == STT_EOF; +} + +static inline int sieve_lexer_token_line(const struct sieve_lexer *lexer) +{ + return lexer->token_line; +} + +const char *sieve_lexer_token_description(const struct sieve_lexer *lexer); + +void sieve_lexer_token_print(const struct sieve_lexer *lexer); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-limits.h b/pigeonhole/src/lib-sieve/sieve-limits.h new file mode 100644 index 0000000..1adbaa6 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-limits.h @@ -0,0 +1,45 @@ +#ifndef SIEVE_LIMITS_H +#define SIEVE_LIMITS_H + +/* + * Scripts + */ + +#define SIEVE_MAX_SCRIPT_NAME_LEN 256 + +#define SIEVE_DEFAULT_MAX_SCRIPT_SIZE (1 << 20) + +#define SIEVE_MAX_LOOP_DEPTH 4 + +/* + * Lexer + */ + +#define SIEVE_MAX_STRING_LEN (1 << 20) +#define SIEVE_MAX_IDENTIFIER_LEN 32 + +/* + * AST + */ + +#define SIEVE_MAX_COMMAND_ARGUMENTS 32 +#define SIEVE_MAX_BLOCK_NESTING 32 +#define SIEVE_MAX_TEST_NESTING 32 + +/* + * Runtime + */ + +#define SIEVE_MAX_MATCH_VALUES 32 +#define SIEVE_HIGH_CPU_TIME_MSECS 1500 +#define SIEVE_DEFAULT_MAX_CPU_TIME_SECS 30 +#define SIEVE_DEFAULT_RESOURCE_USAGE_TIMEOUT_SECS (60 * 60) + +/* + * Actions + */ + +#define SIEVE_DEFAULT_MAX_ACTIONS 32 +#define SIEVE_DEFAULT_MAX_REDIRECTS 4 + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-match-types.c b/pigeonhole/src/lib-sieve/sieve-match-types.c new file mode 100644 index 0000000..a61737e --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-match-types.c @@ -0,0 +1,569 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "compat.h" +#include "mempool.h" +#include "hash.h" +#include "array.h" + +#include "sieve-common.h" +#include "sieve-limits.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-code.h" +#include "sieve-binary.h" +#include "sieve-comparators.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "sieve-match-types.h" + +#include <string.h> + +/* + * Types + */ + +struct sieve_match_values { + pool_t pool; + ARRAY(string_t *) values; + unsigned count; +}; + +/* + * Default match types + */ + +const struct sieve_match_type_def *sieve_core_match_types[] = { + &is_match_type, &contains_match_type, &matches_match_type +}; + +const unsigned int sieve_core_match_types_count = + N_ELEMENTS(sieve_core_match_types); + +/* + * Match-type 'extension' + */ + +static bool mtch_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr); + +const struct sieve_extension_def match_type_extension = { + .name = "@match-types", + .validator_load = mtch_validator_load +}; + +/* + * Validator context: + * name-based match-type registry. + */ + +static struct sieve_validator_object_registry *_get_object_registry +(struct sieve_validator *valdtr) +{ + struct sieve_instance *svinst; + const struct sieve_extension *mcht_ext; + + svinst = sieve_validator_svinst(valdtr); + mcht_ext = sieve_get_match_type_extension(svinst); + return sieve_validator_object_registry_get(valdtr, mcht_ext); +} + +void sieve_match_type_register +(struct sieve_validator *valdtr, const struct sieve_extension *ext, + const struct sieve_match_type_def *mcht_def) +{ + struct sieve_validator_object_registry *regs = _get_object_registry(valdtr); + + sieve_validator_object_registry_add(regs, ext, &mcht_def->obj_def); +} + +static bool sieve_match_type_exists +(struct sieve_validator *valdtr, const char *identifier) +{ + struct sieve_validator_object_registry *regs = _get_object_registry(valdtr); + + return sieve_validator_object_registry_find(regs, identifier, NULL); +} + +static const struct sieve_match_type *sieve_match_type_create_instance +(struct sieve_validator *valdtr, struct sieve_command *cmd, + const char *identifier) +{ + struct sieve_validator_object_registry *regs = _get_object_registry(valdtr); + struct sieve_object object; + struct sieve_match_type *mcht; + + if ( !sieve_validator_object_registry_find(regs, identifier, &object) ) + return NULL; + + mcht = p_new(sieve_command_pool(cmd), struct sieve_match_type, 1); + mcht->object = object; + mcht->def = (const struct sieve_match_type_def *) object.def; + + return mcht; +} + +bool mtch_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + struct sieve_validator_object_registry *regs = + sieve_validator_object_registry_init(valdtr, ext); + unsigned int i; + + /* Register core match-types */ + for ( i = 0; i < sieve_core_match_types_count; i++ ) { + sieve_validator_object_registry_add + (regs, NULL, &(sieve_core_match_types[i]->obj_def)); + } + + return TRUE; +} + +/* + * Interpreter context + */ + +struct mtch_interpreter_context { + struct sieve_match_values *match_values; + bool match_values_enabled; +}; + +static void mtch_interpreter_free +(const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_interpreter *interp ATTR_UNUSED, void *context) +{ + struct mtch_interpreter_context *mctx = + (struct mtch_interpreter_context *) context; + + if ( mctx->match_values != NULL ) { + pool_unref(&mctx->match_values->pool); + } +} + +struct sieve_interpreter_extension +mtch_interpreter_extension = { + .ext_def = &match_type_extension, + .free = mtch_interpreter_free +}; + +static inline struct mtch_interpreter_context *get_interpreter_context +(struct sieve_interpreter *interp, bool create) +{ + struct sieve_instance *svinst; + const struct sieve_extension *mcht_ext; + struct mtch_interpreter_context *ctx; + + svinst = sieve_interpreter_svinst(interp); + mcht_ext = sieve_get_match_type_extension(svinst); + + ctx = (struct mtch_interpreter_context *) + sieve_interpreter_extension_get_context(interp, mcht_ext); + + if ( ctx == NULL && create ) { + pool_t pool = sieve_interpreter_pool(interp); + ctx = p_new(pool, struct mtch_interpreter_context, 1); + + sieve_interpreter_extension_register + (interp, mcht_ext, &mtch_interpreter_extension, (void *) ctx); + } + + return ctx; +} + +/* + * Match values + */ + +bool sieve_match_values_set_enabled +(const struct sieve_runtime_env *renv, bool enable) +{ + struct mtch_interpreter_context *ctx = + get_interpreter_context(renv->interp, enable); + + if ( ctx != NULL ) { + bool previous = ctx->match_values_enabled; + + ctx->match_values_enabled = enable; + return previous; + } + + return FALSE; +} + +bool sieve_match_values_are_enabled +(const struct sieve_runtime_env *renv) +{ + struct mtch_interpreter_context *ctx = + get_interpreter_context(renv->interp, FALSE); + + return ( ctx == NULL ? FALSE : ctx->match_values_enabled ); +} + +struct sieve_match_values *sieve_match_values_start +(const struct sieve_runtime_env *renv) +{ + struct mtch_interpreter_context *ctx = + get_interpreter_context(renv->interp, FALSE); + struct sieve_match_values *match_values; + + if ( ctx == NULL || !ctx->match_values_enabled ) + return NULL; + + pool_t pool = pool_alloconly_create("sieve_match_values", 1024); + + match_values = p_new(pool, struct sieve_match_values, 1); + match_values->pool = pool; + match_values->count = 0; + + p_array_init(&match_values->values, pool, 4); + + return match_values; +} + +static string_t *sieve_match_values_add_entry +(struct sieve_match_values *mvalues) +{ + string_t *entry; + + if ( mvalues == NULL ) return NULL; + + if ( mvalues->count >= SIEVE_MAX_MATCH_VALUES ) return NULL; + + if ( mvalues->count >= array_count(&mvalues->values) ) { + entry = str_new(mvalues->pool, 64); + array_append(&mvalues->values, &entry, 1); } else { + string_t * const *ep = array_idx(&mvalues->values, mvalues->count); + entry = *ep; + str_truncate(entry, 0); + } + + mvalues->count++; + + return entry; +} + +void sieve_match_values_set +(struct sieve_match_values *mvalues, unsigned int index, string_t *value) +{ + if ( mvalues != NULL && index < array_count(&mvalues->values) ) { + string_t * const *ep = array_idx(&mvalues->values, index); + string_t *entry = *ep; + + if ( entry != NULL && value != NULL ) { + str_truncate(entry, 0); + str_append_str(entry, value); + } + } +} + +void sieve_match_values_add +(struct sieve_match_values *mvalues, string_t *value) +{ + string_t *entry = sieve_match_values_add_entry(mvalues); + + if ( entry != NULL && value != NULL ) + str_append_str(entry, value); +} + +void sieve_match_values_add_char +(struct sieve_match_values *mvalues, char c) +{ + string_t *entry = sieve_match_values_add_entry(mvalues); + + if ( entry != NULL ) + str_append_c(entry, c); +} + +void sieve_match_values_skip +(struct sieve_match_values *mvalues, int num) +{ + int i; + + for ( i = 0; i < num; i++ ) + (void) sieve_match_values_add_entry(mvalues); +} + +void sieve_match_values_commit +(const struct sieve_runtime_env *renv, struct sieve_match_values **mvalues) +{ + struct mtch_interpreter_context *ctx; + + if ( (*mvalues) == NULL ) return; + + ctx = get_interpreter_context(renv->interp, FALSE); + if ( ctx == NULL || !ctx->match_values_enabled ) + return; + + if ( ctx->match_values != NULL ) { + pool_unref(&ctx->match_values->pool); + ctx->match_values = NULL; + } + + ctx->match_values = *mvalues; + *mvalues = NULL; +} + +void sieve_match_values_abort +(struct sieve_match_values **mvalues) +{ + if ( (*mvalues) == NULL ) return; + + pool_unref(&(*mvalues)->pool); + *mvalues = NULL; +} + +void sieve_match_values_get +(const struct sieve_runtime_env *renv, unsigned int index, string_t **value_r) +{ + struct mtch_interpreter_context *ctx = + get_interpreter_context(renv->interp, FALSE); + struct sieve_match_values *mvalues; + + if ( ctx == NULL || ctx->match_values == NULL ) { + *value_r = NULL; + return; + } + + mvalues = ctx->match_values; + if ( index < array_count(&mvalues->values) && index < mvalues->count ) { + string_t * const *entry = array_idx(&mvalues->values, index); + + *value_r = *entry; + return; + } + + *value_r = NULL; +} + +/* + * Match-type tagged argument + */ + +/* Forward declarations */ + +static bool tag_match_type_is_instance_of + (struct sieve_validator *valdtr, struct sieve_command *cmd, + const struct sieve_extension *ext, const char *identifier, void **data); +static bool tag_match_type_validate + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd); +static bool tag_match_type_generate + (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *cmd); + +/* Argument object */ + +const struct sieve_argument_def match_type_tag = { + .identifier = "MATCH-TYPE", + .is_instance_of = tag_match_type_is_instance_of, + .validate = tag_match_type_validate, + .generate = tag_match_type_generate +}; + +/* Argument implementation */ + +static bool tag_match_type_is_instance_of +(struct sieve_validator *valdtr, struct sieve_command *cmd, + const struct sieve_extension *ext ATTR_UNUSED, const char *identifier, + void **data) +{ + const struct sieve_match_type *mcht; + + if ( data == NULL ) + return sieve_match_type_exists(valdtr, identifier); + + if ( (mcht=sieve_match_type_create_instance + (valdtr, cmd, identifier)) == NULL ) + return FALSE; + + *data = (void *) mcht; + return TRUE; +} + +static bool tag_match_type_validate +(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd ATTR_UNUSED) +{ + const struct sieve_match_type *mcht = + (const struct sieve_match_type *) (*arg)->argument->data; + struct sieve_match_type_context *mtctx; + + mtctx = p_new(sieve_command_pool(cmd), struct sieve_match_type_context, 1); + mtctx->match_type = mcht; + mtctx->argument = *arg; + mtctx->comparator = NULL; /* Can be filled in later */ + + (*arg)->argument->data = mtctx; + + /* Syntax: + * ":is" / ":contains" / ":matches" (subject to extension) + */ + + /* Skip tag */ + *arg = sieve_ast_argument_next(*arg); + + /* Check whether this match type requires additional validation. + * Additional validation can override the match type recorded in the context + * for later code generation. + */ + if ( mcht->def != NULL && mcht->def->validate != NULL ) { + return mcht->def->validate(valdtr, arg, mtctx); + } + + return TRUE; +} + +static bool tag_match_type_generate +(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *cmd ATTR_UNUSED) +{ + struct sieve_match_type_context *mtctx = + (struct sieve_match_type_context *) arg->argument->data; + + (void) sieve_opr_match_type_emit(cgenv->sblock, mtctx->match_type); + + return TRUE; +} + +void sieve_match_types_link_tags +(struct sieve_validator *valdtr, + struct sieve_command_registration *cmd_reg, int id_code) +{ + struct sieve_instance *svinst; + const struct sieve_extension *mcht_ext; + + svinst = sieve_validator_svinst(valdtr); + mcht_ext = sieve_get_comparator_extension(svinst); + + sieve_validator_register_tag + (valdtr, cmd_reg, mcht_ext, &match_type_tag, id_code); +} + +/* + * Validation + */ + +bool sieve_match_type_validate +(struct sieve_validator *valdtr, struct sieve_command *cmd, + struct sieve_ast_argument *key_arg, + const struct sieve_match_type *mcht_default, + const struct sieve_comparator *cmp_default) +{ + struct sieve_ast_argument *arg = sieve_command_first_argument(cmd); + struct sieve_ast_argument *mt_arg = NULL; + struct sieve_match_type_context *mtctx; + const struct sieve_match_type *mcht = NULL; + const struct sieve_comparator *cmp = NULL; + + /* Find match type and comparator among the arguments */ + while ( arg != NULL && arg != cmd->first_positional ) { + if ( sieve_argument_is_comparator(arg) ) { + cmp = sieve_comparator_tag_get(arg); + if ( mt_arg != NULL ) break; + } + + if ( sieve_argument_is_match_type(arg) ) { + mt_arg = arg; + if ( cmp != NULL ) break; + } + arg = sieve_ast_argument_next(arg); + } + + /* Verify using the default comparator if none is specified explicitly */ + if ( cmp == NULL ) { + cmp = sieve_comparator_copy(sieve_command_pool(cmd), cmp_default); + } + + /* Verify the default match type if none is specified explicitly */ + if ( mt_arg == NULL || mt_arg->argument == NULL || + mt_arg->argument->data == NULL ) { + mcht = sieve_match_type_copy(sieve_command_pool(cmd), mcht_default); + mtctx = t_new(struct sieve_match_type_context, 1); + mtctx->command = cmd; + mtctx->match_type = mcht; + } else { + mtctx = (struct sieve_match_type_context *) mt_arg->argument->data; + mcht = mtctx->match_type; + } + mtctx->comparator = cmp; + + /* Check whether this match type requires additional validation. + * Additional validation can override the match type recorded in the context + * for later code generation. + */ + if ( mcht != NULL && mcht->def != NULL && + mcht->def->validate_context != NULL ) { + return mcht->def->validate_context(valdtr, mt_arg, mtctx, key_arg); + } + + return TRUE; +} + +void sieve_match_type_arguments_remove +(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *cmd) +{ + struct sieve_ast_argument *arg = sieve_command_first_argument(cmd); + + /* Remove any comparator and match type arguments */ + while ( arg != NULL && arg != cmd->first_positional ) { + if ( sieve_argument_is_comparator(arg) ) { + arg = sieve_ast_arguments_detach(arg, 1); + continue; + } + + if ( sieve_argument_is_match_type(arg) ) { + arg = sieve_ast_arguments_detach(arg, 1); + continue; + } + + arg = sieve_ast_argument_next(arg); + } +} + + +/* + * Match-type operand + */ + +const struct sieve_operand_class sieve_match_type_operand_class = + { "match type" }; + +static const struct sieve_extension_objects core_match_types = + SIEVE_EXT_DEFINE_MATCH_TYPES(sieve_core_match_types); + +const struct sieve_operand_def match_type_operand = { + .name = "match-type", + .code = SIEVE_OPERAND_MATCH_TYPE, + .class = &sieve_match_type_operand_class, + .interface = &core_match_types +}; + +/* + * Common validation implementation + */ + +bool sieve_match_substring_validate_context +(struct sieve_validator *valdtr, struct sieve_ast_argument *arg, + struct sieve_match_type_context *ctx, + struct sieve_ast_argument *key_arg ATTR_UNUSED) +{ + const struct sieve_comparator *cmp = ctx->comparator; + + if ( cmp == NULL || cmp->def == NULL ) + return TRUE; + + if ( (cmp->def->flags & SIEVE_COMPARATOR_FLAG_SUBSTRING_MATCH) == 0 ) { + sieve_argument_validate_error(valdtr, arg, + "the specified %s comparator does not support " + "sub-string matching as required by the :%s match type", + cmp->object.def->identifier, ctx->match_type->object.def->identifier ); + + return FALSE; + } + + return TRUE; +} diff --git a/pigeonhole/src/lib-sieve/sieve-match-types.h b/pigeonhole/src/lib-sieve/sieve-match-types.h new file mode 100644 index 0000000..0417e2c --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-match-types.h @@ -0,0 +1,233 @@ +#ifndef SIEVE_MATCH_TYPES_H +#define SIEVE_MATCH_TYPES_H + +#include "sieve-common.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-code.h" +#include "sieve-objects.h" + +/* + * Types + */ + +struct sieve_match_type_context; + +/* + * Core match types + */ + +enum sieve_match_type_code { + SIEVE_MATCH_TYPE_IS, + SIEVE_MATCH_TYPE_CONTAINS, + SIEVE_MATCH_TYPE_MATCHES, + SIEVE_MATCH_TYPE_CUSTOM +}; + +extern const struct sieve_match_type_def is_match_type; +extern const struct sieve_match_type_def contains_match_type; +extern const struct sieve_match_type_def matches_match_type; + +/* + * Match type definition + */ + +struct sieve_match_type_def { + struct sieve_object_def obj_def; + + bool (*validate) + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_match_type_context *ctx); + bool (*validate_context) + (struct sieve_validator *valdtr, struct sieve_ast_argument *arg, + struct sieve_match_type_context *ctx, struct sieve_ast_argument *key_arg); + + /* + * Matching + */ + + /* Custom implementation */ + + int (*match) + (struct sieve_match_context *mctx, struct sieve_stringlist *value_list, + struct sieve_stringlist *key_list); + + /* Default match loop */ + + void (*match_init)(struct sieve_match_context *mctx); + + int (*match_keys) + (struct sieve_match_context *mctx, const char *val, size_t val_size, + struct sieve_stringlist *key_list); + int (*match_key) + (struct sieve_match_context *mctx, const char *val, size_t val_size, + const char *key, size_t key_size); + + void (*match_deinit)(struct sieve_match_context *mctx); +}; + +/* + * Match type instance + */ + +struct sieve_match_type { + struct sieve_object object; + + const struct sieve_match_type_def *def; +}; + +#define SIEVE_MATCH_TYPE_DEFAULT(definition) \ + { SIEVE_OBJECT_DEFAULT(definition), &(definition) } + +#define sieve_match_type_name(mcht) \ + ( (mcht)->object.def->identifier ) +#define sieve_match_type_is(mcht, definition) \ + ( (mcht)->def == &(definition) ) + +static inline const struct sieve_match_type *sieve_match_type_copy +(pool_t pool, const struct sieve_match_type *cmp_orig) +{ + struct sieve_match_type *cmp = p_new(pool, struct sieve_match_type, 1); + + *cmp = *cmp_orig; + + return cmp; +} + +/* + * Match type context + */ + +struct sieve_match_type_context { + struct sieve_command *command; + struct sieve_ast_argument *argument; + + const struct sieve_match_type *match_type; + + /* Only filled in when match_type->validate_context() is called */ + const struct sieve_comparator *comparator; + + /* Context data could be used in the future to pass data between validator and + * generator in match types that use extra parameters. Currently not + * necessary, not even for the relational extension. + */ + void *ctx_data; +}; + +/* + * Match type registration + */ + +void sieve_match_type_register + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + const struct sieve_match_type_def *mcht); + +/* + * Match values + */ + +struct sieve_match_values; + +bool sieve_match_values_set_enabled + (const struct sieve_runtime_env *renv, bool enable); +bool sieve_match_values_are_enabled + (const struct sieve_runtime_env *renv); + +struct sieve_match_values *sieve_match_values_start + (const struct sieve_runtime_env *renv); +void sieve_match_values_set + (struct sieve_match_values *mvalues, unsigned int index, string_t *value); +void sieve_match_values_add + (struct sieve_match_values *mvalues, string_t *value); +void sieve_match_values_add_char + (struct sieve_match_values *mvalues, char c); +void sieve_match_values_skip + (struct sieve_match_values *mvalues, int num); + +void sieve_match_values_commit + (const struct sieve_runtime_env *renv, struct sieve_match_values **mvalues); +void sieve_match_values_abort + (struct sieve_match_values **mvalues); + +void sieve_match_values_get + (const struct sieve_runtime_env *renv, unsigned int index, string_t **value_r); + +/* + * Match type tagged argument + */ + +extern const struct sieve_argument_def match_type_tag; + +static inline bool sieve_argument_is_match_type + (struct sieve_ast_argument *arg) +{ + return ( arg->argument->def == &match_type_tag ); +} + +void sieve_match_types_link_tags + (struct sieve_validator *valdtr, + struct sieve_command_registration *cmd_reg, int id_code); + +/* + * Validation + */ + +bool sieve_match_type_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd, + struct sieve_ast_argument *key_arg, + const struct sieve_match_type *mcht_default, + const struct sieve_comparator *cmp_default); + +void sieve_match_type_arguments_remove + (struct sieve_validator *valdtr, struct sieve_command *cmd); + +/* + * Match type operand + */ + +extern const struct sieve_operand_def match_type_operand; +extern const struct sieve_operand_class sieve_match_type_operand_class; + +#define SIEVE_EXT_DEFINE_MATCH_TYPE(OP) SIEVE_EXT_DEFINE_OBJECT(OP) +#define SIEVE_EXT_DEFINE_MATCH_TYPES(OPS) SIEVE_EXT_DEFINE_OBJECTS(OPS) + +static inline bool sieve_operand_is_match_type +(const struct sieve_operand *operand) +{ + return ( operand != NULL && operand->def != NULL && + operand->def->class == &sieve_match_type_operand_class ); +} + +static inline void sieve_opr_match_type_emit +(struct sieve_binary_block *sblock, const struct sieve_match_type *mcht) +{ + sieve_opr_object_emit(sblock, mcht->object.ext, mcht->object.def); +} + +static inline bool sieve_opr_match_type_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + return sieve_opr_object_dump + (denv, &sieve_match_type_operand_class, address, NULL); +} + +static inline int sieve_opr_match_type_read +(const struct sieve_runtime_env *renv, sieve_size_t *address, + struct sieve_match_type *mcht) +{ + if ( !sieve_opr_object_read + (renv, &sieve_match_type_operand_class, address, &mcht->object) ) + return SIEVE_EXEC_BIN_CORRUPT; + + mcht->def = (const struct sieve_match_type_def *) mcht->object.def; + return SIEVE_EXEC_OK; +} + +/* Common validation implementation */ + +bool sieve_match_substring_validate_context + (struct sieve_validator *valdtr, struct sieve_ast_argument *arg, + struct sieve_match_type_context *ctx, + struct sieve_ast_argument *key_arg); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-match.c b/pigeonhole/src/lib-sieve/sieve-match.c new file mode 100644 index 0000000..37d37fe --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-match.c @@ -0,0 +1,293 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "mempool.h" +#include "hash.h" +#include "array.h" +#include "str-sanitize.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-binary.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-runtime-trace.h" + +#include "sieve-match.h" + +/* + * Matching implementation + */ + +struct sieve_match_context *sieve_match_begin +(const struct sieve_runtime_env *renv, + const struct sieve_match_type *mcht, + const struct sieve_comparator *cmp) +{ + struct sieve_match_context *mctx; + pool_t pool; + + /* Reject unimplemented match-type */ + if ( mcht->def == NULL || (mcht->def->match == NULL && + mcht->def->match_keys == NULL && mcht->def->match_key == NULL) ) + return NULL; + + /* Create match context */ + pool = pool_alloconly_create("sieve_match_context", 1024); + mctx = p_new(pool, struct sieve_match_context, 1); + mctx->pool = pool; + mctx->runenv = renv; + mctx->match_type = mcht; + mctx->comparator = cmp; + mctx->exec_status = SIEVE_EXEC_OK; + mctx->trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING); + + /* Trace */ + if ( mctx->trace ) { + sieve_runtime_trace_descend(renv); + sieve_runtime_trace(renv, 0, + "starting `:%s' match with `%s' comparator:", + sieve_match_type_name(mcht), sieve_comparator_name(cmp)); + } + + /* Initialize match type */ + if ( mcht->def != NULL && mcht->def->match_init != NULL ) { + mcht->def->match_init(mctx); + } + + return mctx; +} + +int sieve_match_value +(struct sieve_match_context *mctx, const char *value, size_t value_size, + struct sieve_stringlist *key_list) +{ + const struct sieve_match_type *mcht = mctx->match_type; + const struct sieve_runtime_env *renv = mctx->runenv; + int match, ret; + + if ( mctx->trace ) { + sieve_runtime_trace(renv, 0, + "matching value `%s'", str_sanitize(value, 80)); + } + + /* Match to key values */ + + sieve_stringlist_reset(key_list); + + if ( mctx->trace ) + sieve_stringlist_set_trace(key_list, TRUE); + + sieve_runtime_trace_descend(renv); + + if ( mcht->def->match_keys != NULL ) { + /* Call match-type's own key match handler */ + match = mcht->def->match_keys(mctx, value, value_size, key_list); + } else { + string_t *key_item = NULL; + + /* Default key match loop */ + match = 0; + while ( match == 0 && + (ret=sieve_stringlist_next_item(key_list, &key_item)) > 0 ) { + T_BEGIN { + match = mcht->def->match_key + (mctx, value, value_size, str_c(key_item), str_len(key_item)); + + if ( mctx->trace ) { + sieve_runtime_trace(renv, 0, + "with key `%s' => %d", str_sanitize(str_c(key_item), 80), + match); + } + } T_END; + } + + if ( ret < 0 ) { + mctx->exec_status = key_list->exec_status; + match = -1; + } + } + + sieve_runtime_trace_ascend(renv); + + if ( mctx->match_status < 0 || match < 0 ) + mctx->match_status = -1; + else + mctx->match_status = + ( mctx->match_status > match ? mctx->match_status : match ); + return match; +} + +int sieve_match_end(struct sieve_match_context **mctx, int *exec_status) +{ + const struct sieve_match_type *mcht = (*mctx)->match_type; + const struct sieve_runtime_env *renv = (*mctx)->runenv; + int match = (*mctx)->match_status; + + if ( mcht->def != NULL && mcht->def->match_deinit != NULL ) + mcht->def->match_deinit(*mctx); + + if ( exec_status != NULL ) + *exec_status = (*mctx)->exec_status; + + pool_unref(&(*mctx)->pool); + + sieve_runtime_trace(renv, SIEVE_TRLVL_MATCHING, + "finishing match with result: %s", + ( match > 0 ? "matched" : ( match < 0 ? "error" : "not matched" ) )); + sieve_runtime_trace_ascend(renv); + + return match; +} + +int sieve_match +(const struct sieve_runtime_env *renv, + const struct sieve_match_type *mcht, + const struct sieve_comparator *cmp, + struct sieve_stringlist *value_list, + struct sieve_stringlist *key_list, + int *exec_status) +{ + struct sieve_match_context *mctx; + string_t *value_item = NULL; + int match, ret; + + if ( (mctx=sieve_match_begin(renv, mcht, cmp)) == NULL ) + return 0; + + /* Match value to keys */ + + sieve_stringlist_reset(value_list); + + if ( mctx->trace ) + sieve_stringlist_set_trace(value_list, TRUE); + + if ( mcht->def->match != NULL ) { + /* Call match-type's match handler */ + match = mctx->match_status = + mcht->def->match(mctx, value_list, key_list); + + } else { + /* Default value match loop */ + + match = 0; + while ( match == 0 && + (ret=sieve_stringlist_next_item(value_list, &value_item)) > 0 ) { + + match = sieve_match_value + (mctx, str_c(value_item), str_len(value_item), key_list); + } + + if ( ret < 0 ) { + mctx->exec_status = value_list->exec_status; + match = -1; + } + } + + (void)sieve_match_end(&mctx, exec_status); + return match; +} + +/* + * Reading match operands + */ + +int sieve_match_opr_optional_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address, int *opt_code) +{ + int _opt_code = 0; + bool final = FALSE, opok = TRUE; + + if ( opt_code == NULL ) { + opt_code = &_opt_code; + final = TRUE; + } + + while ( opok ) { + int opt; + + if ( (opt=sieve_opr_optional_dump(denv, address, opt_code)) <= 0 ) + return opt; + + switch ( *opt_code ) { + case SIEVE_MATCH_OPT_COMPARATOR: + opok = sieve_opr_comparator_dump(denv, address); + break; + case SIEVE_MATCH_OPT_MATCH_TYPE: + opok = sieve_opr_match_type_dump(denv, address); + break; + default: + return ( final ? -1 : 1 ); + } + } + + return -1; +} + +int sieve_match_opr_optional_read +(const struct sieve_runtime_env *renv, sieve_size_t *address, int *opt_code, + int *exec_status, struct sieve_comparator *cmp, struct sieve_match_type *mcht) +{ + int _opt_code = 0; + bool final = FALSE; + int status = SIEVE_EXEC_OK; + + if ( opt_code == NULL ) { + opt_code = &_opt_code; + final = TRUE; + } + + if ( exec_status != NULL ) + *exec_status = SIEVE_EXEC_OK; + + while ( status == SIEVE_EXEC_OK ) { + int opt; + + if ( (opt=sieve_opr_optional_read(renv, address, opt_code)) <= 0 ){ + if ( opt < 0 && exec_status != NULL ) + *exec_status = SIEVE_EXEC_BIN_CORRUPT; + return opt; + } + + switch ( *opt_code ) { + case SIEVE_MATCH_OPT_COMPARATOR: + if (cmp == NULL) { + sieve_runtime_trace_error(renv, "unexpected comparator operand"); + if ( exec_status != NULL ) + *exec_status = SIEVE_EXEC_BIN_CORRUPT; + return -1; + } + status = sieve_opr_comparator_read(renv, address, cmp); + break; + case SIEVE_MATCH_OPT_MATCH_TYPE: + if (mcht == NULL) { + sieve_runtime_trace_error(renv, "unexpected match-type operand"); + if ( exec_status != NULL ) + *exec_status = SIEVE_EXEC_BIN_CORRUPT; + return -1; + } + status = sieve_opr_match_type_read(renv, address, mcht); + break; + default: + if ( final ) { + sieve_runtime_trace_error(renv, "invalid optional operand"); + if ( exec_status != NULL ) + *exec_status = SIEVE_EXEC_BIN_CORRUPT; + return -1; + } + return 1; + } + } + + if ( exec_status != NULL ) + *exec_status = status; + return -1; +} + diff --git a/pigeonhole/src/lib-sieve/sieve-match.h b/pigeonhole/src/lib-sieve/sieve-match.h new file mode 100644 index 0000000..d4d726f --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-match.h @@ -0,0 +1,68 @@ +#ifndef SIEVE_MATCH_H +#define SIEVE_MATCH_H + +#include "sieve-common.h" + +/* + * Matching context + */ + +struct sieve_match_context { + pool_t pool; + + const struct sieve_runtime_env *runenv; + + const struct sieve_match_type *match_type; + const struct sieve_comparator *comparator; + + void *data; + + int match_status; + int exec_status; + + bool trace:1; +}; + +/* + * Matching implementation + */ + +/* Manual value iteration (for when multiple matches are allowed) */ +struct sieve_match_context *sieve_match_begin + (const struct sieve_runtime_env *renv, + const struct sieve_match_type *mcht, + const struct sieve_comparator *cmp); +int sieve_match_value + (struct sieve_match_context *mctx, const char *value, size_t value_size, + struct sieve_stringlist *key_list); +int sieve_match_end(struct sieve_match_context **mctx, int *exec_status); + +/* Default matching operation */ +int sieve_match + (const struct sieve_runtime_env *renv, + const struct sieve_match_type *mcht, + const struct sieve_comparator *cmp, + struct sieve_stringlist *value_list, + struct sieve_stringlist *key_list, + int *exec_status); + +/* + * Read matching operands + */ + +enum sieve_match_opt_operand { + SIEVE_MATCH_OPT_END, + SIEVE_MATCH_OPT_COMPARATOR, + SIEVE_MATCH_OPT_MATCH_TYPE, + SIEVE_MATCH_OPT_LAST +}; + +int sieve_match_opr_optional_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address, int *opt_code); + +int sieve_match_opr_optional_read + (const struct sieve_runtime_env *renv, sieve_size_t *address, int *opt_code, + int *exec_status, struct sieve_comparator *cmp, + struct sieve_match_type *mcht); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-message.c b/pigeonhole/src/lib-sieve/sieve-message.c new file mode 100644 index 0000000..d086883 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-message.c @@ -0,0 +1,1845 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "ioloop.h" +#include "mempool.h" +#include "array.h" +#include "str.h" +#include "str-sanitize.h" +#include "istream.h" +#include "time-util.h" +#include "rfc822-parser.h" +#include "message-date.h" +#include "message-parser.h" +#include "message-decoder.h" +#include "message-header-decode.h" +#include "mail-html2text.h" +#include "mail-storage.h" +#include "mail-user.h" +#include "smtp-params.h" +#include "master-service.h" +#include "master-service-settings.h" +#include "raw-storage.h" + +#include "edit-mail.h" + +#include "sieve-common.h" +#include "sieve-stringlist.h" +#include "sieve-error.h" +#include "sieve-extensions.h" +#include "sieve-code.h" +#include "sieve-address.h" +#include "sieve-address-parts.h" +#include "sieve-runtime.h" +#include "sieve-runtime-trace.h" +#include "sieve-match.h" +#include "sieve-interpreter.h" + +#include "sieve-message.h" + +/* + * Message transmission + */ + +const char *sieve_message_get_new_id(const struct sieve_instance *svinst) +{ + static int count = 0; + + return t_strdup_printf("<dovecot-sieve-%s-%s-%d@%s>", + dec2str(ioloop_timeval.tv_sec), dec2str(ioloop_timeval.tv_usec), + count++, svinst->hostname); +} + +/* + * Message context + */ + +struct sieve_message_header { + const char *name; + + const unsigned char *value, *utf8_value; + size_t value_len, utf8_value_len; +}; + +struct sieve_message_part { + struct sieve_message_part *parent, *next, *children; + + ARRAY(struct sieve_message_header) headers; + + const char *content_type; + const char *content_disposition; + + const char *decoded_body; + const char *text_body; + size_t decoded_body_size; + size_t text_body_size; + + bool have_body:1; /* there's the empty end-of-headers line */ + bool epilogue:1; /* this is a multipart epilogue */ +}; + +struct sieve_message_version { + struct mail *mail; + struct mailbox *box; + struct mailbox_transaction_context *trans; + struct edit_mail *edit_mail; +}; + +struct sieve_message_context { + pool_t pool; + pool_t context_pool; + int refcount; + + struct sieve_instance *svinst; + struct timeval time; + + struct mail_user *mail_user; + const struct sieve_message_data *msgdata; + + /* Message versioning */ + + struct mail_user *raw_mail_user; + ARRAY(struct sieve_message_version) versions; + + /* Context data for extensions */ + + ARRAY(void *) ext_contexts; + + /* Body */ + + ARRAY(struct sieve_message_part *) cached_body_parts; + ARRAY(struct sieve_message_part_data) return_body_parts; + buffer_t *raw_body; + + bool edit_snapshot:1; + bool substitute_snapshot:1; +}; + +/* + * Message versions + */ + +static inline struct sieve_message_version *sieve_message_version_new +(struct sieve_message_context *msgctx) +{ + return array_append_space(&msgctx->versions); +} + +static inline struct sieve_message_version *sieve_message_version_get +(struct sieve_message_context *msgctx) +{ + struct sieve_message_version *versions; + unsigned int count; + + versions = array_get_modifiable(&msgctx->versions, &count); + if ( count == 0 ) + return array_append_space(&msgctx->versions); + + return &versions[count-1]; +} + +static inline void sieve_message_version_free +(struct sieve_message_version *version) +{ + if ( version->edit_mail != NULL ) { + edit_mail_unwrap(&version->edit_mail); + version->edit_mail = NULL; + } + + if ( version->mail != NULL ) { + mail_free(&version->mail); + mailbox_transaction_rollback(&version->trans); + mailbox_free(&version->box); + version->mail = NULL; + } +} + +/* + * Message context object + */ + +struct sieve_message_context *sieve_message_context_create +(struct sieve_instance *svinst, struct mail_user *mail_user, + const struct sieve_message_data *msgdata) +{ + struct sieve_message_context *msgctx; + + msgctx = i_new(struct sieve_message_context, 1); + msgctx->refcount = 1; + msgctx->svinst = svinst; + + msgctx->mail_user = mail_user; + msgctx->msgdata = msgdata; + + i_gettimeofday(&msgctx->time); + + sieve_message_context_reset(msgctx); + + return msgctx; +} + +void sieve_message_context_ref(struct sieve_message_context *msgctx) +{ + msgctx->refcount++; +} + +static void sieve_message_context_clear(struct sieve_message_context *msgctx) +{ + struct sieve_message_version *versions; + unsigned int count, i; + + if ( msgctx->pool != NULL ) { + versions = array_get_modifiable(&msgctx->versions, &count); + + for ( i = 0; i < count; i++ ) { + sieve_message_version_free(&versions[i]); + } + + pool_unref(&(msgctx->pool)); + } +} + +void sieve_message_context_unref(struct sieve_message_context **msgctx) +{ + i_assert((*msgctx)->refcount > 0); + + if (--(*msgctx)->refcount != 0) + return; + + if ( (*msgctx)->raw_mail_user != NULL ) + mail_user_unref(&(*msgctx)->raw_mail_user); + + sieve_message_context_clear(*msgctx); + + if ( (*msgctx)->context_pool != NULL ) + pool_unref(&((*msgctx)->context_pool)); + + i_free(*msgctx); + *msgctx = NULL; +} + +static void sieve_message_context_flush(struct sieve_message_context *msgctx) +{ + pool_t pool; + + if ( msgctx->context_pool != NULL ) + pool_unref(&(msgctx->context_pool)); + + msgctx->context_pool = pool = + pool_alloconly_create("sieve_message_context_data", 2048); + + p_array_init(&msgctx->ext_contexts, pool, + sieve_extensions_get_count(msgctx->svinst)); + + p_array_init(&msgctx->cached_body_parts, pool, 8); + p_array_init(&msgctx->return_body_parts, pool, 8); + msgctx->raw_body = NULL; +} + +void sieve_message_context_reset(struct sieve_message_context *msgctx) +{ + sieve_message_context_clear(msgctx); + + msgctx->pool = pool_alloconly_create("sieve_message_context", 1024); + + p_array_init(&msgctx->versions, msgctx->pool, 4); + + sieve_message_context_flush(msgctx); +} + +pool_t sieve_message_context_pool(struct sieve_message_context *msgctx) +{ + return msgctx->context_pool; +} + +void sieve_message_context_time(struct sieve_message_context *msgctx, + struct timeval *time) +{ + *time = msgctx->time; +} + +/* Extension support */ + +void sieve_message_context_extension_set +(struct sieve_message_context *msgctx, const struct sieve_extension *ext, + void *context) +{ + if ( ext->id < 0 ) return; + + array_idx_set(&msgctx->ext_contexts, (unsigned int) ext->id, &context); +} + +const void *sieve_message_context_extension_get +(struct sieve_message_context *msgctx, const struct sieve_extension *ext) +{ + void * const *ctx; + + if ( ext->id < 0 || ext->id >= (int) array_count(&msgctx->ext_contexts) ) + return NULL; + + ctx = array_idx(&msgctx->ext_contexts, (unsigned int) ext->id); + + return *ctx; +} + +/* Envelope */ + +const struct smtp_address *sieve_message_get_orig_recipient +(struct sieve_message_context *msgctx) +{ + const struct sieve_message_data *msgdata = msgctx->msgdata; + const struct smtp_address *orcpt_to = NULL; + + if ( msgdata->envelope.rcpt_params != NULL ) { + orcpt_to = msgdata->envelope.rcpt_params->orcpt.addr; + if ( !smtp_address_isnull(orcpt_to) ) + return orcpt_to; + } + + orcpt_to = msgdata->envelope.rcpt_to; + return ( !smtp_address_isnull(orcpt_to) ? orcpt_to : NULL ); +} + +const struct smtp_address *sieve_message_get_final_recipient +(struct sieve_message_context *msgctx) +{ + const struct sieve_message_data *msgdata = msgctx->msgdata; + const struct smtp_address *rcpt_to = msgdata->envelope.rcpt_to; + + return ( !smtp_address_isnull(rcpt_to) ? rcpt_to : NULL); +} + +const struct smtp_address *sieve_message_get_sender +(struct sieve_message_context *msgctx) +{ + const struct sieve_message_data *msgdata = msgctx->msgdata; + const struct smtp_address *mail_from = msgdata->envelope.mail_from; + + return ( !smtp_address_isnull(mail_from) ? mail_from : NULL); +} + +/* + * Mail + */ + +int sieve_message_substitute +(struct sieve_message_context *msgctx, struct istream *input) +{ + static const char *wanted_headers[] = { + "From", "Message-ID", "Subject", "Return-Path", NULL + }; + static const struct smtp_address default_sender = { + .localpart = DEFAULT_ENVELOPE_SENDER, + .domain = NULL, + }; + struct mail_user *mail_user = msgctx->mail_user; + struct sieve_message_version *version; + struct mailbox_header_lookup_ctx *headers_ctx; + struct mailbox *box = NULL; + const struct smtp_address *sender; + int ret; + + i_assert(input->blocking); + + if ( msgctx->raw_mail_user == NULL ) { + void **sets = master_service_settings_get_others(master_service); + + msgctx->raw_mail_user = + raw_storage_create_from_set(mail_user->set_info, sets[0]); + } + + i_stream_seek(input, 0); + sender = sieve_message_get_sender(msgctx); + sender = (sender == NULL ? &default_sender : sender); + ret = raw_mailbox_alloc_stream(msgctx->raw_mail_user, input, (time_t)-1, + smtp_address_encode(sender), &box); + + if ( ret < 0 ) { + e_error(msgctx->svinst->event, + "can't open substituted mail as raw: %s", + mailbox_get_last_internal_error(box, NULL)); + return -1; + } + + if ( msgctx->substitute_snapshot ) { + version = sieve_message_version_new(msgctx); + } else { + version = sieve_message_version_get(msgctx); + sieve_message_version_free(version); + } + + version->box = box; + version->trans = mailbox_transaction_begin(box, 0, __func__); + headers_ctx = mailbox_header_lookup_init(box, wanted_headers); + version->mail = mail_alloc(version->trans, 0, headers_ctx); + mailbox_header_lookup_unref(&headers_ctx); + mail_set_seq(version->mail, 1); + + sieve_message_context_flush(msgctx); + + msgctx->substitute_snapshot = FALSE; + msgctx->edit_snapshot = FALSE; + + return 1; +} + +struct mail *sieve_message_get_mail +(struct sieve_message_context *msgctx) +{ + const struct sieve_message_version *versions; + unsigned int count; + + versions = array_get(&msgctx->versions, &count); + if ( count == 0 ) + return msgctx->msgdata->mail; + + if ( versions[count-1].edit_mail != NULL ) + return edit_mail_get_mail(versions[count-1].edit_mail); + + return versions[count-1].mail; +} + +struct edit_mail *sieve_message_edit +(struct sieve_message_context *msgctx) +{ + struct sieve_message_version *version; + + version = sieve_message_version_get(msgctx); + + if ( version->edit_mail == NULL ) { + version->edit_mail = edit_mail_wrap + (( version->mail == NULL ? msgctx->msgdata->mail : version->mail )); + } else if ( msgctx->edit_snapshot ) { + version->edit_mail = edit_mail_snapshot(version->edit_mail); + } + + msgctx->edit_snapshot = FALSE; + + return version->edit_mail; +} + +void sieve_message_snapshot +(struct sieve_message_context *msgctx) +{ + msgctx->edit_snapshot = TRUE; + msgctx->substitute_snapshot = TRUE; +} + +/* + * Message header list + */ + +/* Forward declarations */ + +static int sieve_message_header_list_next_item + (struct sieve_header_list *_hdrlist, const char **name_r, + string_t **value_r); +static int sieve_message_header_list_next_value + (struct sieve_stringlist *_strlist, string_t **value_r); +static void sieve_message_header_list_reset + (struct sieve_stringlist *_strlist); + +/* String list object */ + +struct sieve_message_header_list { + struct sieve_header_list hdrlist; + + struct sieve_stringlist *field_names; + + const char *header_name; + const char *const *headers; + int headers_index; + + bool mime_decode:1; +}; + +struct sieve_header_list *sieve_message_header_list_create +(const struct sieve_runtime_env *renv, + struct sieve_stringlist *field_names, + bool mime_decode) +{ + struct sieve_message_header_list *hdrlist; + + hdrlist = t_new(struct sieve_message_header_list, 1); + hdrlist->hdrlist.strlist.runenv = renv; + hdrlist->hdrlist.strlist.exec_status = SIEVE_EXEC_OK; + hdrlist->hdrlist.strlist.next_item = sieve_message_header_list_next_value; + hdrlist->hdrlist.strlist.reset = sieve_message_header_list_reset; + hdrlist->hdrlist.next_item = sieve_message_header_list_next_item; + hdrlist->field_names = field_names; + hdrlist->mime_decode = mime_decode; + + return &hdrlist->hdrlist; +} + +// NOTE: get rid of this once we have a proper Sieve string type +static inline string_t *_header_right_trim(const char *raw) +{ + string_t *result; + const char *p, *pend; + + pend = raw + strlen(raw); + if (raw == pend) { + result = t_str_new(1); + } else { + for ( p = pend-1; p >= raw; p-- ) { + if ( *p != ' ' && *p != '\t' ) break; + } + result = t_str_new(p - raw + 1); + str_append_data(result, raw, p - raw + 1); + } + return result; +} + +/* String list implementation */ + +static int sieve_message_header_list_next_item +(struct sieve_header_list *_hdrlist, const char **name_r, + string_t **value_r) +{ + struct sieve_message_header_list *hdrlist = + (struct sieve_message_header_list *) _hdrlist; + const struct sieve_runtime_env *renv = _hdrlist->strlist.runenv; + struct mail *mail = sieve_message_get_mail(renv->msgctx); + + if ( name_r != NULL ) + *name_r = NULL; + *value_r = NULL; + + /* Check for end of current header list */ + if ( hdrlist->headers == NULL ) { + hdrlist->headers_index = 0; + } else if ( hdrlist->headers[hdrlist->headers_index] == NULL ) { + hdrlist->headers = NULL; + hdrlist->headers_index = 0; + } + + /* Fetch next header */ + while ( hdrlist->headers == NULL ) { + string_t *hdr_item = NULL; + int ret; + + /* Read next header name from source list */ + if ( (ret=sieve_stringlist_next_item + (hdrlist->field_names, &hdr_item)) <= 0 ) + return ret; + + hdrlist->header_name = str_c(hdr_item); + + if ( _hdrlist->strlist.trace ) { + sieve_runtime_trace(renv, 0, + "extracting `%s' headers from message", + str_sanitize(str_c(hdr_item), 80)); + } + + /* Fetch all matching headers from the e-mail */ + if ( hdrlist->mime_decode ) { + ret = mail_get_headers_utf8(mail, + str_c(hdr_item), &hdrlist->headers); + } else { + ret = mail_get_headers(mail, + str_c(hdr_item), &hdrlist->headers); + } + + if (ret < 0) { + _hdrlist->strlist.exec_status = + sieve_runtime_mail_error(renv, mail, + "failed to read header field `%s'", str_c(hdr_item)); + return -1; + } + + if ( ret == 0 || hdrlist->headers[0] == NULL ) { + /* Try next item when no headers found */ + hdrlist->headers = NULL; + } + } + + /* Return next item */ + if ( name_r != NULL ) + *name_r = hdrlist->header_name; + *value_r = _header_right_trim(hdrlist->headers[hdrlist->headers_index++]); + return 1; +} + +static int sieve_message_header_list_next_value +(struct sieve_stringlist *_strlist, string_t **value_r) +{ + struct sieve_header_list *hdrlist = + (struct sieve_header_list *) _strlist; + + return sieve_message_header_list_next_item + (hdrlist, NULL, value_r); +} + +static void sieve_message_header_list_reset +(struct sieve_stringlist *strlist) +{ + struct sieve_message_header_list *hdrlist = + (struct sieve_message_header_list *) strlist; + + hdrlist->headers = NULL; + hdrlist->headers_index = 0; + sieve_stringlist_reset(hdrlist->field_names); +} + +/* + * Header override operand + */ + +const struct sieve_operand_class sieve_message_override_operand_class = + { "header-override" }; + +bool sieve_opr_message_override_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + struct sieve_message_override svmo; + const struct sieve_message_override_def *hodef; + + if ( !sieve_opr_object_dump + (denv, &sieve_message_override_operand_class, address, &svmo.object) ) + return FALSE; + + hodef = svmo.def = + (const struct sieve_message_override_def *) svmo.object.def; + + if ( hodef->dump_context != NULL ) { + sieve_code_descend(denv); + if ( !hodef->dump_context(&svmo, denv, address) ) { + return FALSE; + } + sieve_code_ascend(denv); + } + + return TRUE; +} + +int sieve_opr_message_override_read +(const struct sieve_runtime_env *renv, sieve_size_t *address, + struct sieve_message_override *svmo) +{ + const struct sieve_message_override_def *hodef; + int ret; + + svmo->context = NULL; + + if ( !sieve_opr_object_read + (renv, &sieve_message_override_operand_class, address, &svmo->object) ) + return SIEVE_EXEC_BIN_CORRUPT; + + hodef = svmo->def = + (const struct sieve_message_override_def *) svmo->object.def; + + if ( hodef->read_context != NULL && + (ret=hodef->read_context(svmo, renv, address, &svmo->context)) <= 0 ) + return ret; + + return SIEVE_EXEC_OK; +} + +/* + * Optional operands + */ + +int sieve_message_opr_optional_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address, + signed int *opt_code) +{ + signed int _opt_code = 0; + bool final = FALSE, opok = TRUE; + + if ( opt_code == NULL ) { + opt_code = &_opt_code; + final = TRUE; + } + + while ( opok ) { + int opt; + + if ( (opt=sieve_addrmatch_opr_optional_dump + (denv, address, opt_code)) <= 0 ) + return opt; + + if ( *opt_code == SIEVE_OPT_MESSAGE_OVERRIDE ) { + opok = sieve_opr_message_override_dump(denv, address); + } else { + return ( final ? -1 : 1 ); + } + } + + return -1; +} + +int sieve_message_opr_optional_read +(const struct sieve_runtime_env *renv, sieve_size_t *address, + signed int *opt_code, int *exec_status, + struct sieve_address_part *addrp, struct sieve_match_type *mcht, + struct sieve_comparator *cmp, + ARRAY_TYPE(sieve_message_override) *svmos) +{ + signed int _opt_code = 0; + bool final = FALSE; + int ret; + + if ( opt_code == NULL ) { + opt_code = &_opt_code; + final = TRUE; + } + + if ( exec_status != NULL ) + *exec_status = SIEVE_EXEC_OK; + + for ( ;; ) { + int opt; + + if ( (opt=sieve_addrmatch_opr_optional_read + (renv, address, opt_code, exec_status, addrp, mcht, cmp)) <= 0 ) + return opt; + + if ( *opt_code == SIEVE_OPT_MESSAGE_OVERRIDE ) { + struct sieve_message_override svmo; + const struct sieve_message_override *svmo_idx; + unsigned int count, i; + + if ( (ret=sieve_opr_message_override_read + (renv, address, &svmo)) <= 0 ) { + if ( exec_status != NULL ) + *exec_status = ret; + return -1; + } + + if ( !array_is_created(svmos) ) + t_array_init(svmos, 8); + /* insert in sorted sequence */ + svmo_idx = array_get(svmos, &count); + for (i = 0; i < count; i++) { + if (svmo.def->sequence < svmo_idx[i].def->sequence) { + array_insert(svmos, i, &svmo, 1); + break; + } + } + if (count == i) + array_append(svmos, &svmo, 1); + } else { + if ( final ) { + sieve_runtime_trace_error(renv, "invalid optional operand"); + if ( exec_status != NULL ) + *exec_status = SIEVE_EXEC_BIN_CORRUPT; + return -1; + } + return 1; + } + } + + i_unreached(); + return -1; +} + +/* + * Message header + */ + +int sieve_message_get_header_fields +(const struct sieve_runtime_env *renv, + struct sieve_stringlist *field_names, + ARRAY_TYPE(sieve_message_override) *svmos, + bool mime_decode, struct sieve_stringlist **fields_r) +{ + const struct sieve_message_override *svmo; + unsigned int count, i; + int ret; + + if ( svmos == NULL || !array_is_created(svmos) || + array_count(svmos) == 0 ) { + struct sieve_header_list *headers; + headers = sieve_message_header_list_create + (renv, field_names, mime_decode); + *fields_r = &headers->strlist; + return SIEVE_EXEC_OK; + } + + svmo = array_get(svmos, &count); + if ( svmo[0].def->sequence == 0 && + svmo[0].def->header_override != NULL ) { + *fields_r = field_names; + } else { + struct sieve_header_list *headers; + headers = sieve_message_header_list_create + (renv, field_names, mime_decode); + *fields_r = &headers->strlist; + } + + for ( i = 0; i < count; i++ ) { + if ( svmo[i].def->header_override != NULL && + (ret=svmo[i].def->header_override + (&svmo[i], renv, mime_decode, fields_r)) <= 0 ) + return ret; + } + return SIEVE_EXEC_OK; +} + +/* + * Message part + */ + +struct sieve_message_part *sieve_message_part_parent +(struct sieve_message_part *mpart) +{ + return mpart->parent; +} + +struct sieve_message_part *sieve_message_part_next +(struct sieve_message_part *mpart) +{ + return mpart->next; +} + +struct sieve_message_part *sieve_message_part_children +(struct sieve_message_part *mpart) +{ + return mpart->children; +} + +const char *sieve_message_part_content_type +(struct sieve_message_part *mpart) +{ + return mpart->content_type; +} + +const char *sieve_message_part_content_disposition +(struct sieve_message_part *mpart) +{ + return mpart->content_disposition; +} + +int sieve_message_part_get_first_header +(struct sieve_message_part *mpart, const char *field, + const char **value_r) +{ + const struct sieve_message_header *headers; + unsigned int i, count; + + headers = array_get(&mpart->headers, &count); + for ( i = 0; i < count; i++ ) { + if ( strcasecmp( headers[i].name, field) == 0 ) { + i_assert( headers[i].value[headers[i].value_len] == '\0' ); + *value_r = (const char *)headers[i].value; + return 1; + } + } + + *value_r = NULL; + return 0; +} + +void sieve_message_part_get_data +(struct sieve_message_part *mpart, + struct sieve_message_part_data *data, bool text) +{ + i_zero(data); + data->content_type = mpart->content_type; + data->content_disposition = mpart->content_disposition; + + if ( !text ) { + data->content = mpart->decoded_body; + data->size = mpart->decoded_body_size; + } else if ( mpart->children != NULL ) { + data->content = ""; + data->size = 0; + } else { + data->content = mpart->text_body; + data->size = mpart->text_body_size; + } +} + +/* + * Message body + */ + +static void str_replace_nuls(string_t *str) +{ + char *data = str_c_modifiable(str); + unsigned int i, len = str_len(str); + + for (i = 0; i < len; i++) { + if (data[i] == '\0') + data[i] = ' '; + } +} + +static bool _is_wanted_content_type +(const char * const *wanted_types, const char *content_type) +ATTR_NULL(1) +{ + const char *subtype; + size_t type_len; + + if ( wanted_types == NULL ) + return TRUE; + + subtype = strchr(content_type, '/'); + type_len = ( subtype == NULL ? strlen(content_type) : + (size_t)(subtype - content_type) ); + + i_assert( wanted_types != NULL ); + + for (; *wanted_types != NULL; wanted_types++) { + const char *wanted_subtype; + + if (**wanted_types == '\0') { + /* empty string matches everything */ + return TRUE; + } + + wanted_subtype = strchr(*wanted_types, '/'); + if (wanted_subtype == NULL) { + /* match only main type */ + if (strlen(*wanted_types) == type_len && + strncasecmp(*wanted_types, content_type, type_len) == 0) + return TRUE; + } else { + /* match whole type/subtype */ + if (strcasecmp(*wanted_types, content_type) == 0) + return TRUE; + } + } + return FALSE; +} + +static bool sieve_message_body_get_return_parts +(const struct sieve_runtime_env *renv, + const char * const *wanted_types, + bool extract_text) +{ + struct sieve_message_context *msgctx = renv->msgctx; + struct sieve_message_part *const *body_parts; + unsigned int i, count; + struct sieve_message_part_data *return_part; + + /* Check whether any body parts are cached already */ + body_parts = array_get(&msgctx->cached_body_parts, &count); + if ( count == 0 ) + return FALSE; + + /* Clear result array */ + array_clear(&msgctx->return_body_parts); + + /* Fill result array with requested content_types */ + for (i = 0; i < count; i++) { + if (!body_parts[i]->have_body) { + /* Part has no body; according to RFC this MUST not match to anything and + * therefore it is not included in the result. + */ + continue; + } + + /* Skip content types that are not requested */ + if (!_is_wanted_content_type + (wanted_types, body_parts[i]->content_type)) + continue; + + /* Add new item to the result */ + return_part = array_append_space(&msgctx->return_body_parts); + return_part->content_type = body_parts[i]->content_type; + return_part->content_disposition = body_parts[i]->content_disposition; + + /* Depending on whether a decoded body part is requested, the appropriate + * cache item is read. If it is missing, this function fails and the cache + * needs to be completed by sieve_message_parts_add_missing(). + */ + if (extract_text) { + if (body_parts[i]->text_body == NULL) + return FALSE; + return_part->content = body_parts[i]->text_body; + return_part->size = body_parts[i]->text_body_size; + } else { + if (body_parts[i]->decoded_body == NULL) + return FALSE; + return_part->content = body_parts[i]->decoded_body; + return_part->size = body_parts[i]->decoded_body_size; + } + } + + return TRUE; +} + +static void sieve_message_part_save +(const struct sieve_runtime_env *renv, buffer_t *buf, + struct sieve_message_part *body_part, + bool extract_text) +{ + struct sieve_message_context *msgctx = renv->msgctx; + pool_t pool = msgctx->context_pool; + buffer_t *result_buf, *text_buf = NULL; + char *part_data; + size_t part_size; + + /* Extract text if requested */ + result_buf = buf; + if ( extract_text && body_part->children == NULL && + !body_part->epilogue ) { + + if ( buf->used > 0 && mail_html2text_content_type_match + (body_part->content_type) ) { + struct mail_html2text *html2text; + + text_buf = buffer_create_dynamic(default_pool, 4096); + + /* Remove HTML markup */ + html2text = mail_html2text_init(0); + mail_html2text_more(html2text, buf->data, buf->used, text_buf); + mail_html2text_deinit(&html2text); + + result_buf = text_buf; + } + } + + /* Add terminating NUL to the body part buffer */ + buffer_append_c(result_buf, '\0'); + + /* Make copy of the buffer */ + part_data = p_malloc(pool, result_buf->used); + memcpy(part_data, result_buf->data, result_buf->used); + part_size = result_buf->used - 1; + + /* Free text buffer if used */ + if ( text_buf != NULL) + buffer_free(&text_buf); + + /* Depending on whether the part is processed into text, store message + * body in the appropriate cache location. + */ + if ( !extract_text ) { + body_part->decoded_body = part_data; + body_part->decoded_body_size = part_size; + } else { + body_part->text_body = part_data; + body_part->text_body_size = part_size; + } + + /* Clear buffer */ + buffer_set_used_size(buf, 0); +} + +static const char * +_parse_content_type(const struct message_header_line *hdr) +{ + struct rfc822_parser_context parser; + string_t *content_type; + + /* Initialize parsing */ + rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL); + (void)rfc822_skip_lwsp(&parser); + + /* Parse content type */ + content_type = t_str_new(64); + if (rfc822_parse_content_type(&parser, content_type) < 0) + return ""; + + /* Content-type value must end here, otherwise it is invalid after all */ + (void)rfc822_skip_lwsp(&parser); + if ( parser.data != parser.end && *parser.data != ';' ) + return ""; + + /* Success */ + return str_c(content_type); +} + +static const char * +_parse_content_disposition(const struct message_header_line *hdr) +{ + struct rfc822_parser_context parser; + string_t *content_disp; + + /* Initialize parsing */ + rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL); + (void)rfc822_skip_lwsp(&parser); + + /* Parse content type */ + content_disp = t_str_new(64); + if (rfc822_parse_mime_token(&parser, content_disp) < 0) + return ""; + + /* Content-type value must end here, otherwise it is invalid after all */ + (void)rfc822_skip_lwsp(&parser); + if ( parser.data != parser.end && *parser.data != ';' ) + return ""; + + /* Success */ + return str_c(content_disp); +} + +/* sieve_message_parts_add_missing(): + * Add requested message body parts to the cache that are missing. + */ +static int sieve_message_parts_add_missing +(const struct sieve_runtime_env *renv, + const char *const *content_types, + bool extract_text, bool iter_all) + ATTR_NULL(2) +{ + struct sieve_message_context *msgctx = renv->msgctx; + pool_t pool = msgctx->context_pool; + struct mail *mail = sieve_message_get_mail(renv->msgctx); + struct message_parser_settings mparser_set = { + .hdr_flags = MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP, + .flags = MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS, + }; + ARRAY(struct sieve_message_header) headers; + struct sieve_message_part *body_part, *header_part, *last_part; + struct message_parser_ctx *parser; + struct message_decoder_context *decoder; + struct message_block block, decoded; + struct message_part *mparts, *prev_mpart = NULL; + buffer_t *buf; + struct istream *input; + unsigned int idx = 0; + bool save_body = FALSE, have_all; + string_t *hdr_content = NULL; + + /* First check whether any are missing */ + if ( !iter_all && sieve_message_body_get_return_parts + (renv, content_types, extract_text) ) { + /* Cache hit; all are present */ + return SIEVE_EXEC_OK; + } + + /* Get the message stream */ + if ( mail_get_stream(mail, NULL, NULL, &input) < 0 ) { + return sieve_runtime_mail_error(renv, mail, + "failed to open input message"); + } + if (mail_get_parts(mail, &mparts) < 0) { + return sieve_runtime_mail_error(renv, mail, + "failed to parse input message parts"); + } + + buf = buffer_create_dynamic(default_pool, 4096); + body_part = header_part = last_part = NULL; + + if (iter_all) { + t_array_init(&headers, 64); + hdr_content = t_str_new(512); + mparser_set.hdr_flags |= MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE; + } else { + i_zero(&headers); + } + + /* Initialize body decoder */ + decoder = message_decoder_init(NULL, 0); + + // FIXME: currently not tested with edit-mail. + //parser = message_parser_init_from_parts(parts, input, + // hparser_flags, mparser_flags); + parser = message_parser_init(pool_datastack_create(), + input, &mparser_set); + while ( message_parser_parse_next_block(parser, &block) > 0 ) { + struct sieve_message_part **body_part_idx; + struct message_header_line *hdr = block.hdr; + struct sieve_message_header *header; + unsigned char *data; + + if ( block.part != prev_mpart ) { + bool message_rfc822 = FALSE; + + /* Save previous body part */ + if ( body_part != NULL ) { + /* Treat message/rfc822 separately; headers become content */ + if ( block.part->parent == prev_mpart && + strcmp(body_part->content_type, "message/rfc822") == 0 ) { + message_rfc822 = TRUE; + } else { + if ( save_body ) { + sieve_message_part_save + (renv, buf, body_part, extract_text); + } + } + if ( iter_all && !array_is_created(&body_part->headers) && + array_count(&headers) > 0 ) { + p_array_init(&body_part->headers, pool, array_count(&headers)); + array_copy(&body_part->headers.arr, 0, + &headers.arr, 0, array_count(&headers)); + } + } + + /* Start processing next part */ + body_part_idx = array_idx_get_space + (&msgctx->cached_body_parts, idx); + if ( *body_part_idx == NULL ) + *body_part_idx = p_new(pool, struct sieve_message_part, 1); + body_part = *body_part_idx; + body_part->content_type = "text/plain"; + if ( iter_all ) + array_clear(&headers); + + /* Copy tree structure */ + if ( block.part->context != NULL ) { + struct sieve_message_part *epipart = + (struct sieve_message_part *)block.part->context; + i_assert(epipart != NULL); + + /* multipart epilogue */ + body_part->content_type = epipart->content_type; + body_part->have_body = TRUE; + body_part->epilogue = TRUE; + save_body = iter_all || _is_wanted_content_type + (content_types, body_part->content_type); + + } else { + struct sieve_message_part *parent = NULL; + + if ( block.part->parent != NULL ) { + body_part->parent = parent = + (struct sieve_message_part *) + block.part->parent->context; + } + + /* new part */ + block.part->context = (void*)body_part; + + if ( last_part != NULL ) { + i_assert( parent != NULL ); + if ( last_part->parent == parent ) { + last_part->next = body_part; + } else if (parent->children == NULL) { + parent->children = body_part; + } else { + struct sieve_message_part *child = parent->children; + while (child->next != NULL && child != body_part) + child = child->next; + if (child != body_part) + child->next = body_part; + } + } + } + last_part = body_part; + + /* If this is message/rfc822 content, retain the enveloping part for + * storing headers as content. + */ + if ( message_rfc822 ) { + i_assert(idx > 0); + body_part_idx = array_idx_modifiable + (&msgctx->cached_body_parts, idx-1); + header_part = *body_part_idx; + } else { + header_part = NULL; + } + + prev_mpart = block.part; + idx++; + } + + if ( hdr != NULL || block.size == 0 ) { + enum { + _HDR_CONTENT_TYPE, + _HDR_CONTENT_DISPOSITION, + _HDR_OTHER + } hdr_field; + + /* Reading headers */ + i_assert( body_part != NULL ); + + /* Decode block */ + (void)message_decoder_decode_next_block + (decoder, &block, &decoded); + + /* Check for end of headers */ + if ( hdr == NULL ) { + /* Save headers for message/rfc822 part */ + if ( header_part != NULL ) { + sieve_message_part_save + (renv, buf, header_part, FALSE); + header_part = NULL; + } + + /* Save bodies only if we have a wanted content-type */ + save_body = iter_all || _is_wanted_content_type + (content_types, body_part->content_type); + continue; + } + + /* Encountered the empty line that indicates the end of the headers and + * the start of the body + */ + if ( hdr->eoh ) { + body_part->have_body = TRUE; + continue; + } else if ( header_part != NULL ) { + /* Save message/rfc822 header as part content */ + if ( hdr->continued ) { + buffer_append(buf, hdr->value, hdr->value_len); + } else { + buffer_append(buf, hdr->name, hdr->name_len); + buffer_append(buf, hdr->middle, hdr->middle_len); + buffer_append(buf, hdr->value, hdr->value_len); + } + if ( !hdr->no_newline ) { + buffer_append(buf, "\r\n", 2); + } + } + + if ( strcasecmp(hdr->name, "Content-Type" ) == 0 ) + hdr_field = _HDR_CONTENT_TYPE; + else if ( strcasecmp(hdr->name, "Content-Disposition" ) == 0 ) + hdr_field = _HDR_CONTENT_DISPOSITION; + else if ( iter_all && !array_is_created(&body_part->headers) ) + hdr_field = _HDR_OTHER; + else { + /* Not interested in this header */ + continue; + } + + /* Header can have folding whitespace. Acquire the full value before + * continuing + */ + if ( hdr->continues ) { + hdr->use_full_value = TRUE; + continue; + } + + if ( iter_all && !array_is_created(&body_part->headers) ) { + const unsigned char *value, *vp; + size_t vlen; + + /* Add header */ + header = array_append_space(&headers); + header->name = p_strdup(pool, hdr->name); + + /* Trim end of field value (not done by parser) */ + value = hdr->full_value; + vp = value + hdr->full_value_len; + while ( vp > value && + (vp[-1] == '\t' || vp[-1] == ' ') ) + vp--; + vlen = (size_t)(vp - value); + + /* Decode MIME encoded-words. */ + str_truncate(hdr_content, 0); + message_header_decode_utf8 + (value, vlen, hdr_content, NULL); + if ( vlen != str_len(hdr_content) || + strncmp(str_c(hdr_content), (const char *)value, + vlen) != 0 ) { + if ( strlen(str_c(hdr_content)) != str_len(hdr_content) ) { + /* replace NULs with spaces */ + str_replace_nuls(hdr_content); + } + /* store raw */ + data = p_malloc(pool, vlen + 1); + data[vlen] = '\0'; + header->value = memcpy(data, value, vlen); + header->value_len = vlen; + /* store decoded */ + data = p_malloc(pool, str_len(hdr_content) + 1); + data[str_len(hdr_content)] = '\0'; + header->utf8_value = memcpy(data, + str_data(hdr_content), str_len(hdr_content)); + header->utf8_value_len = str_len(hdr_content); + } else { + /* raw == decoded */ + data = p_malloc(pool, vlen + 1); + data[vlen] = '\0'; + header->value = header->utf8_value = + memcpy(data, value, vlen); + header->value_len = header->utf8_value_len = vlen; + } + + if ( hdr_field == _HDR_OTHER ) + continue; + } + + /* Parse the content type from the Content-type header */ + T_BEGIN { + switch ( hdr_field ) { + case _HDR_CONTENT_TYPE: + body_part->content_type = + p_strdup(pool, _parse_content_type(block.hdr)); + break; + case _HDR_CONTENT_DISPOSITION: + body_part->content_disposition = + p_strdup(pool, _parse_content_disposition(block.hdr)); + break; + default: + i_unreached(); + } + } T_END; + + continue; + } + + /* Reading body */ + if ( save_body ) { + (void)message_decoder_decode_next_block + (decoder, &block, &decoded); + buffer_append(buf, decoded.data, decoded.size); + } + } + + /* even with an empty message there was at least the "end of headers" + block, which set the body_part. */ + i_assert( body_part != NULL ); + + /* Save last body part if necessary */ + if ( header_part != NULL ) { + sieve_message_part_save + (renv, buf, header_part, FALSE); + } else if ( save_body ) { + sieve_message_part_save + (renv, buf, body_part, extract_text); + } + if ( iter_all && !array_is_created(&body_part->headers) && + array_count(&headers) > 0 ) { + p_array_init(&body_part->headers, pool, array_count(&headers)); + array_copy(&body_part->headers.arr, 0, + &headers.arr, 0, array_count(&headers)); + } + + /* Try to fill the return_body_parts array once more */ + have_all = iter_all || sieve_message_body_get_return_parts + (renv, content_types, extract_text); + + /* This time, failure is a bug */ + i_assert(have_all); + + /* Cleanup */ + (void)message_parser_deinit(&parser, &mparts); + message_decoder_deinit(&decoder); + buffer_free(&buf); + + /* Return status */ + if ( input->stream_errno != 0 ) { + sieve_runtime_critical(renv, NULL, + "failed to read input message", + "read(%s) failed: %s", + i_stream_get_name(input), + i_stream_get_error(input)); + return SIEVE_EXEC_TEMP_FAILURE; + } + return SIEVE_EXEC_OK; +} + +int sieve_message_body_get_content +(const struct sieve_runtime_env *renv, + const char * const *content_types, + struct sieve_message_part_data **parts_r) +{ + struct sieve_message_context *msgctx = renv->msgctx; + int status; + + T_BEGIN { + /* Fill the return_body_parts array */ + status = sieve_message_parts_add_missing + (renv, content_types, FALSE, FALSE); + } T_END; + + /* Check status */ + if ( status <= 0 ) + return status; + + /* Return the array of body items */ + (void) array_append_space(&msgctx->return_body_parts); /* NULL-terminate */ + *parts_r = array_idx_modifiable(&msgctx->return_body_parts, 0); + + return status; +} + +int sieve_message_body_get_text +(const struct sieve_runtime_env *renv, + struct sieve_message_part_data **parts_r) +{ + static const char * const _text_content_types[] = + { "application/xhtml+xml", "text", NULL }; + struct sieve_message_context *msgctx = renv->msgctx; + int status; + + /* We currently only support extracting plain text from: + + - text/html -> HTML + - application/xhtml+xml -> XHTML + + Other text types are read as is. Any non-text types are skipped. + */ + + T_BEGIN { + /* Fill the return_body_parts array */ + status = sieve_message_parts_add_missing + (renv, _text_content_types, TRUE, FALSE); + } T_END; + + /* Check status */ + if ( status <= 0 ) + return status; + + /* Return the array of body items */ + (void) array_append_space(&msgctx->return_body_parts); /* NULL-terminate */ + *parts_r = array_idx_modifiable(&msgctx->return_body_parts, 0); + + return status; +} + +int sieve_message_body_get_raw +(const struct sieve_runtime_env *renv, + struct sieve_message_part_data **parts_r) +{ + struct sieve_message_context *msgctx = renv->msgctx; + struct sieve_message_part_data *return_part; + buffer_t *buf; + + if ( msgctx->raw_body == NULL ) { + struct mail *mail = sieve_message_get_mail(renv->msgctx); + struct istream *input; + struct message_size hdr_size, body_size; + const unsigned char *data; + size_t size; + int ret; + + msgctx->raw_body = buf = buffer_create_dynamic + (msgctx->context_pool, 1024*64); + + /* Get stream for message */ + if ( mail_get_stream(mail, &hdr_size, &body_size, &input) < 0 ) { + return sieve_runtime_mail_error(renv, mail, + "failed to open input message"); + } + + /* Skip stream to beginning of body */ + i_stream_skip(input, hdr_size.physical_size); + + /* Read raw message body */ + while ( (ret=i_stream_read_more(input, &data, &size)) > 0 ) { + buffer_append(buf, data, size); + + i_stream_skip(input, size); + } + + if ( ret < 0 && input->stream_errno != 0 ) { + sieve_runtime_critical(renv, NULL, + "failed to read input message", + "read(%s) failed: %s", + i_stream_get_name(input), + i_stream_get_error(input)); + return SIEVE_EXEC_TEMP_FAILURE; + } + + /* Add terminating NUL to the body part buffer */ + buffer_append_c(buf, '\0'); + + } else { + buf = msgctx->raw_body; + } + + /* Clear result array */ + array_clear(&msgctx->return_body_parts); + + if ( buf->used > 1 ) { + const char *data = (const char *)buf->data; + size_t size = buf->used - 1; + + i_assert( data[size] == '\0' ); + + /* Add single item to the result */ + return_part = array_append_space(&msgctx->return_body_parts); + return_part->content = data; + return_part->size = size; + } + + /* Return the array of body items */ + (void) array_append_space(&msgctx->return_body_parts); /* NULL-terminate */ + *parts_r = array_idx_modifiable(&msgctx->return_body_parts, 0); + + return SIEVE_EXEC_OK; +} + +/* + * Message part iterator + */ + +int sieve_message_part_iter_init +(struct sieve_message_part_iter *iter, + const struct sieve_runtime_env *renv) +{ + struct sieve_message_context *msgctx = renv->msgctx; + struct sieve_message_part *const *parts; + unsigned int count; + int status; + + T_BEGIN { + /* Fill the return_body_parts array */ + status = sieve_message_parts_add_missing + (renv, NULL, TRUE, TRUE); + } T_END; + + /* Check status */ + if ( status <= 0 ) + return status; + + i_zero(iter); + iter->renv = renv; + iter->index = 0; + iter->offset = 0; + + parts = array_get(&msgctx->cached_body_parts, &count); + if (count == 0) + iter->root = NULL; + else + iter->root = parts[0]; + + return SIEVE_EXEC_OK; +} + +void sieve_message_part_iter_subtree(struct sieve_message_part_iter *iter, + struct sieve_message_part_iter *subtree) +{ + const struct sieve_runtime_env *renv = iter->renv; + struct sieve_message_context *msgctx = renv->msgctx; + struct sieve_message_part *const *parts; + unsigned int count; + + *subtree = *iter; + + parts = array_get(&msgctx->cached_body_parts, &count); + if ( subtree->index >= count) + subtree->root = NULL; + else + subtree->root = parts[subtree->index]; + subtree->offset = subtree->index; +} + +void sieve_message_part_iter_children(struct sieve_message_part_iter *iter, + struct sieve_message_part_iter *child) +{ + const struct sieve_runtime_env *renv = iter->renv; + struct sieve_message_context *msgctx = renv->msgctx; + struct sieve_message_part *const *parts; + unsigned int count; + + *child = *iter; + + parts = array_get(&msgctx->cached_body_parts, &count); + if ( (child->index+1) >= count || parts[child->index]->children == NULL) + child->root = NULL; + else + child->root = parts[child->index++]; + child->offset = child->index; +} + +struct sieve_message_part *sieve_message_part_iter_current +(struct sieve_message_part_iter *iter) +{ + const struct sieve_runtime_env *renv = iter->renv; + struct sieve_message_context *msgctx = renv->msgctx; + struct sieve_message_part *const *parts; + unsigned int count; + + if ( iter->root == NULL ) + return NULL; + + parts = array_get(&msgctx->cached_body_parts, &count); + if ( iter->index >= count ) + return NULL; + do { + if ( parts[iter->index] == iter->root->next ) + return NULL; + if ( parts[iter->index] == iter->root->parent ) + return NULL; + } while ( parts[iter->index]->epilogue && ++iter->index < count ); + if ( iter->index >= count ) + return NULL; + return parts[iter->index]; +} + +struct sieve_message_part *sieve_message_part_iter_next +(struct sieve_message_part_iter *iter) +{ + const struct sieve_runtime_env *renv = iter->renv; + struct sieve_message_context *msgctx = renv->msgctx; + + if ( iter->index >= array_count(&msgctx->cached_body_parts) ) + return NULL; + iter->index++; + + return sieve_message_part_iter_current(iter); +} + +void sieve_message_part_iter_reset +(struct sieve_message_part_iter *iter) +{ + iter->index = iter->offset; +} + +/* + * MIME header list + */ + +/* Forward declarations */ + +static int sieve_mime_header_list_next_item + (struct sieve_header_list *_hdrlist, const char **name_r, + string_t **value_r); +static int sieve_mime_header_list_next_value + (struct sieve_stringlist *_strlist, string_t **value_r); +static void sieve_mime_header_list_reset + (struct sieve_stringlist *_strlist); + +/* Header list object */ + +struct sieve_mime_header_list { + struct sieve_header_list hdrlist; + + struct sieve_stringlist *field_names; + + struct sieve_message_part_iter part_iter; + + const char *header_name; + const struct sieve_message_header *headers; + unsigned int headers_index, headers_count; + + bool mime_decode:1; + bool children:1; +}; + +struct sieve_header_list *sieve_mime_header_list_create +(const struct sieve_runtime_env *renv, + struct sieve_stringlist *field_names, + struct sieve_message_part_iter *part_iter, + bool mime_decode, bool children) +{ + struct sieve_mime_header_list *hdrlist; + + hdrlist = t_new(struct sieve_mime_header_list, 1); + hdrlist->hdrlist.strlist.runenv = renv; + hdrlist->hdrlist.strlist.exec_status = SIEVE_EXEC_OK; + hdrlist->hdrlist.strlist.next_item = sieve_mime_header_list_next_value; + hdrlist->hdrlist.strlist.reset = sieve_mime_header_list_reset; + hdrlist->hdrlist.next_item = sieve_mime_header_list_next_item; + hdrlist->field_names = field_names; + hdrlist->mime_decode = mime_decode; + hdrlist->children = children; + + sieve_message_part_iter_subtree(part_iter, &hdrlist->part_iter); + + return &hdrlist->hdrlist; +} + +/* MIME list implementation */ + +static void sieve_mime_header_list_next_name +(struct sieve_mime_header_list *hdrlist) +{ + struct sieve_message_part *mpart; + + sieve_message_part_iter_reset(&hdrlist->part_iter); + mpart = sieve_message_part_iter_current(&hdrlist->part_iter); + + if ( mpart != NULL && array_is_created(&mpart->headers) ) { + hdrlist->headers = array_get + (&mpart->headers, &hdrlist->headers_count); + hdrlist->headers_index = 0; + } +} + +static int sieve_mime_header_list_next_item +(struct sieve_header_list *_hdrlist, const char **name_r, + string_t **value_r) +{ + struct sieve_mime_header_list *hdrlist = + (struct sieve_mime_header_list *) _hdrlist; + const struct sieve_runtime_env *renv = _hdrlist->strlist.runenv; + + if ( name_r != NULL ) + *name_r = NULL; + *value_r = NULL; + + for (;;) { + /* Check for end of current header list */ + if ( hdrlist->headers_count == 0 || + hdrlist->headers_index >= hdrlist->headers_count ) { + hdrlist->headers_count = 0; + hdrlist->headers_index = 0; + hdrlist->headers = NULL; + } + + /* Fetch more headers */ + while ( hdrlist->headers_count == 0 ) { + string_t *hdr_item = NULL; + int ret; + + if ( hdrlist->header_name != NULL && hdrlist->children ) { + struct sieve_message_part *mpart; + + mpart = sieve_message_part_iter_next(&hdrlist->part_iter); + if ( mpart != NULL && array_is_created(&mpart->headers) ) { + hdrlist->headers = array_get + (&mpart->headers, &hdrlist->headers_count); + hdrlist->headers_index = 0; + } + if ( hdrlist->headers_count > 0 ) { + if ( _hdrlist->strlist.trace ) { + sieve_runtime_trace(renv, 0, + "moving to next message part"); + } + break; + } + } + + /* Read next header name from source list */ + if ( (ret=sieve_stringlist_next_item + (hdrlist->field_names, &hdr_item)) <= 0 ) + return ret; + + hdrlist->header_name = str_c(hdr_item); + + if ( _hdrlist->strlist.trace ) { + sieve_runtime_trace(renv, 0, + "extracting `%s' headers from message part", + str_sanitize(str_c(hdr_item), 80)); + } + + sieve_mime_header_list_next_name(hdrlist); + } + + for ( ; hdrlist->headers_index < hdrlist->headers_count; + hdrlist->headers_index++ ) { + const struct sieve_message_header *header = + &hdrlist->headers[hdrlist->headers_index]; + + if ( strcasecmp(header->name, hdrlist->header_name) == 0 ) { + if ( name_r != NULL ) + *name_r = hdrlist->header_name; + if ( hdrlist->mime_decode ) { + *value_r = t_str_new_const + ((const char *)header->utf8_value, header->utf8_value_len); + } else { + *value_r = t_str_new_const + ((const char *)header->value, header->value_len); + } + hdrlist->headers_index++; + return 1; + } + } + } + + i_unreached(); + return -1; +} + +static int sieve_mime_header_list_next_value +(struct sieve_stringlist *_strlist, string_t **value_r) +{ + struct sieve_header_list *hdrlist = + (struct sieve_header_list *) _strlist; + + return sieve_mime_header_list_next_item + (hdrlist, NULL, value_r); +} + +static void sieve_mime_header_list_reset +(struct sieve_stringlist *strlist) +{ + struct sieve_mime_header_list *hdrlist = + (struct sieve_mime_header_list *) strlist; + + sieve_stringlist_reset(hdrlist->field_names); + hdrlist->header_name = NULL; +} diff --git a/pigeonhole/src/lib-sieve/sieve-message.h b/pigeonhole/src/lib-sieve/sieve-message.h new file mode 100644 index 0000000..4cb77cc --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-message.h @@ -0,0 +1,280 @@ +#ifndef SIEVE_MESSAGE_H +#define SIEVE_MESSAGE_H + +#include "sieve-common.h" +#include "sieve-stringlist.h" +#include "sieve-objects.h" + +/* + * Message transmission + */ + +const char *sieve_message_get_new_id(const struct sieve_instance *svinst); + +/* + * Message context + */ + +struct sieve_message_context; + +struct sieve_message_context *sieve_message_context_create + (struct sieve_instance *svinst, struct mail_user *mail_user, + const struct sieve_message_data *msgdata); +void sieve_message_context_ref(struct sieve_message_context *msgctx); +void sieve_message_context_unref(struct sieve_message_context **msgctx); + +void sieve_message_context_reset(struct sieve_message_context *msgctx); + +pool_t sieve_message_context_pool + (struct sieve_message_context *msgctx) ATTR_PURE; +void sieve_message_context_time(struct sieve_message_context *msgctx, + struct timeval *time); + +/* Extension support */ + +void sieve_message_context_extension_set + (struct sieve_message_context *msgctx, const struct sieve_extension *ext, + void *context); +const void *sieve_message_context_extension_get + (struct sieve_message_context *msgctx, const struct sieve_extension *ext); + +/* Envelope */ + +const struct smtp_address *sieve_message_get_final_recipient + (struct sieve_message_context *msgctx); +const struct smtp_address *sieve_message_get_orig_recipient + (struct sieve_message_context *msgctx); + +const struct smtp_address *sieve_message_get_sender + (struct sieve_message_context *msgctx); + +/* Mail */ + +struct mail *sieve_message_get_mail + (struct sieve_message_context *msgctx); + +int sieve_message_substitute + (struct sieve_message_context *msgctx, struct istream *input); +struct edit_mail *sieve_message_edit + (struct sieve_message_context *msgctx); +void sieve_message_snapshot + (struct sieve_message_context *msgctx); + +/* + * Header stringlist + */ + +struct sieve_header_list { + struct sieve_stringlist strlist; + + int (*next_item) + (struct sieve_header_list *_hdrlist, const char **name_r, + string_t **value_r) ATTR_NULL(2); +}; + +static inline int sieve_header_list_next_item +(struct sieve_header_list *hdrlist, const char **name_r, + string_t **value_r) ATTR_NULL(2) +{ + return hdrlist->next_item(hdrlist, name_r, value_r); +} + +static inline void sieve_header_list_reset +(struct sieve_header_list *hdrlist) +{ + sieve_stringlist_reset(&hdrlist->strlist); +} + +static inline int sieve_header_list_get_length +(struct sieve_header_list *hdrlist) +{ + return sieve_stringlist_get_length(&hdrlist->strlist); +} + +static inline void sieve_header_list_set_trace +(struct sieve_header_list *hdrlist, bool trace) +{ + sieve_stringlist_set_trace(&hdrlist->strlist, trace); +} + +struct sieve_header_list *sieve_message_header_list_create + (const struct sieve_runtime_env *renv, + struct sieve_stringlist *field_names, + bool mime_decode); + +/* + * Message override + */ + +/* Header override object */ + +struct sieve_message_override_def { + struct sieve_object_def obj_def; + + unsigned int sequence; + + /* Context coding */ + + bool (*dump_context) + (const struct sieve_message_override *svmo, + const struct sieve_dumptime_env *denv, sieve_size_t *address); + int (*read_context) + (const struct sieve_message_override *svmo, + const struct sieve_runtime_env *renv, sieve_size_t *address, + void **se_context); + + /* Override */ + + int (*header_override) + (const struct sieve_message_override *svmo, + const struct sieve_runtime_env *renv, + bool mime_decode, struct sieve_stringlist **headers); +}; + +struct sieve_message_override { + struct sieve_object object; + + const struct sieve_message_override_def *def; + + void *context; +}; + +ARRAY_DEFINE_TYPE(sieve_message_override, + struct sieve_message_override); + +/* + * Message override operand + */ + +#define SIEVE_EXT_DEFINE_MESSAGE_OVERRIDE(SVMO) SIEVE_EXT_DEFINE_OBJECT(SVMO) +#define SIEVE_EXT_DEFINE_MESSAGE_OVERRIDES(SVMOS) SIEVE_EXT_DEFINE_OBJECTS(SMOS) + +#define SIEVE_OPT_MESSAGE_OVERRIDE (-2) + +extern const struct sieve_operand_class + sieve_message_override_operand_class; + +static inline void sieve_opr_message_override_emit +(struct sieve_binary_block *sblock, const struct sieve_extension *ext, + const struct sieve_message_override_def *seff) +{ + sieve_opr_object_emit(sblock, ext, &seff->obj_def); +} + +bool sieve_opr_message_override_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +int sieve_opr_message_override_read + (const struct sieve_runtime_env *renv, sieve_size_t *address, + struct sieve_message_override *svmo); + +/* + * Optional operands + */ + +int sieve_message_opr_optional_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address, + signed int *opt_code); + +int sieve_message_opr_optional_read + (const struct sieve_runtime_env *renv, sieve_size_t *address, + signed int *opt_code, int *exec_status, + struct sieve_address_part *addrp, struct sieve_match_type *mcht, + struct sieve_comparator *cmp, + ARRAY_TYPE(sieve_message_override) *svmos); + +/* + * Message header + */ + +int sieve_message_get_header_fields + (const struct sieve_runtime_env *renv, + struct sieve_stringlist *field_names, + ARRAY_TYPE(sieve_message_override) *svmos, + bool mime_decode, struct sieve_stringlist **fields_r); + +/* + * Message part + */ + +struct sieve_message_part; + +struct sieve_message_part_data { + const char *content_type; + const char *content_disposition; + + const char *content; + unsigned long size; +}; + +struct sieve_message_part *sieve_message_part_parent + (struct sieve_message_part *mpart) ATTR_PURE; +struct sieve_message_part *sieve_message_part_next + (struct sieve_message_part *mpart) ATTR_PURE; +struct sieve_message_part *sieve_message_part_children + (struct sieve_message_part *mpart) ATTR_PURE; + +const char *sieve_message_part_content_type + (struct sieve_message_part *mpart) ATTR_PURE; +const char *sieve_message_part_content_disposition + (struct sieve_message_part *mpart) ATTR_PURE; + +int sieve_message_part_get_first_header + (struct sieve_message_part *mpart, const char *field, + const char **value_r); + +void sieve_message_part_get_data + (struct sieve_message_part *mpart, + struct sieve_message_part_data *data, bool text); + +/* + * Message body + */ + +int sieve_message_body_get_content + (const struct sieve_runtime_env *renv, + const char * const *content_types, + struct sieve_message_part_data **parts_r); +int sieve_message_body_get_text + (const struct sieve_runtime_env *renv, + struct sieve_message_part_data **parts_r); +int sieve_message_body_get_raw + (const struct sieve_runtime_env *renv, + struct sieve_message_part_data **parts_r); + +/* + * Message part iterator + */ + +struct sieve_message_part_iter { + const struct sieve_runtime_env *renv; + struct sieve_message_part *root; + unsigned int index, offset; +}; + +int sieve_message_part_iter_init +(struct sieve_message_part_iter *iter, + const struct sieve_runtime_env *renv); +void sieve_message_part_iter_subtree(struct sieve_message_part_iter *iter, + struct sieve_message_part_iter *subtree); +void sieve_message_part_iter_children(struct sieve_message_part_iter *iter, + struct sieve_message_part_iter *child); + +struct sieve_message_part *sieve_message_part_iter_current +(struct sieve_message_part_iter *iter); +struct sieve_message_part *sieve_message_part_iter_next +(struct sieve_message_part_iter *iter); + +void sieve_message_part_iter_reset +(struct sieve_message_part_iter *iter); + +/* + * MIME header list + */ + +struct sieve_header_list *sieve_mime_header_list_create +(const struct sieve_runtime_env *renv, + struct sieve_stringlist *field_names, + struct sieve_message_part_iter *part_iter, + bool mime_decode, bool children); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-objects.c b/pigeonhole/src/lib-sieve/sieve-objects.c new file mode 100644 index 0000000..66fc969 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-objects.c @@ -0,0 +1,111 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-extensions.h" +#include "sieve-code.h" +#include "sieve-binary.h" +#include "sieve-dump.h" +#include "sieve-interpreter.h" + +#include "sieve-objects.h" + +/* + * Object coding + */ + +void sieve_opr_object_emit +(struct sieve_binary_block *sblock, const struct sieve_extension *ext, + const struct sieve_object_def *obj_def) +{ + struct sieve_extension_objects *objs = + (struct sieve_extension_objects *) obj_def->operand->interface; + + (void) sieve_operand_emit(sblock, ext, obj_def->operand); + + if ( objs->count > 1 ) { + (void) sieve_binary_emit_byte(sblock, obj_def->code); + } +} + +bool sieve_opr_object_read_data +(struct sieve_binary_block *sblock, const struct sieve_operand *operand, + const struct sieve_operand_class *opclass, sieve_size_t *address, + struct sieve_object *obj) +{ + const struct sieve_extension_objects *objs; + unsigned int obj_code; + + if ( operand == NULL || operand->def->class != opclass ) + return FALSE; + + objs = (struct sieve_extension_objects *) operand->def->interface; + if ( objs == NULL ) + return FALSE; + + if ( objs->count > 1 ) { + if ( !sieve_binary_read_byte(sblock, address, &obj_code) ) + return FALSE; + + if ( obj_code < objs->count ) { + const struct sieve_object_def *const *objects = + (const struct sieve_object_def *const *) objs->objects; + + obj->def = objects[obj_code]; + obj->ext = operand->ext; + return TRUE; + } + } + + obj->def = (const struct sieve_object_def *) objs->objects; + obj->ext = operand->ext; + return TRUE; +} + +bool sieve_opr_object_read +(const struct sieve_runtime_env *renv, + const struct sieve_operand_class *opclass, sieve_size_t *address, + struct sieve_object *obj) +{ + struct sieve_operand operand; + + if ( !sieve_operand_read(renv->sblock, address, NULL, &operand) ) { + return FALSE; + } + + return sieve_opr_object_read_data + (renv->sblock, &operand, opclass, address, obj); +} + +bool sieve_opr_object_dump +(const struct sieve_dumptime_env *denv, + const struct sieve_operand_class *opclass, sieve_size_t *address, + struct sieve_object *obj) +{ + struct sieve_operand operand; + struct sieve_object obj_i; + const char *class; + + if ( obj == NULL ) + obj = &obj_i; + + sieve_code_mark(denv); + + if ( !sieve_operand_read(denv->sblock, address, NULL, &operand) ) { + return FALSE; + } + + if ( !sieve_opr_object_read_data + (denv->sblock, &operand, opclass, address, obj) ) + return FALSE; + + if ( operand.def->class == NULL ) + class = "OBJECT"; + else + class = operand.def->class->name; + + sieve_code_dumpf(denv, "%s: %s", class, obj->def->identifier); + + return TRUE; +} + diff --git a/pigeonhole/src/lib-sieve/sieve-objects.h b/pigeonhole/src/lib-sieve/sieve-objects.h new file mode 100644 index 0000000..e3f300f --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-objects.h @@ -0,0 +1,67 @@ +#ifndef SIEVE_OBJECTS_H +#define SIEVE_OBJECTS_H + +/* + * Object definition + */ + +struct sieve_object_def { + const char *identifier; + const struct sieve_operand_def *operand; + unsigned int code; +}; + +#define SIEVE_OBJECT(_identifier, _operand, _code) \ + .obj_def = { \ + .identifier = (_identifier), \ + .operand = (_operand), \ + .code = (_code) \ + } + +/* + * Object instance + */ + +struct sieve_object { + const struct sieve_object_def *def; + const struct sieve_extension *ext; +}; + +#define SIEVE_OBJECT_DEFAULT(_obj) \ + { &((_obj).obj_def), NULL } + +#define SIEVE_OBJECT_EXTENSION(_obj) \ + (_obj->object.ext) + +#define SIEVE_OBJECT_SET_DEF(_obj, def_value) \ + STMT_START { \ + (_obj)->def = def_value; \ + (_obj)->object.def = &(_obj)->def->obj_def; \ + } STMT_END + + +/* + * Object coding + */ + +void sieve_opr_object_emit + (struct sieve_binary_block *sblock, const struct sieve_extension *ext, + const struct sieve_object_def *obj_def); + +bool sieve_opr_object_read_data + (struct sieve_binary_block *sblock, const struct sieve_operand *operand, + const struct sieve_operand_class *opclass, sieve_size_t *address, + struct sieve_object *obj); + +bool sieve_opr_object_read + (const struct sieve_runtime_env *renv, + const struct sieve_operand_class *opclass, sieve_size_t *address, + struct sieve_object *obj); + +bool sieve_opr_object_dump + (const struct sieve_dumptime_env *denv, + const struct sieve_operand_class *opclass, sieve_size_t *address, + struct sieve_object *obj); + + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-parser.c b/pigeonhole/src/lib-sieve/sieve-parser.c new file mode 100644 index 0000000..ee54994 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-parser.c @@ -0,0 +1,670 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "istream.h" +#include "failures.h" + +#include "sieve-common.h" +#include "sieve-limits.h" +#include "sieve-script.h" +#include "sieve-lexer.h" +#include "sieve-parser.h" +#include "sieve-error.h" +#include "sieve-ast.h" + +/* + * Forward declarations + */ + +static int +sieve_parser_recover(struct sieve_parser *parser, + enum sieve_token_type end_token); + +/* + * Parser object + */ + +struct sieve_parser { + pool_t pool; + + bool valid; + + struct sieve_script *script; + + struct sieve_error_handler *ehandler; + + const struct sieve_lexer *lexer; + struct sieve_ast *ast; +}; + +struct sieve_parser * +sieve_parser_create(struct sieve_script *script, + struct sieve_error_handler *ehandler, + enum sieve_error *error_r) +{ + struct sieve_parser *parser; + const struct sieve_lexer *lexer; + + lexer = sieve_lexer_create(script, ehandler, error_r); + if (lexer != NULL) { + pool_t pool = pool_alloconly_create("sieve_parser", 4096); + + parser = p_new(pool, struct sieve_parser, 1); + parser->pool = pool; + parser->valid = TRUE; + + parser->ehandler = ehandler; + sieve_error_handler_ref(ehandler); + + parser->script = script; + sieve_script_ref(script); + + parser->lexer = lexer; + parser->ast = NULL; + + return parser; + } + + return NULL; +} + +void sieve_parser_free(struct sieve_parser **parser) +{ + if ((*parser)->ast != NULL) + sieve_ast_unref(&(*parser)->ast); + + sieve_lexer_free(&(*parser)->lexer); + sieve_script_unref(&(*parser)->script); + + sieve_error_handler_unref(&(*parser)->ehandler); + + pool_unref(&(*parser)->pool); + + *parser = NULL; +} + +/* + * Internal error handling + */ + +inline static void ATTR_FORMAT(4, 5) +sieve_parser_error(struct sieve_parser *parser, + const char *csrc_filename, unsigned int csrc_linenum, + const char *fmt, ...) +{ + struct sieve_error_params params = { + .log_type = LOG_TYPE_ERROR, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + }; + va_list args; + + va_start(args, fmt); + + /* Don't report a parse error if the lexer complained already */ + if (sieve_lexer_token_type(parser->lexer) != STT_ERROR) { + T_BEGIN { + params.location = sieve_error_script_location( + parser->script, + sieve_lexer_token_line(parser->lexer)); + sieve_logv(parser->ehandler, ¶ms, fmt, args); + } T_END; + } + + parser->valid = FALSE; + + va_end(args); +} +#define sieve_parser_error(parser, ...) \ + sieve_parser_error(parser, __FILE__, __LINE__, __VA_ARGS__) + +/* + * Sieve grammar parsing + */ + +/* sieve_parse_arguments(): + + Parses both command arguments and sub-tests: + arguments = *argument [test / test-list] + argument = string-list / number / tag + string = quoted-string / multi-line [[implicitly handled in lexer]] + string-list = "[" string *("," string) "]" / string ;; if + there is only a single string, the brackets are optional + test-list = "(" test *("," test) ")" + test = identifier arguments + */ +static int +sieve_parse_arguments(struct sieve_parser *parser, struct sieve_ast_node *node, + unsigned int depth) +{ + const struct sieve_lexer *lexer = parser->lexer; + struct sieve_ast_node *test = NULL; + bool test_present = TRUE; + bool arg_present = TRUE; + int result = 1; /* Indicates whether the parser is in a defined, not + necessarily error-free state */ + + /* Parse arguments */ + while (arg_present && result > 0) { + struct sieve_ast_argument *arg; + + if (!parser->valid && + !sieve_errors_more_allowed(parser->ehandler)) { + result = 0; + break; + } + + switch (sieve_lexer_token_type(lexer)) { + /* String list */ + case STT_LSQUARE: + /* Create stinglist object */ + arg = sieve_ast_argument_stringlist_create( + node, sieve_lexer_token_line(parser->lexer)); + if (arg == NULL) break; + sieve_lexer_skip_token(lexer); + + if (sieve_lexer_token_type(lexer) == STT_STRING) { + bool add_failed = FALSE; + + /* Add the string to the list */ + if (!sieve_ast_stringlist_add( + arg, sieve_lexer_token_str(lexer), + sieve_lexer_token_line(parser->lexer))) + add_failed = TRUE; + sieve_lexer_skip_token(lexer); + + while (!add_failed && + sieve_lexer_token_type(lexer) == STT_COMMA) { + sieve_lexer_skip_token(lexer); + + /* Check parser status */ + if (!parser->valid && + !sieve_errors_more_allowed(parser->ehandler)) { + result = sieve_parser_recover(parser, STT_RSQUARE); + break; + } + + if (sieve_lexer_token_type(lexer) == STT_STRING) { + /* Add the string to the list */ + if (!sieve_ast_stringlist_add( + arg, sieve_lexer_token_str(lexer), + sieve_lexer_token_line(parser->lexer))) + add_failed = TRUE; + + sieve_lexer_skip_token(lexer); + } else { + sieve_parser_error(parser, + "expecting string after ',' in string list, " + "but found %s", + sieve_lexer_token_description(lexer)); + result = sieve_parser_recover(parser, STT_RSQUARE); + break; + } + } + + if (add_failed) { + sieve_parser_error(parser, + "failed to accept more items in string list"); + return -1; + } + } else { + sieve_parser_error(parser, + "expecting string after '[' in string list, " + "but found %s", + sieve_lexer_token_description(lexer)); + result = sieve_parser_recover(parser, STT_RSQUARE); + } + + /* Finish the string list */ + if (sieve_lexer_token_type(lexer) == STT_RSQUARE) { + sieve_lexer_skip_token(lexer); + } else { + sieve_parser_error(parser, + "expecting ',' or end of string list ']', " + "but found %s", + sieve_lexer_token_description(lexer)); + + if ((result = sieve_parser_recover(parser, STT_RSQUARE)) > 0) + sieve_lexer_skip_token(lexer); + } + break; + /* Single string */ + case STT_STRING: + arg = sieve_ast_argument_string_create( + node, sieve_lexer_token_str(lexer), + sieve_lexer_token_line(parser->lexer)); + sieve_lexer_skip_token(lexer); + break; + /* Number */ + case STT_NUMBER: + arg = sieve_ast_argument_number_create( + node, sieve_lexer_token_int(lexer), + sieve_lexer_token_line(parser->lexer)); + sieve_lexer_skip_token(lexer); + break; + /* Tag */ + case STT_TAG: + arg = sieve_ast_argument_tag_create( + node, sieve_lexer_token_ident(lexer), + sieve_lexer_token_line(parser->lexer)); + sieve_lexer_skip_token(lexer); + break; + /* End of argument list, continue with tests */ + default: + arg_present = FALSE; + break; + } + + if (arg_present && arg == NULL) { + sieve_parser_error(parser, + "failed to accept more arguments for command '%s'", + node->identifier); + return -1; + } + + if (sieve_ast_argument_count(node) > SIEVE_MAX_COMMAND_ARGUMENTS) { + sieve_parser_error(parser, + "too many arguments for command '%s'", + node->identifier); + return 0; + } + } + + if (result <= 0) + return result; /* Defer recovery to caller */ + + /* --> [ test / test-list ] + test-list = "(" test *("," test) ")" + test = identifier arguments + */ + switch (sieve_lexer_token_type(lexer)) { + /* Single test */ + case STT_IDENTIFIER: + if ((depth + 1) > SIEVE_MAX_TEST_NESTING) { + sieve_parser_error(parser, + "cannot nest tests deeper than %u levels", + SIEVE_MAX_TEST_NESTING); + return 0; + } + + test = sieve_ast_test_create( + node, sieve_lexer_token_ident(lexer), + sieve_lexer_token_line(parser->lexer)); + sieve_lexer_skip_token(lexer); + + /* Theoretically, test can be NULL */ + if (test == NULL) + break; + + /* Parse test arguments, which may include more tests (recurse) */ + if (sieve_parse_arguments(parser, test, depth + 1) <= 0) { + return 0; /* Defer recovery to caller */ + } + + break; + + /* Test list */ + case STT_LBRACKET: + sieve_lexer_skip_token(lexer); + + if (depth+1 > SIEVE_MAX_TEST_NESTING) { + sieve_parser_error(parser, + "cannot nest tests deeper than %u levels", + SIEVE_MAX_TEST_NESTING); + result = sieve_parser_recover(parser, STT_RBRACKET); + + if (result > 0) + sieve_lexer_skip_token(lexer); + return result; + } + + node->test_list = TRUE; + + /* Test starts with identifier */ + if (sieve_lexer_token_type(lexer) == STT_IDENTIFIER) { + test = sieve_ast_test_create( + node, sieve_lexer_token_ident(lexer), + sieve_lexer_token_line(parser->lexer)); + sieve_lexer_skip_token(lexer); + + if (test == NULL) + break; + + /* Parse test arguments, which may include more tests (recurse) */ + if ((result = sieve_parse_arguments(parser, test, depth+1)) > 0) { + + /* More tests ? */ + while (sieve_lexer_token_type(lexer) == STT_COMMA) { + sieve_lexer_skip_token(lexer); + + /* Check parser status */ + if (!parser->valid && + !sieve_errors_more_allowed(parser->ehandler)) { + result = sieve_parser_recover(parser, STT_RBRACKET); + break; + } + + /* Test starts with identifier */ + if (sieve_lexer_token_type(lexer) == STT_IDENTIFIER) { + test = sieve_ast_test_create( + node, sieve_lexer_token_ident(lexer), + sieve_lexer_token_line(parser->lexer)); + sieve_lexer_skip_token(lexer); + + if (test == NULL) + break; + + /* Parse test arguments, which may include more tests (recurse) */ + if ((result = sieve_parse_arguments(parser, test, depth+1)) <= 0) { + if (result < 0) + return result; + result = sieve_parser_recover(parser, STT_RBRACKET); + break; + } + } else { + sieve_parser_error(parser, + "expecting test identifier after ',' in test list, " + "but found %s", + sieve_lexer_token_description(lexer)); + result = sieve_parser_recover(parser, STT_RBRACKET); + break; + } + } + if (test == NULL) + break; + } else { + if (result < 0) + return result; + + result = sieve_parser_recover(parser, STT_RBRACKET); + } + } else { + sieve_parser_error(parser, + "expecting test identifier after '(' in test list, " + "but found %s", + sieve_lexer_token_description(lexer)); + + result = sieve_parser_recover(parser, STT_RBRACKET); + } + + /* The next token should be a ')', indicating the end of the + test list + --> previous sieve_parser_recover calls try to restore this + situation after parse errors. + */ + if (sieve_lexer_token_type(lexer) == STT_RBRACKET) { + sieve_lexer_skip_token(lexer); + } else { + sieve_parser_error(parser, + "expecting ',' or end of test list ')', " + "but found %s", + sieve_lexer_token_description(lexer)); + + /* Recover function tries to make next token equal to + ')'. If it succeeds we need to skip it. + */ + if ((result = sieve_parser_recover(parser, STT_RBRACKET)) > 0) + sieve_lexer_skip_token(lexer); + } + break; + + default: + /* Not an error: test / test-list is optional + --> any errors are detected by the caller + */ + test_present = FALSE; + break; + } + + if (test_present && test == NULL) { + sieve_parser_error(parser, + "failed to accept more tests for command '%s'", + node->identifier); + return -1; + } + + return result; +} + +/* commands = *command + command = identifier arguments ( ";" / block ) + block = "{" commands "}" + */ +static int +sieve_parse_commands(struct sieve_parser *parser, struct sieve_ast_node *block, + unsigned int depth) +{ + const struct sieve_lexer *lexer = parser->lexer; + int result = 1; + + while (result > 0 && + sieve_lexer_token_type(lexer) == STT_IDENTIFIER) { + struct sieve_ast_node *command; + + /* Check parser status */ + if (!parser->valid && + !sieve_errors_more_allowed(parser->ehandler)) { + result = sieve_parser_recover(parser, STT_SEMICOLON); + break; + } + + /* Create command node */ + command = sieve_ast_command_create( + block, sieve_lexer_token_ident(lexer), + sieve_lexer_token_line(parser->lexer)); + sieve_lexer_skip_token(lexer); + + if (command == NULL) { + sieve_parser_error(parser, + "failed to accept more commands inside the block of command '%s'", + block->identifier); + return -1; + } + + result = sieve_parse_arguments(parser, command, 1); + + /* Check whether the command is properly terminated + (i.e. with ; or a new block) + */ + if (result > 0 && + sieve_lexer_token_type(lexer) != STT_SEMICOLON && + sieve_lexer_token_type(lexer) != STT_LCURLY) { + + sieve_parser_error(parser, + "expected end of command ';' or the beginning of a compound block '{', " + "but found %s", + sieve_lexer_token_description(lexer)); + result = 0; + } + + /* Try to recover from parse errors to reacquire a defined state + */ + if (result == 0) + result = sieve_parser_recover(parser, STT_SEMICOLON); + + /* Don't bother to continue if we are not in a defined state */ + if (result <= 0) + return result; + + switch (sieve_lexer_token_type(lexer)) { + /* End of the command */ + case STT_SEMICOLON: + sieve_lexer_skip_token(lexer); + break; + /* Command has a block {...} */ + case STT_LCURLY: + sieve_lexer_skip_token(lexer); + + /* Check current depth first */ + if ((depth + 1) > SIEVE_MAX_BLOCK_NESTING) { + sieve_parser_error(parser, + "cannot nest command blocks deeper than %u levels", + SIEVE_MAX_BLOCK_NESTING); + result = sieve_parser_recover(parser, STT_RCURLY); + + if (result > 0) + sieve_lexer_skip_token(lexer); + break; + } + + command->block = TRUE; + + if ((result = sieve_parse_commands(parser, command, depth + 1)) > 0) { + if (sieve_lexer_token_type(lexer) != STT_RCURLY) { + sieve_parser_error(parser, + "expected end of compound block '}', " + "but found %s", + sieve_lexer_token_description(lexer)); + result = sieve_parser_recover(parser, STT_RCURLY); + } else { + sieve_lexer_skip_token(lexer); + } + } else { + if (result < 0) + return result; + + if ((result = sieve_parser_recover(parser, STT_RCURLY)) > 0) + sieve_lexer_skip_token(lexer); + } + + break; + + default: + /* Recovered previously, so this cannot happen */ + i_unreached(); + } + } + + return result; +} + +bool sieve_parser_run(struct sieve_parser *parser, struct sieve_ast **ast) +{ + if (parser->ast != NULL) + sieve_ast_unref(&parser->ast); + + /* Create AST object if none is provided */ + if (*ast == NULL) + *ast = sieve_ast_create(parser->script); + else + sieve_ast_ref(*ast); + + parser->ast = *ast; + + /* Scan first token */ + sieve_lexer_skip_token(parser->lexer); + + /* Parse */ + if (sieve_parse_commands(parser, sieve_ast_root(parser->ast), 1) > 0 && + parser->valid) { + /* Parsed right to EOF ? */ + if (sieve_lexer_token_type(parser->lexer) != STT_EOF) { + sieve_parser_error(parser, + "unexpected %s found at (the presumed) end of file", + sieve_lexer_token_description(parser->lexer)); + parser->valid = FALSE; + } + } else { + parser->valid = FALSE; + } + + /* Clean up AST if parse failed */ + if (!parser->valid) { + parser->ast = NULL; + sieve_ast_unref(ast); + } + + return parser->valid; +} + +/* Error recovery: + To continue parsing after an error it is important to find the next + parsible item in the stream. The recover function skips over the remaining + garbage after an error. It tries to find the end of the failed syntax + structure and takes nesting of structures into account. + */ + +/* Assign useful names to priorities for readability */ +enum sieve_grammatical_prio { + SGP_BLOCK = 3, + SGP_COMMAND = 2, + SGP_TEST_LIST = 1, + SGP_STRING_LIST = 0, + + SGP_OTHER = -1 +}; + +static inline enum sieve_grammatical_prio +__get_token_priority(enum sieve_token_type token) +{ + switch (token) { + case STT_LCURLY: + case STT_RCURLY: + return SGP_BLOCK; + case STT_SEMICOLON: + return SGP_COMMAND; + case STT_LBRACKET: + case STT_RBRACKET: + return SGP_TEST_LIST; + case STT_LSQUARE: + case STT_RSQUARE: + return SGP_STRING_LIST; + default: + break; + } + + return SGP_OTHER; +} + +static int +sieve_parser_recover(struct sieve_parser *parser, + enum sieve_token_type end_token) +{ + /* The tokens that begin/end a specific block/command/list in order + of ascending grammatical priority. + */ + static const enum sieve_token_type begin_tokens[4] = { + STT_LSQUARE, STT_LBRACKET, STT_NONE, STT_LCURLY }; + static const enum sieve_token_type end_tokens[4] = { + STT_RSQUARE, STT_RBRACKET, STT_SEMICOLON, STT_RCURLY}; + const struct sieve_lexer *lexer = parser->lexer; + int nesting = 1; + enum sieve_grammatical_prio end_priority = + __get_token_priority(end_token); + + i_assert(end_priority != SGP_OTHER); + + while (sieve_lexer_token_type(lexer) != STT_EOF && + __get_token_priority(sieve_lexer_token_type(lexer)) + <= end_priority) { + if (sieve_lexer_token_type(lexer) == + begin_tokens[end_priority]) { + nesting++; + sieve_lexer_skip_token(lexer); + continue; + } + if (sieve_lexer_token_type(lexer) == + end_tokens[end_priority]) { + nesting--; + + if (nesting == 0) { + /* Next character is the end */ + return 1; + } + } + sieve_lexer_skip_token(lexer); + } + + /* Special case: COMMAND */ + if (end_token == STT_SEMICOLON && + sieve_lexer_token_type(lexer) == STT_LCURLY) { + return 1; + } + + /* End not found before eof or end of surrounding grammatical structure + */ + return 0; +} diff --git a/pigeonhole/src/lib-sieve/sieve-parser.h b/pigeonhole/src/lib-sieve/sieve-parser.h new file mode 100644 index 0000000..dfcb63c --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-parser.h @@ -0,0 +1,17 @@ +#ifndef SIEVE_PARSER_H +#define SIEVE_PARSER_H + +#include "lib.h" + +#include "sieve-common.h" + +struct sieve_parser; + +struct sieve_parser * +sieve_parser_create(struct sieve_script *script, + struct sieve_error_handler *ehandler, + enum sieve_error *error_r); +void sieve_parser_free(struct sieve_parser **parser); +bool sieve_parser_run(struct sieve_parser *parser, struct sieve_ast **ast); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-plugins.c b/pigeonhole/src/lib-sieve/sieve-plugins.c new file mode 100644 index 0000000..c0f32b1 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-plugins.c @@ -0,0 +1,181 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "module-dir.h" + +#include "sieve-settings.h" +#include "sieve-extensions.h" + +#include "sieve-common.h" +#include "sieve-plugins.h" + +/* + * Types + */ + +typedef void (*sieve_plugin_load_func_t) + (struct sieve_instance *svinst, void **context); +typedef void (*sieve_plugin_unload_func_t) + (struct sieve_instance *svinst, void *context); + +struct sieve_plugin { + struct module *module; + + void *context; + + struct sieve_plugin *next; +}; + +/* + * Plugin support + */ + +static struct module *sieve_modules = NULL; +static int sieve_modules_refcount = 0; + +static struct module *sieve_plugin_module_find(const char *name) +{ + struct module *module; + + module = sieve_modules; + while ( module != NULL ) { + const char *mod_name; + + /* Strip module names */ + mod_name = module_get_plugin_name(module); + + if ( strcmp(mod_name, name) == 0 ) + return module; + + module = module->next; + } + + return NULL; +} + +void sieve_plugins_load +(struct sieve_instance *svinst, const char *path, const char *plugins) +{ + struct module *module; + struct module_dir_load_settings mod_set; + const char **module_names; + unsigned int i; + + /* Determine what to load */ + + if ( path == NULL && plugins == NULL ) { + path = sieve_setting_get(svinst, "sieve_plugin_dir"); + plugins = sieve_setting_get(svinst, "sieve_plugins"); + } + + if ( plugins == NULL || *plugins == '\0' ) + return; + + if ( path == NULL || *path == '\0' ) + path = MODULEDIR"/sieve"; + + i_zero(&mod_set); + mod_set.abi_version = PIGEONHOLE_ABI_VERSION; + mod_set.require_init_funcs = TRUE; + mod_set.debug = FALSE; + + /* Load missing plugin modules */ + + sieve_modules = module_dir_load_missing + (sieve_modules, path, plugins, &mod_set); + + /* Call plugin load functions for this Sieve instance */ + + if ( svinst->plugins == NULL ) { + sieve_modules_refcount++; + } + + module_names = t_strsplit_spaces(plugins, ", "); + + for (i = 0; module_names[i] != NULL; i++) { + /* Allow giving the module names also in non-base form. */ + module_names[i] = module_file_get_name(module_names[i]); + } + + for (i = 0; module_names[i] != NULL; i++) { + struct sieve_plugin *plugin; + const char *name = module_names[i]; + sieve_plugin_load_func_t load_func; + + /* Find the module */ + module = sieve_plugin_module_find(name); + i_assert(module != NULL); + + /* Check whether the plugin is already loaded in this instance */ + plugin = svinst->plugins; + while ( plugin != NULL ) { + if ( plugin->module == module ) + break; + plugin = plugin->next; + } + + /* Skip it if it is loaded already */ + if ( plugin != NULL ) + continue; + + /* Create plugin list item */ + plugin = p_new(svinst->pool, struct sieve_plugin, 1); + plugin->module = module; + + /* Call load function */ + load_func = (sieve_plugin_load_func_t) module_get_symbol + (module, t_strdup_printf("%s_load", module->name)); + if ( load_func != NULL ) { + load_func(svinst, &plugin->context); + } + + /* Add plugin to the instance */ + if ( svinst->plugins == NULL ) + svinst->plugins = plugin; + else { + struct sieve_plugin *plugin_last; + + plugin_last = svinst->plugins; + while ( plugin_last->next != NULL ) + plugin_last = plugin_last->next; + + plugin_last->next = plugin; + } + } +} + +void sieve_plugins_unload(struct sieve_instance *svinst) +{ + struct sieve_plugin *plugin; + + if ( svinst->plugins == NULL ) + return; + + /* Call plugin unload functions for this instance */ + + plugin = svinst->plugins; + while ( plugin != NULL ) { + struct module *module = plugin->module; + sieve_plugin_unload_func_t unload_func; + + unload_func = (sieve_plugin_unload_func_t)module_get_symbol + (module, t_strdup_printf("%s_unload", module->name)); + if ( unload_func != NULL ) { + unload_func(svinst, plugin->context); + } + + plugin = plugin->next; + } + + /* Physically unload modules */ + + i_assert(sieve_modules_refcount > 0); + + if ( --sieve_modules_refcount != 0 ) + return; + + module_dir_unload(&sieve_modules); +} + diff --git a/pigeonhole/src/lib-sieve/sieve-plugins.h b/pigeonhole/src/lib-sieve/sieve-plugins.h new file mode 100644 index 0000000..996af41 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-plugins.h @@ -0,0 +1,9 @@ +#ifndef SIEVE_PLUGINS_H +#define SIEVE_PLUGINS_H + +#include "sieve-common.h" + +void sieve_plugins_load(struct sieve_instance *svinst, const char *path, const char *plugins); +void sieve_plugins_unload(struct sieve_instance *svinst); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-result.c b/pigeonhole/src/lib-sieve/sieve-result.c new file mode 100644 index 0000000..89d249e --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-result.c @@ -0,0 +1,2445 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "mempool.h" +#include "ostream.h" +#include "hash.h" +#include "str.h" +#include "llist.h" +#include "strfuncs.h" +#include "str-sanitize.h" +#include "var-expand.h" +#include "message-address.h" +#include "mail-storage.h" + +#include "sieve-common.h" +#include "sieve-limits.h" +#include "sieve-script.h" +#include "sieve-error.h" +#include "sieve-interpreter.h" +#include "sieve-actions.h" +#include "sieve-message.h" + +#include "sieve-result.h" + +#include <stdio.h> + +struct event_category event_category_sieve_action = { + .parent = &event_category_sieve, + .name = "sieve-action", +}; + +/* + * Types + */ + +enum sieve_action_execution_state { + SIEVE_ACTION_EXECUTION_STATE_INIT = 0, + SIEVE_ACTION_EXECUTION_STATE_STARTED, + SIEVE_ACTION_EXECUTION_STATE_EXECUTED, + SIEVE_ACTION_EXECUTION_STATE_FINALIZED, +}; + +struct sieve_result_action { + struct sieve_action action; + + struct sieve_side_effects_list *seffects; + + struct sieve_result_action *prev, *next; +}; + +struct sieve_side_effects_list { + struct sieve_result *result; + + struct sieve_result_side_effect *first_effect; + struct sieve_result_side_effect *last_effect; +}; + +struct sieve_result_side_effect { + struct sieve_side_effect seffect; + + struct sieve_result_side_effect *prev, *next; +}; + +struct sieve_result_action_context { + const struct sieve_action_def *action; + struct sieve_side_effects_list *seffects; +}; + +/* + * Result object + */ + +struct sieve_result { + pool_t pool; + int refcount; + + struct sieve_instance *svinst; + struct event *event; + + /* Context data for extensions */ + ARRAY(void *) ext_contexts; + + const struct sieve_execute_env *exec_env; + struct sieve_error_handler *ehandler; + struct sieve_message_context *msgctx; + + unsigned int exec_seq; + struct sieve_result_execution *exec; + + struct sieve_action keep_action; + struct sieve_action failure_action; + + unsigned int action_count; + struct sieve_result_action *actions_head, *actions_tail; + + HASH_TABLE(const struct sieve_action_def *, + struct sieve_result_action_context *) action_contexts; +}; + +static const char * +sieve_result_event_log_message(struct sieve_result *result, + enum log_type log_type, const char *message) +{ + const struct sieve_script_env *senv = result->exec_env->scriptenv; + + i_assert(senv->result_amend_log_message != NULL); + return senv->result_amend_log_message(senv, log_type, message); +} + +struct sieve_result * +sieve_result_create(struct sieve_instance *svinst, pool_t pool, + const struct sieve_execute_env *eenv) +{ + const struct sieve_script_env *senv = eenv->scriptenv; + const struct sieve_message_data *msgdata = eenv->msgdata; + struct sieve_result *result; + + pool_ref(pool); + + result = p_new(pool, struct sieve_result, 1); + result->refcount = 1; + result->pool = pool; + result->svinst = svinst; + + result->event = event_create(eenv->event); + event_add_category(result->event, &event_category_sieve_action); + if (senv->result_amend_log_message != NULL) { + event_set_log_message_callback( + result->event, sieve_result_event_log_message, result); + } + + p_array_init(&result->ext_contexts, pool, 4); + + result->exec_env = eenv; + result->msgctx = + sieve_message_context_create(svinst, senv->user, msgdata); + + result->keep_action.def = &act_store; + result->keep_action.ext = NULL; + result->failure_action.def = &act_store; + result->failure_action.ext = NULL; + + result->action_count = 0; + result->actions_head = NULL; + result->actions_tail = NULL; + + return result; +} + +void sieve_result_ref(struct sieve_result *result) +{ + result->refcount++; +} + +static void +sieve_result_action_deinit(struct sieve_result_action *ract) +{ + event_unref(&ract->action.event); +} + +void sieve_result_unref(struct sieve_result **_result) +{ + struct sieve_result *result = *_result; + struct sieve_result_action *ract; + + i_assert(result->refcount > 0); + + if (--result->refcount != 0) + return; + + sieve_message_context_unref(&result->msgctx); + + hash_table_destroy(&result->action_contexts); + + ract = result->actions_head; + while (ract != NULL) { + sieve_result_action_deinit(ract); + ract = ract->next; + } + event_unref(&result->event); + + pool_unref(&result->pool); + *_result = NULL; +} + +pool_t sieve_result_pool(struct sieve_result *result) +{ + return result->pool; +} + +/* + * Getters/Setters + */ + +const struct sieve_script_env * +sieve_result_get_script_env(struct sieve_result *result) +{ + return result->exec_env->scriptenv; +} + +const struct sieve_message_data * +sieve_result_get_message_data(struct sieve_result *result) +{ + return result->exec_env->msgdata; +} + +struct sieve_message_context * +sieve_result_get_message_context(struct sieve_result *result) +{ + return result->msgctx; +} + +unsigned int sieve_result_get_exec_seq(struct sieve_result *result) +{ + return result->exec_seq; +} + +/* + * Extension support + */ + +void sieve_result_extension_set_context(struct sieve_result *result, + const struct sieve_extension *ext, + void *context) +{ + if (ext->id < 0) + return; + + array_idx_set(&result->ext_contexts, (unsigned int) ext->id, &context); +} + +const void * +sieve_result_extension_get_context(struct sieve_result *result, + const struct sieve_extension *ext) +{ + void * const *ctx; + + if (ext->id < 0 || ext->id >= (int) array_count(&result->ext_contexts)) + return NULL; + + ctx = array_idx(&result->ext_contexts, (unsigned int) ext->id); + + return *ctx; +} + +/* + * Result composition + */ + +static void +sieve_result_init_action_event(struct sieve_result *result, + struct sieve_action *action, bool add_prefix) +{ + const char *name = sieve_action_name(action); + + if (action->event != NULL) + return; + + action->event = event_create(result->event); + if (add_prefix && name != NULL) { + event_set_append_log_prefix( + action->event, t_strconcat(name, " action: ", NULL)); + } + event_add_str(action->event, "action_name", name); + event_add_str(action->event, "script_location", action->location); +} + +void sieve_result_add_implicit_side_effect( + struct sieve_result *result, const struct sieve_action_def *to_action, + bool to_keep, const struct sieve_extension *ext, + const struct sieve_side_effect_def *seff_def, void *context) +{ + struct sieve_result_action_context *actctx = NULL; + struct sieve_side_effect seffect; + + to_action = to_keep ? &act_store : to_action; + + if (!hash_table_is_created(result->action_contexts)) { + hash_table_create_direct(&result->action_contexts, + result->pool, 0); + } else { + actctx = hash_table_lookup(result->action_contexts, to_action); + } + + if (actctx == NULL) { + actctx = p_new(result->pool, + struct sieve_result_action_context, 1); + actctx->action = to_action; + actctx->seffects = sieve_side_effects_list_create(result); + + hash_table_insert(result->action_contexts, to_action, actctx); + } + + seffect.object.def = &seff_def->obj_def; + seffect.object.ext = ext; + seffect.def = seff_def; + seffect.context = context; + + sieve_side_effects_list_add(actctx->seffects, &seffect); +} + +static int +sieve_result_side_effects_merge(const struct sieve_runtime_env *renv, + const struct sieve_action *action, + struct sieve_result_action *old_action, + struct sieve_side_effects_list *new_seffects) +{ + struct sieve_side_effects_list *old_seffects = old_action->seffects; + int ret; + struct sieve_result_side_effect *rsef, *nrsef; + + /* Allow side-effects to merge with existing copy */ + + /* Merge existing side effects */ + rsef = old_seffects != NULL ? old_seffects->first_effect : NULL; + while (rsef != NULL) { + struct sieve_side_effect *seffect = &rsef->seffect; + bool found = FALSE; + + i_assert(seffect->def != NULL); + if (seffect->def->merge != NULL) { + /* Try to find it among the new */ + nrsef = (new_seffects != NULL ? + new_seffects->first_effect : NULL); + while (nrsef != NULL) { + struct sieve_side_effect *nseffect = &nrsef->seffect; + + if (nseffect->def == seffect->def) { + if (seffect->def->merge( + renv, action, seffect, nseffect, + &seffect->context) < 0) + return -1; + + found = TRUE; + break; + } + nrsef = nrsef->next; + } + + /* Not found? */ + if (!found && seffect->def->merge( + renv, action, seffect, NULL, + &rsef->seffect.context) < 0) + return -1; + } + rsef = rsef->next; + } + + /* Merge new Side effects */ + nrsef = new_seffects != NULL ? new_seffects->first_effect : NULL; + while (nrsef != NULL) { + struct sieve_side_effect *nseffect = &nrsef->seffect; + bool found = FALSE; + + i_assert(nseffect->def != NULL); + if (nseffect->def->merge != NULL) { + /* Try to find it among the exising */ + rsef = (old_seffects != NULL ? + old_seffects->first_effect : NULL); + while (rsef != NULL) { + if (rsef->seffect.def == nseffect->def) { + found = TRUE; + break; + } + rsef = rsef->next; + } + + /* Not found? */ + if (!found) { + void *new_context = NULL; + + if ((ret = nseffect->def->merge( + renv, action, nseffect, nseffect, + &new_context)) < 0) + return -1; + + if (ret != 0) { + if (old_action->seffects == NULL) { + old_action->seffects = old_seffects = + sieve_side_effects_list_create(renv->result); + } + + nseffect->context = new_context; + + /* Add side effect */ + sieve_side_effects_list_add(old_seffects, + nseffect); + } + } + } + nrsef = nrsef->next; + } + + return 1; +} + +static void +sieve_result_action_detach(struct sieve_result *result, + struct sieve_result_action *raction) +{ + if (result->actions_head == raction) + result->actions_head = raction->next; + + if (result->actions_tail == raction) + result->actions_tail = raction->prev; + + if (raction->next != NULL) + raction->next->prev = raction->prev; + if (raction->prev != NULL) + raction->prev->next = raction->next; + + raction->next = NULL; + raction->prev = NULL; + + if (result->action_count > 0) + result->action_count--; +} + +static int +_sieve_result_add_action(const struct sieve_runtime_env *renv, + const struct sieve_extension *ext, const char *name, + const struct sieve_action_def *act_def, + struct sieve_side_effects_list *seffects, + void *context, unsigned int instance_limit, + bool preserve_mail, bool keep) +{ + int ret = 0; + unsigned int instance_count = 0; + struct sieve_instance *svinst = renv->exec_env->svinst; + struct sieve_result *result = renv->result; + struct sieve_result_action *raction = NULL, *kaction = NULL; + struct sieve_action action; + + i_assert(name != NULL || act_def != NULL); + action.name = name; + action.def = act_def; + action.ext = ext; + action.location = sieve_runtime_get_full_command_location(renv); + action.context = context; + action.exec_seq = result->exec_seq; + + /* First, check for duplicates or conflicts */ + raction = result->actions_head; + while (raction != NULL) { + const struct sieve_action *oact = &raction->action; + bool oact_new = (oact->exec_seq == result->exec_seq); + + if (keep && raction->action.keep) { + /* Duplicate keep */ + if (oact->def == NULL || !oact_new) { + /* Keep action from preceeding execution */ + + /* Detach existing keep action */ + sieve_result_action_detach(result, raction); + + /* Merge existing side-effects with new keep action */ + if (kaction == NULL) + kaction = raction; + + if ((ret = sieve_result_side_effects_merge( + renv, &action, kaction, seffects)) <= 0) + return ret; + } else { + /* True duplicate */ + return sieve_result_side_effects_merge( + renv, &action, raction, seffects); + } + } else if ( act_def != NULL && raction->action.def == act_def ) { + instance_count++; + + /* Possible duplicate */ + if (act_def->check_duplicate != NULL) { + if ((ret = act_def->check_duplicate( + renv, &action, &raction->action)) < 0) + return ret; + + /* Duplicate */ + if (ret == 1) { + if (keep && !oact->keep) { + /* New keep has higher precedence than + existing duplicate non-keep action. + So, take over the result action object + and transform it into a keep. + */ + if ((ret = sieve_result_side_effects_merge( + renv, &action, raction, seffects)) < 0) + return ret; + + if (kaction == NULL) { + raction->action.context = NULL; + raction->action.location = + p_strdup(result->pool, action.location); + + /* Note that existing execution + status is retained, making sure + that keep is not executed + multiple times. + */ + kaction = raction; + } else { + sieve_result_action_detach(result, raction); + + if ((ret = sieve_result_side_effects_merge( + renv, &action, kaction, + raction->seffects)) < 0) + return ret; + } + } else { + /* Merge side-effects, but don't add new action + */ + return sieve_result_side_effects_merge( + renv, &action, raction, seffects); + } + } + } + } else { + if (act_def != NULL && oact->def != NULL) { + /* Check conflict */ + if (act_def->check_conflict != NULL && + (ret = act_def->check_conflict( + renv, &action, &raction->action)) != 0) + return ret; + + if (oact_new && + oact->def->check_conflict != NULL && + (ret = oact->def->check_conflict( + renv, &raction->action, &action)) != 0) + return ret; + } + } + raction = raction->next; + } + + if (kaction != NULL) { + /* Use existing keep action to define new one */ + raction = kaction; + } else { + /* Check policy limit on total number of actions */ + if (svinst->max_actions > 0 && + result->action_count >= svinst->max_actions) + { + sieve_runtime_error( + renv, action.location, + "total number of actions exceeds policy limit " + "(%u > %u)", + result->action_count+1, svinst->max_actions); + return -1; + } + + /* Check policy limit on number of this class of actions */ + if (instance_limit > 0 && instance_count >= instance_limit) { + sieve_runtime_error( + renv, action.location, + "number of %s actions exceeds policy limit " + "(%u > %u)", + act_def->name, instance_count+1, + instance_limit); + return -1; + } + + /* Create new action object */ + raction = p_new(result->pool, struct sieve_result_action, 1); + raction->seffects = seffects; + } + + raction->action.name = (action.name == NULL ? + act_def->name : + p_strdup(result->pool, action.name)); + raction->action.context = context; + raction->action.def = act_def; + raction->action.ext = ext; + raction->action.location = p_strdup(result->pool, action.location); + raction->action.keep = keep; + raction->action.exec_seq = result->exec_seq; + + if (raction->prev == NULL && raction != result->actions_head) { + /* Add */ + if (result->actions_head == NULL) { + result->actions_head = raction; + result->actions_tail = raction; + raction->prev = NULL; + raction->next = NULL; + } else { + result->actions_tail->next = raction; + raction->prev = result->actions_tail; + result->actions_tail = raction; + raction->next = NULL; + } + result->action_count++; + + /* Apply any implicit side effects */ + if (hash_table_is_created(result->action_contexts)) { + struct sieve_result_action_context *actctx; + + /* Check for implicit side effects to this particular + action */ + actctx = hash_table_lookup( + result->action_contexts, + (keep ? &act_store : act_def)); + + if (actctx != NULL) { + struct sieve_result_side_effect *iseff; + + /* Iterate through all implicit side effects and + add those that are missing. + */ + iseff = actctx->seffects->first_effect; + while (iseff != NULL) { + struct sieve_result_side_effect *seff; + bool exists = FALSE; + + /* Scan for presence */ + if (seffects != NULL) { + seff = seffects->first_effect; + while (seff != NULL) { + if (seff->seffect.def == iseff->seffect.def) { + exists = TRUE; + break; + } + + seff = seff->next; + } + } else { + raction->seffects = seffects = + sieve_side_effects_list_create(result); + } + + /* If not present, add it */ + if (!exists) { + sieve_side_effects_list_add(seffects, &iseff->seffect); + } + + iseff = iseff->next; + } + } + } + } + + if (preserve_mail) { + raction->action.mail = sieve_message_get_mail(renv->msgctx); + sieve_message_snapshot(renv->msgctx); + } else { + raction->action.mail = NULL; + } + + sieve_result_init_action_event(result, &raction->action, !keep); + return 0; +} + +int sieve_result_add_action(const struct sieve_runtime_env *renv, + const struct sieve_extension *ext, const char *name, + const struct sieve_action_def *act_def, + struct sieve_side_effects_list *seffects, + void *context, unsigned int instance_limit, + bool preserve_mail) +{ + return _sieve_result_add_action(renv, ext, name, act_def, seffects, + context, instance_limit, preserve_mail, + FALSE); +} + +int sieve_result_add_keep(const struct sieve_runtime_env *renv, + struct sieve_side_effects_list *seffects) +{ + return _sieve_result_add_action(renv, renv->result->keep_action.ext, + "keep", renv->result->keep_action.def, + seffects, NULL, 0, TRUE, TRUE); +} + +void sieve_result_set_keep_action(struct sieve_result *result, + const struct sieve_extension *ext, + const struct sieve_action_def *act_def) +{ + result->keep_action.def = act_def; + result->keep_action.ext = ext; +} + +void sieve_result_set_failure_action(struct sieve_result *result, + const struct sieve_extension *ext, + const struct sieve_action_def *act_def) +{ + result->failure_action.def = act_def; + result->failure_action.ext = ext; +} + +/* + * Result printing + */ + +void sieve_result_vprintf(const struct sieve_result_print_env *penv, + const char *fmt, va_list args) +{ + string_t *outbuf = t_str_new(128); + + str_vprintfa(outbuf, fmt, args); + + o_stream_nsend(penv->stream, str_data(outbuf), str_len(outbuf)); +} + +void sieve_result_printf(const struct sieve_result_print_env *penv, + const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + sieve_result_vprintf(penv, fmt, args); + va_end(args); +} + +void sieve_result_action_printf(const struct sieve_result_print_env *penv, + const char *fmt, ...) +{ + string_t *outbuf = t_str_new(128); + va_list args; + + va_start(args, fmt); + str_append(outbuf, " * "); + str_vprintfa(outbuf, fmt, args); + str_append_c(outbuf, '\n'); + va_end(args); + + o_stream_nsend(penv->stream, str_data(outbuf), str_len(outbuf)); +} + +void sieve_result_seffect_printf(const struct sieve_result_print_env *penv, + const char *fmt, ...) +{ + string_t *outbuf = t_str_new(128); + va_list args; + + va_start(args, fmt); + str_append(outbuf, " + "); + str_vprintfa(outbuf, fmt, args); + str_append_c(outbuf, '\n'); + va_end(args); + + o_stream_nsend(penv->stream, str_data(outbuf), str_len(outbuf)); +} + +static void +sieve_result_print_side_effects(struct sieve_result_print_env *rpenv, + const struct sieve_action *action, + struct sieve_side_effects_list *slist, + bool *implicit_keep) +{ + struct sieve_result_side_effect *rsef; + + /* Print side effects */ + rsef = (slist != NULL ? slist->first_effect : NULL); + while (rsef != NULL) { + const struct sieve_side_effect *sef = &rsef->seffect; + + i_assert(sef->def != NULL); + + if (sef->def->print != NULL) { + sef->def->print(sef, action, rpenv, + implicit_keep); + } + rsef = rsef->next; + } +} + +static void +sieve_result_print_implicit_side_effects(struct sieve_result_print_env *rpenv) +{ + struct sieve_result *result = rpenv->result; + bool dummy = TRUE; + + /* Print any implicit side effects if applicable */ + if (hash_table_is_created(result->action_contexts)) { + struct sieve_result_action_context *actctx; + + /* Check for implicit side effects to keep action */ + actctx = hash_table_lookup(rpenv->result->action_contexts, + &act_store); + + if (actctx != NULL && actctx->seffects != NULL) { + sieve_result_print_side_effects( + rpenv, &result->keep_action, + actctx->seffects, &dummy); + } + } +} + +bool sieve_result_print(struct sieve_result *result, + const struct sieve_script_env *senv, + struct ostream *stream, bool *keep) +{ + struct sieve_action act_keep = result->keep_action; + struct sieve_result_print_env penv; + bool implicit_keep = TRUE, printed_any = FALSE; + struct sieve_result_action *rac; + + if (keep != NULL) + *keep = FALSE; + + /* Prepare environment */ + + penv.result = result; + penv.stream = stream; + penv.scriptenv = senv; + + sieve_result_printf(&penv, "\nPerformed actions:\n\n"); + + rac = result->actions_head; + while (rac != NULL) { + bool impl_keep = TRUE; + const struct sieve_action *act = &rac->action; + + if (act->exec_seq < result->exec_seq) { + rac = rac->next; + continue; + } + + if (rac->action.keep && keep != NULL) + *keep = TRUE; + + if (act->def != NULL) { + if (act->def->print != NULL) + act->def->print(act, &penv, &impl_keep); + else { + sieve_result_action_printf( + &penv, "%s", act->def->name); + } + } else { + if (act->keep) { + sieve_result_action_printf(&penv, "keep"); + impl_keep = FALSE; + } else { + sieve_result_action_printf(&penv, "[NULL]"); + } + } + printed_any = TRUE; + + /* Print side effects */ + sieve_result_print_side_effects( + &penv, &rac->action, rac->seffects, &impl_keep); + + implicit_keep = implicit_keep && impl_keep; + + rac = rac->next; + } + if (!printed_any) + sieve_result_printf(&penv, " (none)\n"); + + if (implicit_keep && keep != NULL) + *keep = TRUE; + + sieve_result_printf(&penv, "\nImplicit keep:\n\n"); + + if (implicit_keep) { + bool dummy = TRUE; + + if (act_keep.def == NULL) { + sieve_result_action_printf(&penv, "keep"); + + sieve_result_print_implicit_side_effects(&penv); + } else { + /* Scan for execution of keep-equal actions */ + rac = result->actions_head; + while (act_keep.def != NULL && rac != NULL) { + if (rac->action.def == act_keep.def && + act_keep.def->equals != NULL && + act_keep.def->equals(senv, NULL, &rac->action) && + sieve_action_is_executed(&rac->action, + result)) + act_keep.def = NULL; + + rac = rac->next; + } + + if (act_keep.def == NULL) { + sieve_result_printf(&penv, + " (none; keep or equivalent action executed earlier)\n"); + } else { + act_keep.def->print(&act_keep, &penv, &dummy); + + sieve_result_print_implicit_side_effects(&penv); + } + } + } else { + sieve_result_printf(&penv, " (none)\n"); + } + + sieve_result_printf(&penv, "\n"); + + return TRUE; +} + +/* + * Result execution + */ + +struct sieve_side_effect_execution { + struct sieve_result_side_effect *seffect; + + void *tr_context; + + struct sieve_side_effect_execution *prev, *next; +}; + +struct sieve_action_execution { + struct sieve_result_action *action; + unsigned int exec_seq; + struct sieve_action_execution *prev, *next; + + struct sieve_side_effect_execution *seffects_head, *seffects_tail; + + struct sieve_error_handler *ehandler; + void *tr_context; + enum sieve_action_execution_state state; + int status; + + bool commit:1; +}; + +struct sieve_result_execution { + pool_t pool; + struct sieve_action_exec_env action_env; + struct sieve_error_handler *ehandler; + struct event *event; + + int status; + + struct sieve_action_execution *actions_head, *actions_tail; + + struct sieve_result_action keep_action; + struct sieve_action_execution keep; + struct sieve_action_execution *keep_equiv_action; + int keep_status; + + bool keep_success:1; + bool keep_explicit:1; + bool keep_implicit:1; + bool keep_finalizing:1; + bool seen_delivery:1; + bool executed:1; + bool executed_delivery:1; + bool committed:1; +}; + +void sieve_result_mark_executed(struct sieve_result *result) +{ + result->exec_seq++; +} + +/* Side effect */ + +static int +sieve_result_side_effect_pre_execute(struct sieve_result_execution *rexec, + struct sieve_action_execution *aexec, + struct sieve_side_effect_execution *seexec) +{ + struct sieve_result_side_effect *rsef = seexec->seffect; + struct sieve_side_effect *sef = &rsef->seffect; + + i_assert(sef->def != NULL); + if (sef->def->pre_execute == NULL) + return SIEVE_EXEC_OK; + + return sef->def->pre_execute(sef, &rexec->action_env, + aexec->tr_context, &seexec->tr_context); +} + +static int +sieve_result_side_effect_post_execute( + struct sieve_result_execution *rexec, + struct sieve_action_execution *aexec, + struct sieve_side_effect_execution *seexec, bool *impl_keep) +{ + struct sieve_result_side_effect *rsef = seexec->seffect; + struct sieve_side_effect *sef = &rsef->seffect; + + i_assert(sef->def != NULL); + if (sef->def->post_execute == NULL) + return SIEVE_EXEC_OK; + + return sef->def->post_execute(sef, &rexec->action_env, + aexec->tr_context, seexec->tr_context, + impl_keep); +} + +static void +sieve_result_side_effect_post_commit(struct sieve_result_execution *rexec, + struct sieve_action_execution *aexec, + struct sieve_side_effect_execution *seexec, + int commit_status) +{ + struct sieve_result_side_effect *rsef = seexec->seffect; + struct sieve_side_effect *sef = &rsef->seffect; + + i_assert(sef->def != NULL); + if (sef->def->post_commit == NULL) + return; + + sef->def->post_commit(sef, &rexec->action_env, + aexec->tr_context, seexec->tr_context, + commit_status); +} + +static void +sieve_result_side_effect_rollback(struct sieve_result_execution *rexec, + struct sieve_action_execution *aexec, + struct sieve_side_effect_execution *seexec) +{ + struct sieve_result_side_effect *rsef = seexec->seffect; + struct sieve_side_effect *sef = &rsef->seffect; + + i_assert(sef->def != NULL); + if (sef->def->rollback == NULL) + return; + + sef->def->rollback(sef, &rexec->action_env, + aexec->tr_context, seexec->tr_context, + (aexec->status == SIEVE_EXEC_OK)); +} + +static void +sieve_action_execution_add_side_effect(struct sieve_result_execution *rexec, + struct sieve_action_execution *aexec, + struct sieve_result_side_effect *seffect) +{ + struct sieve_side_effect_execution *seexec; + + seexec = aexec->seffects_head; + while (seexec != NULL) { + if (seexec->seffect == seffect) + return; + seexec = seexec->next; + } + + seexec = p_new(rexec->pool, struct sieve_side_effect_execution, 1); + seexec->seffect = seffect; + + DLLIST2_APPEND(&aexec->seffects_head, &aexec->seffects_tail, seexec); +} + +static void +sieve_action_execution_add_side_effects(struct sieve_result_execution *rexec, + struct sieve_action_execution *aexec, + struct sieve_result_action *rac) +{ + struct sieve_result_side_effect *rsef; + + rsef = (rac->seffects == NULL ? NULL : rac->seffects->first_effect); + while (rsef != NULL) { + sieve_action_execution_add_side_effect(rexec, aexec, rsef); + rsef = rsef->next; + } +} + +/* Action */ + +static void +sieve_action_execution_pre(struct sieve_result_execution *rexec, + struct sieve_action_execution *aexec) +{ + if (aexec->ehandler == NULL) + aexec->ehandler = rexec->ehandler; + rexec->action_env.action = &aexec->action->action; + rexec->action_env.event = aexec->action->action.event; + rexec->action_env.ehandler = aexec->ehandler; +} + +static void +sieve_action_execution_post(struct sieve_result_execution *rexec) +{ + rexec->action_env.action = NULL; + rexec->action_env.event = rexec->action_env.result->event; + rexec->action_env.ehandler = NULL; +} + +static int +sieve_result_action_start(struct sieve_result_execution *rexec, + struct sieve_action_execution *aexec) +{ + struct sieve_result_action *rac = aexec->action; + struct sieve_action *act = &rac->action; + int status = SIEVE_EXEC_OK; + + /* Skip actions that are already started. */ + if (aexec->state >= SIEVE_ACTION_EXECUTION_STATE_STARTED) + return status; + aexec->state = SIEVE_ACTION_EXECUTION_STATE_STARTED; + aexec->status = status; + + /* Skip non-actions (inactive keep). */ + if (act->def == NULL) + return status; + + if (act->def->start != NULL) { + sieve_action_execution_pre(rexec, aexec); + status = act->def->start(&rexec->action_env, + &aexec->tr_context); + aexec->status = status; + sieve_action_execution_post(rexec); + } + return status; +} + +static int +sieve_result_action_execute(struct sieve_result_execution *rexec, + struct sieve_action_execution *aexec, + int start_status) +{ + struct sieve_result_action *rac = aexec->action; + struct sieve_action *act = &rac->action; + struct sieve_side_effect_execution *seexec; + int status = start_status; + bool impl_keep = TRUE; + + /* Skip actions that are already executed. */ + if (aexec->state >= SIEVE_ACTION_EXECUTION_STATE_EXECUTED) + return status; + aexec->state = SIEVE_ACTION_EXECUTION_STATE_EXECUTED; + + /* Record explicit keep when it is not the final implicit keep */ + if (act->keep && aexec != &rexec->keep) + rexec->keep_explicit = TRUE; + + /* Skip non-actions (inactive keep) */ + if (act->def == NULL) { + i_assert(aexec != &rexec->keep); + if (act->keep) + e_debug(rexec->event, "Executed explicit keep"); + return status; + } + + /* Don't execute if others already failed */ + if (status != SIEVE_EXEC_OK) + return status; + + if (aexec == &rexec->keep) + e_debug(rexec->event, "Executing implicit keep action"); + else { + e_debug(rexec->event, "Executing %s action%s", + sieve_action_name(act), + (act->keep ? " (explicit keep)" : "")); + } + + sieve_action_execution_pre(rexec, aexec); + + /* Execute pre-execute event of side effects */ + seexec = aexec->seffects_head; + while (status == SIEVE_EXEC_OK && seexec != NULL) { + status = sieve_result_side_effect_pre_execute( + rexec, aexec, seexec); + seexec = seexec->next; + } + + /* Execute the action itself */ + if (status == SIEVE_EXEC_OK && act->def != NULL && + act->def->execute != NULL) { + status = act->def->execute(&rexec->action_env, + aexec->tr_context, + &impl_keep); + if (status == SIEVE_EXEC_OK) + rexec->executed = TRUE; + } + + /* Execute post-execute event of side effects */ + seexec = aexec->seffects_head; + while (status == SIEVE_EXEC_OK && seexec != NULL) { + status = sieve_result_side_effect_post_execute( + rexec, aexec, seexec, &impl_keep); + seexec = seexec->next; + } + + if (aexec == &rexec->keep) { + e_debug(rexec->event, + "Finished executing implicit keep action (status=%s)", + sieve_execution_exitcode_to_str(status)); + } else { + e_debug(rexec->event, "Finished executing %s action " + "(status=%s, keep=%s)", sieve_action_name(act), + sieve_execution_exitcode_to_str(status), + (act->keep ? "explicit" : + (impl_keep && rexec->keep_implicit ? + "implicit" : "canceled"))); + } + + if (status == SIEVE_EXEC_OK && + (act->def->flags & SIEVE_ACTFLAG_TRIES_DELIVER) != 0) + rexec->seen_delivery = TRUE; + + /* Update implicit keep status (but only when we're not running the + implicit keep right now). */ + if (aexec != &rexec->keep) + rexec->keep_implicit = rexec->keep_implicit && impl_keep; + + sieve_action_execution_post(rexec); + + aexec->status = status; + return status; +} + +static int +sieve_result_action_commit(struct sieve_result_execution *rexec, + struct sieve_action_execution *aexec) +{ + struct sieve_result_action *rac = aexec->action; + struct sieve_action *act = &rac->action; + struct sieve_side_effect_execution *seexec; + int cstatus = SIEVE_EXEC_OK; + + if (aexec == &rexec->keep) { + e_debug(rexec->event, "Commit implicit keep action"); + } else { + e_debug(rexec->event, "Commit %s action%s", + sieve_action_name(act), + (act->keep ? " (explicit keep)" : "")); + } + + sieve_action_execution_pre(rexec, aexec); + + if (act->def->commit != NULL) { + cstatus = act->def->commit(&rexec->action_env, + aexec->tr_context); + if (cstatus == SIEVE_EXEC_OK) + rexec->committed = TRUE; + } + + /* Execute post_commit event of side effects */ + seexec = aexec->seffects_head; + while (seexec != NULL) { + sieve_result_side_effect_post_commit( + rexec, aexec, seexec, cstatus); + seexec = seexec->next; + } + + sieve_action_execution_post(rexec); + + return cstatus; +} + +static void +sieve_result_action_rollback(struct sieve_result_execution *rexec, + struct sieve_action_execution *aexec) +{ + struct sieve_result_action *rac = aexec->action; + struct sieve_action *act = &rac->action; + struct sieve_side_effect_execution *seexec; + + if (aexec == &rexec->keep) { + e_debug(rexec->event, "Roll back implicit keep action"); + } else { + e_debug(rexec->event, "Roll back %s action%s", + sieve_action_name(act), + (act->keep ? " (explicit keep)" : "")); + } + + sieve_action_execution_pre(rexec, aexec); + + if (act->def->rollback != NULL) { + act->def->rollback(&rexec->action_env, aexec->tr_context, + (aexec->status == SIEVE_EXEC_OK)); + } + + /* Rollback side effects */ + seexec = aexec->seffects_head; + while (seexec != NULL) { + sieve_result_side_effect_rollback(rexec, aexec, seexec); + seexec = seexec->next; + } + + sieve_action_execution_post(rexec); +} + +static int +sieve_result_action_commit_or_rollback(struct sieve_result_execution *rexec, + struct sieve_action_execution *aexec, + int status, int *commit_status) +{ + struct sieve_result_action *rac = aexec->action; + struct sieve_action *act = &rac->action; + + /* Skip actions that are already finalized. */ + if (aexec->state >= SIEVE_ACTION_EXECUTION_STATE_FINALIZED) + return status; + aexec->state = SIEVE_ACTION_EXECUTION_STATE_FINALIZED; + + if (aexec == &rexec->keep) { + e_debug(rexec->event, "Finalize implicit keep action" + "(status=%s, action_status=%s, commit_status=%s)", + sieve_execution_exitcode_to_str(status), + sieve_execution_exitcode_to_str(aexec->status), + sieve_execution_exitcode_to_str(*commit_status)); + } else { + e_debug(rexec->event, "Finalize %s action " + "(%sstatus=%s, action_status=%s, commit_status=%s, " + "pre-commit=%s)", + sieve_action_name(act), + (act->keep ? "explicit keep, " : ""), + sieve_execution_exitcode_to_str(status), + sieve_execution_exitcode_to_str(aexec->status), + sieve_execution_exitcode_to_str(*commit_status), + (aexec->commit ? "yes" : "no")); + } + + /* Skip non-actions (inactive keep) */ + if (act->def == NULL) + return status; + + if (aexec->status == SIEVE_EXEC_OK && + (status == SIEVE_EXEC_OK || + (aexec->commit && *commit_status == SIEVE_EXEC_OK))) { + int cstatus = SIEVE_EXEC_OK; + + cstatus = sieve_result_action_commit(rexec, aexec); + if (cstatus != SIEVE_EXEC_OK) { + /* This is bad; try to salvage as much as possible */ + if (*commit_status == SIEVE_EXEC_OK) { + *commit_status = cstatus; + if (!rexec->committed) { + /* We haven't executed anything yet; + continue as rollback */ + status = cstatus; + } + } + } + } else { + sieve_result_action_rollback(rexec, aexec); + } + + if (act->keep) { + if (status == SIEVE_EXEC_FAILURE) + status = SIEVE_EXEC_KEEP_FAILED; + if (*commit_status == SIEVE_EXEC_FAILURE) + *commit_status = SIEVE_EXEC_KEEP_FAILED; + } + + return status; +} + +static void +sieve_result_action_finish(struct sieve_result_execution *rexec, + struct sieve_action_execution *aexec, int status) +{ + struct sieve_result_action *rac = aexec->action; + struct sieve_action *act = &rac->action; + + /* Skip non-actions (inactive keep) */ + if (act->def == NULL) + return; + + if (aexec == &rexec->keep) { + e_debug(rexec->event, "Finish implicit keep action"); + } else { + e_debug(rexec->event, "Finish %s action%s", + sieve_action_name(act), + (act->keep ? " (explicit keep)" : "")); + } + + if (act->def->finish != NULL) { + sieve_action_execution_pre(rexec, aexec); + act->def->finish(&rexec->action_env, aexec->tr_context, status); + sieve_action_execution_post(rexec); + } +} + +static void +sieve_result_action_abort(struct sieve_result_execution *rexec, + struct sieve_action_execution *aexec) +{ + if (aexec->state > SIEVE_ACTION_EXECUTION_STATE_INIT && + aexec->state < SIEVE_ACTION_EXECUTION_STATE_FINALIZED) + sieve_result_action_rollback(rexec, aexec); + DLLIST2_REMOVE(&rexec->actions_head, &rexec->actions_tail, aexec); +} + +static void +sieve_action_execution_update(struct sieve_result_execution *rexec, + struct sieve_action_execution *aexec) +{ + const struct sieve_action_exec_env *aenv = &rexec->action_env; + struct sieve_result *result = aenv->result; + struct sieve_result_action *rac; + + rac = result->actions_head; + while (rac != NULL) { + if (aexec->action == rac) + break; + rac = rac->next; + } + + if (rac == NULL) { + /* Action was removed; abort it. */ + sieve_result_action_abort(rexec, aexec); + return; + } + + if (aexec->exec_seq != rac->action.exec_seq) { + i_assert(rac->action.keep); + + /* Recycled keep */ + aexec->exec_seq = rac->action.exec_seq; + aexec->state = SIEVE_ACTION_EXECUTION_STATE_INIT; + } + + sieve_action_execution_add_side_effects(rexec, aexec, rac); +} + +static void +sieve_result_execution_add_action(struct sieve_result_execution *rexec, + struct sieve_result_action *rac) +{ + struct sieve_action_execution *aexec; + + aexec = rexec->actions_head; + while (aexec != NULL) { + if (aexec->action == rac) + return; + aexec = aexec->next; + } + + aexec = p_new(rexec->pool, struct sieve_action_execution, 1); + aexec->action = rac; + aexec->exec_seq = rac->action.exec_seq; + aexec->ehandler = rexec->ehandler; + + DLLIST2_APPEND(&rexec->actions_head, &rexec->actions_tail, aexec); + + sieve_action_execution_add_side_effects(rexec, aexec, rac); +} + +/* Result */ + +struct sieve_result_execution * +sieve_result_execution_create(struct sieve_result *result, pool_t pool) +{ + struct sieve_result_execution *rexec; + + pool_ref(pool); + rexec = p_new(pool, struct sieve_result_execution, 1); + rexec->pool = pool; + rexec->event = result->event; + rexec->action_env.result = result; + rexec->action_env.event = result->event; + rexec->action_env.exec_env = result->exec_env; + rexec->action_env.msgctx = result->msgctx; + rexec->status = SIEVE_EXEC_OK; + rexec->keep_success = TRUE; + rexec->keep_status = SIEVE_EXEC_OK; + rexec->keep_explicit = FALSE; + rexec->keep_implicit = TRUE; + + sieve_result_ref(result); + result->exec = rexec; + + return rexec; +} + +void sieve_result_execution_destroy(struct sieve_result_execution **_rexec) +{ + struct sieve_result_execution *rexec = *_rexec; + + *_rexec = NULL; + + if (rexec == NULL) + return; + + rexec->action_env.result->exec = NULL; + sieve_result_unref(&rexec->action_env.result); + pool_unref(&rexec->pool); +} + +static void +sieve_result_implicit_keep_execute(struct sieve_result_execution *rexec) +{ + const struct sieve_action_exec_env *aenv = &rexec->action_env; + struct sieve_result *result = aenv->result; + const struct sieve_execute_env *eenv = aenv->exec_env; + struct sieve_action_execution *aexec; + int status = SIEVE_EXEC_OK; + struct sieve_action_execution *aexec_keep = &rexec->keep; + struct sieve_result_action *ract_keep = &rexec->keep_action; + struct sieve_action *act_keep = &ract_keep->action; + bool success = FALSE; + + switch (rexec->status) { + case SIEVE_EXEC_OK: + success = TRUE; + break; + case SIEVE_EXEC_TEMP_FAILURE: + case SIEVE_EXEC_RESOURCE_LIMIT: + if (rexec->committed) { + e_debug(rexec->event, + "Temporary failure occurred (status=%s), " + "but other actions were already committed: " + "execute failure implicit keep", + sieve_execution_exitcode_to_str(rexec->status)); + break; + } + if (rexec->keep_finalizing) + break; + + e_debug(rexec->event, + "Skip implicit keep for temporary failure " + "(state=execute, status=%s)", + sieve_execution_exitcode_to_str(rexec->status)); + return; + default: + break; + } + + if (rexec->keep_equiv_action != NULL) { + e_debug(rexec->event, "No implicit keep needed " + "(equivalent action already executed)"); + return; + } + + rexec->keep.action = &rexec->keep_action; + rexec->keep.ehandler = rexec->ehandler; + rexec->keep_success = success; + rexec->keep_status = status; + + if ((eenv->flags & SIEVE_EXECUTE_FLAG_DEFER_KEEP) != 0) { + e_debug(rexec->event, "Execution of implicit keep is deferred"); + return; + } + + if (!success) + *act_keep = result->failure_action; + else + *act_keep = result->keep_action; + act_keep->name = "keep"; + act_keep->mail = NULL; + act_keep->keep = TRUE; + + /* If keep is a non-action, return right away */ + if (act_keep->def == NULL) { + e_debug(rexec->event, "Keep is not defined yet"); + return; + } + + /* Scan for execution of keep-equal actions */ + aexec = rexec->actions_head; + while (aexec != NULL) { + struct sieve_result_action *rac = aexec->action; + + if (rac->action.def == act_keep->def && + act_keep->def->equals != NULL && + act_keep->def->equals(eenv->scriptenv, NULL, + &rac->action) && + aexec->state >= SIEVE_ACTION_EXECUTION_STATE_EXECUTED) { + e_debug(rexec->event, "No implicit keep needed " + "(equivalent %s action already executed)", + sieve_action_name(&rac->action)); + rexec->keep_equiv_action = aexec; + return; + } + + aexec = aexec->next; + } + + /* Scan for deferred keep */ + aexec = rexec->actions_tail; + while (aexec != NULL) { + struct sieve_result_action *rac = aexec->action; + + if (aexec->state < SIEVE_ACTION_EXECUTION_STATE_EXECUTED) { + aexec = NULL; + break; + } + if (rac->action.keep && rac->action.def == NULL) + break; + aexec = aexec->prev; + } + + if (aexec == NULL) { + if (success) + act_keep->mail = sieve_message_get_mail(aenv->msgctx); + } else { + e_debug(rexec->event, "Found deferred keep action"); + + if (success) { + act_keep->location = aexec->action->action.location; + act_keep->mail = aexec->action->action.mail; + ract_keep->seffects = aexec->action->seffects; + } + aexec->state = SIEVE_ACTION_EXECUTION_STATE_FINALIZED; + } + + if (ract_keep->seffects == NULL) { + /* Apply any implicit side effects if applicable */ + if (success && hash_table_is_created(result->action_contexts)) { + struct sieve_result_action_context *actctx; + + /* Check for implicit side effects to keep action */ + actctx = hash_table_lookup(result->action_contexts, + act_keep->def); + + if (actctx != NULL) + ract_keep->seffects = actctx->seffects; + } + } + + e_debug(rexec->event, "Execute implicit keep (status=%s)", + sieve_execution_exitcode_to_str(rexec->status)); + + /* Initialize side effects */ + sieve_action_execution_add_side_effects(rexec, aexec_keep, ract_keep); + + /* Initialize keep action event */ + sieve_result_init_action_event(result, act_keep, FALSE); + + /* Start keep action */ + status = sieve_result_action_start(rexec, aexec_keep); + + /* Execute keep action */ + if (status == SIEVE_EXEC_OK) + status = sieve_result_action_execute(rexec, aexec_keep, status); + if (status == SIEVE_EXEC_OK) + aexec_keep->commit = TRUE; + + rexec->executed_delivery = rexec->seen_delivery; + rexec->keep_status = status; + sieve_action_execution_post(rexec); +} + +static int +sieve_result_implicit_keep_finalize(struct sieve_result_execution *rexec) +{ + const struct sieve_action_exec_env *aenv = &rexec->action_env; + const struct sieve_execute_env *eenv = aenv->exec_env; + struct sieve_action_execution *aexec_keep = &rexec->keep; + struct sieve_result_action *ract_keep = &rexec->keep_action; + struct sieve_action *act_keep = &ract_keep->action; + int commit_status = SIEVE_EXEC_OK; + bool success = FALSE, temp_failure = FALSE; + + switch (rexec->status) { + case SIEVE_EXEC_OK: + success = TRUE; + break; + case SIEVE_EXEC_TEMP_FAILURE: + case SIEVE_EXEC_RESOURCE_LIMIT: + if (rexec->committed) { + e_debug(rexec->event, + "Temporary failure occurred (status=%s), " + "but other actions were already committed: " + "commit failure implicit keep", + sieve_execution_exitcode_to_str(rexec->status)); + break; + } + + if (aexec_keep->state != + SIEVE_ACTION_EXECUTION_STATE_EXECUTED) { + e_debug(rexec->event, + "Skip implicit keep for temporary failure " + "(state=commit, status=%s)", + sieve_execution_exitcode_to_str(rexec->status)); + return rexec->status; + } + /* Roll back for temporary failure when no other action + is committed. */ + commit_status = rexec->status; + temp_failure = TRUE; + break; + default: + break; + } + + if ((eenv->flags & SIEVE_EXECUTE_FLAG_DEFER_KEEP) != 0) { + e_debug(rexec->event, "Execution of implicit keep is deferred"); + return rexec->keep_status; + } + + rexec->keep_finalizing = TRUE; + + /* Start keep if necessary */ + if (temp_failure) { + rexec->keep_status = rexec->status; + } else if (act_keep->def == NULL || + aexec_keep->state != SIEVE_ACTION_EXECUTION_STATE_EXECUTED) { + sieve_result_implicit_keep_execute(rexec); + /* Switch to failure keep if necessary. */ + } else if (rexec->keep_success && !success){ + e_debug(rexec->event, "Switch to failure implicit keep"); + + /* Failed transaction, rollback success keep action. */ + sieve_result_action_rollback(rexec, aexec_keep); + + event_unref(&act_keep->event); + i_zero(aexec_keep); + + /* Start failure keep action. */ + sieve_result_implicit_keep_execute(rexec); + } + if (act_keep->def == NULL) + return rexec->keep_status; + + if (rexec->keep_equiv_action != NULL) { + struct sieve_action_execution *ke_aexec = + rexec->keep_equiv_action; + + i_assert(ke_aexec->state >= + SIEVE_ACTION_EXECUTION_STATE_FINALIZED); + + e_debug(rexec->event, "No implicit keep needed " + "(equivalent %s action already finalized)", + sieve_action_name(&ke_aexec->action->action)); + return ke_aexec->status; + } + + e_debug(rexec->event, "Finalize implicit keep (status=%s)", + sieve_execution_exitcode_to_str(rexec->status)); + + i_assert(aexec_keep->state == SIEVE_ACTION_EXECUTION_STATE_EXECUTED); + + /* Finalize keep action */ + rexec->keep_status = sieve_result_action_commit_or_rollback( + rexec, aexec_keep, rexec->keep_status, &commit_status); + + /* Finish keep action */ + sieve_result_action_finish(rexec, aexec_keep, + rexec->keep_status); + + sieve_action_execution_post(rexec); + event_unref(&act_keep->event); + + if (rexec->keep_status == SIEVE_EXEC_FAILURE) + rexec->keep_status = SIEVE_EXEC_KEEP_FAILED; + return rexec->keep_status; +} + +bool sieve_result_executed(struct sieve_result_execution *rexec) +{ + return rexec->executed; +} + +bool sieve_result_committed(struct sieve_result_execution *rexec) +{ + return rexec->committed; +} + +bool sieve_result_executed_delivery(struct sieve_result_execution *rexec) +{ + return rexec->executed_delivery; +} + +static int sieve_result_transaction_start(struct sieve_result_execution *rexec) +{ + struct sieve_action_execution *aexec; + int status = SIEVE_EXEC_OK; + + e_debug(rexec->event, "Starting execution of actions"); + + aexec = rexec->actions_head; + while (status == SIEVE_EXEC_OK && aexec != NULL) { + status = sieve_result_action_start(rexec, aexec); + aexec = aexec->next; + } + sieve_action_execution_post(rexec); + + return status; +} + +static int +sieve_result_transaction_execute(struct sieve_result_execution *rexec, + int start_status) +{ + struct sieve_action_execution *aexec; + int status = SIEVE_EXEC_OK; + + e_debug(rexec->event, "Executing actions"); + + rexec->seen_delivery = FALSE; + aexec = rexec->actions_head; + while (status == SIEVE_EXEC_OK && aexec != NULL) { + status = sieve_result_action_execute(rexec, aexec, + start_status); + aexec = aexec->next; + } + sieve_action_execution_post(rexec); + + if (status == SIEVE_EXEC_OK) { + /* Since this execution series is successful so far, mark all + actions in it to be committed. */ + aexec = rexec->actions_head; + while (aexec != NULL) { + aexec->commit = TRUE; + aexec = aexec->next; + } + + rexec->executed_delivery = + rexec->executed_delivery || rexec->seen_delivery; + } + + e_debug(rexec->event, "Finished executing actions " + "(status=%s, keep=%s, executed=%s)", + sieve_execution_exitcode_to_str(status), + (rexec->keep_explicit ? "explicit" : + (rexec->keep_implicit ? "implicit" : "none")), + (rexec->executed ? "yes" : "no")); + return status; +} + +static int +sieve_result_transaction_commit_or_rollback( + struct sieve_result_execution *rexec, int status) +{ + struct sieve_action_execution *aexec; + int commit_status = SIEVE_EXEC_OK; + + switch (status) { + case SIEVE_EXEC_TEMP_FAILURE: + /* Roll back all actions */ + commit_status = status; + break; + default: + break; + } + + e_debug(rexec->event, "Finalizing actions"); + + /* First commit/rollback all storage actions */ + aexec = rexec->actions_head; + while (aexec != NULL) { + struct sieve_result_action *rac = aexec->action; + struct sieve_action *act = &rac->action; + + if (act->def == NULL || + (act->def->flags & SIEVE_ACTFLAG_MAIL_STORAGE) == 0) { + aexec = aexec->next; + continue; + } + + status = sieve_result_action_commit_or_rollback( + rexec, aexec, status, &commit_status); + + aexec = aexec->next; + } + + /* Then commit/rollback all other actions */ + aexec = rexec->actions_head; + while (aexec != NULL) { + struct sieve_result_action *rac = aexec->action; + struct sieve_action *act = &rac->action; + + if (act->def != NULL && + (act->def->flags & SIEVE_ACTFLAG_MAIL_STORAGE) != 0) { + aexec = aexec->next; + continue; + } + + status = sieve_result_action_commit_or_rollback( + rexec, aexec, status, &commit_status); + + aexec = aexec->next; + } + + e_debug(rexec->event, "Finished finalizing actions " + "(status=%s, keep=%s, committed=%s)", + sieve_execution_exitcode_to_str(status), + (rexec->keep_explicit ? "explicit" : + (rexec->keep_implicit ? "implicit" : "none")), + (rexec->committed ? "yes" : "no")); + + return commit_status; +} + +static void +sieve_result_transaction_finish(struct sieve_result_execution *rexec, + int status) +{ + struct sieve_action_execution *aexec; + + e_debug(rexec->event, "Finishing actions"); + + aexec = rexec->actions_head; + while (aexec != NULL) { + sieve_result_action_finish(rexec, aexec, status); + aexec = aexec->next; + } + sieve_action_execution_post(rexec); +} + +static void +sieve_result_execute_update_status(struct sieve_result_execution *rexec, + int status) +{ + switch (status) { + case SIEVE_EXEC_OK: + break; + case SIEVE_EXEC_TEMP_FAILURE: + rexec->status = status; + break; + case SIEVE_EXEC_BIN_CORRUPT: + i_unreached(); + case SIEVE_EXEC_FAILURE: + case SIEVE_EXEC_KEEP_FAILED: + if (rexec->status == SIEVE_EXEC_OK) + rexec->status = status; + break; + case SIEVE_EXEC_RESOURCE_LIMIT: + if (rexec->status != SIEVE_EXEC_TEMP_FAILURE) + rexec->status = status; + break; + } +} + +static void +sieve_result_execution_update(struct sieve_result_execution *rexec) +{ + const struct sieve_action_exec_env *aenv = &rexec->action_env; + struct sieve_result *result = aenv->result; + struct sieve_action_execution *aexec; + struct sieve_result_action *rac; + + aexec = rexec->actions_head; + while (aexec != NULL) { + struct sieve_action_execution *aexec_next = aexec->next; + + sieve_action_execution_update(rexec, aexec); + aexec = aexec_next; + } + + rac = result->actions_head; + while (rac != NULL) { + sieve_result_execution_add_action(rexec, rac); + rac = rac->next; + } +} + +int sieve_result_execute(struct sieve_result_execution *rexec, int status, + bool commit, struct sieve_error_handler *ehandler, + bool *keep_r) +{ + const struct sieve_action_exec_env *aenv = &rexec->action_env; + struct sieve_result *result = aenv->result; + int result_status, ret; + + e_debug(rexec->event, "Executing result (status=%s, commit=%s)", + sieve_execution_exitcode_to_str(status), + (commit ? "yes" : "no")); + + if (keep_r != NULL) + *keep_r = FALSE; + sieve_result_mark_executed(result); + + /* Prepare environment */ + + rexec->ehandler = ehandler; + + /* Update actions in execution from result */ + + sieve_result_execution_update(rexec); + + /* Transaction start and execute */ + + if (status != SIEVE_EXEC_OK) { + sieve_result_execute_update_status(rexec, status); + } else if (rexec->status == SIEVE_EXEC_OK) { + /* Transaction start */ + + status = sieve_result_transaction_start(rexec); + + /* Transaction execute */ + + status = sieve_result_transaction_execute(rexec, status); + sieve_result_execute_update_status(rexec, status); + } + + if (!commit) { + sieve_action_execution_post(rexec); + rexec->ehandler = NULL; + + /* Merge explicit keep status into implicit keep for the next + execution round. */ + rexec->keep_implicit = (rexec->keep_explicit || + rexec->keep_implicit); + rexec->keep_explicit = FALSE; + + e_debug(rexec->event, "Finished executing result " + "(no commit, status=%s, keep=%s)", + sieve_execution_exitcode_to_str(rexec->status), + (rexec->keep_implicit ? "yes" : "no")); + + if (keep_r != NULL) + *keep_r = rexec->keep_implicit; + return rexec->status; + } + + /* Execute implicit keep if the transaction failed or when the + implicit keep was not canceled during transaction. + */ + if (rexec->status != SIEVE_EXEC_OK || rexec->keep_implicit) + sieve_result_implicit_keep_execute(rexec); + + /* Transaction commit/rollback */ + + status = sieve_result_transaction_commit_or_rollback(rexec, status); + sieve_result_execute_update_status(rexec, status); + + /* Commit implicit keep if necessary */ + + result_status = rexec->status; + + /* Commit implicit keep if the transaction failed or when the + implicit keep was not canceled during transaction. + */ + if (rexec->status != SIEVE_EXEC_OK || rexec->keep_implicit) { + ret = sieve_result_implicit_keep_finalize(rexec); + switch (ret) { + case SIEVE_EXEC_OK: + if (result_status == SIEVE_EXEC_TEMP_FAILURE) + result_status = SIEVE_EXEC_FAILURE; + break; + case SIEVE_EXEC_TEMP_FAILURE: + case SIEVE_EXEC_RESOURCE_LIMIT: + if (!rexec->committed) { + result_status = ret; + break; + } + /* fall through */ + default: + result_status = SIEVE_EXEC_KEEP_FAILED; + } + } + if (rexec->status == SIEVE_EXEC_OK) + rexec->status = result_status; + + /* Finish execution */ + + sieve_result_transaction_finish(rexec, rexec->status); + + sieve_action_execution_post(rexec); + rexec->ehandler = NULL; + + rexec->status = result_status; + + /* Merge explicit keep status into implicit keep (in this case only for + completeness). + */ + rexec->keep_implicit = (rexec->keep_explicit || + rexec->keep_implicit); + rexec->keep_explicit = FALSE; + + e_debug(rexec->event, "Finished executing result " + "(final, status=%s, keep=%s)", + sieve_execution_exitcode_to_str(result_status), + (rexec->keep_implicit ? "yes" : "no")); + + if (keep_r != NULL) + *keep_r = rexec->keep_implicit; + return result_status; +} + +/* + * Result evaluation + */ + +struct sieve_result_iterate_context { + struct sieve_result *result; + struct sieve_result_action *current_action; + struct sieve_result_action *next_action; +}; + +struct sieve_result_iterate_context * +sieve_result_iterate_init(struct sieve_result *result) +{ + struct sieve_result_iterate_context *rictx = + t_new(struct sieve_result_iterate_context, 1); + + rictx->result = result; + rictx->current_action = NULL; + rictx->next_action = result->actions_head; + + return rictx; +} + +const struct sieve_action * +sieve_result_iterate_next(struct sieve_result_iterate_context *rictx, + bool *keep) +{ + struct sieve_result_action *rac; + + if (rictx == NULL) + return NULL; + + rac = rictx->current_action = rictx->next_action; + if (rac != NULL) { + rictx->next_action = rac->next; + + if (keep != NULL) + *keep = rac->action.keep; + + return &rac->action; + } + + return NULL; +} + +void sieve_result_iterate_delete(struct sieve_result_iterate_context *rictx) +{ + struct sieve_result *result; + struct sieve_result_action *rac; + + if (rictx == NULL || rictx->current_action == NULL) + return; + + result = rictx->result; + rac = rictx->current_action; + + /* Delete action */ + + if (rac->prev == NULL) + result->actions_head = rac->next; + else + rac->prev->next = rac->next; + + if (rac->next == NULL) + result->actions_tail = rac->prev; + else + rac->next->prev = rac->prev; + + sieve_result_action_deinit(rac); + + /* Skip to next action in iteration */ + + rictx->current_action = NULL; +} + +/* + * Side effects list + */ + +struct sieve_side_effects_list * +sieve_side_effects_list_create(struct sieve_result *result) +{ + struct sieve_side_effects_list *list = + p_new(result->pool, struct sieve_side_effects_list, 1); + + list->result = result; + list->first_effect = NULL; + list->last_effect = NULL; + + return list; +} + +void sieve_side_effects_list_add(struct sieve_side_effects_list *list, + const struct sieve_side_effect *seffect) +{ + struct sieve_result_side_effect *reffect, *reffect_pos; + + /* Prevent duplicates */ + reffect = list->first_effect; + reffect_pos = NULL; + while (reffect != NULL) { + const struct sieve_side_effect_def *ref_def = reffect->seffect.def; + const struct sieve_side_effect_def *sef_def = seffect->def; + + i_assert(ref_def != NULL); + i_assert(sef_def != NULL); + + if (sef_def == ref_def) { + /* already listed */ + i_assert(reffect_pos == NULL); + return; + } + if (sef_def->precedence > ref_def->precedence) { + /* insert it before this position */ + reffect_pos = reffect; + } + + reffect = reffect->next; + } + + /* Create new side effect object */ + reffect = p_new(list->result->pool, struct sieve_result_side_effect, 1); + reffect->seffect = *seffect; + + if (reffect_pos != NULL) { + /* Insert */ + reffect->next = reffect_pos; + reffect_pos->prev = reffect; + if (list->first_effect == reffect_pos) + list->first_effect = reffect; + } else { + /* Add */ + if ( list->first_effect == NULL ) { + list->first_effect = reffect; + list->last_effect = reffect; + reffect->prev = NULL; + reffect->next = NULL; + } else { + list->last_effect->next = reffect; + reffect->prev = list->last_effect; + list->last_effect = reffect; + reffect->next = NULL; + } + } +} + +/* + * Error handling + */ + +#undef sieve_result_error +void sieve_result_error(const struct sieve_action_exec_env *aenv, + const char *csrc_filename, unsigned int csrc_linenum, + const char *fmt, ...) +{ + struct sieve_error_params params = { + .log_type = LOG_TYPE_ERROR, + .event = aenv->event, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + }; + va_list args; + + va_start(args, fmt); + sieve_logv(aenv->ehandler, ¶ms, fmt, args); + va_end(args); +} + +#undef sieve_result_global_error +void sieve_result_global_error(const struct sieve_action_exec_env *aenv, + const char *csrc_filename, + unsigned int csrc_linenum, const char *fmt, ...) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + struct sieve_error_params params = { + .log_type = LOG_TYPE_ERROR, + .event = aenv->event, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + }; + va_list args; + + va_start(args, fmt); + sieve_global_logv(eenv->svinst, aenv->ehandler, ¶ms, fmt, args); + va_end(args); +} + +#undef sieve_result_warning +void sieve_result_warning(const struct sieve_action_exec_env *aenv, + const char *csrc_filename, unsigned int csrc_linenum, + const char *fmt, ...) +{ + struct sieve_error_params params = { + .log_type = LOG_TYPE_WARNING, + .event = aenv->event, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + }; + va_list args; + + va_start(args, fmt); + sieve_logv(aenv->ehandler, ¶ms, fmt, args); + va_end(args); +} + +#undef sieve_result_global_warning +void sieve_result_global_warning(const struct sieve_action_exec_env *aenv, + const char *csrc_filename, + unsigned int csrc_linenum, + const char *fmt, ...) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + struct sieve_error_params params = { + .log_type = LOG_TYPE_WARNING, + .event = aenv->event, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + }; + va_list args; + + va_start(args, fmt); + sieve_global_logv(eenv->svinst, aenv->ehandler, ¶ms, fmt, args); + va_end(args); +} + +#undef sieve_result_log +void sieve_result_log(const struct sieve_action_exec_env *aenv, + const char *csrc_filename, unsigned int csrc_linenum, + const char *fmt, ...) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + struct sieve_error_params params = { + .log_type = (HAS_ALL_BITS(eenv->flags, + SIEVE_EXECUTE_FLAG_LOG_RESULT) ? + LOG_TYPE_INFO : LOG_TYPE_DEBUG), + .event = aenv->event, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + }; + va_list args; + + va_start(args, fmt); + sieve_logv(aenv->ehandler, ¶ms, fmt, args); + va_end(args); +} + +#undef sieve_result_global_log +void sieve_result_global_log(const struct sieve_action_exec_env *aenv, + const char *csrc_filename, + unsigned int csrc_linenum, const char *fmt, ...) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + struct sieve_error_params params = { + .log_type = (HAS_ALL_BITS(eenv->flags, + SIEVE_EXECUTE_FLAG_LOG_RESULT) ? + LOG_TYPE_INFO : LOG_TYPE_DEBUG), + .event = aenv->event, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + }; + va_list args; + + va_start(args, fmt); + sieve_global_logv(eenv->svinst, aenv->ehandler, ¶ms, fmt, args); + va_end(args); +} + +#undef sieve_result_global_log_error +void sieve_result_global_log_error(const struct sieve_action_exec_env *aenv, + const char *csrc_filename, + unsigned int csrc_linenum, + const char *fmt, ...) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + struct sieve_error_params params = { + .log_type = LOG_TYPE_ERROR, + .event = aenv->event, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + }; + va_list args; + + va_start(args, fmt); + sieve_global_info_logv(eenv->svinst, aenv->ehandler, ¶ms, + fmt, args); + va_end(args); +} + +#undef sieve_result_global_log_warning +void sieve_result_global_log_warning(const struct sieve_action_exec_env *aenv, + const char *csrc_filename, + unsigned int csrc_linenum, + const char *fmt, ...) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + struct sieve_error_params params = { + .log_type = LOG_TYPE_WARNING, + .event = aenv->event, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + }; + va_list args; + + va_start(args, fmt); + sieve_global_info_logv(eenv->svinst, aenv->ehandler, ¶ms, + fmt, args); + va_end(args); +} + +#undef sieve_result_event_log +void sieve_result_event_log(const struct sieve_action_exec_env *aenv, + const char *csrc_filename, + unsigned int csrc_linenum, struct event *event, + const char *fmt, ...) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + struct sieve_error_params params = { + .log_type = (HAS_ALL_BITS(eenv->flags, + SIEVE_EXECUTE_FLAG_LOG_RESULT) ? + LOG_TYPE_INFO : LOG_TYPE_DEBUG), + .event = event, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + }; + va_list args; + + va_start(args, fmt); + sieve_global_logv(eenv->svinst, aenv->ehandler, ¶ms, fmt, args); + va_end(args); +} + + +#undef sieve_result_critical +void sieve_result_critical(const struct sieve_action_exec_env *aenv, + const char *csrc_filename, unsigned int csrc_linenum, + const char *user_prefix, const char *fmt, ...) +{ + const struct sieve_execute_env *eenv = aenv->exec_env; + struct sieve_error_params params = { + .log_type = LOG_TYPE_ERROR, + .event = aenv->event, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + }; + va_list args; + + va_start(args, fmt); + + T_BEGIN { + sieve_criticalv(eenv->svinst, aenv->ehandler, ¶ms, + user_prefix, fmt, args); + } T_END; + + va_end(args); +} + +#undef sieve_result_mail_error +int sieve_result_mail_error(const struct sieve_action_exec_env *aenv, + struct mail *mail, + const char *csrc_filename, + unsigned int csrc_linenum, const char *fmt, ...) +{ + const char *error_msg, *user_prefix; + va_list args; + + error_msg = mailbox_get_last_internal_error(mail->box, NULL); + + va_start(args, fmt); + user_prefix = t_strdup_vprintf(fmt, args); + sieve_result_critical(aenv, csrc_filename, csrc_linenum, + user_prefix, "%s: %s", user_prefix, error_msg); + va_end(args); + + return SIEVE_EXEC_TEMP_FAILURE; +} diff --git a/pigeonhole/src/lib-sieve/sieve-result.h b/pigeonhole/src/lib-sieve/sieve-result.h new file mode 100644 index 0000000..a9fcadc --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-result.h @@ -0,0 +1,224 @@ +#ifndef SIEVE_RESULT_H +#define SIEVE_RESULT_H + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-execute.h" + +/* + * Types + */ + +struct sieve_side_effects_list; + +/* + * Result object + */ + +struct sieve_result; + +struct sieve_result * +sieve_result_create(struct sieve_instance *svinst, pool_t pool, + const struct sieve_execute_env *eenv); + +void sieve_result_ref(struct sieve_result *result); + +void sieve_result_unref(struct sieve_result **result); + +pool_t sieve_result_pool(struct sieve_result *result); + +/* + * Getters/Setters + */ + +const struct sieve_script_env * +sieve_result_get_script_env(struct sieve_result *result); +const struct sieve_message_data * +sieve_result_get_message_data(struct sieve_result *result); +struct sieve_message_context * +sieve_result_get_message_context(struct sieve_result *result); +unsigned int sieve_result_get_exec_seq(struct sieve_result *result); + +/* + * Extension support + */ + +void sieve_result_extension_set_context(struct sieve_result *result, + const struct sieve_extension *ext, + void *context); +const void * +sieve_result_extension_get_context(struct sieve_result *result, + const struct sieve_extension *ext); + +/* + * Result printing + */ + +struct sieve_result_print_env { + struct sieve_result *result; + const struct sieve_script_env *scriptenv; + struct ostream *stream; +}; + +void sieve_result_vprintf(const struct sieve_result_print_env *penv, + const char *fmt, va_list args) ATTR_FORMAT(2, 0); +void sieve_result_printf(const struct sieve_result_print_env *penv, + const char *fmt, ...) ATTR_FORMAT(2, 3); +void sieve_result_action_printf(const struct sieve_result_print_env *penv, + const char *fmt, ...) ATTR_FORMAT(2, 3); +void sieve_result_seffect_printf(const struct sieve_result_print_env *penv, + const char *fmt, ...) ATTR_FORMAT(2, 3); + +bool sieve_result_print(struct sieve_result *result, + const struct sieve_script_env *senv, + struct ostream *stream, bool *keep); + +/* + * Result composition + */ + +void sieve_result_add_implicit_side_effect( + struct sieve_result *result, const struct sieve_action_def *to_action, + bool to_keep, const struct sieve_extension *ext, + const struct sieve_side_effect_def *seffect, void *context); + +int sieve_result_add_action(const struct sieve_runtime_env *renv, + const struct sieve_extension *ext, const char *name, + const struct sieve_action_def *act_def, + struct sieve_side_effects_list *seffects, + void *context, unsigned int instance_limit, + bool preserve_mail); +int sieve_result_add_keep(const struct sieve_runtime_env *renv, + struct sieve_side_effects_list *seffects); + +void sieve_result_set_keep_action(struct sieve_result *result, + const struct sieve_extension *ext, + const struct sieve_action_def *act_def); +void sieve_result_set_failure_action(struct sieve_result *result, + const struct sieve_extension *ext, + const struct sieve_action_def *act_def); + +/* + * Result execution + */ + +struct sieve_result_execution; + +void sieve_result_mark_executed(struct sieve_result *result); + +struct sieve_result_execution * +sieve_result_execution_create(struct sieve_result *result, pool_t pool); +void sieve_result_execution_destroy(struct sieve_result_execution **_rexec); + +void *sieve_result_execution_get_dup_transaction( + struct sieve_result_execution *rexec); + +int sieve_result_execute(struct sieve_result_execution *rexec, int status, + bool commit, struct sieve_error_handler *ehandler, + bool *keep_r); + +bool sieve_result_executed(struct sieve_result_execution *rexec); +bool sieve_result_committed(struct sieve_result_execution *rexec); + +bool sieve_result_executed_delivery(struct sieve_result_execution *rexec); + +/* + * Result evaluation + */ + +struct sieve_result_iterate_context; + +struct sieve_result_iterate_context * +sieve_result_iterate_init(struct sieve_result *result); +const struct sieve_action * +sieve_result_iterate_next(struct sieve_result_iterate_context *rictx, + bool *keep); +void sieve_result_iterate_delete(struct sieve_result_iterate_context *rictx); + +/* + * Side effects list + */ + +struct sieve_side_effects_list * +sieve_side_effects_list_create(struct sieve_result *result); +void sieve_side_effects_list_add(struct sieve_side_effects_list *list, + const struct sieve_side_effect *seffect); + +/* + * Error handling + */ + +void sieve_result_error(const struct sieve_action_exec_env *aenv, + const char *csrc_filename, unsigned int csrc_linenum, + const char *fmt, ...) + ATTR_FORMAT(4, 5); +#define sieve_result_error(aenv, ...) \ + sieve_result_error(aenv, __FILE__, __LINE__, __VA_ARGS__) +void sieve_result_global_error(const struct sieve_action_exec_env *aenv, + const char *csrc_filename, + unsigned int csrc_linenum, const char *fmt, ...) + ATTR_FORMAT(4, 5); +#define sieve_result_global_error(aenv, ...) \ + sieve_result_global_error(aenv, __FILE__, __LINE__, __VA_ARGS__) +void sieve_result_warning(const struct sieve_action_exec_env *aenv, + const char *csrc_filename, unsigned int csrc_linenum, + const char *fmt, ...) + ATTR_FORMAT(4, 5); +#define sieve_result_warning(aenv, ...) \ + sieve_result_warning(aenv, __FILE__, __LINE__, __VA_ARGS__) +void sieve_result_global_warning(const struct sieve_action_exec_env *aenv, + const char *csrc_filename, + unsigned int csrc_linenum, + const char *fmt, ...) + ATTR_FORMAT(4, 5); +#define sieve_result_global_warning(aenv, ...) \ + sieve_result_global_warning(aenv, __FILE__, __LINE__, __VA_ARGS__) +void sieve_result_log(const struct sieve_action_exec_env *aenv, + const char *csrc_filename, unsigned int csrc_linenum, + const char *fmt, ...) + ATTR_FORMAT(4, 5); +#define sieve_result_log(aenv, ...) \ + sieve_result_log(aenv, __FILE__, __LINE__, __VA_ARGS__) +void sieve_result_global_log(const struct sieve_action_exec_env *aenv, + const char *csrc_filename, + unsigned int csrc_linenum, const char *fmt, ...) + ATTR_FORMAT(4, 5); +#define sieve_result_global_log(aenv, ...) \ + sieve_result_global_log(aenv, __FILE__, __LINE__, __VA_ARGS__) +void sieve_result_global_log_error(const struct sieve_action_exec_env *aenv, + const char *csrc_filename, + unsigned int csrc_linenum, + const char *fmt, ...) + ATTR_FORMAT(4, 5); +#define sieve_result_global_log_error(aenv, ...) \ + sieve_result_global_log_error(aenv, __FILE__, __LINE__, __VA_ARGS__) +void sieve_result_global_log_warning(const struct sieve_action_exec_env *aenv, + const char *csrc_filename, + unsigned int csrc_linenum, + const char *fmt, ...) + ATTR_FORMAT(4, 5); +#define sieve_result_global_log_warning(aenv, ...) \ + sieve_result_global_log_warning(aenv, __FILE__, __LINE__, __VA_ARGS__) + +void sieve_result_event_log(const struct sieve_action_exec_env *aenv, + const char *csrc_filename, + unsigned int csrc_linenum, struct event *event, + const char *fmt, ...) ATTR_FORMAT(5, 0); +#define sieve_result_event_log(aenv, event, ...) \ + sieve_result_event_log(aenv, __FILE__, __LINE__, event, __VA_ARGS__) + +void sieve_result_critical(const struct sieve_action_exec_env *aenv, + const char *csrc_filename, unsigned int csrc_linenum, + const char *user_prefix, const char *fmt, ...) + ATTR_FORMAT(5, 6); +#define sieve_result_critical(aenv, ...) \ + sieve_result_critical(aenv, __FILE__, __LINE__, __VA_ARGS__) +int sieve_result_mail_error(const struct sieve_action_exec_env *aenv, + struct mail *mail, + const char *csrc_filename, + unsigned int csrc_linenum, const char *fmt, ...) + ATTR_FORMAT(5, 6); +#define sieve_result_mail_error(aenv, mail, ...) \ + sieve_result_mail_error(aenv, mail, __FILE__, __LINE__, __VA_ARGS__) + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-runtime-trace.c b/pigeonhole/src/lib-sieve/sieve-runtime-trace.c new file mode 100644 index 0000000..a9351a3 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-runtime-trace.c @@ -0,0 +1,151 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "ostream.h" + +#include "sieve-common.h" +#include "sieve-script.h" +#include "sieve-binary.h" +#include "sieve-code.h" +#include "sieve-interpreter.h" +#include "sieve-runtime.h" +#include "sieve-runtime-trace.h" + +static inline string_t *_trace_line_new +(const struct sieve_runtime_env *renv, sieve_size_t address, unsigned int cmd_line) +{ + string_t *trline; + unsigned int i; + + trline = t_str_new(128); + if ( (renv->trace->config.flags & SIEVE_TRFLG_ADDRESSES) > 0 ) + str_printfa(trline, "%08llx: ", (unsigned long long) address); + if ( cmd_line > 0 ) + str_printfa(trline, "%4d: ", cmd_line); + else + str_append(trline, " "); + + for ( i = 0; i < renv->trace->indent; i++ ) + str_append(trline, " "); + + return trline; +} + +static inline void _trace_line_print +(string_t *trline, const struct sieve_runtime_env *renv) +{ + sieve_trace_log_write_line(renv->trace->log, trline); +} + +static inline void _trace_line_print_empty +(const struct sieve_runtime_env *renv) +{ + sieve_trace_log_write_line(renv->trace->log, NULL); +} + +/* + * Trace errors + */ + +void _sieve_runtime_trace_error +(const struct sieve_runtime_env *renv, const char *fmt, va_list args) +{ + string_t *trline = _trace_line_new(renv, renv->pc, 0); + + str_printfa(trline, "%s: #ERROR#: ", sieve_operation_mnemonic(renv->oprtn)); + str_vprintfa(trline, fmt, args); + + _trace_line_print(trline, renv); +} + +void _sieve_runtime_trace_operand_error +(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + const char *fmt, va_list args) +{ + string_t *trline = _trace_line_new(renv, oprnd->address, + sieve_runtime_get_source_location(renv, oprnd->address)); + + str_printfa(trline, "%s: #ERROR#: ", sieve_operation_mnemonic(renv->oprtn)); + + if ( oprnd->field_name != NULL ) + str_printfa(trline, "%s: ", oprnd->field_name); + + str_vprintfa(trline, fmt, args); + + _trace_line_print(trline, renv); +} + +/* + * Trace info + */ + +static inline void ATTR_FORMAT(4, 0) _sieve_runtime_trace_vprintf +(const struct sieve_runtime_env *renv, sieve_size_t address, + unsigned int cmd_line, const char *fmt, va_list args) +{ + string_t *trline = _trace_line_new(renv, address, cmd_line); + + str_vprintfa(trline, fmt, args); + + _trace_line_print(trline, renv); +} + +static inline void ATTR_FORMAT(4, 5) _sieve_runtime_trace_printf +(const struct sieve_runtime_env *renv, sieve_size_t address, + unsigned int cmd_line, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + _sieve_runtime_trace_vprintf(renv, address, cmd_line, fmt, args); + va_end(args); +} + +void ATTR_FORMAT(2, 0) _sieve_runtime_trace +(const struct sieve_runtime_env *renv, const char *fmt, va_list args) +{ + _sieve_runtime_trace_vprintf + (renv, renv->oprtn->address, sieve_runtime_get_command_location(renv), + fmt, args); +} + +void _sieve_runtime_trace_address +(const struct sieve_runtime_env *renv, sieve_size_t address, + const char *fmt, va_list args) +{ + _sieve_runtime_trace_vprintf + (renv, address, sieve_runtime_get_source_location(renv, address), fmt, + args); +} + +/* + * Trace boundaries + */ + +void _sieve_runtime_trace_begin(const struct sieve_runtime_env *renv) +{ + const char *script_name = ( renv->script != NULL ? + sieve_script_name(renv->script) : sieve_binary_path(renv->sbin) ); + + _trace_line_print_empty(renv); + _sieve_runtime_trace_printf(renv, renv->pc, 0, + "## Started executing script '%s'", script_name); +} + +void _sieve_runtime_trace_end(const struct sieve_runtime_env *renv) +{ + const char *script_name = ( renv->script != NULL ? + sieve_script_name(renv->script) : sieve_binary_path(renv->sbin) ); + + _sieve_runtime_trace_printf(renv, renv->pc, 0, + "## Finished executing script '%s'", script_name); + _trace_line_print_empty(renv); +} + +void _sieve_runtime_trace_sep(const struct sieve_runtime_env *renv) +{ + _trace_line_print_empty(renv); +} + diff --git a/pigeonhole/src/lib-sieve/sieve-runtime-trace.h b/pigeonhole/src/lib-sieve/sieve-runtime-trace.h new file mode 100644 index 0000000..b165586 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-runtime-trace.h @@ -0,0 +1,182 @@ +#ifndef SIEVE_RUNTIME_TRACE_H +#define SIEVE_RUNTIME_TRACE_H + +#include "sieve-common.h" +#include "sieve-runtime.h" + +/* + * Runtime trace + */ + +struct sieve_runtime_trace { + struct sieve_trace_config config; + struct sieve_trace_log *log; + unsigned int indent; +}; + +/* Trace configuration */ + +static inline bool sieve_runtime_trace_active +(const struct sieve_runtime_env *renv, sieve_trace_level_t trace_level) +{ + return ( renv->trace != NULL && trace_level <= renv->trace->config.level ); +} + +static inline bool sieve_runtime_trace_hasflag +(const struct sieve_runtime_env *renv, unsigned int flag) +{ + return ( renv->trace != NULL && (renv->trace->config.flags & flag) != 0 ); +} + +/* Trace indent */ + +static inline void sieve_runtime_trace_descend +(const struct sieve_runtime_env *renv) +{ + if ( renv->trace != NULL ) renv->trace->indent++; +} + +static inline void sieve_runtime_trace_ascend +(const struct sieve_runtime_env *renv) +{ + if ( renv->trace != NULL ) renv->trace->indent--; +} + +static inline void sieve_runtime_trace_toplevel +(const struct sieve_runtime_env *renv) +{ + if ( renv->trace != NULL ) renv->trace->indent = 0; +} + +/* Trace errors */ + +void _sieve_runtime_trace_error + (const struct sieve_runtime_env *renv, const char *fmt, va_list args) + ATTR_FORMAT(2, 0); + +void _sieve_runtime_trace_operand_error + (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + const char *fmt, va_list args) ATTR_FORMAT(3, 0); + +static inline void sieve_runtime_trace_error + (const struct sieve_runtime_env *renv, const char *fmt, ...) + ATTR_FORMAT(2, 3); + +static inline void sieve_runtime_trace_operand_error + (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + const char *fmt, ...) ATTR_FORMAT(3, 4); + +static inline void ATTR_FORMAT(2, 3) sieve_runtime_trace_error + (const struct sieve_runtime_env *renv, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + if ( renv->trace != NULL ) + _sieve_runtime_trace_error(renv, fmt, args); + va_end(args); +} + +static inline void ATTR_FORMAT(3, 4) sieve_runtime_trace_operand_error + (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + if ( renv->trace != NULL ) + _sieve_runtime_trace_operand_error(renv, oprnd, fmt, args); + va_end(args); +} + +/* Trace info */ + +void _sieve_runtime_trace + (const struct sieve_runtime_env *renv, const char *fmt, va_list args) + ATTR_FORMAT(2, 0); + +static inline void sieve_runtime_trace + (const struct sieve_runtime_env *renv, sieve_trace_level_t trace_level, + const char *fmt, ...) ATTR_FORMAT(3, 4); + +static inline void ATTR_FORMAT(3, 4) sieve_runtime_trace +(const struct sieve_runtime_env *renv, sieve_trace_level_t trace_level, + const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + if ( renv->trace != NULL && trace_level <= renv->trace->config.level ) { + _sieve_runtime_trace(renv, fmt, args); + } + + va_end(args); +} + +void _sieve_runtime_trace_address + (const struct sieve_runtime_env *renv, sieve_size_t address, + const char *fmt, va_list args) ATTR_FORMAT(3, 0); + +static inline void sieve_runtime_trace_address + (const struct sieve_runtime_env *renv, sieve_trace_level_t trace_level, + sieve_size_t address, const char *fmt, ...) ATTR_FORMAT(4, 5); + +static inline void ATTR_FORMAT(4, 5) sieve_runtime_trace_address +(const struct sieve_runtime_env *renv, sieve_trace_level_t trace_level, + sieve_size_t address, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + if ( renv->trace != NULL && trace_level <= renv->trace->config.level ) { + _sieve_runtime_trace_address(renv, address, fmt, args); + } + + va_end(args); +} + +static inline void ATTR_FORMAT(3, 4) sieve_runtime_trace_here +(const struct sieve_runtime_env *renv, sieve_trace_level_t trace_level, + const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + if ( renv->trace != NULL && trace_level <= renv->trace->config.level ) { + _sieve_runtime_trace_address(renv, renv->pc, fmt, args); + } + + va_end(args); +} + +/* Trace boundaries */ + +void _sieve_runtime_trace_begin(const struct sieve_runtime_env *renv); +void _sieve_runtime_trace_end(const struct sieve_runtime_env *renv); +void _sieve_runtime_trace_sep(const struct sieve_runtime_env *renv); + +static inline void sieve_runtime_trace_begin +(const struct sieve_runtime_env *renv) +{ + if ( renv->trace != NULL ) + _sieve_runtime_trace_begin(renv); +} + +static inline void sieve_runtime_trace_end +(const struct sieve_runtime_env *renv) +{ + if ( renv->trace != NULL ) + _sieve_runtime_trace_end(renv); +} + +static inline void sieve_runtime_trace_sep +(const struct sieve_runtime_env *renv) +{ + if ( renv->trace != NULL ) + _sieve_runtime_trace_sep(renv); +} + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-runtime.h b/pigeonhole/src/lib-sieve/sieve-runtime.h new file mode 100644 index 0000000..fa50602 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-runtime.h @@ -0,0 +1,40 @@ +#ifndef SIEVE_RUNTIME_H +#define SIEVE_RUNTIME_H + +#include "sieve-common.h" +#include "sieve-execute.h" + +/* + * Runtime environment + */ + +struct sieve_runtime_env { + const struct sieve_execute_env *exec_env; + struct event *event; + + /* Interpreter */ + struct sieve_interpreter *interp; + struct sieve_error_handler *ehandler; + + /* Executing script */ + struct sieve_script *script; + + /* Executing binary */ + struct sieve_binary *sbin; + struct sieve_binary_block *sblock; + + /* Current code */ + sieve_size_t pc; + const struct sieve_operation *oprtn; + + /* Tested message */ + struct sieve_message_context *msgctx; + + /* Filter result */ + struct sieve_result *result; + + /* Runtime tracing */ + struct sieve_runtime_trace *trace; +}; + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-script-private.h b/pigeonhole/src/lib-sieve/sieve-script-private.h new file mode 100644 index 0000000..7f23c19 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-script-private.h @@ -0,0 +1,100 @@ +#ifndef SIEVE_SCRIPT_PRIVATE_H +#define SIEVE_SCRIPT_PRIVATE_H + +#include "sieve-common.h" +#include "sieve-script.h" + +/* + * Script object + */ + +struct sieve_script_vfuncs { + void (*destroy)(struct sieve_script *script); + + int (*open)(struct sieve_script *script, enum sieve_error *error_r); + + int (*get_stream)(struct sieve_script *script, + struct istream **stream_r, enum sieve_error *error_r); + + /* binary */ + int (*binary_read_metadata)(struct sieve_script *_script, + struct sieve_binary_block *sblock, + sieve_size_t *offset); + void (*binary_write_metadata)(struct sieve_script *script, + struct sieve_binary_block *sblock); + bool (*binary_dump_metadata)(struct sieve_script *script, + struct sieve_dumptime_env *denv, + struct sieve_binary_block *sblock, + sieve_size_t *offset); + struct sieve_binary *(*binary_load)(struct sieve_script *script, + enum sieve_error *error_r); + int (*binary_save)(struct sieve_script *script, + struct sieve_binary *sbin, bool update, + enum sieve_error *error_r); + const char *(*binary_get_prefix)(struct sieve_script *script); + + /* management */ + int (*rename)(struct sieve_script *script, const char *newname); + int (*delete)(struct sieve_script *script); + int (*is_active)(struct sieve_script *script); + int (*activate)(struct sieve_script *script); + + /* properties */ + int (*get_size)(const struct sieve_script *script, uoff_t *size_r); + + /* matching */ + bool (*equals)(const struct sieve_script *script, + const struct sieve_script *other); +}; + +struct sieve_script { + pool_t pool; + unsigned int refcount; + struct sieve_storage *storage; + struct event *event; + + const char *driver_name; + const struct sieve_script *script_class; + struct sieve_script_vfuncs v; + + const char *name; + const char *location; + + /* Stream */ + struct istream *stream; + + bool open:1; +}; + +void sieve_script_init(struct sieve_script *script, + struct sieve_storage *storage, + const struct sieve_script *script_class, + const char *location, const char *name); + +/* + * Built-in script drivers + */ + +extern const struct sieve_script sieve_data_script; +extern const struct sieve_script sieve_file_script; +extern const struct sieve_script sieve_dict_script; +extern const struct sieve_script sieve_ldap_script; + +/* + * Error handling + */ + +void sieve_script_set_error(struct sieve_script *script, enum sieve_error error, + const char *fmt, ...) ATTR_FORMAT(3, 4); +void sieve_script_set_internal_error(struct sieve_script *script); +void sieve_script_set_critical(struct sieve_script *script, + const char *fmt, ...) ATTR_FORMAT(2, 3); + +/* + * Script sequence + */ + +void sieve_script_sequence_init(struct sieve_script_sequence *seq, + struct sieve_storage *storage); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-script.c b/pigeonhole/src/lib-sieve/sieve-script.c new file mode 100644 index 0000000..32963a0 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-script.c @@ -0,0 +1,906 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "compat.h" +#include "unichar.h" +#include "str.h" +#include "str-sanitize.h" +#include "hash.h" +#include "array.h" +#include "eacces-error.h" +#include "istream.h" + +#include "sieve-common.h" +#include "sieve-limits.h" +#include "sieve-settings.h" +#include "sieve-error.h" +#include "sieve-dump.h" +#include "sieve-binary.h" + +#include "sieve-storage-private.h" +#include "sieve-script-private.h" + +/* + * Script name + */ + +bool sieve_script_name_is_valid(const char *scriptname) +{ + ARRAY_TYPE(unichars) uni_name; + unsigned int count, i; + const unichar_t *name_chars; + size_t namelen = strlen(scriptname); + + /* Check minimum length */ + if (namelen == 0) + return FALSE; + + /* Check worst-case maximum length */ + if (namelen > SIEVE_MAX_SCRIPT_NAME_LEN * 4) + return FALSE; + + /* Intialize array for unicode characters */ + t_array_init(&uni_name, namelen * 4); + + /* Convert UTF-8 to UCS4/UTF-32 */ + if (uni_utf8_to_ucs4(scriptname, &uni_name) < 0) + return FALSE; + name_chars = array_get(&uni_name, &count); + + /* Check true maximum length */ + if (count > SIEVE_MAX_SCRIPT_NAME_LEN) + return FALSE; + + /* Scan name for invalid characters + * FIXME: compliance with Net-Unicode Definition (Section 2 of + * RFC 5198) is not checked fully and no normalization + * is performed. + */ + for (i = 0; i < count; i++) { + /* 0000-001F; [CONTROL CHARACTERS] */ + if (name_chars[i] <= 0x001f) + return FALSE; + /* 002F; SLASH (not RFC-prohibited, but '/' is dangerous) */ + if (name_chars[i] == 0x002f) + return FALSE; + /* 007F; DELETE */ + if (name_chars[i] == 0x007f) + return FALSE; + /* 0080-009F; [CONTROL CHARACTERS] */ + if (name_chars[i] >= 0x0080 && name_chars[i] <= 0x009f) + return FALSE; + /* 00FF */ + if (name_chars[i] == 0x00ff) + return FALSE; + /* 2028; LINE SEPARATOR */ + /* 2029; PARAGRAPH SEPARATOR */ + if (name_chars[i] == 0x2028 || name_chars[i] == 0x2029) + return FALSE; + } + + return TRUE; +} + +/* + * Script instance + */ + +void sieve_script_init(struct sieve_script *script, + struct sieve_storage *storage, + const struct sieve_script *script_class, + const char *location, const char *name) +{ + i_assert(storage != NULL); + + script->script_class = script_class; + script->refcount = 1; + script->storage = storage; + script->location = p_strdup_empty(script->pool, location); + script->name = p_strdup(script->pool, name); + + script->event = event_create(storage->event); + event_add_str(script->event, "script_name", name); + event_add_str(script->event, "script_location", location); + if (name == NULL) + event_set_append_log_prefix(script->event, "script: "); + else { + event_set_append_log_prefix( + script->event, t_strdup_printf("script `%s': ", name)); + } + + sieve_storage_ref(storage); +} + +struct sieve_script * +sieve_script_create(struct sieve_instance *svinst, const char *location, + const char *name, enum sieve_error *error_r) +{ + struct sieve_storage *storage; + struct sieve_script *script; + enum sieve_error error; + + if (error_r != NULL) + *error_r = SIEVE_ERROR_NONE; + else + error_r = &error; + + storage = sieve_storage_create(svinst, location, 0, error_r); + if (storage == NULL) + return NULL; + + script = sieve_storage_get_script(storage, name, error_r); + + sieve_storage_unref(&storage); + return script; +} + +void sieve_script_ref(struct sieve_script *script) +{ + script->refcount++; +} + +void sieve_script_unref(struct sieve_script **_script) +{ + struct sieve_script *script = *_script; + + *_script = NULL; + + if (script == NULL) + return; + + i_assert(script->refcount > 0); + if (--script->refcount != 0) + return; + + if (script->stream != NULL) { + struct event_passthrough *e = + event_create_passthrough(script->event)-> + set_name("sieve_script_closed"); + e_debug(e->event(), "Closed script"); + } + i_stream_unref(&script->stream); + + if (script->v.destroy != NULL) + script->v.destroy(script); + + sieve_storage_unref(&script->storage); + event_unref(&script->event); + pool_unref(&script->pool); +} + +int sieve_script_open(struct sieve_script *script, enum sieve_error *error_r) +{ + enum sieve_error error; + + if (error_r != NULL) + *error_r = SIEVE_ERROR_NONE; + else + error_r = &error; + + if (script->open) + return 0; + + if (script->v.open(script, error_r) < 0) + return -1; + + i_assert(script->location != NULL); + i_assert(script->name != NULL); + script->open = TRUE; + + if (*script->name != '\0') { + e_debug(script->event, "Opened script `%s' from `%s'", + script->name, script->location); + } else { + e_debug(script->event, "Opened nameless script from `%s'", + script->location); + } + return 0; +} + +int sieve_script_open_as(struct sieve_script *script, const char *name, + enum sieve_error *error_r) +{ + if (sieve_script_open(script, error_r) < 0) + return -1; + + /* override name */ + script->name = p_strdup(script->pool, name); + event_add_str(script->event, "script_name", name); + return 0; +} + +struct sieve_script * +sieve_script_create_open(struct sieve_instance *svinst, const char *location, + const char *name, enum sieve_error *error_r) +{ + struct sieve_script *script; + + script = sieve_script_create(svinst, location, name, error_r); + if (script == NULL) + return NULL; + + if (sieve_script_open(script, error_r) < 0) { + sieve_script_unref(&script); + return NULL; + } + + return script; +} + +int sieve_script_check(struct sieve_instance *svinst, const char *location, + const char *name, enum sieve_error *error_r) +{ + struct sieve_script *script; + enum sieve_error error; + + if (error_r == NULL) + error_r = &error; + + script = sieve_script_create_open(svinst, location, name, error_r); + if (script == NULL) + return (*error_r == SIEVE_ERROR_NOT_FOUND ? 0 : -1); + + sieve_script_unref(&script); + return 1; +} + +/* + * Properties + */ + +const char *sieve_script_name(const struct sieve_script *script) +{ + return script->name; +} + +const char *sieve_script_location(const struct sieve_script *script) +{ + return script->location; +} + +struct sieve_instance *sieve_script_svinst(const struct sieve_script *script) +{ + return script->storage->svinst; +} + +int sieve_script_get_size(struct sieve_script *script, uoff_t *size_r) +{ + struct istream *stream; + int ret; + + if (script->v.get_size != NULL) { + if ((ret = script->v.get_size(script, size_r)) != 0) + return ret; + } + + /* Try getting size from the stream */ + if (script->stream == NULL && + sieve_script_get_stream(script, &stream, NULL) < 0) + return -1; + + if (i_stream_get_size(script->stream, TRUE, size_r) < 0) { + sieve_storage_set_critical(script->storage, + "i_stream_get_size(%s) failed: %s", + i_stream_get_name(script->stream), + i_stream_get_error(script->stream)); + return -1; + } + return 0; +} + +bool sieve_script_is_open(const struct sieve_script *script) +{ + return script->open; +} + +bool sieve_script_is_default(const struct sieve_script *script) +{ + return script->storage->is_default; +} + +/* + * Stream management + */ + +int sieve_script_get_stream(struct sieve_script *script, + struct istream **stream_r, + enum sieve_error *error_r) +{ + struct sieve_storage *storage = script->storage; + enum sieve_error error; + int ret; + + if (error_r != NULL) + *error_r = SIEVE_ERROR_NONE; + else + error_r = &error; + + if (script->stream != NULL) { + *stream_r = script->stream; + return 0; + } + + // FIXME: necessary? + i_assert(script->open); + + T_BEGIN { + ret = script->v.get_stream(script, &script->stream, error_r); + } T_END; + + if (ret < 0) { + struct event_passthrough *e = + event_create_passthrough(script->event)-> + add_str("error", storage->error)-> + set_name("sieve_script_opened"); + e_debug(e->event(), "Failed to open script for reading: %s", + storage->error); + return -1; + } + + struct event_passthrough *e = + event_create_passthrough(script->event)-> + set_name("sieve_script_opened"); + e_debug(e->event(), "Opened script for reading"); + + *stream_r = script->stream; + return 0; +} + +/* + * Comparison + */ + +bool sieve_script_equals(const struct sieve_script *script, + const struct sieve_script *other) +{ + if (script == other) + return TRUE; + if (script == NULL || other == NULL) + return FALSE; + if (script->script_class != other->script_class) + return FALSE; + + if (script->v.equals == NULL) { + i_assert (script->location != NULL && other->location != NULL); + + return (strcmp(script->location, other->location) == 0); + } + + return script->v.equals(script, other); +} + +unsigned int sieve_script_hash(const struct sieve_script *script) +{ + i_assert(script->name != NULL); + + return str_hash(script->name); +} + +/* + * Binary + */ + +int sieve_script_binary_read_metadata(struct sieve_script *script, + struct sieve_binary_block *sblock, + sieve_size_t *offset) +{ + struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock); + string_t *storage_class, *location; + unsigned int version; + + if ((sieve_binary_block_get_size(sblock) - *offset) == 0) + return 0; + + /* storage class */ + if (!sieve_binary_read_string(sblock, offset, &storage_class)) { + e_error(script->event, + "Binary `%s' has invalid metadata for script `%s': " + "Invalid storage class", + sieve_binary_path(sbin), script->location); + return -1; + } + if (strcmp(str_c(storage_class), script->driver_name) != 0) { + e_debug(script->event, + "Binary `%s' reports unexpected driver name for script `%s' " + "(`%s' rather than `%s')", + sieve_binary_path(sbin), script->location, + str_c(storage_class), script->driver_name); + return 0; + } + + /* version */ + if (!sieve_binary_read_unsigned(sblock, offset, &version)) { + e_error(script->event, + "Binary `%s' has invalid metadata for script `%s': " + "Invalid version", + sieve_binary_path(sbin), script->location); + return -1; + } + if (script->storage->version != version) { + e_debug(script->event, + "Binary `%s' was compiled with " + "a different version of the `%s' script storage class " + "(compiled v%d, expected v%d; " + "automatically fixed when re-compiled)", + sieve_binary_path(sbin), script->driver_name, + version, script->storage->version); + return 0; + } + + /* location */ + if (!sieve_binary_read_string(sblock, offset, &location)) { + e_error(script->event, + "Binary `%s' has invalid metadata for script `%s': " + "Invalid location", + sieve_binary_path(sbin), script->location); + return -1; + } + i_assert(script->location != NULL); + if (strcmp(str_c(location), script->location) != 0) { + e_debug(script->event, + "Binary `%s' reports different location " + "for script `%s' (binary points to `%s')", + sieve_binary_path(sbin), script->location, + str_c(location)); + return 0; + } + + if (script->v.binary_read_metadata == NULL) + return 1; + + return script->v.binary_read_metadata(script, sblock, offset); +} + +void sieve_script_binary_write_metadata(struct sieve_script *script, + struct sieve_binary_block *sblock) +{ + sieve_binary_emit_cstring(sblock, script->driver_name); + sieve_binary_emit_unsigned(sblock, script->storage->version); + sieve_binary_emit_cstring(sblock, (script->location == NULL ? + "" : script->location)); + + if (script->v.binary_write_metadata == NULL) + return; + + script->v.binary_write_metadata(script, sblock); +} + +bool sieve_script_binary_dump_metadata(struct sieve_script *script, + struct sieve_dumptime_env *denv, + struct sieve_binary_block *sblock, + sieve_size_t *offset) +{ + struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock); + struct sieve_instance *svinst = sieve_binary_svinst(sbin); + string_t *storage_class, *location; + struct sieve_script *adhoc_script = NULL; + unsigned int version; + bool result = TRUE; + + /* storage class */ + if (!sieve_binary_read_string(sblock, offset, &storage_class)) + return FALSE; + sieve_binary_dumpf(denv, "class = %s\n", str_c(storage_class)); + + /* version */ + if (!sieve_binary_read_unsigned(sblock, offset, &version)) + return FALSE; + sieve_binary_dumpf(denv, "class.version = %d\n", version); + + /* location */ + if (!sieve_binary_read_string(sblock, offset, &location)) + return FALSE; + sieve_binary_dumpf(denv, "location = %s\n", str_c(location)); + + if (script == NULL) { + script = adhoc_script = + sieve_script_create(svinst, str_c(location), + NULL, NULL); + } + + if (script != NULL && script->v.binary_dump_metadata != NULL) { + result = script->v.binary_dump_metadata( + script, denv, sblock, offset); + } + + if (adhoc_script != NULL) + sieve_script_unref(&adhoc_script); + return result; +} + +struct sieve_binary * +sieve_script_binary_load(struct sieve_script *script, enum sieve_error *error_r) +{ + if (script->v.binary_load == NULL) { + *error_r = SIEVE_ERROR_NOT_POSSIBLE; + return NULL; + } + + return script->v.binary_load(script, error_r); +} + +int sieve_script_binary_save(struct sieve_script *script, + struct sieve_binary *sbin, bool update, + enum sieve_error *error_r) +{ + struct sieve_script *bin_script = sieve_binary_script(sbin); + enum sieve_error error; + + if (error_r != NULL) + *error_r = SIEVE_ERROR_NONE; + else + error_r = &error; + + i_assert(bin_script == NULL || sieve_script_equals(bin_script, script)); + + if (script->v.binary_save == NULL) { + *error_r = SIEVE_ERROR_NOT_POSSIBLE; + return -1; + } + + return script->v.binary_save(script, sbin, update, error_r); +} + +const char *sieve_script_binary_get_prefix(struct sieve_script *script) +{ + struct sieve_storage *storage = script->storage; + + if (storage->bin_dir != NULL && + sieve_storage_setup_bindir(storage, 0700) >= 0) { + return t_strconcat(storage->bin_dir, "/", script->name, NULL); + } + + if (script->v.binary_get_prefix == NULL) + return NULL; + + return script->v.binary_get_prefix(script); +} + +/* + * Management + */ + +static int +sieve_script_copy_from_default(struct sieve_script *script, const char *newname) +{ + struct sieve_storage *storage = script->storage; + struct istream *input; + int ret; + + /* copy from default */ + if ((ret = sieve_script_open(script, NULL)) < 0 || + (ret = sieve_script_get_stream(script, &input, NULL)) < 0) { + sieve_storage_copy_error(storage->default_for, storage); + return ret; + } + + ret = sieve_storage_save_as(storage->default_for, input, newname); + if (ret < 0) { + sieve_storage_copy_error(storage, storage->default_for); + } else if (sieve_script_is_active(script) > 0) { + struct sieve_script *newscript; + enum sieve_error error; + + newscript = sieve_storage_open_script(storage->default_for, + newname, &error); + if (newscript == NULL) { + /* Somehow not actually saved */ + ret = (error == SIEVE_ERROR_NOT_FOUND ? 0 : -1); + } else if (sieve_script_activate(newscript, (time_t)-1) < 0) { + /* Failed to activate; roll back */ + ret = -1; + (void)sieve_script_delete(newscript, TRUE); + } + if (newscript != NULL) + sieve_script_unref(&newscript); + + if (ret < 0) { + e_error(storage->event, + "Failed to implicitly activate script `%s' " + "after rename", newname); + sieve_storage_copy_error(storage->default_for, storage); + } + } + + return ret; +} + +int sieve_script_rename(struct sieve_script *script, const char *newname) +{ + struct sieve_storage *storage = script->storage; + const char *oldname = script->name; + struct event_passthrough *event; + int ret; + + i_assert(newname != NULL); + + /* Check script name */ + if (!sieve_script_name_is_valid(newname)) { + sieve_script_set_error(script, + SIEVE_ERROR_BAD_PARAMS, + "Invalid new Sieve script name `%s'.", + str_sanitize(newname, 80)); + return -1; + } + + i_assert(script->open); // FIXME: auto-open? + + if (storage->default_for == NULL) { + i_assert((storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0); + + /* rename script */ + i_assert(script->v.rename != NULL); + ret = script->v.rename(script, newname); + + /* rename INBOX mailbox attribute */ + if (ret >= 0 && oldname != NULL) { + (void)sieve_storage_sync_script_rename(storage, oldname, + newname); + } + } else if (sieve_storage_check_script(storage->default_for, + newname, NULL) > 0) { + sieve_script_set_error(script, SIEVE_ERROR_EXISTS, + "A sieve script with that name already exists."); + sieve_storage_copy_error(storage->default_for, storage); + ret = -1; + } else { + ret = sieve_script_copy_from_default(script, newname); + } + + event = event_create_passthrough(script->event)-> + clear_field("script_name")-> + add_str("old_script_name", script->name)-> + add_str("new_script_name", newname)-> + set_name("sieve_script_renamed"); + + if (ret >= 0) { + e_debug(event->event(), "Script renamed to `%s'", newname); + } else { + event = event->add_str("error", storage->error); + + e_debug(event->event(), "Failed to rename script: %s", + storage->error); + } + + return ret; +} + +int sieve_script_delete(struct sieve_script *script, bool ignore_active) +{ + struct sieve_storage *storage = script->storage; + bool is_active = FALSE; + int ret = 0; + + i_assert(script->open); // FIXME: auto-open? + + /* Is the requested script active? */ + if (sieve_script_is_active(script) > 0) { + is_active = TRUE; + if (!ignore_active) { + sieve_script_set_error(script, SIEVE_ERROR_ACTIVE, + "Cannot delete the active Sieve script."); + if (storage->default_for != NULL) { + sieve_storage_copy_error(storage->default_for, + storage); + } + return -1; + } + } + + /* Trying to delete the default script? */ + if (storage->is_default) { + /* ignore */ + return 0; + } + + i_assert((script->storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0); + + /* Deactivate it explicity */ + if (ignore_active && is_active) + (void)sieve_storage_deactivate(storage, (time_t)-1); + + i_assert(script->v.delete != NULL); + ret = script->v.delete(script); + + if (ret >= 0) { + struct event_passthrough *e = + event_create_passthrough(script->event)-> + set_name("sieve_script_deleted"); + e_debug(e->event(), "Script deleted"); + + /* unset INBOX mailbox attribute */ + (void)sieve_storage_sync_script_delete(storage, script->name); + } else { + struct event_passthrough *e = + event_create_passthrough(script->event)-> + add_str("error", storage->error)-> + set_name("sieve_script_deleted"); + e_debug(e->event(), "Failed to delete script: %s", + storage->error); + } + return ret; +} + +int sieve_script_is_active(struct sieve_script *script) +{ + struct sieve_storage *storage = script->storage; + + /* Special handling if this is a default script */ + if (storage->default_for != NULL) { + int ret = sieve_storage_active_script_is_default( + storage->default_for); + if (ret < 0) + sieve_storage_copy_error(storage, storage->default_for); + return ret; + } + + if (script->v.is_active == NULL) + return 0; + return script->v.is_active(script); +} + +int sieve_script_activate(struct sieve_script *script, time_t mtime) +{ + struct sieve_storage *storage = script->storage; + int ret = 0; + + i_assert(script->open); // FIXME: auto-open? + + if (storage->default_for == NULL) { + i_assert((storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0); + + i_assert(script->v.activate != NULL); + ret = script->v.activate(script); + + if (ret >= 0) { + struct event_passthrough *e = + event_create_passthrough(script->event)-> + set_name("sieve_script_activated"); + e_debug(e->event(), "Script activated"); + + sieve_storage_set_modified(storage, mtime); + (void)sieve_storage_sync_script_activate(storage); + } else { + struct event_passthrough *e = + event_create_passthrough(script->event)-> + add_str("error", storage->error)-> + set_name("sieve_script_activated"); + e_debug(e->event(), "Failed to activate script: %s", + storage->error); + } + + } else { + /* Activating the default script is equal to deactivating + the storage */ + ret = sieve_storage_deactivate(storage->default_for, + (time_t)-1); + if (ret < 0) + sieve_storage_copy_error(storage, storage->default_for); + } + + return ret; +} + +/* + * Error handling + */ + +void sieve_script_set_error(struct sieve_script *script, enum sieve_error error, + const char *fmt, ...) +{ + struct sieve_storage *storage = script->storage; + va_list va; + + sieve_storage_clear_error(storage); + + if (fmt != NULL) { + va_start(va, fmt); + storage->error = i_strdup_vprintf(fmt, va); + va_end(va); + } + storage->error_code = error; +} + +void sieve_script_set_internal_error(struct sieve_script *script) +{ + sieve_storage_set_internal_error(script->storage); +} + +void sieve_script_set_critical(struct sieve_script *script, + const char *fmt, ...) +{ + struct sieve_storage *storage = script->storage; + + va_list va; + + if (fmt != NULL) { + if ((storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0) { + va_start(va, fmt); + e_error(script->event, "%s", t_strdup_vprintf(fmt, va)); + va_end(va); + + sieve_storage_set_internal_error(storage); + + } else { + sieve_storage_clear_error(storage); + + /* no user is involved while synchronizing, so do it the + normal way */ + va_start(va, fmt); + storage->error = i_strdup_vprintf(fmt, va); + va_end(va); + + storage->error_code = SIEVE_ERROR_TEMP_FAILURE; + } + } +} + +const char * +sieve_script_get_last_error(struct sieve_script *script, + enum sieve_error *error_r) +{ + return sieve_storage_get_last_error(script->storage, error_r); +} + +const char *sieve_script_get_last_error_lcase(struct sieve_script *script) +{ + return sieve_error_from_external(script->storage->error); +} + +/* + * Script sequence + */ + +void sieve_script_sequence_init(struct sieve_script_sequence *seq, + struct sieve_storage *storage) +{ + seq->storage = storage; + sieve_storage_ref(storage); +} + +struct sieve_script_sequence * +sieve_script_sequence_create(struct sieve_instance *svinst, + const char *location, enum sieve_error *error_r) +{ + struct sieve_storage *storage; + struct sieve_script_sequence *seq; + enum sieve_error error; + + if (error_r != NULL) + *error_r = SIEVE_ERROR_NONE; + else + error_r = &error; + + storage = sieve_storage_create(svinst, location, 0, error_r); + if (storage == NULL) + return NULL; + + seq = sieve_storage_get_script_sequence(storage, error_r); + + sieve_storage_unref(&storage); + return seq; +} + +struct sieve_script * +sieve_script_sequence_next(struct sieve_script_sequence *seq, + enum sieve_error *error_r) +{ + struct sieve_storage *storage = seq->storage; + + i_assert(storage->v.script_sequence_next != NULL); + return storage->v.script_sequence_next(seq, error_r); +} + +void sieve_script_sequence_free(struct sieve_script_sequence **_seq) +{ + struct sieve_script_sequence *seq = *_seq; + struct sieve_storage *storage = seq->storage; + + if (storage->v.script_sequence_destroy != NULL) + storage->v.script_sequence_destroy(seq); + + sieve_storage_unref(&storage); + *_seq = NULL; +} + diff --git a/pigeonhole/src/lib-sieve/sieve-script.h b/pigeonhole/src/lib-sieve/sieve-script.h new file mode 100644 index 0000000..8ee53f4 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-script.h @@ -0,0 +1,163 @@ +#ifndef SIEVE_SCRIPT_H +#define SIEVE_SCRIPT_H + +#include "sieve-common.h" + +#include <sys/types.h> + + +/* + * Sieve script name + */ + +bool sieve_script_name_is_valid(const char *scriptname); + +/* + * Sieve script file + */ + +bool sieve_script_file_has_extension(const char *filename); + +/* + * Sieve script class + */ + +void sieve_script_class_register(struct sieve_instance *svinst, + const struct sieve_script *script_class); +void sieve_script_class_unregister(struct sieve_instance *svinst, + const struct sieve_script *script_class); + +/* + * Sieve script instance + */ + +struct sieve_script; + +ARRAY_DEFINE_TYPE(sieve_script, struct sieve_script *); + +struct sieve_script * +sieve_script_create(struct sieve_instance *svinst, const char *location, + const char *name, enum sieve_error *error_r) ATTR_NULL(3,4); + +void sieve_script_ref(struct sieve_script *script); +void sieve_script_unref(struct sieve_script **script); + +int sieve_script_open(struct sieve_script *script, enum sieve_error *error_r) + ATTR_NULL(2); +int sieve_script_open_as(struct sieve_script *script, const char *name, + enum sieve_error *error_r) ATTR_NULL(3); + +struct sieve_script * +sieve_script_create_open(struct sieve_instance *svinst, const char *location, + const char *name, enum sieve_error *error_r) + ATTR_NULL(3, 4); +int sieve_script_check(struct sieve_instance *svinst, const char *location, + const char *name, enum sieve_error *error_r) + ATTR_NULL(3, 4); + +/* + * Data script + */ + +struct sieve_script * +sieve_data_script_create_from_input(struct sieve_instance *svinst, + const char *name, struct istream *input); + +/* + * Binary + */ + +int sieve_script_binary_read_metadata(struct sieve_script *script, + struct sieve_binary_block *sblock, + sieve_size_t *offset); +void sieve_script_binary_write_metadata(struct sieve_script *script, + struct sieve_binary_block *sblock); +bool sieve_script_binary_dump_metadata(struct sieve_script *script, + struct sieve_dumptime_env *denv, + struct sieve_binary_block *sblock, + sieve_size_t *offset) ATTR_NULL(1); + +struct sieve_binary * +sieve_script_binary_load(struct sieve_script *script, + enum sieve_error *error_r); +int sieve_script_binary_save(struct sieve_script *script, + struct sieve_binary *sbin, bool update, + enum sieve_error *error_r) ATTR_NULL(4); + +const char *sieve_script_binary_get_prefix(struct sieve_script *script); + +/* + * Stream management + */ + +int sieve_script_get_stream(struct sieve_script *script, + struct istream **stream_r, + enum sieve_error *error_r) ATTR_NULL(3); + +/* + * Management + */ + +// FIXME: check read/write flag! + +int sieve_script_rename(struct sieve_script *script, const char *newname); +int sieve_script_is_active(struct sieve_script *script); +int sieve_script_activate(struct sieve_script *script, time_t mtime); +int sieve_script_delete(struct sieve_script *script, bool ignore_active); + +/* + * Properties + */ + +const char *sieve_script_name(const struct sieve_script *script) ATTR_PURE; +const char *sieve_script_location(const struct sieve_script *script) ATTR_PURE; +struct sieve_instance * +sieve_script_svinst(const struct sieve_script *script) ATTR_PURE; + +int sieve_script_get_size(struct sieve_script *script, uoff_t *size_r); +bool sieve_script_is_open(const struct sieve_script *script) ATTR_PURE; +bool sieve_script_is_default(const struct sieve_script *script) ATTR_PURE; + +const char * +sieve_file_script_get_dirpath(const struct sieve_script *script) ATTR_PURE; +const char * +sieve_file_script_get_path(const struct sieve_script *script) ATTR_PURE; + +/* + * Comparison + */ + +bool sieve_script_equals(const struct sieve_script *script, + const struct sieve_script *other); + +unsigned int sieve_script_hash(const struct sieve_script *script); +static inline int +sieve_script_cmp(const struct sieve_script *script, + const struct sieve_script *other) +{ + return ( sieve_script_equals(script, other) ? 0 : -1 ); +} + +/* + * Error handling + */ + +const char *sieve_script_get_last_error(struct sieve_script *script, + enum sieve_error *error_r) ATTR_NULL(2); +const char *sieve_script_get_last_error_lcase(struct sieve_script *script); + +/* + * Script sequence + */ + +struct sieve_script_sequence; + +struct sieve_script_sequence * +sieve_script_sequence_create(struct sieve_instance *svinst, + const char *location, enum sieve_error *error_r); +struct sieve_script * +sieve_script_sequence_next(struct sieve_script_sequence *seq, + enum sieve_error *error_r); +void sieve_script_sequence_free(struct sieve_script_sequence **_seq); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-settings.c b/pigeonhole/src/lib-sieve/sieve-settings.c new file mode 100644 index 0000000..47f70da --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-settings.c @@ -0,0 +1,270 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" + +#include "sieve-common.h" +#include "sieve-limits.h" +#include "sieve-error.h" +#include "sieve-address.h" +#include "sieve-address-source.h" +#include "sieve-settings.h" + +#include <ctype.h> + +/* + * Access to settings + */ + +bool sieve_setting_get_uint_value(struct sieve_instance *svinst, + const char *setting, + unsigned long long int *value_r) +{ + const char *str_value; + + str_value = sieve_setting_get(svinst, setting); + + if (str_value == NULL || *str_value == '\0') + return FALSE; + + if (str_to_ullong(str_value, value_r) < 0) { + e_warning(svinst->event, + "invalid unsigned integer value for setting '%s': " + "'%s'", setting, str_value); + return FALSE; + } + return TRUE; +} + +bool sieve_setting_get_int_value(struct sieve_instance *svinst, + const char *setting, long long int *value_r) +{ + const char *str_value; + + str_value = sieve_setting_get(svinst, setting); + if (str_value == NULL || *str_value == '\0') + return FALSE; + + if (str_to_llong(str_value, value_r) < 0) { + e_warning(svinst->event, + "invalid integer value for setting '%s': '%s'", + setting, str_value); + return FALSE; + } + return TRUE; +} + +bool sieve_setting_get_size_value(struct sieve_instance *svinst, + const char *setting, size_t *value_r) +{ + const char *str_value; + uintmax_t value, multiply = 1; + const char *endp; + + str_value = sieve_setting_get(svinst, setting); + if (str_value == NULL || *str_value == '\0') + return FALSE; + + if (str_parse_uintmax(str_value, &value, &endp) < 0) { + e_warning(svinst->event, + "invalid size value for setting '%s': '%s'", + setting, str_value); + return FALSE; + } + switch (i_toupper(*endp)) { + case '\0': /* default */ + case 'B': /* byte (useless) */ + multiply = 1; + break; + case 'K': /* kilobyte */ + multiply = 1024; + break; + case 'M': /* megabyte */ + multiply = 1024*1024; + break; + case 'G': /* gigabyte */ + multiply = 1024*1024*1024; + break; + case 'T': /* terabyte */ + multiply = 1024ULL*1024*1024*1024; + break; + default: + e_warning(svinst->event, + "invalid size value for setting '%s': '%s'", + setting, str_value); + return FALSE; + } + + if (value > SSIZE_T_MAX / multiply) { + e_warning(svinst->event, + "overflowing size value for setting '%s': '%s'", + setting, str_value); + return FALSE; + } + + *value_r = (size_t)(value * multiply); + return TRUE; +} + +bool sieve_setting_get_bool_value(struct sieve_instance *svinst, + const char *setting, bool *value_r) +{ + const char *str_value; + + str_value = sieve_setting_get(svinst, setting); + if (str_value == NULL) + return FALSE; + + str_value = t_str_trim(str_value, "\t "); + if (*str_value == '\0') + return FALSE; + + if (strcasecmp(str_value, "yes") == 0) { + *value_r = TRUE; + return TRUE; + } + + if (strcasecmp(str_value, "no") == 0) { + *value_r = FALSE; + return TRUE; + } + + e_warning(svinst->event, + "invalid boolean value for setting '%s': '%s'", + setting, str_value); + return FALSE; +} + +bool sieve_setting_get_duration_value(struct sieve_instance *svinst, + const char *setting, + sieve_number_t *value_r) +{ + const char *str_value; + uintmax_t value, multiply = 1; + const char *endp; + + str_value = sieve_setting_get(svinst, setting); + if (str_value == NULL) + return FALSE; + + str_value = t_str_trim(str_value, "\t "); + if (*str_value == '\0') + return FALSE; + + if (str_parse_uintmax(str_value, &value, &endp) < 0) { + e_warning(svinst->event, + "invalid duration value for setting '%s': '%s'", + setting, str_value); + return FALSE; + } + + switch (i_tolower(*endp)) { + case '\0': /* default */ + case 's': /* seconds */ + multiply = 1; + break; + case 'm': /* minutes */ + multiply = 60; + break; + case 'h': /* hours */ + multiply = 60*60; + break; + case 'd': /* days */ + multiply = 24*60*60; + break; + default: + e_warning(svinst->event, + "invalid duration value for setting '%s': '%s'", + setting, str_value); + return FALSE; + } + + if (value > SIEVE_MAX_NUMBER / multiply) { + e_warning(svinst->event, + "overflowing duration value for setting '%s': '%s'", + setting, str_value); + return FALSE; + } + + *value_r = (unsigned int)(value * multiply); + return TRUE; +} + +/* + * Main Sieve engine settings + */ + +void sieve_settings_load(struct sieve_instance *svinst) +{ + const char *str_setting, *error; + unsigned long long int uint_setting; + size_t size_setting; + sieve_number_t period; + + svinst->max_script_size = SIEVE_DEFAULT_MAX_SCRIPT_SIZE; + if (sieve_setting_get_size_value(svinst, "sieve_max_script_size", + &size_setting)) + svinst->max_script_size = size_setting; + + svinst->max_actions = SIEVE_DEFAULT_MAX_ACTIONS; + if (sieve_setting_get_uint_value(svinst, "sieve_max_actions", + &uint_setting)) + svinst->max_actions = (unsigned int)uint_setting; + + svinst->max_redirects = SIEVE_DEFAULT_MAX_REDIRECTS; + if (sieve_setting_get_uint_value(svinst, "sieve_max_redirects", + &uint_setting)) + svinst->max_redirects = (unsigned int)uint_setting; + + svinst->max_cpu_time_secs = + (svinst->env_location == SIEVE_ENV_LOCATION_MS ? + 0 : SIEVE_DEFAULT_MAX_CPU_TIME_SECS); + if (sieve_setting_get_duration_value(svinst, "sieve_max_cpu_time", + &period)) { + if (period > (UINT_MAX / 1000)) + svinst->max_cpu_time_secs = (UINT_MAX / 1000); + else + svinst->max_cpu_time_secs = (unsigned int)period; + } + svinst->resource_usage_timeout_secs = + SIEVE_DEFAULT_RESOURCE_USAGE_TIMEOUT_SECS; + if (sieve_setting_get_duration_value( + svinst, "sieve_resource_usage_timeout", &period)) { + if (period > UINT_MAX) + svinst->resource_usage_timeout_secs = UINT_MAX; + else { + svinst->resource_usage_timeout_secs = + (unsigned int)period; + } + } + + (void)sieve_address_source_parse_from_setting( + svinst, svinst->pool, "sieve_redirect_envelope_from", + &svinst->redirect_from); + + svinst->redirect_duplicate_period = DEFAULT_REDIRECT_DUPLICATE_PERIOD; + if (sieve_setting_get_duration_value( + svinst, "sieve_redirect_duplicate_period", &period)) { + if (period > UINT_MAX) + svinst->redirect_duplicate_period = UINT_MAX; + else { + svinst->redirect_duplicate_period = + (unsigned int)period; + } + } + + str_setting = sieve_setting_get(svinst, "sieve_user_email"); + if (str_setting != NULL && *str_setting != '\0') { + struct smtp_address *address; + if (smtp_address_parse_path( + svinst->pool, str_setting, + SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL, + &address, &error) < 0) { + e_warning(svinst->event, + "Invalid address value for setting " + "`sieve_user_email': %s", error); + } else { + svinst->user_email = address; + } + } +} diff --git a/pigeonhole/src/lib-sieve/sieve-settings.h b/pigeonhole/src/lib-sieve/sieve-settings.h new file mode 100644 index 0000000..f2dfca2 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-settings.h @@ -0,0 +1,57 @@ +#ifndef SIEVE_SETTINGS_H +#define SIEVE_SETTINGS_H + +#include "sieve-common.h" + +/* + * Access to settings + */ + +static inline const char * +sieve_setting_get(struct sieve_instance *svinst, const char *identifier) +{ + const struct sieve_callbacks *callbacks = svinst->callbacks; + + if (callbacks == NULL || callbacks->get_setting == NULL) + return NULL; + + return callbacks->get_setting(svinst->context, identifier); +} + +bool sieve_setting_get_uint_value(struct sieve_instance *svinst, + const char *setting, + unsigned long long int *value_r); +bool sieve_setting_get_int_value(struct sieve_instance *svinst, + const char *setting, long long int *value_r); +bool sieve_setting_get_size_value(struct sieve_instance *svinst, + const char *setting, size_t *value_r); +bool sieve_setting_get_bool_value(struct sieve_instance *svinst, + const char *setting, bool *value_r); +bool sieve_setting_get_duration_value(struct sieve_instance *svinst, + const char *setting, + sieve_number_t *value_r); + +/* + * Main Sieve engine settings + */ + +void sieve_settings_load(struct sieve_instance *svinst); + +/* + * Home directory + */ + +static inline const char * +sieve_environment_get_homedir(struct sieve_instance *svinst) +{ + const struct sieve_callbacks *callbacks = svinst->callbacks; + + if (svinst->home_dir != NULL) + return svinst->home_dir; + if (callbacks == NULL || callbacks->get_homedir == NULL) + return NULL; + + return callbacks->get_homedir(svinst->context); +} + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-smtp.c b/pigeonhole/src/lib-sieve/sieve-smtp.c new file mode 100644 index 0000000..40b40ea --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-smtp.c @@ -0,0 +1,95 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ +#include "lib.h" +#include "smtp-address.h" + +#include "sieve-common.h" +#include "sieve-smtp.h" + +struct sieve_smtp_context { + const struct sieve_script_env *senv; + void *handle; + + bool sent:1; +}; + +bool sieve_smtp_available +(const struct sieve_script_env *senv) +{ + return ( senv->smtp_start != NULL && senv->smtp_add_rcpt != NULL && + senv->smtp_send != NULL && senv->smtp_finish != NULL ); +} + +struct sieve_smtp_context *sieve_smtp_start +(const struct sieve_script_env *senv, + const struct smtp_address *mail_from) +{ + struct sieve_smtp_context *sctx; + void *handle; + + if ( !sieve_smtp_available(senv) ) + return NULL; + + handle = senv->smtp_start(senv, mail_from); + i_assert( handle != NULL ); + + sctx = i_new(struct sieve_smtp_context, 1); + sctx->senv = senv; + sctx->handle = handle; + + return sctx; +} + +void sieve_smtp_add_rcpt +(struct sieve_smtp_context *sctx, + const struct smtp_address *rcpt_to) +{ + i_assert(!sctx->sent); + sctx->senv->smtp_add_rcpt(sctx->senv, sctx->handle, rcpt_to); +} + +struct ostream *sieve_smtp_send +(struct sieve_smtp_context *sctx) +{ + i_assert(!sctx->sent); + sctx->sent = TRUE; + + return sctx->senv->smtp_send(sctx->senv, sctx->handle); +} + +struct sieve_smtp_context *sieve_smtp_start_single +(const struct sieve_script_env *senv, + const struct smtp_address *rcpt_to, + const struct smtp_address *mail_from, + struct ostream **output_r) +{ + struct sieve_smtp_context *sctx; + + sctx = sieve_smtp_start(senv, mail_from); + sieve_smtp_add_rcpt(sctx, rcpt_to); + *output_r = sieve_smtp_send(sctx); + + return sctx; +} + +void sieve_smtp_abort +(struct sieve_smtp_context *sctx) +{ + const struct sieve_script_env *senv = sctx->senv; + void *handle = sctx->handle; + + i_free(sctx); + i_assert(senv->smtp_abort != NULL); + senv->smtp_abort(senv, handle); +} + +int sieve_smtp_finish +(struct sieve_smtp_context *sctx, const char **error_r) +{ + const struct sieve_script_env *senv = sctx->senv; + void *handle = sctx->handle; + + i_free(sctx); + return senv->smtp_finish(senv, handle, error_r); +} + diff --git a/pigeonhole/src/lib-sieve/sieve-smtp.h b/pigeonhole/src/lib-sieve/sieve-smtp.h new file mode 100644 index 0000000..f7b9c84 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-smtp.h @@ -0,0 +1,31 @@ +#ifndef SIEVE_SMTP_H +#define SIEVE_SMTP_H + +#include "sieve-common.h" + +bool sieve_smtp_available + (const struct sieve_script_env *senv); + +struct sieve_smtp_context; + +struct sieve_smtp_context *sieve_smtp_start + (const struct sieve_script_env *senv, + const struct smtp_address *mail_from); +void sieve_smtp_add_rcpt + (struct sieve_smtp_context *sctx, + const struct smtp_address *rcpt_to); +struct ostream *sieve_smtp_send + (struct sieve_smtp_context *sctx); + +struct sieve_smtp_context *sieve_smtp_start_single + (const struct sieve_script_env *senv, + const struct smtp_address *rcpt_to, + const struct smtp_address *mail_from, + struct ostream **output_r); + +void sieve_smtp_abort + (struct sieve_smtp_context *sctx); +int sieve_smtp_finish + (struct sieve_smtp_context *sctx, const char **error_r); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-storage-private.h b/pigeonhole/src/lib-sieve/sieve-storage-private.h new file mode 100644 index 0000000..16af3d4 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-storage-private.h @@ -0,0 +1,256 @@ +#ifndef SIEVE_STORAGE_PRIVATE_H +#define SIEVE_STORAGE_PRIVATE_H + +#include "sieve.h" +#include "sieve-error-private.h" + +#include "sieve-storage.h" + +#define MAILBOX_ATTRIBUTE_PREFIX_SIEVE \ + MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER"sieve/" +#define MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES \ + MAILBOX_ATTRIBUTE_PREFIX_SIEVE"files/" +#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT \ + MAILBOX_ATTRIBUTE_PREFIX_SIEVE"default" + +#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_LINK 'L' +#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_SCRIPT 'S' + +struct sieve_storage; + +ARRAY_DEFINE_TYPE(sieve_storage_class, const struct sieve_storage *); + +struct sieve_storage_vfuncs { + struct sieve_storage *(*alloc)(void); + void (*destroy)(struct sieve_storage *storage); + int (*init)(struct sieve_storage *storage, const char *const *options, + enum sieve_error *error_r); + + int (*get_last_change)(struct sieve_storage *storage, + time_t *last_change_r); + void (*set_modified)(struct sieve_storage *storage, time_t mtime); + + int (*is_singular)(struct sieve_storage *storage); + + /* script access */ + struct sieve_script *(*get_script)(struct sieve_storage *storage, + const char *name); + + /* script sequence */ + struct sieve_script_sequence *(*get_script_sequence)( + struct sieve_storage *storage, enum sieve_error *error_r); + struct sieve_script *(*script_sequence_next)( + struct sieve_script_sequence *seq, enum sieve_error *error_r); + void (*script_sequence_destroy)(struct sieve_script_sequence *seq); + + /* active script */ + int (*active_script_get_name)(struct sieve_storage *storage, + const char **name_r); + struct sieve_script *(*active_script_open)( + struct sieve_storage *storage); + int (*deactivate)(struct sieve_storage *storage); + int (*active_script_get_last_change)(struct sieve_storage *storage, + time_t *last_change_r); + + /* listing scripts */ + struct sieve_storage_list_context *(*list_init)( + struct sieve_storage *storage); + const char *(*list_next)(struct sieve_storage_list_context *lctx, + bool *active_r); + int (*list_deinit)(struct sieve_storage_list_context *lctx); + + /* saving scripts */ + // FIXME: simplify this API; reduce this mostly to a single save function + struct sieve_storage_save_context *(*save_alloc)( + struct sieve_storage *storage); + int (*save_init)(struct sieve_storage_save_context *sctx, + const char *scriptname, struct istream *input); + int (*save_continue)(struct sieve_storage_save_context *sctx); + int (*save_finish)(struct sieve_storage_save_context *sctx); + struct sieve_script *(*save_get_tempscript)( + struct sieve_storage_save_context *sctx); + void (*save_cancel)(struct sieve_storage_save_context *sctx); + int (*save_commit)(struct sieve_storage_save_context *sctx); + int (*save_as)(struct sieve_storage *storage, struct istream *input, + const char *name); + int (*save_as_active)(struct sieve_storage *storage, + struct istream *input, time_t mtime); + + /* checking quota */ + int (*quota_havespace)(struct sieve_storage *storage, + const char *scriptname, size_t size, + enum sieve_storage_quota *quota_r, + uint64_t *limit_r); +}; + +struct sieve_storage { + pool_t pool; + unsigned int refcount; + struct sieve_instance *svinst; + struct event *event; + + const char *driver_name; + unsigned int version; + + const struct sieve_storage *storage_class; + struct sieve_storage_vfuncs v; + + uint64_t max_scripts; + uint64_t max_storage; + + char *error; + enum sieve_error error_code; + + const char *data; + const char *location; + const char *script_name; + const char *bin_dir; + + const char *default_name; + const char *default_location; + struct sieve_storage *default_for; + + struct mail_namespace *sync_inbox_ns; + + enum sieve_storage_flags flags; + + /* this is the main personal storage */ + bool main_storage:1; + bool allows_synchronization:1; + bool is_default:1; +}; + +struct event * +sieve_storage_event_create(struct sieve_instance *svinst, + const struct sieve_storage *storage_class); +struct sieve_storage * +sieve_storage_alloc(struct sieve_instance *svinst, struct event *event, + const struct sieve_storage *storage_class, const char *data, + enum sieve_storage_flags flags, bool main) ATTR_NULL(2, 4); + +int sieve_storage_setup_bindir(struct sieve_storage *storage, mode_t mode); + +/* + * Active script + */ + +int sieve_storage_active_script_is_default(struct sieve_storage *storage); + +/* + * Listing scripts + */ + +struct sieve_storage_list_context { + struct sieve_storage *storage; + + bool seen_active:1; // Just present for assertions + bool seen_default:1; +}; + +/* + * Script sequence + */ + +struct sieve_script_sequence { + struct sieve_storage *storage; +}; + +/* + * Saving scripts + */ + +struct sieve_storage_save_context { + pool_t pool; + struct sieve_storage *storage; + struct event *event; + + const char *scriptname, *active_scriptname; + struct sieve_script *scriptobject; + + struct istream *input; + + time_t mtime; + + bool failed:1; + bool finished:1; +}; + +/* + * Storage class + */ + +struct sieve_storage_class_registry; + +void sieve_storages_init(struct sieve_instance *svinst); +void sieve_storages_deinit(struct sieve_instance *svinst); + +void sieve_storage_class_register(struct sieve_instance *svinst, + const struct sieve_storage *storage_class); +void sieve_storage_class_unregister(struct sieve_instance *svinst, + const struct sieve_storage *storage_class); +const struct sieve_storage * +sieve_storage_find_class(struct sieve_instance *svinst, const char *name); + +/* + * Built-in storage drivers + */ + +/* data (currently only for internal use) */ + +#define SIEVE_DATA_STORAGE_DRIVER_NAME "data" + +extern const struct sieve_storage sieve_data_storage; + +/* file */ + +#define SIEVE_FILE_STORAGE_DRIVER_NAME "file" + +extern const struct sieve_storage sieve_file_storage; + +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) ATTR_NULL(6); + +/* dict */ + +#define SIEVE_DICT_STORAGE_DRIVER_NAME "dict" + +extern const struct sieve_storage sieve_dict_storage; + +/* ldap */ + +#define SIEVE_LDAP_STORAGE_DRIVER_NAME "ldap" + +extern const struct sieve_storage sieve_ldap_storage; + +/* + * Error handling + */ + +void sieve_storage_set_internal_error(struct sieve_storage *storage); + +void sieve_storage_copy_error(struct sieve_storage *storage, + const struct sieve_storage *source); + +/* + * Synchronization + */ + +int sieve_storage_sync_init(struct sieve_storage *storage, + struct mail_user *user); +void sieve_storage_sync_deinit(struct sieve_storage *storage); + +int sieve_storage_sync_script_save(struct sieve_storage *storage, + const char *name); +int sieve_storage_sync_script_rename(struct sieve_storage *storage, + const char *oldname, const char *newname); +int sieve_storage_sync_script_delete(struct sieve_storage *storage, + const char *name); + +int sieve_storage_sync_script_activate(struct sieve_storage *storage); +int sieve_storage_sync_deactivate(struct sieve_storage *storage); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-storage-sync.c b/pigeonhole/src/lib-sieve/sieve-storage-sync.c new file mode 100644 index 0000000..b1abfcc --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-storage-sync.c @@ -0,0 +1,195 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" +#include "str-sanitize.h" +#include "home-expand.h" +#include "eacces-error.h" +#include "mkdir-parents.h" +#include "ioloop.h" +#include "mail-storage-private.h" + +#include "sieve-common.h" +#include "sieve-settings.h" +#include "sieve-error-private.h" + +#include "sieve-script-private.h" +#include "sieve-storage-private.h" + +/* + * Synchronization + */ + +int sieve_storage_sync_init +(struct sieve_storage *storage, struct mail_user *user) +{ + enum sieve_storage_flags sflags = storage->flags; + + if ( (sflags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 && + (sflags & SIEVE_STORAGE_FLAG_READWRITE) == 0 ) + return 0; + + if ( !storage->allows_synchronization ) { + if ( (sflags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) != 0 ) + return -1; + return 0; + } + + e_debug(storage->event, "sync: Synchronization active"); + + storage->sync_inbox_ns = mail_namespace_find_inbox(user->namespaces); + return 0; +} + +void sieve_storage_sync_deinit +(struct sieve_storage *storage ATTR_UNUSED) +{ + /* nothing */ +} + +/* + * Sync attributes + */ + +static int sieve_storage_sync_transaction_begin +(struct sieve_storage *storage, struct mailbox_transaction_context **trans_r) +{ + enum mailbox_flags mflags = MAILBOX_FLAG_IGNORE_ACLS; + struct mail_namespace *ns = storage->sync_inbox_ns; + struct mailbox *inbox; + enum mail_error error; + + if (ns == NULL) + return 0; + + inbox = mailbox_alloc(ns->list, "INBOX", mflags); + if (mailbox_open(inbox) < 0) { + e_warning(storage->event, "sync: " + "Failed to open user INBOX for attribute modifications: %s", + mailbox_get_last_internal_error(inbox, &error)); + mailbox_free(&inbox); + return -1; + } + + *trans_r = mailbox_transaction_begin(inbox, + MAILBOX_TRANSACTION_FLAG_EXTERNAL, + __func__); + return 1; +} + +static int sieve_storage_sync_transaction_finish +(struct sieve_storage *storage, struct mailbox_transaction_context **trans) +{ + struct mailbox *inbox; + int ret; + + inbox = mailbox_transaction_get_mailbox(*trans); + + if ((ret=mailbox_transaction_commit(trans)) < 0) { + enum mail_error error; + + e_warning(storage->event, "sync: " + "Failed to update INBOX attributes: %s", + mail_storage_get_last_error( + mailbox_get_storage(inbox), &error)); + } + + mailbox_free(&inbox); + return ret; +} + +int sieve_storage_sync_script_save +(struct sieve_storage *storage, const char *name) +{ + struct mailbox_transaction_context *trans; + const char *key; + int ret; + + if ((ret=sieve_storage_sync_transaction_begin + (storage, &trans)) <= 0) + return ret; + + key = t_strconcat + (MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, name, NULL); + + mail_index_attribute_set(trans->itrans, TRUE, key, ioloop_time, 0); + + return sieve_storage_sync_transaction_finish(storage, &trans); +} + +int sieve_storage_sync_script_rename +(struct sieve_storage *storage, const char *oldname, + const char *newname) +{ + struct mailbox_transaction_context *trans; + const char *oldkey, *newkey; + int ret; + + if ((ret=sieve_storage_sync_transaction_begin + (storage, &trans)) <= 0) + return ret; + + oldkey = t_strconcat + (MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, oldname, NULL); + newkey = t_strconcat + (MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, newname, NULL); + + mail_index_attribute_unset(trans->itrans, TRUE, oldkey, ioloop_time); + mail_index_attribute_set(trans->itrans, TRUE, newkey, ioloop_time, 0); + + return sieve_storage_sync_transaction_finish(storage, &trans); +} + +int sieve_storage_sync_script_delete +(struct sieve_storage *storage, const char *name) +{ + struct mailbox_transaction_context *trans; + const char *key; + int ret; + + if ((ret=sieve_storage_sync_transaction_begin + (storage, &trans)) <= 0) + return ret; + + key = t_strconcat + (MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, name, NULL); + + mail_index_attribute_unset(trans->itrans, TRUE, key, ioloop_time); + + return sieve_storage_sync_transaction_finish(storage, &trans); +} + +int sieve_storage_sync_script_activate +(struct sieve_storage *storage) +{ + struct mailbox_transaction_context *trans; + int ret; + + if ((ret=sieve_storage_sync_transaction_begin + (storage, &trans)) <= 0) + return ret; + + mail_index_attribute_set(trans->itrans, + TRUE, MAILBOX_ATTRIBUTE_SIEVE_DEFAULT, ioloop_time, 0); + + return sieve_storage_sync_transaction_finish(storage, &trans); +} + +int sieve_storage_sync_deactivate +(struct sieve_storage *storage) +{ + struct mailbox_transaction_context *trans; + int ret; + + if ((ret=sieve_storage_sync_transaction_begin + (storage, &trans)) <= 0) + return ret; + + mail_index_attribute_unset(trans->itrans, + TRUE, MAILBOX_ATTRIBUTE_SIEVE_DEFAULT, ioloop_time); + + return sieve_storage_sync_transaction_finish(storage, &trans); +} + + diff --git a/pigeonhole/src/lib-sieve/sieve-storage.c b/pigeonhole/src/lib-sieve/sieve-storage.c new file mode 100644 index 0000000..1e2aa34 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-storage.c @@ -0,0 +1,1590 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" +#include "str-sanitize.h" +#include "home-expand.h" +#include "eacces-error.h" +#include "mkdir-parents.h" +#include "ioloop.h" + +#include "sieve-common.h" +#include "sieve-settings.h" +#include "sieve-error-private.h" + +#include "sieve-script-private.h" +#include "sieve-storage-private.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <time.h> +#include <utime.h> + +#define CRITICAL_MSG \ + "Internal error occurred. Refer to server log for more information." +#define CRITICAL_MSG_STAMP CRITICAL_MSG " [%Y-%m-%d %H:%M:%S]" + +struct event_category event_category_sieve_storage = { + .parent = &event_category_sieve, + .name = "sieve-storage", +}; + +/* + * Storage class + */ + +struct sieve_storage_class_registry { + ARRAY_TYPE(sieve_storage_class) storage_classes; +}; + +void sieve_storages_init(struct sieve_instance *svinst) +{ + svinst->storage_reg = p_new(svinst->pool, + struct sieve_storage_class_registry, 1); + p_array_init(&svinst->storage_reg->storage_classes, svinst->pool, 8); + + sieve_storage_class_register(svinst, &sieve_file_storage); + sieve_storage_class_register(svinst, &sieve_dict_storage); + sieve_storage_class_register(svinst, &sieve_ldap_storage); +} + +void sieve_storages_deinit(struct sieve_instance *svinst ATTR_UNUSED) +{ + /* nothing yet */ +} + +void sieve_storage_class_register(struct sieve_instance *svinst, + const struct sieve_storage *storage_class) +{ + struct sieve_storage_class_registry *reg = svinst->storage_reg; + const struct sieve_storage *old_class; + + old_class = sieve_storage_find_class(svinst, + storage_class->driver_name); + if (old_class != NULL) { + if (old_class->v.alloc == NULL) { + /* replacing a "support not compiled in" storage class + */ + sieve_storage_class_unregister(svinst, old_class); + } else { + i_panic("sieve_storage_class_register(%s): " + "Already registered", + storage_class->driver_name); + } + } + + array_append(®->storage_classes, &storage_class, 1); +} + +void sieve_storage_class_unregister(struct sieve_instance *svinst, + const struct sieve_storage *storage_class) +{ + struct sieve_storage_class_registry *reg = svinst->storage_reg; + const struct sieve_storage *const *classes; + unsigned int i, count; + + classes = array_get(®->storage_classes, &count); + for (i = 0; i < count; i++) { + if (classes[i] == storage_class) { + array_delete(®->storage_classes, i, 1); + break; + } + } +} + +const struct sieve_storage * +sieve_storage_find_class(struct sieve_instance *svinst, const char *name) +{ + struct sieve_storage_class_registry *reg = svinst->storage_reg; + const struct sieve_storage *const *classes; + unsigned int i, count; + + i_assert(name != NULL); + + classes = array_get(®->storage_classes, &count); + for (i = 0; i < count; i++) { + if (strcasecmp(classes[i]->driver_name, name) == 0) + return classes[i]; + } + return NULL; +} + +/* + * Storage instance + */ + +static const char *split_next_arg(const char *const **_args) +{ + const char *const *args = *_args; + const char *str = args[0]; + + /* join arguments for escaped ";" separator */ + + args++; + while (*args != NULL && **args == '\0') { + args++; + if (*args == NULL) { + /* string ends with ";", just ignore it. */ + break; + } + str = t_strconcat(str, ";", *args, NULL); + args++; + } + *_args = args; + return str; +} + +static int +sieve_storage_driver_parse(struct sieve_instance *svinst, const char **data, + const struct sieve_storage **driver_r) +{ + const struct sieve_storage *storage_class = NULL; + const char *p; + + p = strchr(*data, ':'); + if (p == NULL) + return 0; + + /* Lookup storage driver */ + T_BEGIN { + const char *driver; + + driver = t_strdup_until(*data, p); + *data = p+1; + + storage_class = sieve_storage_find_class(svinst, driver); + if (storage_class == NULL) { + e_error(svinst->event, + "Unknown storage driver module `%s'", + driver); + } else if (storage_class->v.alloc == NULL) { + e_error(svinst->event, + "Support not compiled in for storage driver `%s'", + driver); + storage_class = NULL; + } + } T_END; + + *driver_r = storage_class; + return (storage_class == NULL ? -1 : 1); +} + +static int +sieve_storage_data_parse(struct sieve_storage *storage, const char *data, + const char **location_r, const char *const **options_r) +{ + ARRAY_TYPE(const_string) options; + const char *const *tmp; + + if (*data == '\0') { + *options_r = NULL; + *location_r = data; + return 0; + } + + /* <location> */ + tmp = t_strsplit(data, ";"); + *location_r = split_next_arg(&tmp); + + if (options_r != NULL) { + t_array_init(&options, 8); + + /* [<option> *(';' <option>)] */ + while (*tmp != NULL) { + const char *option = split_next_arg(&tmp); + + if (strncasecmp(option, "name=", 5) == 0) { + if (option[5] == '\0') { + e_error(storage->event, + "Failed to parse storage location: " + "Empty name not allowed"); + return -1; + } + + if (storage->script_name == NULL) { + if (!sieve_script_name_is_valid(option+5)) { + e_error(storage->event, + "Failed to parse storage location: " + "Invalid script name `%s'.", + str_sanitize(option+5, 80)); + return -1; + } + storage->script_name = p_strdup(storage->pool, option+5); + } + + } else if (strncasecmp(option, "bindir=", 7) == 0) { + const char *bin_dir = option+7; + + if (bin_dir[0] == '\0') { + e_error(storage->event, + "Failed to parse storage location: " + "Empty bindir not allowed"); + return -1; + } + + if (bin_dir[0] == '~') { + /* home-relative path. change to absolute. */ + const char *home = sieve_environment_get_homedir(storage->svinst); + + if (home != NULL) { + bin_dir = home_expand_tilde(bin_dir, home); + } else if (bin_dir[1] == '/' || bin_dir[1] == '\0') { + e_error(storage->event, + "Failed to parse storage location: " + "bindir is relative to home directory (~/), " + "but home directory cannot be determined"); + return -1; + } + } + + storage->bin_dir = p_strdup(storage->pool, bin_dir); + } else { + array_append(&options, &option, 1); + } + } + + (void)array_append_space(&options); + *options_r = array_idx(&options, 0); + } + + return 0; +} + +struct event * +sieve_storage_event_create(struct sieve_instance *svinst, + const struct sieve_storage *storage_class) +{ + struct event *event; + + event = event_create(svinst->event); + event_add_category(event, &event_category_sieve_storage); + event_add_str(event, "driver", storage_class->driver_name); + event_set_append_log_prefix( + event, t_strdup_printf("%s storage: ", + storage_class->driver_name)); + + return event; +} + +struct sieve_storage * +sieve_storage_alloc(struct sieve_instance *svinst, struct event *event, + const struct sieve_storage *storage_class, const char *data, + enum sieve_storage_flags flags, bool main) +{ + struct sieve_storage *storage; + + i_assert(storage_class->v.alloc != NULL); + storage = storage_class->v.alloc(); + + storage->storage_class = storage_class; + storage->refcount = 1; + storage->svinst = svinst; + storage->flags = flags; + storage->data = p_strdup_empty(storage->pool, data); + storage->main_storage = main; + + if (event != NULL) { + storage->event = event; + event_ref(storage->event); + } else { + storage->event = + sieve_storage_event_create(svinst, storage_class); + } + + return storage; +} + +static struct sieve_storage * +sieve_storage_init(struct sieve_instance *svinst, + const struct sieve_storage *storage_class, const char *data, + enum sieve_storage_flags flags, bool main, + enum sieve_error *error_r) +{ + struct sieve_storage *storage; + const char *const *options; + const char *location; + struct event *event; + enum sieve_error error; + + if (error_r != NULL) + *error_r = SIEVE_ERROR_NONE; + else + error_r = &error; + + i_assert(storage_class->v.init != NULL); + + event = sieve_storage_event_create(svinst, storage_class); + + if ((flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) != 0 && + !storage_class->allows_synchronization) { + e_debug(event, "Storage does not support synchronization"); + *error_r = SIEVE_ERROR_NOT_POSSIBLE; + event_unref(&event); + return NULL; + } + + if ((flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 && + storage_class->v.save_init == NULL) { + e_error(event, "Storage does not support write access"); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + event_unref(&event); + return NULL; + } + + T_BEGIN { + storage = sieve_storage_alloc(svinst, event, storage_class, + data, flags, main); + + if (sieve_storage_data_parse(storage, data, + &location, &options) < 0) { + *error_r = SIEVE_ERROR_TEMP_FAILURE; + sieve_storage_unref(&storage); + storage = NULL; + } else { + storage->location = p_strdup(storage->pool, location); + + event_add_str(event, "script_location", + storage->location); + + if (storage_class->v.init(storage, options, + error_r) < 0) { + sieve_storage_unref(&storage); + storage = NULL; + } + } + } T_END; + + event_unref(&event); + return storage; +} + +struct sieve_storage * +sieve_storage_create(struct sieve_instance *svinst, const char *location, + enum sieve_storage_flags flags, enum sieve_error *error_r) +{ + const struct sieve_storage *storage_class; + enum sieve_error error; + const char *data; + int ret; + + /* Dont use this function for creating a synchronizing storage */ + i_assert((flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0); + + if (error_r != NULL) + *error_r = SIEVE_ERROR_NONE; + else + error_r = &error; + + data = location; + if ((ret = sieve_storage_driver_parse(svinst, &data, + &storage_class)) < 0) { + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return NULL; + } + + if (ret == 0) + storage_class = &sieve_file_storage; + + return sieve_storage_init(svinst, storage_class, data, flags, + FALSE, error_r); +} + +static struct sieve_storage * +sieve_storage_do_create_main(struct sieve_instance *svinst, + struct mail_user *user, + enum sieve_storage_flags flags, + enum sieve_error *error_r) +{ + struct sieve_storage *storage = NULL; + const struct sieve_storage + *sieve_class = NULL, + *sieve_dir_class = NULL; + const char *set_sieve, *set_sieve_dir; + const char *data, *storage_path; + unsigned long long int uint_setting; + size_t size_setting; + int ret; + + /* Sieve storage location */ + + set_sieve = sieve_setting_get(svinst, "sieve"); + + if (set_sieve != NULL) { + if (*set_sieve == '\0') { + /* disabled */ + e_debug(svinst->event, "storage: " + "Personal storage is disabled (sieve=\"\")"); + *error_r = SIEVE_ERROR_NOT_FOUND; + return NULL; + } + + data = set_sieve; + if ((ret = sieve_storage_driver_parse(svinst, &data, + &sieve_class)) < 0) { + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return NULL; + } + + if (ret > 0) { + /* The normal case: explicit driver name */ + storage = sieve_storage_init(svinst, sieve_class, data, + flags, TRUE, error_r); + if (storage == NULL) + return NULL; + } + + /* No driver name */ + } + + if (storage == NULL) { + /* Script storage directory configuration (deprecated) */ + + set_sieve_dir = sieve_setting_get(svinst, "sieve_dir"); + if (set_sieve_dir == NULL) { + set_sieve_dir = sieve_setting_get(svinst, + "sieve_storage"); + } + + if (set_sieve_dir == NULL || *set_sieve_dir == '\0') { + storage_path = ""; + } else { + const char *p; + + /* Parse and check driver */ + storage_path = set_sieve_dir; + if ((ret = sieve_storage_driver_parse( + svinst, &storage_path, &sieve_dir_class)) < 0) { + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return NULL; + } + + if (ret > 0 && sieve_dir_class != &sieve_file_storage) { + e_error(svinst->event, "storage: " + "Cannot use deprecated sieve_dir= setting " + "with `%s' driver for main Sieve storage", + sieve_dir_class->driver_name); + } + + /* Ignore any options */ + p = strchr(storage_path, ';'); + if (p != NULL) + storage_path = t_strdup_until(storage_path, p); + } + + storage = sieve_file_storage_init_legacy(svinst, set_sieve, + storage_path, flags, + error_r); + } + + if (storage == NULL) + return NULL; + + (void)sieve_storage_sync_init(storage, user); + + /* Get quota settings if storage driver provides none */ + + if (storage->max_storage == 0 && + sieve_setting_get_size_value(svinst, "sieve_quota_max_storage", + &size_setting)) { + storage->max_storage = size_setting; + } + + if (storage->max_scripts == 0 && + sieve_setting_get_uint_value(svinst, "sieve_quota_max_scripts", + &uint_setting)) { + storage->max_scripts = uint_setting; + } + + if (storage->max_storage > 0) { + e_debug(storage->event, "quota: " + "Storage limit: %llu bytes", + (unsigned long long int) storage->max_storage); + } + if (storage->max_scripts > 0) { + e_debug(storage->event, "quota: " + "Script count limit: %llu scripts", + (unsigned long long int) storage->max_scripts); + } + return storage; +} + +struct sieve_storage * +sieve_storage_create_main(struct sieve_instance *svinst, struct mail_user *user, + enum sieve_storage_flags flags, + enum sieve_error *error_r) +{ + struct sieve_storage *storage; + const char *set_enabled, *set_default, *set_default_name; + enum sieve_error error; + + if (error_r != NULL) + *error_r = SIEVE_ERROR_NONE; + else + error_r = &error; + + /* Check whether Sieve is disabled for this user */ + if ((set_enabled = sieve_setting_get(svinst, "sieve_enabled")) != NULL && + strcasecmp(set_enabled, "no") == 0) { + e_debug(svinst->event, + "Sieve is disabled for this user"); + *error_r = SIEVE_ERROR_NOT_POSSIBLE; + return NULL; + } + + /* Determine location for default script */ + set_default = sieve_setting_get(svinst, "sieve_default"); + if (set_default == NULL) { + /* For backwards compatibility */ + set_default = sieve_setting_get(svinst, "sieve_global_path"); + } + + /* Attempt to locate user's main storage */ + storage = sieve_storage_do_create_main(svinst, user, flags, error_r); + if (storage != NULL) { + /* Success; record default script location for later use */ + storage->default_location = + p_strdup_empty(storage->pool, set_default); + + set_default_name = + sieve_setting_get(svinst, "sieve_default_name"); + if (set_default_name != NULL && *set_default_name != '\0' && + !sieve_script_name_is_valid(set_default_name)) { + e_error(storage->event, + "Invalid script name `%s' for `sieve_default_name' setting.", + str_sanitize(set_default_name, 80)); + set_default_name = NULL; + } + storage->default_name = + p_strdup_empty(storage->pool, set_default_name); + + if (storage->default_location != NULL && + storage->default_name != NULL) { + e_debug(storage->event, + "Default script at `%s' is visible by name `%s'", + storage->default_location, storage->default_name); + } + } else if (*error_r != SIEVE_ERROR_TEMP_FAILURE && + (flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 && + (flags & SIEVE_STORAGE_FLAG_READWRITE) == 0) { + + /* Failed; try using default script location + (not for temporary failures, read/write access, or dsync) */ + if (set_default == NULL) { + e_debug(svinst->event, "storage: " + "No default script location configured"); + } else { + e_debug(svinst->event, "storage: " + "Trying default script location `%s'", + set_default); + + storage = sieve_storage_create(svinst, set_default, 0, + error_r); + if (storage == NULL) { + switch (*error_r) { + case SIEVE_ERROR_NOT_FOUND: + e_debug(svinst->event, "storage: " + "Default script location `%s' not found", + set_default); + break; + case SIEVE_ERROR_TEMP_FAILURE: + e_error(svinst->event, "storage: " + "Failed to access default script location `%s' " + "(temporary failure)", + set_default); + break; + default: + e_error(svinst->event, "storage: " + "Failed to access default script location `%s'", + set_default); + break; + } + } + } + if (storage != NULL) + storage->is_default = TRUE; + } + return storage; +} + +void sieve_storage_ref(struct sieve_storage *storage) +{ + storage->refcount++; +} + +void sieve_storage_unref(struct sieve_storage **_storage) +{ + struct sieve_storage *storage = *_storage; + + i_assert(storage->refcount > 0); + + if (--storage->refcount != 0) + return; + + if (storage->default_for != NULL) { + i_assert(storage->is_default); + sieve_storage_unref(&storage->default_for); + } + + sieve_storage_sync_deinit(storage); + + if (storage->v.destroy != NULL) + storage->v.destroy(storage); + + i_free(storage->error); + event_unref(&storage->event); + pool_unref(&storage->pool); + *_storage = NULL; +} + +int sieve_storage_setup_bindir(struct sieve_storage *storage, mode_t mode) +{ + const char *bin_dir = storage->bin_dir; + struct stat st; + + if (bin_dir == NULL) + return -1; + + if (stat(bin_dir, &st) == 0) + return 0; + + if (errno == EACCES) { + e_error(storage->event, + "Failed to setup directory for binaries: " + "%s", eacces_error_get("stat", bin_dir)); + return -1; + } else if (errno != ENOENT) { + e_error(storage->event, + "Failed to setup directory for binaries: " + "stat(%s) failed: %m", + bin_dir); + return -1; + } + + if (mkdir_parents(bin_dir, mode) == 0) { + e_debug(storage->event, + "Created directory for binaries: %s", bin_dir); + return 1; + } + + switch (errno) { + case EEXIST: + return 0; + case ENOENT: + e_error(storage->event, + "Directory for binaries was deleted while it was being created"); + break; + case EACCES: + e_error(storage->event, + "%s", eacces_error_get_creating("mkdir_parents_chgrp", bin_dir)); + break; + default: + e_error(storage->event, + "mkdir_parents_chgrp(%s) failed: %m", bin_dir); + break; + } + + return -1; +} + +int sieve_storage_is_singular(struct sieve_storage *storage) +{ + if (storage->v.is_singular == NULL) + return 1; + return storage->v.is_singular(storage); +} + +int sieve_storage_get_last_change(struct sieve_storage *storage, + time_t *last_change_r) +{ + i_assert(storage->v.get_last_change != NULL); + return storage->v.get_last_change(storage, last_change_r); +} + +void sieve_storage_set_modified(struct sieve_storage *storage, time_t mtime) +{ + if (storage->v.set_modified == NULL) + return; + + storage->v.set_modified(storage, mtime); +} + +/* + * Script access + */ + +static struct sieve_script * +sieve_storage_get_script_direct(struct sieve_storage *storage, const char *name, + enum sieve_error *error_r) +{ + struct sieve_script *script; + + if (error_r != NULL) + *error_r = SIEVE_ERROR_NONE; + sieve_storage_clear_error(storage); + + /* Validate script name */ + if (name != NULL && !sieve_script_name_is_valid(name)) { + sieve_storage_set_error(storage, + SIEVE_ERROR_BAD_PARAMS, + "Invalid script name `%s'.", + str_sanitize(name, 80)); + if (error_r != NULL) + *error_r = storage->error_code; + return NULL; + } + + i_assert(storage->v.get_script != NULL); + script = storage->v.get_script(storage, name); + return script; +} + +struct sieve_script * +sieve_storage_get_script(struct sieve_storage *storage, const char *name, + enum sieve_error *error_r) +{ + struct sieve_instance *svinst = storage->svinst; + struct sieve_script *script; + + script = sieve_storage_get_script_direct(storage, name, error_r); + if (script == NULL) { + /* Error */ + if (storage->error_code == SIEVE_ERROR_NOT_FOUND && + (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 && + storage->default_name != NULL && + storage->default_location != NULL && + strcmp(storage->default_name, name) == 0) { + /* Not found; if this name maps to the default script, + try to access that instead */ + i_assert(*storage->default_location != '\0'); + + e_debug(storage->event, + "Trying default script instead"); + + script = sieve_script_create( + svinst, storage->default_location, NULL, + error_r); + if (script != NULL) { + script->storage->is_default = TRUE; + script->storage->default_for = storage; + sieve_storage_ref(storage); + } + + } else if (error_r != NULL) { + *error_r = storage->error_code; + } + } + return script; +} + +struct sieve_script * +sieve_storage_open_script(struct sieve_storage *storage, const char *name, + enum sieve_error *error_r) +{ + struct sieve_instance *svinst = storage->svinst; + struct sieve_script *script; + + script = sieve_storage_get_script(storage, name, error_r); + if (script == NULL) + return NULL; + + if (sieve_script_open(script, error_r) >= 0) + return script; + + /* Error */ + sieve_script_unref(&script); + script = NULL; + + if (storage->error_code == SIEVE_ERROR_NOT_FOUND && + (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 && + storage->default_name != NULL && + storage->default_location != NULL && + strcmp(storage->default_name, name) == 0) { + /* Not found; if this name maps to the default script, + try to open that instead */ + i_assert(*storage->default_location != '\0'); + + e_debug(storage->event, "Trying default script instead"); + + script = sieve_script_create_open( + svinst, storage->default_location, NULL, error_r); + if (script != NULL) { + script->storage->is_default = TRUE; + script->storage->default_for = storage; + sieve_storage_ref(storage); + } + } + return script; +} + +static int +sieve_storage_check_script_direct(struct sieve_storage *storage, + const char *name, enum sieve_error *error_r) + ATTR_NULL(3) +{ + struct sieve_script *script; + enum sieve_error error; + int ret; + + if (error_r == NULL) + error_r = &error; + + script = sieve_storage_get_script_direct(storage, name, error_r); + if (script == NULL) + return (*error_r == SIEVE_ERROR_NOT_FOUND ? 0 : -1); + + ret = sieve_script_open(script, error_r); + sieve_script_unref(&script); + return (ret >= 0 ? 1 : (*error_r == SIEVE_ERROR_NOT_FOUND ? 0 : -1)); +} + +int sieve_storage_check_script(struct sieve_storage *storage, const char *name, + enum sieve_error *error_r) +{ + struct sieve_script *script; + enum sieve_error error; + + if (error_r == NULL) + error_r = &error; + + script = sieve_storage_open_script(storage, name, error_r); + if (script == NULL) + return (*error_r == SIEVE_ERROR_NOT_FOUND ? 0 : -1); + + sieve_script_unref(&script); + return 1; +} + +/* + * Script sequence + */ + +struct sieve_script_sequence * +sieve_storage_get_script_sequence(struct sieve_storage *storage, + enum sieve_error *error_r) +{ + enum sieve_error error; + + if (error_r != NULL) + *error_r = SIEVE_ERROR_NONE; + else + error_r = &error; + + i_assert(storage->v.get_script_sequence != NULL); + return storage->v.get_script_sequence(storage, error_r); +} + +/* + * Active script + */ + +static int +sieve_storage_active_script_do_get_name(struct sieve_storage *storage, + const char **name_r, bool *default_r) + ATTR_NULL(3) +{ + struct sieve_instance *svinst = storage->svinst; + enum sieve_error error; + int ret; + + if (default_r != NULL) + *default_r = FALSE; + + i_assert(storage->v.active_script_get_name != NULL); + ret = storage->v.active_script_get_name(storage, name_r); + + if (ret != 0 || + (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) != 0 || + storage->default_location == NULL || + storage->default_name == NULL) { + return ret; + } + + *name_r = storage->default_name; + + ret = sieve_script_check(svinst, storage->default_location, + NULL, &error); + if (ret <= 0) + return ret; + + if (default_r != NULL) + *default_r = TRUE; + return 1; +} + +int sieve_storage_active_script_get_name(struct sieve_storage *storage, + const char **name_r) +{ + return sieve_storage_active_script_do_get_name(storage, name_r, NULL); +} + +int sieve_storage_active_script_is_default(struct sieve_storage *storage) +{ + const char *name; + bool is_default = FALSE; + int ret; + + ret = sieve_storage_active_script_do_get_name(storage, &name, + &is_default); + return (ret < 0 ? -1 : (is_default ? 1 : 0)); +} + +struct sieve_script * +sieve_storage_active_script_open(struct sieve_storage *storage, + enum sieve_error *error_r) +{ + struct sieve_instance *svinst = storage->svinst; + struct sieve_script *script; + + i_assert(storage->v.active_script_open != NULL); + script = storage->v.active_script_open(storage); + + if (script != NULL || + (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) != 0 || + storage->default_location == NULL) { + if (error_r != NULL) + *error_r = storage->error_code; + return script; + } + + /* Try default script location */ + script = sieve_script_create_open(svinst, storage->default_location, + NULL, error_r); + if (script != NULL) { + script->storage->is_default = TRUE; + script->storage->default_for = storage; + sieve_storage_ref(storage); + } + return script; +} + +int sieve_storage_deactivate(struct sieve_storage *storage, time_t mtime) +{ + int ret; + + i_assert((storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0); + + i_assert(storage->v.deactivate != NULL); + ret = storage->v.deactivate(storage); + + if (ret >= 0) { + struct event_passthrough *e = + event_create_passthrough(storage->event)-> + set_name("sieve_storage_deactivated"); + e_debug(e->event(), "Storage activated"); + + sieve_storage_set_modified(storage, mtime); + (void)sieve_storage_sync_deactivate(storage); + } else { + struct event_passthrough *e = + event_create_passthrough(storage->event)-> + add_str("error", storage->error)-> + set_name("sieve_storage_deactivated"); + e_debug(e->event(), "Failed to deactivate storage: %s", + storage->error); + } + + return ret; +} + +int sieve_storage_active_script_get_last_change(struct sieve_storage *storage, + time_t *last_change_r) +{ + i_assert(storage->v.active_script_get_last_change != NULL); + + return storage->v.active_script_get_last_change(storage, last_change_r); +} + +/* + * Listing scripts + */ + +struct sieve_storage_list_context * +sieve_storage_list_init(struct sieve_storage *storage) +{ + struct sieve_storage_list_context *lctx; + + i_assert(storage->v.list_init != NULL); + lctx = storage->v.list_init(storage); + + if (lctx != NULL) + lctx->storage = storage; + + return lctx; +} + +const char * +sieve_storage_list_next(struct sieve_storage_list_context *lctx, bool *active_r) +{ + struct sieve_storage *storage = lctx->storage; + struct sieve_instance *svinst = storage->svinst; + const char *scriptname; + bool have_default, script_active = FALSE; + + have_default = (storage->default_name != NULL && + storage->default_location != NULL && + (storage->flags & + SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0); + + i_assert(storage->v.list_next != NULL); + scriptname = storage->v.list_next(lctx, &script_active); + + i_assert(!script_active || !lctx->seen_active); + if (script_active) + lctx->seen_active = TRUE; + + if (scriptname != NULL) { + /* Remember when we see that the storage has its own script for + default */ + if (have_default && + strcmp(scriptname, storage->default_name) == 0) + lctx->seen_default = TRUE; + + } else if (have_default && !lctx->seen_default && + sieve_script_check(svinst, storage->default_location, + NULL, NULL) > 0) { + + /* Return default script at the end if it was not listed + thus far (storage backend has no script under default + name) */ + scriptname = storage->default_name; + lctx->seen_default = TRUE; + + /* Mark default as active if no normal script is active */ + if (!lctx->seen_active) { + script_active = TRUE; + lctx->seen_active = TRUE; + } + } + + if (active_r != NULL) + *active_r = script_active; + return scriptname; +} + +int sieve_storage_list_deinit(struct sieve_storage_list_context **_lctx) +{ + struct sieve_storage_list_context *lctx = *_lctx; + struct sieve_storage *storage = lctx->storage; + int ret; + + i_assert(storage->v.list_deinit != NULL); + ret = storage->v.list_deinit(lctx); + + *_lctx = NULL; + return ret; +} + +/* + * Saving scripts + */ + +static struct event * +sieve_storage_save_create_event(struct sieve_storage *storage, + const char *scriptname) ATTR_NULL(2) +{ + struct event *event; + + event = event_create(storage->event); + event_add_str(event, "script_name", scriptname); + if (scriptname == NULL) { + event_set_append_log_prefix(event, "save: "); + } else { + event_set_append_log_prefix( + event, t_strdup_printf("script `%s': save: ", + scriptname)); + } + + return event; +} + +static void sieve_storage_save_cleanup(struct sieve_storage_save_context *sctx) +{ + if (sctx->scriptobject != NULL) + sieve_script_unref(&sctx->scriptobject); +} + +static void sieve_storage_save_deinit(struct sieve_storage_save_context **_sctx) +{ + struct sieve_storage_save_context *sctx = *_sctx; + + *_sctx = NULL; + if (sctx == NULL) + return; + + sieve_storage_save_cleanup(sctx); + event_unref(&sctx->event); + pool_unref(&sctx->pool); +} + +struct sieve_storage_save_context * +sieve_storage_save_init(struct sieve_storage *storage, const char *scriptname, + struct istream *input) +{ + struct sieve_storage_save_context *sctx; + + if (scriptname != NULL) { + /* Validate script name */ + if (!sieve_script_name_is_valid(scriptname)) { + sieve_storage_set_error(storage, + SIEVE_ERROR_BAD_PARAMS, + "Invalid Sieve script name `%s'.", + str_sanitize(scriptname, 80)); + return NULL; + } + } + + i_assert((storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0); + + i_assert(storage->v.save_alloc != NULL); + sctx = storage->v.save_alloc(storage); + sctx->storage = storage; + + sctx->event = sieve_storage_save_create_event(storage, scriptname); + + struct event_passthrough *e = + event_create_passthrough(sctx->event)-> + set_name("sieve_storage_save_started"); + e_debug(e->event(), "Started saving script"); + + i_assert(storage->v.save_init != NULL); + if ((storage->v.save_init(sctx, scriptname, input)) < 0) { + struct event_passthrough *e = + event_create_passthrough(sctx->event)-> + add_str("error", storage->error)-> + set_name("sieve_storage_save_finished"); + e_debug(e->event(), "Failed to save script: %s", + storage->error); + + sieve_storage_save_deinit(&sctx); + return NULL; + } + + sctx->mtime = (time_t)-1; + + i_assert(sctx->input != NULL); + + return sctx; +} + +int sieve_storage_save_continue(struct sieve_storage_save_context *sctx) +{ + struct sieve_storage *storage = sctx->storage; + int ret; + + i_assert(storage->v.save_continue != NULL); + ret = storage->v.save_continue(sctx); + if (ret < 0) + sctx->failed = TRUE; + return ret; +} + +int sieve_storage_save_finish(struct sieve_storage_save_context *sctx) +{ + struct sieve_storage *storage = sctx->storage; + int ret; + + i_assert(!sctx->finished); + sctx->finished = TRUE; + + i_assert(storage->v.save_finish != NULL); + ret = storage->v.save_finish(sctx); + if (ret < 0) { + struct event_passthrough *e = + event_create_passthrough(sctx->event)-> + add_str("error", storage->error)-> + set_name("sieve_storage_save_finished"); + e_debug(e->event(), "Failed to upload script: %s", + storage->error); + + sctx->failed = TRUE; + } + return ret; +} + +void sieve_storage_save_set_mtime(struct sieve_storage_save_context *sctx, + time_t mtime) +{ + sctx->mtime = mtime; +} + +struct sieve_script * +sieve_storage_save_get_tempscript(struct sieve_storage_save_context *sctx) +{ + struct sieve_storage *storage = sctx->storage; + + if (sctx->failed) + return NULL; + + if (sctx->scriptobject != NULL) + return sctx->scriptobject; + + i_assert(storage->v.save_get_tempscript != NULL); + sctx->scriptobject = storage->v.save_get_tempscript(sctx); + + i_assert(sctx->scriptobject != NULL || + storage->error_code != SIEVE_ERROR_NONE); + return sctx->scriptobject; +} + +bool sieve_storage_save_will_activate(struct sieve_storage_save_context *sctx) +{ + if (sctx->scriptname == NULL) + return FALSE; + + if (sctx->active_scriptname == NULL) { + const char *scriptname; + + if (sieve_storage_active_script_get_name(sctx->storage, + &scriptname) > 0) { + sctx->active_scriptname = + p_strdup(sctx->pool, scriptname); + } + } + + /* Is the requested script active? */ + return (sctx->active_scriptname != NULL && + strcmp(sctx->scriptname, sctx->active_scriptname) == 0); +} + +int sieve_storage_save_commit(struct sieve_storage_save_context **_sctx) +{ + struct sieve_storage_save_context *sctx = *_sctx; + struct sieve_storage *storage; + const char *scriptname; + bool default_activate = FALSE; + int ret; + + *_sctx = NULL; + if (sctx == NULL) + return 0; + + storage = sctx->storage; + scriptname = sctx->scriptname; + + i_assert(!sctx->failed); + i_assert(sctx->finished); + i_assert(sctx->scriptname != NULL); + + /* Check whether we're replacing the default active script */ + if (storage->default_name != NULL && + storage->default_location != NULL && + (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 && + strcmp(sctx->scriptname, storage->default_name) == 0 && + sieve_storage_save_will_activate(sctx) && + sieve_storage_check_script_direct(storage, storage->default_name, + NULL) <= 0) + default_activate = TRUE; + + sieve_storage_save_cleanup(sctx); + + i_assert(storage->v.save_commit != NULL); + ret = storage->v.save_commit(sctx); + + /* Implicitly activate it when we're replacing the default + active script */ + if (ret >= 0 && default_activate) { + struct sieve_script *script; + enum sieve_error error; + + script = sieve_storage_open_script(storage, scriptname, &error); + if (script == NULL) { + /* Somehow not actually saved */ + ret = (error == SIEVE_ERROR_NOT_FOUND ? 0 : -1); + } else if (sieve_script_activate(script, (time_t)-1) < 0) { + /* Failed to activate; roll back */ + ret = -1; + (void)sieve_script_delete(script, TRUE); + } + if (script != NULL) + sieve_script_unref(&script); + + if (ret < 0) { + e_error(sctx->event, + "Failed to implicitly activate script `%s' " + "while replacing the default active script", + scriptname); + } + } + + if (ret >= 0) { + struct event_passthrough *e = + event_create_passthrough(sctx->event)-> + set_name("sieve_storage_save_finished"); + e_debug(e->event(), "Finished saving script"); + + /* set INBOX mailbox attribute */ + (void)sieve_storage_sync_script_save(storage, scriptname); + } else { + struct event_passthrough *e = + event_create_passthrough(sctx->event)-> + add_str("error", storage->error)-> + set_name("sieve_storage_save_finished"); + + e_debug(e->event(), "Failed to save script: %s", + storage->error); + } + + sieve_storage_save_deinit(&sctx); + return ret; +} + +void sieve_storage_save_cancel(struct sieve_storage_save_context **_sctx) +{ + struct sieve_storage_save_context *sctx = *_sctx; + struct sieve_storage *storage; + + *_sctx = NULL; + if (sctx == NULL) + return; + + storage = sctx->storage; + + sctx->failed = TRUE; + + sieve_storage_save_cleanup(sctx); + + if (!sctx->finished) + (void)sieve_storage_save_finish(sctx); + + struct event_passthrough *e = + event_create_passthrough(sctx->event)-> + add_str("error", "Canceled")-> + set_name("sieve_storage_save_finished"); + e_debug(e->event(), "Canceled saving script"); + + i_assert(storage->v.save_cancel != NULL); + storage->v.save_cancel(sctx); + + sieve_storage_save_deinit(&sctx); +} + +int sieve_storage_save_as_active(struct sieve_storage *storage, + struct istream *input, time_t mtime) +{ + struct event *event; + int ret; + + event = event_create(storage->event); + event_set_append_log_prefix(event, "active script: save: "); + + struct event_passthrough *e = + event_create_passthrough(event)-> + set_name("sieve_storage_save_started"); + e_debug(e->event(), "Started saving active script"); + + i_assert(storage->v.save_as_active != NULL); + ret = storage->v.save_as_active(storage, input, mtime); + + if (ret >= 0) { + struct event_passthrough *e = + event_create_passthrough(event)-> + set_name("sieve_storage_save_finished"); + e_debug(e->event(), "Finished saving active script"); + } else { + struct event_passthrough *e = + event_create_passthrough(event)-> + add_str("error", storage->error)-> + set_name("sieve_storage_save_finished"); + e_debug(e->event(), "Failed to save active script: %s", + storage->error); + } + + event_unref(&event); + return ret; +} + +int sieve_storage_save_as(struct sieve_storage *storage, struct istream *input, + const char *name) +{ + struct event *event; + int ret; + + event = sieve_storage_save_create_event(storage, name); + + struct event_passthrough *e = + event_create_passthrough(event)-> + set_name("sieve_storage_save_started"); + e_debug(e->event(), "Started saving script"); + + i_assert(storage->v.save_as != NULL); + ret = storage->v.save_as(storage, input, name); + + if (ret >= 0) { + struct event_passthrough *e = + event_create_passthrough(event)-> + set_name("sieve_storage_save_finished"); + e_debug(e->event(), "Finished saving sieve script"); + } else { + struct event_passthrough *e = + event_create_passthrough(event)-> + add_str("error", storage->error)-> + set_name("sieve_storage_save_finished"); + e_debug(e->event(), "Failed to save script: %s", + storage->error); + } + + event_unref(&event); + return ret; +} + +/* + * Checking quota + */ + +bool sieve_storage_quota_validsize(struct sieve_storage *storage, size_t size, + uint64_t *limit_r) +{ + uint64_t max_size; + + max_size = sieve_max_script_size(storage->svinst); + if (max_size > 0 && size > max_size) { + *limit_r = max_size; + return FALSE; + } + + return TRUE; +} + +uint64_t sieve_storage_quota_max_script_size(struct sieve_storage *storage) +{ + return sieve_max_script_size(storage->svinst); +} + +int sieve_storage_quota_havespace(struct sieve_storage *storage, + const char *scriptname, size_t size, + enum sieve_storage_quota *quota_r, + uint64_t *limit_r) +{ + *quota_r = SIEVE_STORAGE_QUOTA_NONE; + *limit_r = 0; + + /* Check the script size */ + if (!sieve_storage_quota_validsize(storage, size, limit_r)) { + *quota_r = SIEVE_STORAGE_QUOTA_MAXSIZE; + return 0; + } + + /* Do we need to scan the storage (quota enabled) ? */ + if (storage->max_scripts == 0 && storage->max_storage == 0) + return 1; + + if (storage->v.quota_havespace == NULL) + return 1; + + return storage->v.quota_havespace(storage, scriptname, size, + quota_r, limit_r); +} + +/* + * Properties + */ + +const char *sieve_storage_location(const struct sieve_storage *storage) +{ + return storage->location; +} + +bool sieve_storage_is_default(const struct sieve_storage *storage) +{ + return storage->is_default; +} + +/* + * Error handling + */ + +void sieve_storage_clear_error(struct sieve_storage *storage) +{ + i_free(storage->error); + storage->error_code = SIEVE_ERROR_NONE; + storage->error = NULL; +} + +void sieve_storage_set_error(struct sieve_storage *storage, + enum sieve_error error, const char *fmt, ...) +{ + va_list va; + + sieve_storage_clear_error(storage); + + if (fmt != NULL) { + va_start(va, fmt); + storage->error = i_strdup_vprintf(fmt, va); + va_end(va); + } + + storage->error_code = error; +} + +void sieve_storage_copy_error(struct sieve_storage *storage, + const struct sieve_storage *source) +{ + sieve_storage_clear_error(storage); + storage->error = i_strdup(source->error); + storage->error_code = source->error_code; +} + +void sieve_storage_set_internal_error(struct sieve_storage *storage) +{ + struct tm *tm; + char str[256]; + + sieve_storage_clear_error(storage); + + /* critical errors may contain sensitive data, so let user + see only "Internal error" with a timestamp to make it + easier to look from log files the actual error message. */ + tm = localtime(&ioloop_time); + + storage->error = + (strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ? + i_strdup(str) : i_strdup(CRITICAL_MSG)); + + storage->error_code = SIEVE_ERROR_TEMP_FAILURE; +} + +void sieve_storage_set_critical(struct sieve_storage *storage, + const char *fmt, ...) +{ + va_list va; + + if (fmt != NULL) { + if ((storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0) { + va_start(va, fmt); + e_error(storage->svinst->event, "%s storage: %s", + storage->driver_name, + t_strdup_vprintf(fmt, va)); + va_end(va); + + sieve_storage_set_internal_error(storage); + + } else { + sieve_storage_clear_error(storage); + + /* no user is involved while synchronizing, so do it the + normal way */ + va_start(va, fmt); + storage->error = i_strdup_vprintf(fmt, va); + va_end(va); + + storage->error_code = SIEVE_ERROR_TEMP_FAILURE; + } + } +} + +const char * +sieve_storage_get_last_error(struct sieve_storage *storage, + enum sieve_error *error_r) +{ + /* We get here only in error situations, so we have to return some + error. If storage->error is NULL, it means we forgot to set it at + some point.. + */ + + if (error_r != NULL) + *error_r = storage->error_code; + + return storage->error != NULL ? storage->error : "Unknown error"; +} diff --git a/pigeonhole/src/lib-sieve/sieve-storage.h b/pigeonhole/src/lib-sieve/sieve-storage.h new file mode 100644 index 0000000..125a86e --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-storage.h @@ -0,0 +1,187 @@ +#ifndef SIEVE_STORAGE_H +#define SIEVE_STORAGE_H + +#define MAILBOX_ATTRIBUTE_PREFIX_SIEVE \ + MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER"sieve/" +#define MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES \ + MAILBOX_ATTRIBUTE_PREFIX_SIEVE"files/" +#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT \ + MAILBOX_ATTRIBUTE_PREFIX_SIEVE"default" + +#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_LINK 'L' +#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_SCRIPT 'S' + +/* + * Storage object + */ + +enum sieve_storage_flags { + /* Storage is opened for read/write access (e.g. ManageSieve) */ + SIEVE_STORAGE_FLAG_READWRITE = 0x01, + /* This storage is used for synchronization (and not normal ManageSieve) + */ + SIEVE_STORAGE_FLAG_SYNCHRONIZING = 0x02 +}; + +struct sieve_storage; + +struct sieve_storage * +sieve_storage_create(struct sieve_instance *svinst, const char *location, + enum sieve_storage_flags flags, enum sieve_error *error_r) + ATTR_NULL(4); +struct sieve_storage * +sieve_storage_create_main(struct sieve_instance *svinst, struct mail_user *user, + enum sieve_storage_flags flags, + enum sieve_error *error_r) ATTR_NULL(4); + +void sieve_storage_ref(struct sieve_storage *storage); +void sieve_storage_unref(struct sieve_storage **_storage); + +/* + * Script access + */ + +struct sieve_script * +sieve_storage_get_script(struct sieve_storage *storage, const char *name, + enum sieve_error *error_r) ATTR_NULL(3); +struct sieve_script * +sieve_storage_open_script(struct sieve_storage *storage, const char *name, + enum sieve_error *error_r) ATTR_NULL(3); +int sieve_storage_check_script(struct sieve_storage *storage, const char *name, + enum sieve_error *error_r) ATTR_NULL(3); + +/* + * Script sequence + */ + +struct sieve_script_sequence * +sieve_storage_get_script_sequence(struct sieve_storage *storage, + enum sieve_error *error_r); + +/* + * Active script + */ + +int sieve_storage_active_script_get_name(struct sieve_storage *storage, + const char **name_r); + +struct sieve_script * +sieve_storage_active_script_open(struct sieve_storage *storage, + enum sieve_error *error_r) ATTR_NULL(2); + +int sieve_storage_active_script_get_last_change(struct sieve_storage *storage, + time_t *last_change_r); + +/* + * Listing scripts in storage + */ + +struct sieve_storage_list_context; + +/* Create a context for listing the scripts in the storage */ +struct sieve_storage_list_context * +sieve_storage_list_init(struct sieve_storage *storage); +/* Get the next script in the storage. */ +const char *sieve_storage_list_next(struct sieve_storage_list_context *lctx, + bool *active_r) ATTR_NULL(2); +/* Destroy the listing context */ +int sieve_storage_list_deinit(struct sieve_storage_list_context **lctx); + +/* + * Saving scripts in storage + */ + +struct sieve_storage_save_context; + +struct sieve_storage_save_context * +sieve_storage_save_init(struct sieve_storage *storage, const char *scriptname, + struct istream *input); + +int sieve_storage_save_continue(struct sieve_storage_save_context *sctx); + +int sieve_storage_save_finish(struct sieve_storage_save_context *sctx); + +struct sieve_script * +sieve_storage_save_get_tempscript(struct sieve_storage_save_context *sctx); + +bool sieve_storage_save_will_activate(struct sieve_storage_save_context *sctx); + +void sieve_storage_save_set_mtime(struct sieve_storage_save_context *sctx, + time_t mtime); + +void sieve_storage_save_cancel(struct sieve_storage_save_context **sctx); + +int sieve_storage_save_commit(struct sieve_storage_save_context **sctx); + +int sieve_storage_save_as(struct sieve_storage *storage, struct istream *input, + const char *name); + +/* Saves input directly as a regular file at the active script path. + * This is needed for the doveadm-sieve plugin. + */ +int sieve_storage_save_as_active(struct sieve_storage *storage, + struct istream *input, time_t mtime); + +/* + * Management + */ + +int sieve_storage_deactivate(struct sieve_storage *storage, time_t mtime); + +/* + * Storage quota + */ + +enum sieve_storage_quota { + SIEVE_STORAGE_QUOTA_NONE, + SIEVE_STORAGE_QUOTA_MAXSIZE, + SIEVE_STORAGE_QUOTA_MAXSCRIPTS, + SIEVE_STORAGE_QUOTA_MAXSTORAGE +}; + +bool sieve_storage_quota_validsize(struct sieve_storage *storage, size_t size, + uint64_t *limit_r); + +uint64_t sieve_storage_quota_max_script_size(struct sieve_storage *storage); + +int sieve_storage_quota_havespace(struct sieve_storage *storage, + const char *scriptname, size_t size, + enum sieve_storage_quota *quota_r, + uint64_t *limit_r); + +/* + * Properties + */ + +const char *sieve_storage_location(const struct sieve_storage *storage) + ATTR_PURE; +bool sieve_storage_is_default(const struct sieve_storage *storage) ATTR_PURE; + +int sieve_storage_is_singular(struct sieve_storage *storage); + +/* + * Error handling + */ + +void sieve_storage_clear_error(struct sieve_storage *storage); + +void sieve_storage_set_error(struct sieve_storage *storage, + enum sieve_error error, const char *fmt, ...) + ATTR_FORMAT(3, 4); +void sieve_storage_set_critical(struct sieve_storage *storage, + const char *fmt, ...) ATTR_FORMAT(2, 3); + +const char *sieve_storage_get_last_error(struct sieve_storage *storage, + enum sieve_error *error_r) + ATTR_NULL(2); + +/* + * + */ + +int sieve_storage_get_last_change(struct sieve_storage *storage, + time_t *last_change_r); +void sieve_storage_set_modified(struct sieve_storage *storage, + time_t mtime); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-stringlist.c b/pigeonhole/src/lib-sieve/sieve-stringlist.c new file mode 100644 index 0000000..17b7d5c --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-stringlist.c @@ -0,0 +1,276 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" +#include "str.h" + +#include "sieve-common.h" +#include "sieve-stringlist.h" + +/* + * Default implementation + */ + +int sieve_stringlist_read_all +(struct sieve_stringlist *strlist, pool_t pool, + const char * const **list_r) +{ + if ( strlist->read_all == NULL ) { + ARRAY(const char *) items; + string_t *item; + int ret; + + sieve_stringlist_reset(strlist); + + p_array_init(&items, pool, 4); + + item = NULL; + while ( (ret=sieve_stringlist_next_item(strlist, &item)) > 0 ) { + const char *stritem = p_strdup(pool, str_c(item)); + + array_append(&items, &stritem, 1); + } + + (void)array_append_space(&items); + *list_r = array_idx(&items, 0); + + return ( ret < 0 ? -1 : 1 ); + } + + return strlist->read_all(strlist, pool, list_r); +} + +int sieve_stringlist_get_length +(struct sieve_stringlist *strlist) +{ + if ( strlist->get_length == NULL ) { + string_t *item; + int count = 0; + int ret; + + sieve_stringlist_reset(strlist); + while ( (ret=sieve_stringlist_next_item(strlist, &item)) > 0 ) { + count++; + } + sieve_stringlist_reset(strlist); + + return ( ret < 0 ? -1 : count ); + } + + return strlist->get_length(strlist); +} + +/* + * Single Stringlist + */ + +/* Object */ + +static int sieve_single_stringlist_next_item + (struct sieve_stringlist *_strlist, string_t **str_r); +static void sieve_single_stringlist_reset + (struct sieve_stringlist *_strlist); +static int sieve_single_stringlist_get_length + (struct sieve_stringlist *_strlist); + +struct sieve_single_stringlist { + struct sieve_stringlist strlist; + + string_t *value; + + bool end:1; + bool count_empty:1; +}; + +struct sieve_stringlist *sieve_single_stringlist_create +(const struct sieve_runtime_env *renv, string_t *str, bool count_empty) +{ + struct sieve_single_stringlist *strlist; + + strlist = t_new(struct sieve_single_stringlist, 1); + strlist->strlist.runenv = renv; + strlist->strlist.exec_status = SIEVE_EXEC_OK; + strlist->strlist.next_item = sieve_single_stringlist_next_item; + strlist->strlist.reset = sieve_single_stringlist_reset; + strlist->strlist.get_length = sieve_single_stringlist_get_length; + strlist->count_empty = count_empty; + strlist->value = str; + + return &strlist->strlist; +} + +struct sieve_stringlist *sieve_single_stringlist_create_cstr +(const struct sieve_runtime_env *renv, const char *cstr, bool count_empty) +{ + string_t *str = t_str_new_const(cstr, strlen(cstr)); + + return sieve_single_stringlist_create(renv, str, count_empty); +} + +/* Implementation */ + +static int sieve_single_stringlist_next_item +(struct sieve_stringlist *_strlist, string_t **str_r) +{ + struct sieve_single_stringlist *strlist = + (struct sieve_single_stringlist *)_strlist; + + if ( strlist->end ) { + *str_r = NULL; + return 0; + } + + *str_r = strlist->value; + strlist->end = TRUE; + return 1; +} + +static void sieve_single_stringlist_reset +(struct sieve_stringlist *_strlist) +{ + struct sieve_single_stringlist *strlist = + (struct sieve_single_stringlist *)_strlist; + + strlist->end = FALSE; +} + +static int sieve_single_stringlist_get_length +(struct sieve_stringlist *_strlist) +{ + struct sieve_single_stringlist *strlist = + (struct sieve_single_stringlist *)_strlist; + + return ( strlist->count_empty || str_len(strlist->value) > 0 ? 1 : 0 ); +} + +/* + * Index Stringlist + */ + +/* Object */ + +static int sieve_index_stringlist_next_item + (struct sieve_stringlist *_strlist, string_t **str_r); +static void sieve_index_stringlist_reset + (struct sieve_stringlist *_strlist); +static int sieve_index_stringlist_get_length + (struct sieve_stringlist *_strlist); +static void sieve_index_stringlist_set_trace + (struct sieve_stringlist *strlist, bool trace); + +struct sieve_index_stringlist { + struct sieve_stringlist strlist; + + struct sieve_stringlist *source; + + int index; + bool end:1; +}; + +struct sieve_stringlist *sieve_index_stringlist_create +(const struct sieve_runtime_env *renv, struct sieve_stringlist *source, + int index) +{ + struct sieve_index_stringlist *strlist; + + strlist = t_new(struct sieve_index_stringlist, 1); + strlist->strlist.runenv = renv; + strlist->strlist.exec_status = SIEVE_EXEC_OK; + strlist->strlist.next_item = sieve_index_stringlist_next_item; + strlist->strlist.reset = sieve_index_stringlist_reset; + strlist->strlist.get_length = sieve_index_stringlist_get_length; + strlist->strlist.set_trace = sieve_index_stringlist_set_trace; + strlist->source = source; + strlist->index = index; + + return &strlist->strlist; +} + +/* Implementation */ + +static int sieve_index_stringlist_next_item +(struct sieve_stringlist *_strlist, string_t **str_r) +{ + struct sieve_index_stringlist *strlist = + (struct sieve_index_stringlist *)_strlist; + int index, ret; + + if ( strlist->end ) { + *str_r = NULL; + return 0; + } + + if ( strlist->index < 0 ) { + int len = sieve_stringlist_get_length(strlist->source); + if (len < 0) { + _strlist->exec_status = strlist->source->exec_status; + return -1; + } + + if (len < -strlist->index) { + *str_r = NULL; + strlist->end = TRUE; + return 0; + } + index = len + 1 + strlist->index; + } else { + index = strlist->index; + } + + i_assert(index > 0); + while ( index > 0 ) { + if ( (ret=sieve_stringlist_next_item(strlist->source, str_r)) <= 0 ) { + if (ret < 0) + _strlist->exec_status = strlist->source->exec_status; + return ret; + } + + index--; + } + + strlist->end = TRUE; + return 1; +} + +static void sieve_index_stringlist_reset +(struct sieve_stringlist *_strlist) +{ + struct sieve_index_stringlist *strlist = + (struct sieve_index_stringlist *)_strlist; + + sieve_stringlist_reset(strlist->source); + strlist->end = FALSE; +} + +static int sieve_index_stringlist_get_length +(struct sieve_stringlist *_strlist) +{ + struct sieve_index_stringlist *strlist = + (struct sieve_index_stringlist *)_strlist; + int len; + + len = sieve_stringlist_get_length(strlist->source); + if (len < 0) { + _strlist->exec_status = strlist->source->exec_status; + return -1; + } + + if ( strlist->index < 0 ) { + if ( -strlist->index >= len ) + return 0; + } else if ( strlist->index >= len ) { + return 0; + } + + return 1; +} + +static void sieve_index_stringlist_set_trace +(struct sieve_stringlist *_strlist, bool trace) +{ + struct sieve_index_stringlist *strlist = + (struct sieve_index_stringlist *)_strlist; + + sieve_stringlist_set_trace(strlist->source, trace); +} diff --git a/pigeonhole/src/lib-sieve/sieve-stringlist.h b/pigeonhole/src/lib-sieve/sieve-stringlist.h new file mode 100644 index 0000000..1909b7a --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-stringlist.h @@ -0,0 +1,74 @@ +#ifndef SIEVE_STRINGLIST_H +#define SIEVE_STRINGLIST_H + +/* + * Stringlist API + */ + +struct sieve_stringlist { + int (*next_item) + (struct sieve_stringlist *strlist, string_t **str_r); + void (*reset) + (struct sieve_stringlist *strlist); + int (*get_length) + (struct sieve_stringlist *strlist); + + int (*read_all) + (struct sieve_stringlist *strlist, pool_t pool, + const char * const **list_r); + + void (*set_trace) + (struct sieve_stringlist *strlist, bool trace); + + const struct sieve_runtime_env *runenv; + int exec_status; + + bool trace:1; +}; + +static inline void sieve_stringlist_set_trace +(struct sieve_stringlist *strlist, bool trace) +{ + strlist->trace = trace; + + if ( strlist->set_trace != NULL ) + strlist->set_trace(strlist, trace); +} + +static inline int sieve_stringlist_next_item +(struct sieve_stringlist *strlist, string_t **str_r) +{ + return strlist->next_item(strlist, str_r); +} + +static inline void sieve_stringlist_reset +(struct sieve_stringlist *strlist) +{ + strlist->reset(strlist); +} + +int sieve_stringlist_get_length + (struct sieve_stringlist *strlist); + +int sieve_stringlist_read_all + (struct sieve_stringlist *strlist, pool_t pool, + const char * const **list_r); + +/* + * Single Stringlist + */ + +struct sieve_stringlist *sieve_single_stringlist_create + (const struct sieve_runtime_env *renv, string_t *str, bool count_empty); +struct sieve_stringlist *sieve_single_stringlist_create_cstr +(const struct sieve_runtime_env *renv, const char *cstr, bool count_empty); + +/* + * Index Stringlist + */ + +struct sieve_stringlist *sieve_index_stringlist_create + (const struct sieve_runtime_env *renv, struct sieve_stringlist *source, + int index); + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-types.h b/pigeonhole/src/lib-sieve/sieve-types.h new file mode 100644 index 0000000..439b26e --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-types.h @@ -0,0 +1,316 @@ +#ifndef SIEVE_TYPES_H +#define SIEVE_TYPES_H + +#include "lib.h" +#include "smtp-address.h" + +#include <stdio.h> + +/* + * Forward declarations + */ + +struct smtp_params_mail; +struct smtp_params_rcpt; + +struct sieve_instance; +struct sieve_callbacks; + +struct sieve_script; +struct sieve_binary; + +struct sieve_message_data; +struct sieve_script_env; +struct sieve_exec_status; +struct sieve_trace_log; + +/* + * System environment + */ + +enum sieve_flag { + /* Relative paths are resolved to HOME */ + SIEVE_FLAG_HOME_RELATIVE = (1 << 0) +}; + +/* Sieve evaluation can be performed at various different points as messages + are processed. */ +enum sieve_env_location { + /* Unknown */ + SIEVE_ENV_LOCATION_UNKNOWN = 0, + /* "MDA" - evaluation is being performed by a Mail Delivery Agent */ + SIEVE_ENV_LOCATION_MDA, + /* "MTA" - the Sieve script is being evaluated by a Message Transfer Agent */ + SIEVE_ENV_LOCATION_MTA, + /* "MS" - evaluation is being performed by a Message Store */ + SIEVE_ENV_LOCATION_MS +}; + +/* The point relative to final delivery where the Sieve script is being + evaluated. */ +enum sieve_delivery_phase { + SIEVE_DELIVERY_PHASE_UNKNOWN = 0, + SIEVE_DELIVERY_PHASE_PRE, + SIEVE_DELIVERY_PHASE_DURING, + SIEVE_DELIVERY_PHASE_POST, +}; + +struct sieve_environment { + const char *hostname; + const char *domainname; + + const char *base_dir; + const char *username; + const char *home_dir; + const char *temp_dir; + + struct event *event_parent; + + enum sieve_flag flags; + enum sieve_env_location location; + enum sieve_delivery_phase delivery_phase; +}; + +/* + * Callbacks + */ + +struct sieve_callbacks { + const char *(*get_homedir)(void *context); + const char *(*get_setting)(void *context, const char *identifier); +}; + +/* + * Errors + */ + +enum sieve_error { + SIEVE_ERROR_NONE = 0, + + /* Temporary internal error */ + SIEVE_ERROR_TEMP_FAILURE, + /* It's not possible to do the wanted operation */ + SIEVE_ERROR_NOT_POSSIBLE, + /* Invalid parameters (eg. script name not valid) */ + SIEVE_ERROR_BAD_PARAMS, + /* No permission to do the request */ + SIEVE_ERROR_NO_PERMISSION, + /* Out of disk space */ + SIEVE_ERROR_NO_QUOTA, + /* Item (e.g. script or binary) cannot be found */ + SIEVE_ERROR_NOT_FOUND, + /* Item (e.g. script or binary) already exists */ + SIEVE_ERROR_EXISTS, + /* Referenced item (e.g. script or binary) is not valid or currupt */ + SIEVE_ERROR_NOT_VALID, + /* Not allowed to perform the operation because the item is in active use */ + SIEVE_ERROR_ACTIVE, + /* Operation exceeds resource limit */ + SIEVE_ERROR_RESOURCE_LIMIT, +}; + +/* + * Compile flags + */ + +enum sieve_compile_flags { + /* No global extensions are allowed + * (as marked by sieve_global_extensions setting) + */ + SIEVE_COMPILE_FLAG_NOGLOBAL = (1<<0), + /* Script is being uploaded (usually through ManageSieve) */ + SIEVE_COMPILE_FLAG_UPLOADED = (1<<1), + /* Script is being activated (usually through ManageSieve) */ + SIEVE_COMPILE_FLAG_ACTIVATED = (1<<2), + /* Compiled for environment with no access to envelope */ + SIEVE_COMPILE_FLAG_NO_ENVELOPE = (1<<3) +}; + +/* + * Message data + * + * - The mail message + envelope data + */ + +struct sieve_message_data { + struct mail *mail; + + const char *auth_user; + const char *id; + + struct { + const struct smtp_address *mail_from; + const struct smtp_params_mail *mail_params; + + const struct smtp_address *rcpt_to; + const struct smtp_params_rcpt *rcpt_params; + } envelope; +}; + +/* + * Runtime flags + */ + +enum sieve_execute_flags { + /* No global extensions are allowed + * (as marked by sieve_global_extensions setting) + */ + SIEVE_EXECUTE_FLAG_NOGLOBAL = (1<<0), + /* Do not execute (implicit keep) at the end */ + SIEVE_EXECUTE_FLAG_DEFER_KEEP = (1<<1), + /* There is no envelope */ + SIEVE_EXECUTE_FLAG_NO_ENVELOPE = (1<<2), + /* Skip sending responses */ + SIEVE_EXECUTE_FLAG_SKIP_RESPONSES = (1<<3), + /* Log result as info (when absent, only debug logging is performed) */ + SIEVE_EXECUTE_FLAG_LOG_RESULT = (1<<4), +}; + +/* + * Runtime trace settings + */ + +typedef enum { + SIEVE_TRLVL_NONE = 0, + SIEVE_TRLVL_ACTIONS, + SIEVE_TRLVL_COMMANDS, + SIEVE_TRLVL_TESTS, + SIEVE_TRLVL_MATCHING +} sieve_trace_level_t; + +enum { + SIEVE_TRFLG_DEBUG = (1 << 0), + SIEVE_TRFLG_ADDRESSES = (1 << 1) +}; + +struct sieve_trace_config { + sieve_trace_level_t level; + unsigned int flags; +}; + +/* + * Duplicate checking + */ + +enum sieve_duplicate_check_result { + SIEVE_DUPLICATE_CHECK_RESULT_EXISTS = 1, + SIEVE_DUPLICATE_CHECK_RESULT_NOT_FOUND = 0, + SIEVE_DUPLICATE_CHECK_RESULT_FAILURE = -1, + SIEVE_DUPLICATE_CHECK_RESULT_TEMP_FAILURE = -2, +}; + +/* + * Script environment + * + * - Environment for currently executing script + */ + +struct sieve_script_env { + /* Mail-related */ + struct mail_user *user; + const struct message_address *postmaster_address; + const char *default_mailbox; + bool mailbox_autocreate; + bool mailbox_autosubscribe; + + /* External context data */ + + void *script_context; + + /* Callbacks */ + + /* Interface for sending mail */ + void *(*smtp_start) + (const struct sieve_script_env *senv, + const struct smtp_address *mail_from); + /* Add a new recipient */ + void (*smtp_add_rcpt) + (const struct sieve_script_env *senv, void *handle, + const struct smtp_address *rcpt_to); + /* Get an output stream where the message can be written to. The recipients + must already be added before calling this. */ + struct ostream *(*smtp_send) + (const struct sieve_script_env *senv, void *handle); + /* Abort the SMTP transaction after smtp_send() is already issued */ + void (*smtp_abort) + (const struct sieve_script_env *senv, void *handle); + /* Returns 1 on success, 0 on permanent failure, -1 on temporary failure. */ + int (*smtp_finish) + (const struct sieve_script_env *senv, void *handle, + const char **error_r); + + /* Interface for marking and checking duplicates */ + void *(*duplicate_transaction_begin)( + const struct sieve_script_env *senv); + void (*duplicate_transaction_commit)(void **_dup_trans); + void (*duplicate_transaction_rollback)(void **_dup_trans); + + enum sieve_duplicate_check_result + (*duplicate_check)(void *dup_trans, const struct sieve_script_env *senv, + const void *id, size_t id_size); + void (*duplicate_mark)(void *dup_trans, + const struct sieve_script_env *senv, + const void *id, size_t id_size, time_t time); + + /* Interface for rejecting mail */ + int (*reject_mail)(const struct sieve_script_env *senv, + const struct smtp_address *recipient, const char *reason); + + /* Interface for amending result messages */ + const char * + (*result_amend_log_message)(const struct sieve_script_env *senv, + enum log_type log_type, + const char *message); + + /* Execution status record */ + struct sieve_exec_status *exec_status; + + /* Runtime trace*/ + struct sieve_trace_log *trace_log; + struct sieve_trace_config trace_config; +}; + +#define SIEVE_SCRIPT_DEFAULT_MAILBOX(senv) \ + (senv->default_mailbox == NULL ? "INBOX" : senv->default_mailbox ) + +/* + * Resource usage + */ + +struct sieve_resource_usage { + /* The total amount of system + user CPU time consumed while executing + the Sieve script. */ + unsigned int cpu_time_msecs; +}; + +/* + * Script execution status + */ + +struct sieve_exec_status { + struct mail_storage *last_storage; + + struct sieve_resource_usage resource_usage; + + bool message_saved:1; + bool message_forwarded:1; + bool tried_default_save:1; + bool keep_original:1; + bool store_failed:1; + bool significant_action_executed:1; +}; + +/* + * Execution exit codes + */ + +enum sieve_execution_exitcode { + SIEVE_EXEC_OK = 1, + SIEVE_EXEC_FAILURE = 0, + SIEVE_EXEC_TEMP_FAILURE = -1, + SIEVE_EXEC_BIN_CORRUPT = -2, + SIEVE_EXEC_KEEP_FAILED = -3, + SIEVE_EXEC_RESOURCE_LIMIT = -4, +}; + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve-validator.c b/pigeonhole/src/lib-sieve/sieve-validator.c new file mode 100644 index 0000000..bef4a7d --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-validator.c @@ -0,0 +1,1706 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "str-sanitize.h" +#include "array.h" +#include "buffer.h" +#include "mempool.h" +#include "hash.h" + +#include "sieve-common.h" +#include "sieve-extensions.h" +#include "sieve-script.h" +#include "sieve-ast.h" +#include "sieve-commands.h" +#include "sieve-validator.h" + +#include "sieve-comparators.h" +#include "sieve-address-parts.h" + +/* + * Forward declarations + */ + +static void +sieve_validator_register_core_commands(struct sieve_validator *valdtr); +static void +sieve_validator_register_core_tests(struct sieve_validator *valdtr); + +/* + * Types + */ + +/* Tag registration */ + +struct sieve_tag_registration { + const struct sieve_argument_def *tag_def; + const struct sieve_extension *ext; + + const char *identifier; + int id_code; +}; + +/* Command registration */ + +struct sieve_command_registration { + const struct sieve_command_def *cmd_def; + const struct sieve_extension *ext; + + ARRAY(struct sieve_tag_registration *) normal_tags; + ARRAY(struct sieve_tag_registration *) instanced_tags; + ARRAY(struct sieve_tag_registration *) persistent_tags; +}; + +/* Default (literal) arguments */ + +struct sieve_default_argument { + const struct sieve_argument_def *arg_def; + const struct sieve_extension *ext; + + struct sieve_default_argument *overrides; +}; + +/* + * Validator extension + */ + +struct sieve_validator_extension_reg { + const struct sieve_validator_extension *valext; + const struct sieve_extension *ext; + struct sieve_ast_argument *arg; + void *context; + + bool loaded:1; + bool required:1; +}; + +/* + * Validator + */ + +struct sieve_validator { + pool_t pool; + + struct sieve_instance *svinst; + struct sieve_ast *ast; + struct sieve_script *script; + enum sieve_compile_flags flags; + + struct sieve_error_handler *ehandler; + + bool finished_require; + + /* Registries */ + + HASH_TABLE(const char *, struct sieve_command_registration *) commands; + + ARRAY(struct sieve_validator_extension_reg) extensions; + + /* This is currently a wee bit ugly and needs more thought */ + struct sieve_default_argument default_arguments[SAT_COUNT]; + + /* Default argument processing state (FIXME: ugly) */ + struct sieve_default_argument *current_defarg; + enum sieve_argument_type current_defarg_type; + bool current_defarg_constant; +}; + +/* + * Validator object + */ + +struct sieve_validator * +sieve_validator_create(struct sieve_ast *ast, + struct sieve_error_handler *ehandler, + enum sieve_compile_flags flags) +{ + pool_t pool; + struct sieve_validator *valdtr; + const struct sieve_extension *const *ext_preloaded; + unsigned int i, ext_count; + + pool = pool_alloconly_create("sieve_validator", 16384); + valdtr = p_new(pool, struct sieve_validator, 1); + valdtr->pool = pool; + + valdtr->ehandler = ehandler; + sieve_error_handler_ref(ehandler); + + valdtr->ast = ast; + sieve_ast_ref(ast); + + valdtr->script = sieve_ast_script(ast); + valdtr->svinst = sieve_script_svinst(valdtr->script); + valdtr->flags = flags; + + /* Setup default arguments */ + valdtr->default_arguments[SAT_NUMBER].arg_def = &number_argument; + valdtr->default_arguments[SAT_NUMBER].ext = NULL; + valdtr->default_arguments[SAT_VAR_STRING].arg_def = &string_argument; + valdtr->default_arguments[SAT_VAR_STRING].ext = NULL; + valdtr->default_arguments[SAT_CONST_STRING].arg_def = &string_argument; + valdtr->default_arguments[SAT_CONST_STRING].ext = NULL; + valdtr->default_arguments[SAT_STRING_LIST].arg_def = &string_list_argument; + valdtr->default_arguments[SAT_STRING_LIST].ext = NULL; + + /* Setup storage for extension contexts */ + p_array_init(&valdtr->extensions, pool, + sieve_extensions_get_count(valdtr->svinst)); + + /* Setup command registry */ + hash_table_create(&valdtr->commands, pool, 0, strcase_hash, strcasecmp); + sieve_validator_register_core_commands(valdtr); + sieve_validator_register_core_tests(valdtr); + + /* Pre-load core language features implemented as 'extensions' */ + ext_preloaded = + sieve_extensions_get_preloaded(valdtr->svinst, &ext_count); + for (i = 0; i < ext_count; i++) { + const struct sieve_extension_def *ext_def = + ext_preloaded[i]->def; + + if (ext_def != NULL && ext_def->validator_load != NULL) + (void)ext_def->validator_load(ext_preloaded[i], valdtr); + } + + return valdtr; +} + +void sieve_validator_free(struct sieve_validator **valdtr) +{ + const struct sieve_validator_extension_reg *extrs; + unsigned int ext_count, i; + + hash_table_destroy(&(*valdtr)->commands); + sieve_ast_unref(&(*valdtr)->ast); + + sieve_error_handler_unref(&(*valdtr)->ehandler); + + /* Signal registered extensions that the validator is being destroyed */ + extrs = array_get(&(*valdtr)->extensions, &ext_count); + for (i = 0; i < ext_count; i++) { + if (extrs[i].valext != NULL && extrs[i].valext->free != NULL) + extrs[i].valext->free(extrs[i].ext, *valdtr, + extrs[i].context); + } + + pool_unref(&(*valdtr)->pool); + + *valdtr = NULL; +} + +/* + * Accessors + */ + +// FIXME: build validate environment + +pool_t sieve_validator_pool(struct sieve_validator *valdtr) +{ + return valdtr->pool; +} + +struct sieve_error_handler * +sieve_validator_error_handler(struct sieve_validator *valdtr) +{ + return valdtr->ehandler; +} + +struct sieve_ast *sieve_validator_ast(struct sieve_validator *valdtr) +{ + return valdtr->ast; +} + +struct sieve_script *sieve_validator_script(struct sieve_validator *valdtr) +{ + return valdtr->script; +} + +struct sieve_instance *sieve_validator_svinst(struct sieve_validator *valdtr) +{ + return valdtr->svinst; +} + +enum sieve_compile_flags +sieve_validator_compile_flags(struct sieve_validator *valdtr) +{ + return valdtr->flags; +} + +/* + * Command registry + */ + +/* Dummy command object to mark unknown commands in the registry */ + +static bool _cmd_unknown_validate(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_command *cmd ATTR_UNUSED) +{ + i_unreached(); + return FALSE; +} + +static const struct sieve_command_def unknown_command = { + .identifier = "", + .type = SCT_NONE, + .positional_args = 0, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = _cmd_unknown_validate +}; + +/* Registration of the core commands of the language */ + +static void +sieve_validator_register_core_tests(struct sieve_validator *valdtr) +{ + unsigned int i; + + for (i = 0; i < sieve_core_tests_count; i++) { + sieve_validator_register_command(valdtr, NULL, + sieve_core_tests[i]); + } +} + +static void +sieve_validator_register_core_commands(struct sieve_validator *valdtr) +{ + unsigned int i; + + for (i = 0; i < sieve_core_commands_count; i++) { + sieve_validator_register_command(valdtr, NULL, + sieve_core_commands[i]); + } +} + +/* Registry functions */ + +static struct sieve_command_registration * +sieve_validator_find_command_registration(struct sieve_validator *valdtr, + const char *command) +{ + return hash_table_lookup(valdtr->commands, command); +} + +static struct sieve_command_registration * +_sieve_validator_register_command(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + const struct sieve_command_def *cmd_def, + const char *identifier) +{ + struct sieve_command_registration *cmd_reg = + p_new(valdtr->pool, struct sieve_command_registration, 1); + + cmd_reg->cmd_def = cmd_def; + cmd_reg->ext = ext; + + hash_table_insert(valdtr->commands, identifier, cmd_reg); + + return cmd_reg; +} + +void sieve_validator_register_command(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + const struct sieve_command_def *cmd_def) +{ + struct sieve_command_registration *cmd_reg = + sieve_validator_find_command_registration( + valdtr, cmd_def->identifier); + + if (cmd_reg == NULL) { + cmd_reg = _sieve_validator_register_command( + valdtr, ext, cmd_def, cmd_def->identifier); + } else { + cmd_reg->cmd_def = cmd_def; + cmd_reg->ext = ext; + } + + if (cmd_def->registered != NULL) + cmd_def->registered(valdtr, ext, cmd_reg); +} + +static void +sieve_validator_register_unknown_command(struct sieve_validator *valdtr, + const char *command) +{ + struct sieve_command_registration *cmd_reg = + sieve_validator_find_command_registration(valdtr, command); + + if (cmd_reg == NULL) { + (void)_sieve_validator_register_command( + valdtr, NULL, &unknown_command, command); + } else { + i_assert(cmd_reg->cmd_def == NULL); + cmd_reg->cmd_def = &unknown_command; + } +} + +/*const struct sieve_command *sieve_validator_find_command +(struct sieve_validator *valdtr, const char *command) +{ + struct sieve_command_registration *cmd_reg = + sieve_validator_find_command_registration(valdtr, command); + + return ( record == NULL ? NULL : record->command ); +}*/ + +/* + * Per-command tagged argument registry + */ + +/* Dummy argument object to mark unknown arguments in the registry */ + +static bool +_unknown_tag_validate(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_ast_argument **arg ATTR_UNUSED, + struct sieve_command *tst ATTR_UNUSED) +{ + i_unreached(); + return FALSE; +} + +static const struct sieve_argument_def _unknown_tag = { + .identifier = "", + .validate = _unknown_tag_validate, +}; + +static inline bool +_tag_registration_is_unknown(struct sieve_tag_registration *tag_reg) +{ + return (tag_reg != NULL && tag_reg->tag_def == &_unknown_tag); +} + +/* Registry functions */ + +static void +_sieve_validator_register_tag(struct sieve_validator *valdtr, + struct sieve_command_registration *cmd_reg, + const struct sieve_extension *ext, + const struct sieve_argument_def *tag_def, + const char *identifier, int id_code) +{ + struct sieve_tag_registration *reg; + + reg = p_new(valdtr->pool, struct sieve_tag_registration, 1); + reg->ext = ext; + reg->tag_def = tag_def; + reg->id_code = id_code; + if (identifier == NULL) + reg->identifier = tag_def->identifier; + else + reg->identifier = p_strdup(valdtr->pool, identifier); + + if (!array_is_created(&cmd_reg->normal_tags)) + p_array_init(&cmd_reg->normal_tags, valdtr->pool, 4); + + array_append(&cmd_reg->normal_tags, ®, 1); +} + +void sieve_validator_register_persistent_tag( + struct sieve_validator *valdtr, const char *command, + const struct sieve_extension *ext, + const struct sieve_argument_def *tag_def) +{ + /* Add the tag to the persistent tags list if necessary */ + if (tag_def->validate_persistent != NULL) { + struct sieve_command_registration *cmd_reg = + sieve_validator_find_command_registration( + valdtr, command); + + if (cmd_reg == NULL) { + cmd_reg = _sieve_validator_register_command( + valdtr, NULL, NULL, command); + } + + struct sieve_tag_registration *reg; + + if (!array_is_created(&cmd_reg->persistent_tags)) { + p_array_init(&cmd_reg->persistent_tags, + valdtr->pool, 4); + } else { + struct sieve_tag_registration *reg_idx; + + /* Avoid dupplicate registration */ + array_foreach_elem(&cmd_reg->persistent_tags, reg_idx) { + if (reg_idx->tag_def == tag_def) + return; + } + } + + reg = p_new(valdtr->pool, struct sieve_tag_registration, 1); + reg->ext = ext; + reg->tag_def = tag_def; + reg->id_code = -1; + + array_append(&cmd_reg->persistent_tags, ®, 1); + } +} + +void sieve_validator_register_external_tag( + struct sieve_validator *valdtr, const char *command, + const struct sieve_extension *ext, + const struct sieve_argument_def *tag_def, int id_code) +{ + struct sieve_command_registration *cmd_reg = + sieve_validator_find_command_registration(valdtr, command); + + if (cmd_reg == NULL) { + cmd_reg = _sieve_validator_register_command( + valdtr, NULL, NULL, command); + } + + _sieve_validator_register_tag(valdtr, cmd_reg, ext, tag_def, + NULL, id_code); +} + +void sieve_validator_register_tag( + struct sieve_validator *valdtr, + struct sieve_command_registration *cmd_reg, + const struct sieve_extension *ext, + const struct sieve_argument_def *tag_def, int id_code) +{ + if (tag_def->is_instance_of == NULL) { + _sieve_validator_register_tag(valdtr, cmd_reg, ext, tag_def, + NULL, id_code); + } else { + struct sieve_tag_registration *reg = + p_new(valdtr->pool, struct sieve_tag_registration, 1); + reg->ext = ext; + reg->tag_def = tag_def; + reg->id_code = id_code; + + if (!array_is_created(&cmd_reg->instanced_tags)) + p_array_init(&cmd_reg->instanced_tags, valdtr->pool, 4); + + array_append(&cmd_reg->instanced_tags, ®, 1); + } +} + +static void +sieve_validator_register_unknown_tag(struct sieve_validator *valdtr, + struct sieve_command_registration *cmd_reg, + const char *tag) +{ + _sieve_validator_register_tag(valdtr, cmd_reg, NULL, + &_unknown_tag, tag, 0); +} + +static struct sieve_tag_registration * +_sieve_validator_command_tag_get(struct sieve_validator *valdtr, + struct sieve_command *cmd, + const char *tag, void **data) +{ + struct sieve_command_registration *cmd_reg = cmd->reg; + struct sieve_tag_registration * const *regs; + unsigned int i, reg_count; + + /* First check normal tags */ + if (array_is_created(&cmd_reg->normal_tags)) { + regs = array_get(&cmd_reg->normal_tags, ®_count); + + for (i = 0; i < reg_count; i++) { + if (regs[i]->tag_def != NULL && + strcasecmp(regs[i]->identifier, tag) == 0) { + + return regs[i]; + } + } + } + + /* Not found so far, try the instanced tags */ + if (array_is_created(&cmd_reg->instanced_tags)) { + regs = array_get(&cmd_reg->instanced_tags, ®_count); + + for (i = 0; i < reg_count; i++) { + if (regs[i]->tag_def != NULL) { + if (regs[i]->tag_def->is_instance_of( + valdtr, cmd, regs[i]->ext, tag, data)) + return regs[i]; + } + } + } + + return NULL; +} + +static bool +sieve_validator_command_tag_exists(struct sieve_validator *valdtr, + struct sieve_command *cmd, const char *tag) +{ + return (_sieve_validator_command_tag_get(valdtr, cmd, + tag, NULL) != NULL); +} + +static struct sieve_tag_registration * +sieve_validator_command_tag_get(struct sieve_validator *valdtr, + struct sieve_command *cmd, + struct sieve_ast_argument *arg, void **data) +{ + const char *tag = sieve_ast_argument_tag(arg); + + return _sieve_validator_command_tag_get(valdtr, cmd, tag, data); +} + +/* + * Extension support + */ + +static bool +sieve_validator_extensions_check_conficts(struct sieve_validator *valdtr, + struct sieve_ast_argument *ext_arg, + const struct sieve_extension *ext) +{ + struct sieve_validator_extension_reg *ext_reg; + struct sieve_validator_extension_reg *regs; + unsigned int count, i; + + if (ext->id < 0) + return TRUE; + + ext_reg = array_idx_get_space(&valdtr->extensions, + (unsigned int) ext->id); + + regs = array_get_modifiable(&valdtr->extensions, &count); + for (i = 0; i < count; i++) { + bool required = ext_reg->required && regs[i].required; + + if (regs[i].ext == NULL) + continue; + if (regs[i].ext == ext) + continue; + if (!regs[i].loaded) + continue; + + /* Check this extension vs other extension */ + if (ext_reg->valext != NULL && + ext_reg->valext->check_conflict != NULL) { + struct sieve_ast_argument *this_ext_arg = + (ext_arg == NULL ? regs[i].arg : ext_arg); + + if (!ext_reg->valext->check_conflict( + ext, valdtr, ext_reg->context, this_ext_arg, + regs[i].ext, required)) + return FALSE; + } + + /* Check other extension vs this extension */ + if (regs[i].valext != NULL && + regs[i].valext->check_conflict != NULL) { + if (!regs[i].valext->check_conflict( + regs[i].ext, valdtr, regs[i].context, + regs[i].arg, ext, required)) + return FALSE; + } + } + return TRUE; +} + +bool sieve_validator_extension_load(struct sieve_validator *valdtr, + struct sieve_command *cmd, + struct sieve_ast_argument *ext_arg, + const struct sieve_extension *ext, + bool required) +{ + const struct sieve_extension_def *extdef = ext->def; + struct sieve_validator_extension_reg *reg = NULL; + + if (ext->global && + (valdtr->flags & SIEVE_COMPILE_FLAG_NOGLOBAL) != 0) { + const char *cmd_prefix = (cmd == NULL ? "" : + t_strdup_printf("%s %s: ", + sieve_command_identifier(cmd), + sieve_command_type_name(cmd))); + sieve_argument_validate_error( + valdtr, ext_arg, + "%sfailed to load Sieve capability `%s': " + "its use is restricted to global scripts", + cmd_prefix, sieve_extension_name(ext)); + return FALSE; + } + + /* Register extension no matter what and store the + * AST argument registering it */ + if (ext->id >= 0) { + reg = array_idx_get_space(&valdtr->extensions, + (unsigned int)ext->id); + i_assert(reg->ext == NULL || reg->ext == ext); + reg->ext = ext; + reg->required = reg->required || required; + if (reg->arg == NULL) + reg->arg = ext_arg; + } + + if (extdef->validator_load != NULL && + !extdef->validator_load(ext, valdtr)) { + const char *cmd_prefix = (cmd == NULL ? "" : + t_strdup_printf("%s %s: ", + sieve_command_identifier(cmd), + sieve_command_type_name(cmd))); + sieve_argument_validate_error( + valdtr, ext_arg, + "%sfailed to load Sieve capability `%s'", + cmd_prefix, sieve_extension_name(ext)); + return FALSE; + } + + /* Check conflicts with other extensions */ + if (!sieve_validator_extensions_check_conficts(valdtr, ext_arg, ext)) + return FALSE; + + /* Link extension to AST for use at code generation */ + if (reg != NULL) { + sieve_ast_extension_link(valdtr->ast, ext, reg->required); + reg->loaded = TRUE; + } + + return TRUE; +} + +const struct sieve_extension * +sieve_validator_extension_load_by_name(struct sieve_validator *valdtr, + struct sieve_command *cmd, + struct sieve_ast_argument *ext_arg, + const char *ext_name) +{ + const struct sieve_extension *ext; + + ext = sieve_extension_get_by_name(valdtr->svinst, ext_name); + + if (ext == NULL || ext->def == NULL || !ext->enabled) { + unsigned int i; + bool core_test = FALSE; + bool core_command = FALSE; + + for (i = 0; !core_command && i < sieve_core_commands_count; + i++) { + if (strcasecmp(sieve_core_commands[i]->identifier, + ext_name) == 0) + core_command = TRUE; + } + + for (i = 0; !core_test && i < sieve_core_tests_count; i++) { + if (strcasecmp(sieve_core_tests[i]->identifier, + ext_name) == 0) + core_test = TRUE; + } + + if (core_test || core_command) { + sieve_argument_validate_error( + valdtr, ext_arg, + "%s %s: `%s' is not known as a Sieve capability, " + "but it is known as a Sieve %s that is always available", + sieve_command_identifier(cmd), + sieve_command_type_name(cmd), + str_sanitize(ext_name, 128), + (core_test ? "test" : "command")); + } else { + sieve_argument_validate_error( + valdtr, ext_arg, + "%s %s: unknown Sieve capability `%s'", + sieve_command_identifier(cmd), + sieve_command_type_name(cmd), + str_sanitize(ext_name, 128)); + } + return NULL; + } + + if (!sieve_validator_extension_load(valdtr, cmd, ext_arg, ext, TRUE)) + return NULL; + + return ext; +} + +const struct sieve_extension * +sieve_validator_extension_load_implicit(struct sieve_validator *valdtr, + const char *ext_name) +{ + const struct sieve_extension *ext; + + ext = sieve_extension_get_by_name(valdtr->svinst, ext_name); + + if (ext == NULL || ext->def == NULL) + return NULL; + + if (!sieve_validator_extension_load(valdtr, NULL, NULL, ext, TRUE)) + return NULL; + + return ext; +} + +void sieve_validator_extension_register( + struct sieve_validator *valdtr, const struct sieve_extension *ext, + const struct sieve_validator_extension *valext, void *context) +{ + struct sieve_validator_extension_reg *reg; + + if (ext->id < 0) + return; + + reg = array_idx_get_space(&valdtr->extensions, (unsigned int) ext->id); + i_assert(reg->ext == NULL || reg->ext == ext); + reg->ext = ext; + reg->valext = valext; + reg->context = context; +} + +bool sieve_validator_extension_loaded(struct sieve_validator *valdtr, + const struct sieve_extension *ext) +{ + const struct sieve_validator_extension_reg *reg; + + if (ext->id < 0 || ext->id >= (int) array_count(&valdtr->extensions)) + return FALSE; + + reg = array_idx(&valdtr->extensions, (unsigned int) ext->id); + + return (reg->loaded); +} + +void sieve_validator_extension_set_context(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + void *context) +{ + struct sieve_validator_extension_reg *reg; + + if (ext->id < 0) + return; + + reg = array_idx_get_space(&valdtr->extensions, (unsigned int) ext->id); + reg->context = context; +} + +void *sieve_validator_extension_get_context(struct sieve_validator *valdtr, + const struct sieve_extension *ext) +{ + const struct sieve_validator_extension_reg *reg; + + if (ext->id < 0 || ext->id >= (int) array_count(&valdtr->extensions)) + return NULL; + + reg = array_idx(&valdtr->extensions, (unsigned int) ext->id); + + return reg->context; +} + +/* + * Overriding the default literal arguments + */ + +void sieve_validator_argument_override(struct sieve_validator *valdtr, + enum sieve_argument_type type, + const struct sieve_extension *ext, + const struct sieve_argument_def *arg_def) +{ + struct sieve_default_argument *arg; + + if (valdtr->default_arguments[type].arg_def != NULL) { + arg = p_new(valdtr->pool, struct sieve_default_argument, 1); + *arg = valdtr->default_arguments[type]; + + valdtr->default_arguments[type].overrides = arg; + } + + valdtr->default_arguments[type].arg_def = arg_def; + valdtr->default_arguments[type].ext = ext; +} + +static bool +sieve_validator_argument_default_activate(struct sieve_validator *valdtr, + struct sieve_command *cmd, + struct sieve_default_argument *defarg, + struct sieve_ast_argument *arg) +{ + bool result = TRUE; + struct sieve_default_argument *prev_defarg; + + prev_defarg = valdtr->current_defarg; + valdtr->current_defarg = defarg; + + if (arg->argument == NULL) { + arg->argument = sieve_argument_create(arg->ast, defarg->arg_def, + defarg->ext, 0); + } else { + arg->argument->def = defarg->arg_def; + arg->argument->ext = defarg->ext; + } + + if (defarg->arg_def != NULL && defarg->arg_def->validate != NULL) + result = defarg->arg_def->validate(valdtr, &arg, cmd); + + valdtr->current_defarg = prev_defarg; + + return result; +} + +bool sieve_validator_argument_activate_super(struct sieve_validator *valdtr, + struct sieve_command *cmd, + struct sieve_ast_argument *arg, + bool constant ATTR_UNUSED) +{ + struct sieve_default_argument *defarg; + + if (valdtr->current_defarg == NULL || + valdtr->current_defarg->overrides == NULL) + return FALSE; + + if (valdtr->current_defarg->overrides->arg_def == &string_argument) { + switch (valdtr->current_defarg_type) { + case SAT_CONST_STRING: + if (!valdtr->current_defarg_constant) { + valdtr->current_defarg_type = SAT_VAR_STRING; + defarg = &valdtr->default_arguments[SAT_VAR_STRING]; + } else { + defarg = valdtr->current_defarg->overrides; + } + break; + case SAT_VAR_STRING: + defarg = valdtr->current_defarg->overrides; + break; + default: + return FALSE; + } + } else { + defarg = valdtr->current_defarg->overrides; + } + + return sieve_validator_argument_default_activate(valdtr, cmd, + defarg, arg); +} + +/* + * Argument Validation API + */ + +bool sieve_validator_argument_activate(struct sieve_validator *valdtr, + struct sieve_command *cmd, + struct sieve_ast_argument *arg, + bool constant) +{ + struct sieve_default_argument *defarg; + + switch (sieve_ast_argument_type(arg)) { + case SAAT_NUMBER: + valdtr->current_defarg_type = SAT_NUMBER; + break; + case SAAT_STRING: + valdtr->current_defarg_type = SAT_CONST_STRING; + break; + case SAAT_STRING_LIST: + valdtr->current_defarg_type = SAT_STRING_LIST; + break; + default: + return FALSE; + } + + valdtr->current_defarg_constant = constant; + defarg = &valdtr->default_arguments[valdtr->current_defarg_type]; + + if (!constant && defarg->arg_def == &string_argument) { + valdtr->current_defarg_type = SAT_VAR_STRING; + defarg = &valdtr->default_arguments[SAT_VAR_STRING]; + } + + return sieve_validator_argument_default_activate(valdtr, cmd, + defarg, arg); +} + +bool sieve_validate_positional_argument(struct sieve_validator *valdtr, + struct sieve_command *cmd, + struct sieve_ast_argument *arg, + const char *arg_name, + unsigned int arg_pos, + enum sieve_ast_argument_type req_type) +{ + i_assert(arg != NULL); + + if (sieve_ast_argument_type(arg) != req_type && + (sieve_ast_argument_type(arg) != SAAT_STRING || + req_type != SAAT_STRING_LIST)) + { + sieve_argument_validate_error( + valdtr, arg, + "the %s %s expects %s as argument %d (%s), " + "but %s was found", + sieve_command_identifier(cmd), + sieve_command_type_name(cmd), + sieve_ast_argument_type_name(req_type), + arg_pos, arg_name, sieve_ast_argument_name(arg)); + return FALSE; + } + + return TRUE; +} + +bool sieve_validate_tag_parameter(struct sieve_validator *valdtr, + struct sieve_command *cmd, + struct sieve_ast_argument *tag, + struct sieve_ast_argument *param, + const char *arg_name, unsigned int arg_pos, + enum sieve_ast_argument_type req_type, + bool constant) +{ + i_assert(tag != NULL); + + if (param == NULL) { + const char *position = (arg_pos == 0 ? "" : + t_strdup_printf(" %d (%s)", arg_pos, arg_name)); + + sieve_argument_validate_error( + valdtr, tag, + "the :%s tag for the %s %s requires %s as parameter%s, " + "but no parameters were found", + sieve_ast_argument_tag(tag), + sieve_command_identifier(cmd), + sieve_command_type_name(cmd), + sieve_ast_argument_type_name(req_type), position); + return FALSE; + } + + if (sieve_ast_argument_type(param) != req_type && + (sieve_ast_argument_type(param) != SAAT_STRING || + req_type != SAAT_STRING_LIST)) + { + const char *position = (arg_pos == 0 ? "" : + t_strdup_printf(" %d (%s)", arg_pos, arg_name)); + + sieve_argument_validate_error( + valdtr, param, + "the :%s tag for the %s %s requires %s as parameter%s, " + "but %s was found", + sieve_ast_argument_tag(tag), + sieve_command_identifier(cmd), + sieve_command_type_name(cmd), + sieve_ast_argument_type_name(req_type), position, + sieve_ast_argument_name(param)); + return FALSE; + } + + if (!sieve_validator_argument_activate(valdtr, cmd, param, constant)) + return FALSE; + + param->argument->id_code = tag->argument->id_code; + + return TRUE; +} + +/* + * Command argument validation + */ + +static bool +sieve_validate_command_arguments(struct sieve_validator *valdtr, + struct sieve_command *cmd) +{ + int arg_count = cmd->def->positional_args; + int real_count = 0; + struct sieve_ast_argument *arg; + struct sieve_command_registration *cmd_reg = cmd->reg; + + /* Resolve tagged arguments */ + arg = sieve_ast_argument_first(cmd->ast_node); + while (arg != NULL) { + void *arg_data = NULL; + struct sieve_tag_registration *tag_reg; + const struct sieve_argument_def *tag_def; + + if (sieve_ast_argument_type(arg) != SAAT_TAG) { + arg = sieve_ast_argument_next(arg); + continue; + } + + tag_reg = sieve_validator_command_tag_get(valdtr, cmd, + arg, &arg_data); + + if (tag_reg == NULL) { + sieve_argument_validate_error( + valdtr, arg, + "unknown tagged argument ':%s' for the %s %s " + "(reported only once at first occurrence)", + sieve_ast_argument_tag(arg), + sieve_command_identifier(cmd), + sieve_command_type_name(cmd)); + sieve_validator_register_unknown_tag( + valdtr, cmd_reg, sieve_ast_argument_tag(arg)); + return FALSE; + } + + /* Check whether previously tagged as unknown */ + if (_tag_registration_is_unknown(tag_reg)) + return FALSE; + + tag_def = tag_reg->tag_def; + + /* Assign the tagged argument type to the ast for later + reference */ + arg->argument = sieve_argument_create( + arg->ast, tag_def, tag_reg->ext, tag_reg->id_code); + arg->argument->data = arg_data; + + arg = sieve_ast_argument_next(arg); + } + + /* Validate tagged arguments */ + arg = sieve_ast_argument_first(cmd->ast_node); + while (arg != NULL && sieve_ast_argument_type(arg) == SAAT_TAG) { + const struct sieve_argument_def *tag_def = arg->argument->def; + struct sieve_ast_argument *parg; + + /* Scan backwards for any duplicates */ + if ((tag_def->flags & SIEVE_ARGUMENT_FLAG_MULTIPLE) == 0) { + parg = sieve_ast_argument_prev(arg); + while (parg != NULL) { + if ((sieve_ast_argument_type(parg) == SAAT_TAG && + parg->argument->def == tag_def) || + (arg->argument->id_code > 0 && + parg->argument != NULL && + parg->argument->id_code == arg->argument->id_code)) + { + const char *tag_id = sieve_ast_argument_tag(arg); + const char *tag_desc = + strcmp(tag_def->identifier, tag_id) != 0 ? + t_strdup_printf("%s argument (:%s)", + tag_def->identifier, tag_id) : + t_strdup_printf(":%s argument", + tag_def->identifier); + + sieve_argument_validate_error( + valdtr, arg, + "encountered duplicate %s for the %s %s", + tag_desc, sieve_command_identifier(cmd), + sieve_command_type_name(cmd)); + + return FALSE; + } + + parg = sieve_ast_argument_prev(parg); + } + } + + /* Call the validation function for the tag (if present) + Fail if the validation fails: + Let's not whine multiple times about a single command + having multiple bad arguments... + */ + if (tag_def->validate != NULL) { + if (!tag_def->validate(valdtr, &arg, cmd)) + return FALSE; + } else { + arg = sieve_ast_argument_next(arg); + } + } + + /* Remaining arguments should be positional (tags are not allowed + here) */ + cmd->first_positional = arg; + + while (arg != NULL) { + if (sieve_ast_argument_type(arg) == SAAT_TAG) { + sieve_argument_validate_error( + valdtr, arg, + "encountered an unexpected tagged argument ':%s' " + "while validating positional arguments for the %s %s", + sieve_ast_argument_tag(arg), + sieve_command_identifier(cmd), + sieve_command_type_name(cmd)); + return FALSE; + } + + real_count++; + + arg = sieve_ast_argument_next(arg); + } + + /* Check the required count versus the real number of arguments */ + if (arg_count >= 0 && real_count != arg_count) { + sieve_command_validate_error( + valdtr, cmd, + "the %s %s requires %d positional argument(s), " + "but %d is/are specified", + sieve_command_identifier(cmd), + sieve_command_type_name(cmd), + arg_count, real_count); + return FALSE; + } + + /* Call initial validation for persistent arguments */ + if (array_is_created(&cmd_reg->persistent_tags)) { + struct sieve_tag_registration * const *regs; + unsigned int i, reg_count; + + regs = array_get(&cmd_reg->persistent_tags, ®_count); + for (i = 0; i < reg_count; i++) { + const struct sieve_argument_def *tag_def = + regs[i]->tag_def; + + if (tag_def != NULL && + tag_def->validate_persistent != NULL) { + /* To be sure */ + if (!tag_def->validate_persistent( + valdtr, cmd, regs[i]->ext)) + return FALSE; + } + } + } + + return TRUE; +} + +static bool +sieve_validate_arguments_context(struct sieve_validator *valdtr, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *arg = + sieve_command_first_argument(cmd); + + while (arg != NULL) { + const struct sieve_argument *argument = arg->argument; + + if (argument != NULL && argument->def != NULL && + argument->def->validate_context != NULL) { + + if (!argument->def->validate_context(valdtr, arg, cmd)) + return FALSE; + } + + arg = sieve_ast_argument_next(arg); + } + + return TRUE; +} + +/* + * Command Validation API + */ + +static bool +sieve_validate_command_subtests(struct sieve_validator *valdtr, + struct sieve_command *cmd, + const unsigned int count) +{ + switch (count) { + case 0: + if (sieve_ast_test_count(cmd->ast_node) > 0) { + /* Unexpected command specified */ + enum sieve_command_type ctype = SCT_NONE; + struct sieve_command_registration *cmd_reg; + struct sieve_ast_node *test = + sieve_ast_test_first(cmd->ast_node); + + cmd_reg = sieve_validator_find_command_registration( + valdtr, test->identifier); + + /* First check what we are dealing with */ + if (cmd_reg != NULL && cmd_reg->cmd_def != NULL) + ctype = cmd_reg->cmd_def->type; + + switch (ctype) { + case SCT_TEST: /* Spurious test */ + case SCT_HYBRID: + sieve_command_validate_error( + valdtr, cmd, + "the %s %s accepts no sub-tests, " + "but tests are specified", + sieve_command_identifier(cmd), + sieve_command_type_name(cmd)); + break; + case SCT_NONE: /* Unknown command */ + /* Is it perhaps a tag for which the ':' was + omitted ? */ + if (sieve_validator_command_tag_exists( + valdtr, cmd, test->identifier)) { + sieve_command_validate_error( + valdtr, cmd, + "missing colon ':' before ':%s' tag in %s %s", + test->identifier, + sieve_command_identifier(cmd), + sieve_command_type_name(cmd)); + break; + } + /* Fall through */ + case SCT_COMMAND: + sieve_command_validate_error( + valdtr, cmd, + "missing semicolon ';' after %s %s", + sieve_command_identifier(cmd), + sieve_command_type_name(cmd)); + break; + } + return FALSE; + } + break; + case 1: + if (sieve_ast_test_count(cmd->ast_node) == 0) { + sieve_command_validate_error( + valdtr, cmd, + "the %s %s requires one sub-test, " + "but none is specified", + sieve_command_identifier(cmd), + sieve_command_type_name(cmd)); + return FALSE; + + } else if (sieve_ast_test_count(cmd->ast_node) > 1 || + cmd->ast_node->test_list) { + sieve_command_validate_error( + valdtr, cmd, + "the %s %s requires one sub-test, " + "but a list of tests is specified", + sieve_command_identifier(cmd), + sieve_command_type_name(cmd)); + return FALSE; + } + break; + default: + if (sieve_ast_test_count(cmd->ast_node) == 0) { + sieve_command_validate_error( + valdtr, cmd, + "the %s %s requires a list of sub-tests, " + "but none is specified", + sieve_command_identifier(cmd), + sieve_command_type_name(cmd)); + return FALSE; + } else if (sieve_ast_test_count(cmd->ast_node) == 1 && + !cmd->ast_node->test_list) { + sieve_command_validate_error( + valdtr, cmd, + "the %s %s requires a list of sub-tests, " + "but a single test is specified", + sieve_command_identifier(cmd), + sieve_command_type_name(cmd)); + return FALSE; + } + break; + } + + return TRUE; +} + +static bool +sieve_validate_command_block(struct sieve_validator *valdtr, + struct sieve_command *cmd, bool block_allowed, + bool block_required) +{ + i_assert(cmd->ast_node->type == SAT_COMMAND); + + if (block_required) { + if (!cmd->ast_node->block) { + sieve_command_validate_error( + valdtr, cmd, + "the %s command requires a command block, " + "but it is missing", + sieve_command_identifier(cmd)); + return FALSE; + } + } else if (!block_allowed && cmd->ast_node->block) { + sieve_command_validate_error( + valdtr, cmd, + "the %s command does not accept a command block, " + "but one is specified anyway", + sieve_command_identifier(cmd)); + return FALSE; + } + + return TRUE; +} + +/* + * AST Validation + */ + +static bool +sieve_validate_test_list(struct sieve_validator *valdtr, + struct sieve_ast_node *test_list, int *const_r); +static bool +sieve_validate_block(struct sieve_validator *valdtr, + struct sieve_ast_node *block); +static bool +sieve_validate_command(struct sieve_validator *valdtr, + struct sieve_ast_node *cmd_node, int *const_r); + +static bool +sieve_validate_command_context(struct sieve_validator *valdtr, + struct sieve_ast_node *cmd_node) +{ + enum sieve_ast_type ast_type = sieve_ast_node_type(cmd_node); + struct sieve_command_registration *cmd_reg; + + i_assert(ast_type == SAT_TEST || ast_type == SAT_COMMAND); + + /* Verify the command specified by this node */ + cmd_reg = sieve_validator_find_command_registration( + valdtr, cmd_node->identifier); + + if (cmd_reg != NULL && cmd_reg->cmd_def != NULL) { + const struct sieve_command_def *cmd_def = cmd_reg->cmd_def; + + /* Identifier = "" when the command was previously marked as + unknown */ + if (*(cmd_def->identifier) != '\0') { + if ((cmd_def->type == SCT_COMMAND && ast_type == SAT_TEST) || + (cmd_def->type == SCT_TEST && ast_type == SAT_COMMAND)) { + sieve_validator_error( + valdtr, cmd_node->source_line, + "attempted to use %s '%s' as %s", + sieve_command_def_type_name(cmd_def), + cmd_node->identifier, + sieve_ast_type_name(ast_type)); + return FALSE; + } + + cmd_node->command = sieve_command_create( + cmd_node, cmd_reg->ext, cmd_def, cmd_reg); + } else { + return FALSE; + } + } else { + sieve_validator_error( + valdtr, cmd_node->source_line, + "unknown %s '%s' (only reported once at first occurrence)", + sieve_ast_type_name(ast_type), cmd_node->identifier); + + sieve_validator_register_unknown_command( + valdtr, cmd_node->identifier); + return FALSE; + } + + return TRUE; +} + +static bool +sieve_validate_command(struct sieve_validator *valdtr, + struct sieve_ast_node *cmd_node, int *const_r) +{ + enum sieve_ast_type ast_type = sieve_ast_node_type(cmd_node); + struct sieve_command *cmd = + (cmd_node == NULL ? NULL : cmd_node->command); + const struct sieve_command_def *cmd_def = + (cmd != NULL ? cmd->def : NULL); + bool result = TRUE; + + i_assert(ast_type == SAT_TEST || ast_type == SAT_COMMAND); + + if (cmd_def != NULL && *(cmd_def->identifier) != '\0') { + if (cmd_def->pre_validate == NULL || + cmd_def->pre_validate(valdtr, cmd)) { + /* Check argument syntax */ + if (!sieve_validate_command_arguments(valdtr, cmd)) { + result = FALSE; + + /* A missing ':' causes a tag to become a test. + This can be the cause of the arguments + validation failing. Therefore we must produce + an error for the sub-tests as well if + appropriate. */ + (void)sieve_validate_command_subtests( + valdtr, cmd, cmd_def->subtests); + } else if (!sieve_validate_command_subtests( + valdtr, cmd, cmd_def->subtests) || + (ast_type == SAT_COMMAND && + !sieve_validate_command_block( + valdtr, cmd, cmd_def->block_allowed, + cmd_def->block_required))) { + result = FALSE; + } else { + /* Call command validation function if specified + */ + if (cmd_def->validate != NULL) { + result = cmd_def->validate(valdtr, cmd) && + result; + } + } + } else { + /* If pre-validation fails, don't bother to validate + further as context might be missing and doing so is + not very useful for further error reporting anyway */ + return FALSE; + } + + result = result && sieve_validate_arguments_context(valdtr, cmd); + } + + /* + * Descend further into the AST + */ + + if (cmd_def != NULL) { + /* Tests */ + if (cmd_def->subtests > 0) { + if (result || + sieve_errors_more_allowed(valdtr->ehandler)) { + result = sieve_validate_test_list( + valdtr, cmd_node, const_r) && result; + } + } else if (result) { + if (cmd_def->validate_const != NULL) { + (void)cmd_def->validate_const( + valdtr, cmd, const_r, -1); + } else { + *const_r = -1; + } + } + + /* Skip block if result of test is const FALSE */ + if (result && *const_r == 0) + return TRUE; + + /* Command block */ + if (cmd_def->block_allowed && ast_type == SAT_COMMAND && + (result || sieve_errors_more_allowed(valdtr->ehandler))) { + result = sieve_validate_block(valdtr, cmd_node) && + result; + } + } + + return result; +} + +static bool +sieve_validate_test_list(struct sieve_validator *valdtr, + struct sieve_ast_node *test_node, int *const_r) +{ + struct sieve_command *tst = test_node->command; + const struct sieve_command_def *tst_def = + (tst != NULL ? tst->def : NULL); + struct sieve_ast_node *test; + bool result = TRUE; + + if (tst_def != NULL && tst_def->validate_const != NULL) { + if (!tst_def->validate_const(valdtr, tst, const_r, -2)) + return TRUE; + } + + test = sieve_ast_test_first(test_node); + while (test != NULL && + (result || sieve_errors_more_allowed(valdtr->ehandler))) { + int const_value = -2; + + result = sieve_validate_command_context(valdtr, test) && + sieve_validate_command(valdtr, test, &const_value) && + result; + + if (result) { + if (tst_def != NULL && + tst_def->validate_const != NULL) { + if (!tst_def->validate_const( + valdtr, tst, const_r, const_value)) + return TRUE; + } else { + *const_r = -1; + } + } + + if (result && const_value >= 0) + test = sieve_ast_node_detach(test); + else + test = sieve_ast_test_next(test); + } + + return result; +} + +static bool +sieve_validate_block(struct sieve_validator *valdtr, + struct sieve_ast_node *block) +{ + bool result = TRUE, fatal = FALSE; + struct sieve_ast_node *cmd_node, *next; + + T_BEGIN { + cmd_node = sieve_ast_command_first(block); + while (!fatal && cmd_node != NULL && + (result || + sieve_errors_more_allowed(valdtr->ehandler))) { + bool command_success; + int const_value = -2; + + next = sieve_ast_command_next(cmd_node); + + /* Check if this is the first non-require command */ + if (sieve_ast_node_type(block) == SAT_ROOT && + !valdtr->finished_require && + strcasecmp(cmd_node->identifier, + cmd_require.identifier) != 0) { + const struct sieve_validator_extension_reg *extrs; + const struct sieve_extension *const *exts; + unsigned int ext_count, i; + + valdtr->finished_require = TRUE; + + /* Load implicit extensions */ + exts = sieve_extensions_get_all(valdtr->svinst, &ext_count); + for (i = 0; i < ext_count; i++) { + if (exts[i]->implicit) { + (void)sieve_validator_extension_load( + valdtr, NULL, NULL, exts[i], TRUE); + } + } + + /* Validate all 'require'd extensions */ + extrs = array_get(&valdtr->extensions, &ext_count); + for (i = 0; i < ext_count; i++) { + if (extrs[i].loaded && extrs[i].valext != NULL && + extrs[i].valext->validate != NULL) { + if (!extrs[i].valext->validate( + extrs[i].ext, valdtr, + extrs[i].context, extrs[i].arg, + extrs[i].required)) { + fatal = TRUE; + break; + } + } + } + } + + command_success = + sieve_validate_command_context(valdtr, cmd_node); + result = command_success && result; + + result = !fatal && + sieve_validate_command(valdtr, cmd_node, + &const_value) && result; + + cmd_node = next; + } + } T_END; + + return result && !fatal; +} + +bool sieve_validator_run(struct sieve_validator *valdtr) +{ + return sieve_validate_block(valdtr, sieve_ast_root(valdtr->ast)); +} + +/* + * Validator object registry + */ + +struct sieve_validator_object_reg { + const struct sieve_object_def *obj_def; + const struct sieve_extension *ext; +}; + +struct sieve_validator_object_registry { + struct sieve_validator *valdtr; + ARRAY(struct sieve_validator_object_reg) registrations; +}; + +struct sieve_validator_object_registry * +sieve_validator_object_registry_get(struct sieve_validator *valdtr, + const struct sieve_extension *ext) +{ + return (struct sieve_validator_object_registry *) + sieve_validator_extension_get_context(valdtr, ext); +} + +void sieve_validator_object_registry_add( + struct sieve_validator_object_registry *regs, + const struct sieve_extension *ext, + const struct sieve_object_def *obj_def) +{ + struct sieve_validator_object_reg *reg; + + reg = array_append_space(®s->registrations); + reg->ext = ext; + reg->obj_def = obj_def; +} + +bool sieve_validator_object_registry_find( + struct sieve_validator_object_registry *regs, const char *identifier, + struct sieve_object *obj) +{ + unsigned int i; + + for (i = 0; i < array_count(®s->registrations); i++) { + const struct sieve_validator_object_reg *reg = + array_idx(®s->registrations, i); + + if (strcasecmp(reg->obj_def->identifier, identifier) == 0) { + if (obj != NULL) { + obj->def = reg->obj_def; + obj->ext = reg->ext; + } + return TRUE; + } + } + + return FALSE; +} + +struct sieve_validator_object_registry * +sieve_validator_object_registry_create(struct sieve_validator *valdtr) +{ + pool_t pool = valdtr->pool; + struct sieve_validator_object_registry *regs = + p_new(pool, struct sieve_validator_object_registry, 1); + + /* Setup registry */ + p_array_init(®s->registrations, valdtr->pool, 4); + + regs->valdtr = valdtr; + + return regs; +} + +struct sieve_validator_object_registry * +sieve_validator_object_registry_init(struct sieve_validator *valdtr, + const struct sieve_extension *ext) +{ + struct sieve_validator_object_registry *regs = + sieve_validator_object_registry_create(valdtr); + + sieve_validator_extension_set_context(valdtr, ext, regs); + return regs; +} + +/* + * Error handling + */ + +#undef sieve_validator_error +void sieve_validator_error(struct sieve_validator *valdtr, + const char *csrc_filename, unsigned int csrc_linenum, + unsigned int source_line, const char *fmt, ...) +{ + struct sieve_error_params params = { + .log_type = LOG_TYPE_ERROR, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + }; + va_list args; + + params.location = + sieve_error_script_location(valdtr->script, source_line); + + va_start(args, fmt); + sieve_logv(valdtr->ehandler, ¶ms, fmt, args); + va_end(args); +} + +#undef sieve_validator_warning +void sieve_validator_warning(struct sieve_validator *valdtr, + const char *csrc_filename, + unsigned int csrc_linenum, + unsigned int source_line, const char *fmt, ...) +{ + struct sieve_error_params params = { + .log_type = LOG_TYPE_WARNING, + .csrc = { + .filename = csrc_filename, + .linenum = csrc_linenum, + }, + }; + va_list args; + + params.location = + sieve_error_script_location(valdtr->script, source_line); + + va_start(args, fmt); + sieve_logv(valdtr->ehandler, ¶ms, fmt, args); + va_end(args); + +} diff --git a/pigeonhole/src/lib-sieve/sieve-validator.h b/pigeonhole/src/lib-sieve/sieve-validator.h new file mode 100644 index 0000000..21b41cc --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-validator.h @@ -0,0 +1,197 @@ +#ifndef SIEVE_VALIDATOR_H +#define SIEVE_VALIDATOR_H + +#include "lib.h" + +#include "sieve-common.h" + +/* + * Types + */ + +enum sieve_argument_type { + SAT_NUMBER, + SAT_CONST_STRING, + SAT_VAR_STRING, + SAT_STRING_LIST, + + SAT_COUNT +}; + +struct sieve_command_registration; + +/* + * Validator + */ + +struct sieve_validator; + +struct sieve_validator * +sieve_validator_create(struct sieve_ast *ast, + struct sieve_error_handler *ehandler, + enum sieve_compile_flags flags); +void sieve_validator_free(struct sieve_validator **valdtr); +pool_t sieve_validator_pool(struct sieve_validator *valdtr); + +bool sieve_validator_run(struct sieve_validator *valdtr); + +/* + * Accessors + */ + +struct sieve_error_handler * +sieve_validator_error_handler(struct sieve_validator *valdtr); +struct sieve_ast *sieve_validator_ast(struct sieve_validator *valdtr); +struct sieve_script *sieve_validator_script(struct sieve_validator *valdtr); +struct sieve_instance *sieve_validator_svinst(struct sieve_validator *valdtr); +enum sieve_compile_flags +sieve_validator_compile_flags(struct sieve_validator *valdtr); + +/* + * Command/Test registry + */ + +void sieve_validator_register_command(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + const struct sieve_command_def *command); + +/* + * Per-command tagged argument registry + */ + +void sieve_validator_register_tag(struct sieve_validator *valdtr, + struct sieve_command_registration *cmd_reg, + const struct sieve_extension *ext, + const struct sieve_argument_def *tag_def, + int id_code); +void sieve_validator_register_external_tag( + struct sieve_validator *valdtr, const char *command, + const struct sieve_extension *ext, + const struct sieve_argument_def *tag_def, int id_code); +void sieve_validator_register_persistent_tag( + struct sieve_validator *valdtr, const char *command, + const struct sieve_extension *ext, + const struct sieve_argument_def *tag_def); + +/* + * Overriding the default literal arguments + */ + +void sieve_validator_argument_override( + struct sieve_validator *valdtr, enum sieve_argument_type type, + const struct sieve_extension *ext, + const struct sieve_argument_def *arg_def); +bool sieve_validator_argument_activate_super( + struct sieve_validator *valdtr, struct sieve_command *cmd, + struct sieve_ast_argument *arg, bool constant); + +/* + * Argument validation API + */ + +bool sieve_validate_positional_argument(struct sieve_validator *valdtr, + struct sieve_command *cmd, + struct sieve_ast_argument *arg, + const char *arg_name, + unsigned int arg_pos, + enum sieve_ast_argument_type req_type); +bool sieve_validator_argument_activate(struct sieve_validator *valdtr, + struct sieve_command *cmd, + struct sieve_ast_argument *arg, + bool constant); + +bool sieve_validate_tag_parameter(struct sieve_validator *valdtr, + struct sieve_command *cmd, + struct sieve_ast_argument *tag, + struct sieve_ast_argument *param, + const char *arg_name, unsigned int arg_pos, + enum sieve_ast_argument_type req_type, + bool constant); + +/* + * Extension support + */ + +struct sieve_validator_extension { + const struct sieve_extension_def *ext; + + bool (*check_conflict)(const struct sieve_extension *ext, + struct sieve_validator *valdtr, void *context, + struct sieve_ast_argument *require_arg, + const struct sieve_extension *ext_other, + bool required); + bool (*validate)(const struct sieve_extension *ext, + struct sieve_validator *valdtr, void *context, + struct sieve_ast_argument *require_arg, bool required); + + void (*free)(const struct sieve_extension *ext, + struct sieve_validator *valdtr, void *context); +}; + +bool sieve_validator_extension_load(struct sieve_validator *valdtr, + struct sieve_command *cmd, + struct sieve_ast_argument *ext_arg, + const struct sieve_extension *ext, + bool required) ATTR_NULL(2, 3); +const struct sieve_extension * +sieve_validator_extension_load_by_name(struct sieve_validator *valdtr, + struct sieve_command *cmd, + struct sieve_ast_argument *ext_arg, + const char *ext_name); +const struct sieve_extension * +sieve_validator_extension_load_implicit(struct sieve_validator *valdtr, + const char *ext_name); + +void sieve_validator_extension_register( + struct sieve_validator *valdtr, const struct sieve_extension *ext, + const struct sieve_validator_extension *valext, void *context); +bool sieve_validator_extension_loaded(struct sieve_validator *valdtr, + const struct sieve_extension *ext); + +void sieve_validator_extension_set_context(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + void *context); +void *sieve_validator_extension_get_context(struct sieve_validator *valdtr, + const struct sieve_extension *ext); + +/* + * Validator object registry + */ + +struct sieve_validator_object_registry; + +struct sieve_validator_object_registry * +sieve_validator_object_registry_get(struct sieve_validator *valdtr, + const struct sieve_extension *ext); +void sieve_validator_object_registry_add( + struct sieve_validator_object_registry *regs, + const struct sieve_extension *ext, + const struct sieve_object_def *obj_def); +bool sieve_validator_object_registry_find( + struct sieve_validator_object_registry *regs, const char *identifier, + struct sieve_object *obj); +struct sieve_validator_object_registry * +sieve_validator_object_registry_create(struct sieve_validator *valdtr); +struct sieve_validator_object_registry * +sieve_validator_object_registry_init(struct sieve_validator *valdtr, + const struct sieve_extension *ext); + +/* + * Error handling + */ + +void sieve_validator_error(struct sieve_validator *valdtr, + const char *csrc_filename, unsigned int csrc_linenum, + unsigned int source_line, const char *fmt, ...) + ATTR_FORMAT(5, 6); +#define sieve_validator_error(valdtr, ...) \ + sieve_validator_error(valdtr, __FILE__, __LINE__, __VA_ARGS__) +void sieve_validator_warning(struct sieve_validator *valdtr, + const char *csrc_filename, + unsigned int csrc_linenum, + unsigned int source_line, const char *fmt, ...) + ATTR_FORMAT(5, 6); +#define sieve_validator_warning(valdtr, ...) \ + sieve_validator_warning(valdtr, __FILE__, __LINE__, __VA_ARGS__) + +#endif diff --git a/pigeonhole/src/lib-sieve/sieve.c b/pigeonhole/src/lib-sieve/sieve.c new file mode 100644 index 0000000..69827a9 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve.c @@ -0,0 +1,1303 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "istream.h" +#include "ostream.h" +#include "buffer.h" +#include "time-util.h" +#include "eacces-error.h" +#include "home-expand.h" +#include "hostpid.h" +#include "message-address.h" +#include "mail-user.h" + +#include "sieve-settings.h" +#include "sieve-extensions.h" +#include "sieve-plugins.h" + +#include "sieve-address.h" +#include "sieve-script.h" +#include "sieve-storage-private.h" +#include "sieve-ast.h" +#include "sieve-binary.h" +#include "sieve-actions.h" +#include "sieve-result.h" + +#include "sieve-parser.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-binary-dumper.h" + +#include "sieve.h" +#include "sieve-common.h" +#include "sieve-limits.h" +#include "sieve-error-private.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <dirent.h> + +struct event_category event_category_sieve = { + .name = "sieve", +}; + +/* + * Main Sieve library interface + */ + +struct sieve_instance * +sieve_init(const struct sieve_environment *env, + const struct sieve_callbacks *callbacks, void *context, bool debug) +{ + struct sieve_instance *svinst; + const char *domain; + pool_t pool; + + /* Create Sieve engine instance */ + pool = pool_alloconly_create("sieve", 8192); + svinst = p_new(pool, struct sieve_instance, 1); + svinst->pool = pool; + svinst->callbacks = callbacks; + svinst->context = context; + svinst->debug = debug; + svinst->base_dir = p_strdup_empty(pool, env->base_dir); + svinst->username = p_strdup_empty(pool, env->username); + svinst->home_dir = p_strdup_empty(pool, env->home_dir); + svinst->temp_dir = p_strdup_empty(pool, env->temp_dir); + svinst->flags = env->flags; + svinst->env_location = env->location; + svinst->delivery_phase = env->delivery_phase; + + svinst->event = event_create(env->event_parent); + event_add_category(svinst->event, &event_category_sieve); + event_set_forced_debug(svinst->event, debug); + event_set_append_log_prefix(svinst->event, "sieve: "); + event_add_str(svinst->event, "user", env->username); + + /* Determine domain */ + if (env->domainname != NULL && *(env->domainname) != '\0') + domain = env->domainname; + else { + /* Fall back to parsing username localpart@domain */ + domain = svinst->username == NULL ? NULL : + strchr(svinst->username, '@'); + if (domain == NULL || *(domain+1) == '\0') { + /* Fall back to parsing hostname host.domain */ + domain = (env->hostname != NULL ? + strchr(env->hostname, '.') : NULL); + if (domain == NULL || *(domain+1) == '\0' || + strchr(domain+1, '.') == NULL) { + /* Fall back to bare hostname */ + domain = env->hostname; + } else { + domain++; + } + } else { + domain++; + } + } + svinst->hostname = p_strdup_empty(pool, env->hostname); + svinst->domainname = p_strdup(pool, domain); + + sieve_errors_init(svinst); + + e_debug(svinst->event, "%s version %s initializing", + PIGEONHOLE_NAME, PIGEONHOLE_VERSION_FULL); + + /* Read configuration */ + + sieve_settings_load(svinst); + + /* Initialize extensions */ + if (!sieve_extensions_init(svinst)) { + sieve_deinit(&svinst); + return NULL; + } + + /* Initialize storage classes */ + sieve_storages_init(svinst); + + /* Initialize plugins */ + sieve_plugins_load(svinst, NULL, NULL); + + /* Configure extensions */ + sieve_extensions_configure(svinst); + + return svinst; +} + +void sieve_deinit(struct sieve_instance **_svinst) +{ + struct sieve_instance *svinst = *_svinst; + + sieve_plugins_unload(svinst); + sieve_storages_deinit(svinst); + sieve_extensions_deinit(svinst); + sieve_errors_deinit(svinst); + + event_unref(&svinst->event); + + pool_unref(&(svinst)->pool); + *_svinst = NULL; +} + +void sieve_set_extensions(struct sieve_instance *svinst, const char *extensions) +{ + sieve_extensions_set_string(svinst, extensions, FALSE, FALSE); +} + +const char * +sieve_get_capabilities(struct sieve_instance *svinst, const char *name) +{ + if (name == NULL || *name == '\0') + return sieve_extensions_get_string(svinst); + + return sieve_extension_capabilities_get_string(svinst, name); +} + +struct event *sieve_get_event(struct sieve_instance *svinst) +{ + return svinst->event; +} + +/* + * Low-level compiler functions + */ + +struct sieve_ast * +sieve_parse(struct sieve_script *script, struct sieve_error_handler *ehandler, + enum sieve_error *error_r) +{ + struct sieve_parser *parser; + struct sieve_ast *ast = NULL; + + /* Parse */ + parser = sieve_parser_create(script, ehandler, error_r); + if (parser == NULL) + return NULL; + + if (!sieve_parser_run(parser, &ast)) + ast = NULL; + else + sieve_ast_ref(ast); + + sieve_parser_free(&parser); + + if (error_r != NULL) { + if (ast == NULL) + *error_r = SIEVE_ERROR_NOT_VALID; + else + *error_r = SIEVE_ERROR_NONE; + } + return ast; +} + +bool sieve_validate(struct sieve_ast *ast, struct sieve_error_handler *ehandler, + enum sieve_compile_flags flags, enum sieve_error *error_r) +{ + bool result = TRUE; + struct sieve_validator *validator = + sieve_validator_create(ast, ehandler, flags); + + if (!sieve_validator_run(validator)) + result = FALSE; + + sieve_validator_free(&validator); + + if (error_r != NULL) { + if (!result) + *error_r = SIEVE_ERROR_NOT_VALID; + else + *error_r = SIEVE_ERROR_NONE; + } + return result; +} + +static struct sieve_binary * +sieve_generate(struct sieve_ast *ast, struct sieve_error_handler *ehandler, + enum sieve_compile_flags flags, enum sieve_error *error_r) +{ + struct sieve_generator *generator = + sieve_generator_create(ast, ehandler, flags); + struct sieve_binary *sbin = NULL; + + sbin = sieve_generator_run(generator, NULL); + + sieve_generator_free(&generator); + + if (error_r != NULL) { + if (sbin == NULL) + *error_r = SIEVE_ERROR_NOT_VALID; + else + *error_r = SIEVE_ERROR_NONE; + } + return sbin; +} + +/* + * Sieve compilation + */ + +struct sieve_binary * +sieve_compile_script(struct sieve_script *script, + struct sieve_error_handler *ehandler, + enum sieve_compile_flags flags, + enum sieve_error *error_r) +{ + struct sieve_ast *ast; + struct sieve_binary *sbin; + enum sieve_error error, *errorp; + + if (error_r != NULL) + errorp = error_r; + else + errorp = &error; + *errorp = SIEVE_ERROR_NONE; + + /* Parse */ + ast = sieve_parse(script, ehandler, errorp); + if (ast == NULL) { + switch (*errorp) { + case SIEVE_ERROR_NOT_FOUND: + if (error_r == NULL) { + sieve_error(ehandler, sieve_script_name(script), + "script not found"); + } + break; + default: + sieve_error(ehandler, sieve_script_name(script), + "parse failed"); + } + return NULL; + } + + /* Validate */ + if (!sieve_validate(ast, ehandler, flags, errorp)) { + sieve_error(ehandler, sieve_script_name(script), + "validation failed"); + + sieve_ast_unref(&ast); + return NULL; + } + + /* Generate */ + sbin = sieve_generate(ast, ehandler, flags, errorp); + if (sbin == NULL) { + sieve_error(ehandler, sieve_script_name(script), + "code generation failed"); + sieve_ast_unref(&ast); + return NULL; + } + + /* Cleanup */ + sieve_ast_unref(&ast); + return sbin; +} + +struct sieve_binary * +sieve_compile(struct sieve_instance *svinst, const char *script_location, + const char *script_name, struct sieve_error_handler *ehandler, + enum sieve_compile_flags flags, enum sieve_error *error_r) +{ + struct sieve_script *script; + struct sieve_binary *sbin; + enum sieve_error error; + + script = sieve_script_create_open(svinst, script_location, + script_name, &error); + if (script == NULL) { + if (error_r != NULL) + *error_r = error; + switch (error) { + case SIEVE_ERROR_NOT_FOUND: + sieve_error(ehandler, script_name, "script not found"); + break; + default: + sieve_internal_error(ehandler, script_name, + "failed to open script"); + } + return NULL; + } + + sbin = sieve_compile_script(script, ehandler, flags, error_r); + if (sbin != NULL) { + e_debug(svinst->event, + "Script `%s' from %s successfully compiled", + sieve_script_name(script), + sieve_script_location(script)); + } + + sieve_script_unref(&script); + return sbin; +} + +/* + * Sieve runtime + */ + +static int +sieve_run(struct sieve_binary *sbin, struct sieve_result *result, + struct sieve_execute_env *eenv, struct sieve_error_handler *ehandler) +{ + struct sieve_interpreter *interp; + int ret = 0; + + /* Create the interpreter */ + interp = sieve_interpreter_create(sbin, NULL, eenv, ehandler); + if (interp == NULL) + return SIEVE_EXEC_BIN_CORRUPT; + + /* Run the interpreter */ + ret = sieve_interpreter_run(interp, result); + + /* Free the interpreter */ + sieve_interpreter_free(&interp); + + return ret; +} + +/* + * Reading/writing sieve binaries + */ + +struct sieve_binary * +sieve_load(struct sieve_instance *svinst, const char *bin_path, + enum sieve_error *error_r) +{ + return sieve_binary_open(svinst, bin_path, NULL, error_r); +} + +static struct sieve_binary * +sieve_open_script_real(struct sieve_script *script, + struct sieve_error_handler *ehandler, + enum sieve_compile_flags flags, + enum sieve_error *error_r) +{ + struct sieve_instance *svinst = sieve_script_svinst(script); + struct sieve_resource_usage rusage; + struct sieve_binary *sbin; + enum sieve_error error; + const char *errorstr = NULL; + int ret; + + if (error_r == NULL) + error_r = &error; + + sieve_resource_usage_init(&rusage); + + /* Try to open the matching binary */ + sbin = sieve_script_binary_load(script, error_r); + if (sbin != NULL) { + sieve_binary_get_resource_usage(sbin, &rusage); + + /* Ok, it exists; now let's see if it is up to date */ + if (!sieve_resource_usage_is_excessive(svinst, &rusage) && + !sieve_binary_up_to_date(sbin, flags)) { + /* Not up to date */ + e_debug(svinst->event, + "Script binary %s is not up-to-date", + sieve_binary_path(sbin)); + sieve_binary_close(&sbin); + } + } + + /* If the binary does not exist or is not up-to-date, we need + * to (re-)compile. + */ + if (sbin != NULL) { + e_debug(svinst->event, + "Script binary %s successfully loaded", + sieve_binary_path(sbin)); + } else { + sbin = sieve_compile_script(script, ehandler, flags, error_r); + if (sbin == NULL) + return NULL; + + e_debug(svinst->event, + "Script `%s' from %s successfully compiled", + sieve_script_name(script), + sieve_script_location(script)); + + sieve_binary_set_resource_usage(sbin, &rusage); + } + + /* Check whether binary can be executed. */ + ret = sieve_binary_check_executable(sbin, error_r, &errorstr); + if (ret <= 0) { + const char *path = sieve_binary_path(sbin); + + if (path != NULL) { + e_debug(svinst->event, + "Script binary %s cannot be executed", + path); + } else { + e_debug(svinst->event, + "Script binary from %s cannot be executed", + sieve_binary_source(sbin)); + } + if (ret < 0) { + sieve_internal_error(ehandler, + sieve_script_name(script), + "failed to open script"); + } else { + sieve_error(ehandler, sieve_script_name(script), + "%s", errorstr); + } + sieve_binary_close(&sbin); + } + + return sbin; +} + +struct sieve_binary * +sieve_open_script(struct sieve_script *script, + struct sieve_error_handler *ehandler, + enum sieve_compile_flags flags, enum sieve_error *error_r) +{ + struct sieve_binary *sbin; + + T_BEGIN { + sbin = sieve_open_script_real(script, ehandler, flags, error_r); + } T_END; + + return sbin; +} + +struct sieve_binary * +sieve_open(struct sieve_instance *svinst, const char *script_location, + const char *script_name, struct sieve_error_handler *ehandler, + enum sieve_compile_flags flags, enum sieve_error *error_r) +{ + struct sieve_script *script; + struct sieve_binary *sbin; + enum sieve_error error; + + /* First open the scriptfile itself */ + script = sieve_script_create_open(svinst, script_location, + script_name, &error); + if (script == NULL) { + /* Failed */ + if (error_r != NULL) + *error_r = error; + switch (error) { + case SIEVE_ERROR_NOT_FOUND: + sieve_error(ehandler, script_name, "script not found"); + break; + default: + sieve_internal_error(ehandler, script_name, + "failed to open script"); + } + return NULL; + } + + sbin = sieve_open_script(script, ehandler, flags, error_r); + + /* Drop script reference, if sbin != NULL it holds a reference of its own. + * Otherwise the script object is freed here. + */ + sieve_script_unref(&script); + + return sbin; +} + +const char *sieve_get_source(struct sieve_binary *sbin) +{ + return sieve_binary_source(sbin); +} + +bool sieve_is_loaded(struct sieve_binary *sbin) +{ + return sieve_binary_loaded(sbin); +} + +int sieve_save_as(struct sieve_binary *sbin, const char *bin_path, bool update, + mode_t save_mode, enum sieve_error *error_r) +{ + if (bin_path == NULL) + return sieve_save(sbin, update, error_r); + + return sieve_binary_save(sbin, bin_path, update, save_mode, error_r); +} + +int sieve_save(struct sieve_binary *sbin, bool update, + enum sieve_error *error_r) +{ + struct sieve_script *script = sieve_binary_script(sbin); + + if (script == NULL) + return sieve_binary_save(sbin, NULL, update, 0600, error_r); + + return sieve_script_binary_save(script, sbin, update, error_r); +} + +bool sieve_record_resource_usage(struct sieve_binary *sbin, + const struct sieve_resource_usage *rusage) +{ + return sieve_binary_record_resource_usage(sbin, rusage); +} + +int sieve_check_executable(struct sieve_binary *sbin, + enum sieve_error *error_r, + const char **client_error_r) +{ + return sieve_binary_check_executable(sbin, error_r, client_error_r); +} + +void sieve_close(struct sieve_binary **_sbin) +{ + sieve_binary_close(_sbin); +} + +/* + * Debugging + */ + +void sieve_dump(struct sieve_binary *sbin, struct ostream *stream, bool verbose) +{ + struct sieve_binary_dumper *dumpr = sieve_binary_dumper_create(sbin); + + sieve_binary_dumper_run(dumpr, stream, verbose); + + sieve_binary_dumper_free(&dumpr); +} + +void sieve_hexdump(struct sieve_binary *sbin, struct ostream *stream) +{ + struct sieve_binary_dumper *dumpr = sieve_binary_dumper_create(sbin); + + sieve_binary_dumper_hexdump(dumpr, stream); + + sieve_binary_dumper_free(&dumpr); +} + +int sieve_test(struct sieve_binary *sbin, + const struct sieve_message_data *msgdata, + const struct sieve_script_env *senv, + struct sieve_error_handler *ehandler, struct ostream *stream, + enum sieve_execute_flags flags) +{ + struct sieve_instance *svinst = sieve_binary_svinst(sbin); + struct sieve_result *result; + struct sieve_execute_env eenv; + pool_t pool; + int ret; + + pool = pool_alloconly_create("sieve execution", 4096); + sieve_execute_init(&eenv, svinst, pool, msgdata, senv, flags); + + /* Create result object */ + result = sieve_result_create(svinst, pool, &eenv); + + /* Run the script */ + ret = sieve_run(sbin, result, &eenv, ehandler); + + /* Print result if successful */ + if (ret > 0) { + ret = (sieve_result_print(result, senv, stream, NULL) ? + SIEVE_EXEC_OK : SIEVE_EXEC_FAILURE); + } + + /* Cleanup */ + if (result != NULL) + sieve_result_unref(&result); + sieve_execute_deinit(&eenv); + pool_unref(&pool); + + return ret; +} + +/* + * Script execution + */ + +int sieve_script_env_init(struct sieve_script_env *senv, struct mail_user *user, + const char **error_r) +{ + const struct message_address *postmaster; + const char *error; + + if (!mail_user_get_postmaster_address(user, &postmaster, &error)) { + *error_r = t_strdup_printf( + "Invalid postmaster_address: %s", error); + return -1; + } + + i_zero(senv); + senv->user = user; + senv->postmaster_address = postmaster; + return 0; +} + +int sieve_execute(struct sieve_binary *sbin, + const struct sieve_message_data *msgdata, + const struct sieve_script_env *senv, + struct sieve_error_handler *exec_ehandler, + struct sieve_error_handler *action_ehandler, + enum sieve_execute_flags flags) +{ + struct sieve_instance *svinst = sieve_binary_svinst(sbin); + struct sieve_result *result = NULL; + struct sieve_result_execution *rexec; + struct sieve_execute_env eenv; + pool_t pool; + int ret; + + pool = pool_alloconly_create("sieve execution", 4096); + sieve_execute_init(&eenv, svinst, pool, msgdata, senv, flags); + + /* Create result object */ + result = sieve_result_create(svinst, pool, &eenv); + + /* Run the script */ + ret = sieve_run(sbin, result, &eenv, exec_ehandler); + + rexec = sieve_result_execution_create(result, pool); + + /* Evaluate status and execute the result: + Strange situations, e.g. currupt binaries, must be handled by the + caller. In that case no implicit keep is attempted, because the + situation may be resolved. + */ + ret = sieve_result_execute(rexec, ret, TRUE, action_ehandler, NULL); + + sieve_result_execution_destroy(&rexec); + + /* Cleanup */ + if (result != NULL) + sieve_result_unref(&result); + sieve_execute_finish(&eenv, ret); + sieve_execute_deinit(&eenv); + pool_unref(&pool); + + return ret; +} + +/* + * Multiscript support + */ + +struct sieve_multiscript { + pool_t pool; + struct sieve_execute_env exec_env; + struct sieve_result *result; + struct sieve_result_execution *rexec; + struct event *event; + + int status; + bool keep; + + struct ostream *teststream; + + bool active:1; + bool discard_handled:1; +}; + +struct sieve_multiscript * +sieve_multiscript_start_execute(struct sieve_instance *svinst, + const struct sieve_message_data *msgdata, + const struct sieve_script_env *senv) +{ + pool_t pool; + struct sieve_result *result; + struct sieve_multiscript *mscript; + + pool = pool_alloconly_create("sieve execution", 4096); + mscript = p_new(pool, struct sieve_multiscript, 1); + mscript->pool = pool; + sieve_execute_init(&mscript->exec_env, svinst, pool, msgdata, senv, 0); + + mscript->event = event_create(mscript->exec_env.event); + event_set_append_log_prefix(mscript->event, "multi-script: "); + + result = sieve_result_create(svinst, pool, &mscript->exec_env); + sieve_result_set_keep_action(result, NULL, NULL); + mscript->result = result; + + mscript->rexec = sieve_result_execution_create(result, pool); + + mscript->status = SIEVE_EXEC_OK; + mscript->active = TRUE; + mscript->keep = TRUE; + + e_debug(mscript->event, "Start execute sequence"); + + return mscript; +} + +static void sieve_multiscript_destroy(struct sieve_multiscript **_mscript) +{ + struct sieve_multiscript *mscript = *_mscript; + + if (mscript == NULL) + return; + *_mscript = NULL; + + e_debug(mscript->event, "Destroy"); + + event_unref(&mscript->event); + + sieve_result_execution_destroy(&mscript->rexec); + sieve_result_unref(&mscript->result); + sieve_execute_deinit(&mscript->exec_env); + pool_unref(&mscript->pool); +} + +struct sieve_multiscript * +sieve_multiscript_start_test(struct sieve_instance *svinst, + const struct sieve_message_data *msgdata, + const struct sieve_script_env *senv, + struct ostream *stream) +{ + struct sieve_multiscript *mscript = + sieve_multiscript_start_execute(svinst, msgdata, senv); + + mscript->teststream = stream; + + return mscript; +} + +static void +sieve_multiscript_test(struct sieve_multiscript *mscript) +{ + const struct sieve_script_env *senv = mscript->exec_env.scriptenv; + + e_debug(mscript->event, "Test result"); + + if (mscript->status > 0) { + mscript->status = + (sieve_result_print(mscript->result, senv, + mscript->teststream, + &mscript->keep) ? + SIEVE_EXEC_OK : SIEVE_EXEC_FAILURE); + } else { + mscript->keep = TRUE; + } + + sieve_result_mark_executed(mscript->result); +} + +static void +sieve_multiscript_execute(struct sieve_multiscript *mscript, + struct sieve_error_handler *ehandler, + enum sieve_execute_flags flags) +{ + e_debug(mscript->event, "Execute result"); + + mscript->exec_env.flags = flags; + + if (mscript->status > 0) { + mscript->status = sieve_result_execute(mscript->rexec, + SIEVE_EXEC_OK, FALSE, + ehandler, + &mscript->keep); + } +} + +bool sieve_multiscript_run(struct sieve_multiscript *mscript, + struct sieve_binary *sbin, + struct sieve_error_handler *exec_ehandler, + struct sieve_error_handler *action_ehandler, + enum sieve_execute_flags flags) +{ + if (!mscript->active) { + e_debug(mscript->event, "Sequence ended"); + return FALSE; + } + + e_debug(mscript->event, "Run script `%s'", sieve_binary_source(sbin)); + + /* Run the script */ + mscript->exec_env.flags = flags; + mscript->status = sieve_run(sbin, mscript->result, &mscript->exec_env, + exec_ehandler); + + if (mscript->status >= 0) { + mscript->keep = FALSE; + + if (mscript->teststream != NULL) + sieve_multiscript_test(mscript); + else { + sieve_multiscript_execute(mscript, action_ehandler, + flags); + } + if (!mscript->keep) + mscript->active = FALSE; + } + + if (!mscript->active || mscript->status <= 0) { + e_debug(mscript->event, "Sequence ended"); + mscript->active = FALSE; + return FALSE; + } + + e_debug(mscript->event, "Sequence active"); + return TRUE; +} + +bool sieve_multiscript_will_discard(struct sieve_multiscript *mscript) +{ + return (!mscript->active && mscript->status == SIEVE_EXEC_OK && + !sieve_result_executed_delivery(mscript->rexec)); +} + +void sieve_multiscript_run_discard(struct sieve_multiscript *mscript, + struct sieve_binary *sbin, + struct sieve_error_handler *exec_ehandler, + struct sieve_error_handler *action_ehandler, + enum sieve_execute_flags flags) +{ + if (!sieve_multiscript_will_discard(mscript)) { + e_debug(mscript->event, "Not running discard script"); + return; + } + i_assert(!mscript->discard_handled); + + e_debug(mscript->event, "Run discard script `%s'", + sieve_binary_source(sbin)); + + sieve_result_set_keep_action(mscript->result, NULL, &act_store); + + /* Run the discard script */ + flags |= SIEVE_EXECUTE_FLAG_DEFER_KEEP; + mscript->exec_env.flags = flags; + mscript->status = sieve_run(sbin, mscript->result, &mscript->exec_env, + exec_ehandler); + + if (mscript->status >= 0) { + mscript->keep = FALSE; + + if (mscript->teststream != NULL) + sieve_multiscript_test(mscript); + else { + sieve_multiscript_execute(mscript, action_ehandler, + flags); + } + if (mscript->status == SIEVE_EXEC_FAILURE) + mscript->status = SIEVE_EXEC_KEEP_FAILED; + mscript->active = FALSE; + } + + mscript->discard_handled = TRUE; +} + +int sieve_multiscript_status(struct sieve_multiscript *mscript) +{ + return mscript->status; +} + +int sieve_multiscript_finish(struct sieve_multiscript **_mscript, + struct sieve_error_handler *action_ehandler, + enum sieve_execute_flags flags, int status) +{ + struct sieve_multiscript *mscript = *_mscript; + + if (mscript == NULL) + return SIEVE_EXEC_OK; + *_mscript = NULL; + + switch (status) { + case SIEVE_EXEC_OK: + status = mscript->status; + break; + case SIEVE_EXEC_TEMP_FAILURE: + break; + case SIEVE_EXEC_BIN_CORRUPT: + case SIEVE_EXEC_FAILURE: + case SIEVE_EXEC_KEEP_FAILED: + case SIEVE_EXEC_RESOURCE_LIMIT: + if (mscript->status == SIEVE_EXEC_TEMP_FAILURE) + status = mscript->status; + break; + } + + e_debug(mscript->event, "Finishing sequence (status=%s)", + sieve_execution_exitcode_to_str(status)); + + mscript->exec_env.flags = flags; + sieve_result_set_keep_action(mscript->result, NULL, &act_store); + + mscript->keep = FALSE; + if (mscript->teststream != NULL) + mscript->keep = TRUE; + else { + status = sieve_result_execute( + mscript->rexec, status, TRUE, action_ehandler, + &mscript->keep); + } + + e_debug(mscript->event, "Sequence finished (status=%s, keep=%s)", + sieve_execution_exitcode_to_str(status), + (mscript->keep ? "yes" : "no")); + + sieve_execute_finish(&mscript->exec_env, status); + + /* Cleanup */ + sieve_multiscript_destroy(&mscript); + + return status; +} + +/* + * Configured Limits + */ + +unsigned int sieve_max_redirects(struct sieve_instance *svinst) +{ + return svinst->max_redirects; +} + +unsigned int sieve_max_actions(struct sieve_instance *svinst) +{ + return svinst->max_actions; +} + +size_t sieve_max_script_size(struct sieve_instance *svinst) +{ + return svinst->max_script_size; +} + +/* + * User log + */ + +const char * +sieve_user_get_log_path(struct sieve_instance *svinst, + struct sieve_script *user_script) +{ + const char *log_path = NULL; + + /* Determine user log file path */ + log_path = sieve_setting_get(svinst, "sieve_user_log"); + if (log_path == NULL) { + const char *path; + + if (user_script == NULL || + (path = sieve_file_script_get_path(user_script)) == NULL) { + /* Default */ + if (svinst->home_dir != NULL) { + log_path = t_strconcat( + svinst->home_dir, "/.dovecot.sieve.log", + NULL); + } + } else { + /* Use script file as a base (legacy behavior) */ + log_path = t_strconcat(path, ".log", NULL); + } + } else if (svinst->home_dir != NULL) { + /* Expand home dir if necessary */ + if (log_path[0] == '~') { + log_path = home_expand_tilde(log_path, + svinst->home_dir); + } else if (log_path[0] != '/') { + log_path = t_strconcat(svinst->home_dir, "/", + log_path, NULL); + } + } + return log_path; +} + +/* + * Script trace log + */ + +struct sieve_trace_log { + struct ostream *output; +}; + +int sieve_trace_log_create(struct sieve_instance *svinst, const char *path, + struct sieve_trace_log **trace_log_r) +{ + struct sieve_trace_log *trace_log; + struct ostream *output; + int fd; + + *trace_log_r = NULL; + + if (path == NULL) + output = o_stream_create_fd(1, 0); + else { + fd = open(path, O_CREAT | O_APPEND | O_WRONLY, 0600); + if (fd == -1) { + e_error(svinst->event, "trace: " + "creat(%s) failed: %m", path); + return -1; + } + output = o_stream_create_fd_autoclose(&fd, 0); + o_stream_set_name(output, path); + } + + trace_log = i_new(struct sieve_trace_log, 1); + trace_log->output = output; + + *trace_log_r = trace_log; + return 0; +} + +int sieve_trace_log_create_dir(struct sieve_instance *svinst, const char *dir, + struct sieve_trace_log **trace_log_r) +{ + static unsigned int counter = 0; + const char *timestamp, *prefix; + struct stat st; + + *trace_log_r = NULL; + + if (stat(dir, &st) < 0) { + if (errno != ENOENT && errno != EACCES) { + e_error(svinst->event, "trace: " + "stat(%s) failed: %m", dir); + } + return -1; + } + + timestamp = t_strflocaltime("%Y%m%d-%H%M%S", ioloop_time); + + counter++; + + prefix = t_strdup_printf("%s/%s.%s.%u.trace", + dir, timestamp, my_pid, counter); + return sieve_trace_log_create(svinst, prefix, trace_log_r); +} + +int sieve_trace_log_open(struct sieve_instance *svinst, + struct sieve_trace_log **trace_log_r) +{ + const char *trace_dir = + sieve_setting_get(svinst, "sieve_trace_dir"); + + *trace_log_r = NULL; + if (trace_dir == NULL) + return -1; + + if (svinst->home_dir != NULL) { + /* Expand home dir if necessary */ + if (trace_dir[0] == '~') { + trace_dir = home_expand_tilde(trace_dir, + svinst->home_dir); + } else if (trace_dir[0] != '/') { + trace_dir = t_strconcat(svinst->home_dir, "/", + trace_dir, NULL); + } + } + + return sieve_trace_log_create_dir(svinst, trace_dir, trace_log_r); +} + +void sieve_trace_log_write_line(struct sieve_trace_log *trace_log, + const string_t *line) +{ + struct const_iovec iov[2]; + + if (line == NULL) { + o_stream_nsend_str(trace_log->output, "\n"); + return; + } + + memset(iov, 0, sizeof(iov)); + iov[0].iov_base = str_data(line); + iov[0].iov_len = str_len(line); + iov[1].iov_base = "\n"; + iov[1].iov_len = 1; + o_stream_nsendv(trace_log->output, iov, 2); +} + +void sieve_trace_log_printf(struct sieve_trace_log *trace_log, + const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + T_BEGIN { + o_stream_nsend_str(trace_log->output, + t_strdup_vprintf(fmt, args)); + } T_END; + va_end(args); +} + +void sieve_trace_log_free(struct sieve_trace_log **_trace_log) +{ + struct sieve_trace_log *trace_log = *_trace_log; + + *_trace_log = NULL; + + if (o_stream_finish(trace_log->output) < 0) { + i_error("write(%s) failed: %s", + o_stream_get_name(trace_log->output), + o_stream_get_error(trace_log->output)); + } + o_stream_destroy(&trace_log->output); + i_free(trace_log); +} + +int sieve_trace_config_get(struct sieve_instance *svinst, + struct sieve_trace_config *tr_config) +{ + const char *tr_level = + sieve_setting_get(svinst, "sieve_trace_level"); + bool tr_debug, tr_addresses; + + i_zero(tr_config); + + if (tr_level == NULL || *tr_level == '\0' || + strcasecmp(tr_level, "none") == 0) + return -1; + + if (strcasecmp(tr_level, "actions") == 0) + tr_config->level = SIEVE_TRLVL_ACTIONS; + else if (strcasecmp(tr_level, "commands") == 0) + tr_config->level = SIEVE_TRLVL_COMMANDS; + else if (strcasecmp(tr_level, "tests") == 0) + tr_config->level = SIEVE_TRLVL_TESTS; + else if (strcasecmp(tr_level, "matching") == 0) + tr_config->level = SIEVE_TRLVL_MATCHING; + else { + e_error(svinst->event, "Unknown trace level: %s", tr_level); + return -1; + } + + tr_debug = FALSE; + (void)sieve_setting_get_bool_value(svinst, "sieve_trace_debug", + &tr_debug); + tr_addresses = FALSE; + (void)sieve_setting_get_bool_value(svinst, "sieve_trace_addresses", + &tr_addresses); + + if (tr_debug) + tr_config->flags |= SIEVE_TRFLG_DEBUG; + if (tr_addresses) + tr_config->flags |= SIEVE_TRFLG_ADDRESSES; + return 0; +} + +/* + * Execution exit codes + */ + +const char *sieve_execution_exitcode_to_str(int code) +{ + switch (code) { + case SIEVE_EXEC_OK: + return "ok"; + case SIEVE_EXEC_FAILURE: + return "failure"; + case SIEVE_EXEC_TEMP_FAILURE: + return "temporary_failure"; + case SIEVE_EXEC_BIN_CORRUPT: + return "binary_corrupt"; + case SIEVE_EXEC_KEEP_FAILED: + return "keep_failed"; + case SIEVE_EXEC_RESOURCE_LIMIT: + return "resource_limit"; + } + i_unreached(); +} + +/* + * User e-mail address + */ + +const struct smtp_address *sieve_get_user_email(struct sieve_instance *svinst) +{ + struct smtp_address *address; + const char *username = svinst->username; + + if (svinst->user_email_implicit != NULL) + return svinst->user_email_implicit; + if (svinst->user_email != NULL) + return svinst->user_email; + + if (smtp_address_parse_mailbox(svinst->pool, username, 0, + &address, NULL) >= 0) { + svinst->user_email_implicit = address; + return svinst->user_email_implicit; + } + + if (svinst->domainname != NULL) { + svinst->user_email_implicit = smtp_address_create( + svinst->pool, username, svinst->domainname); + return svinst->user_email_implicit; + } + return NULL; +} + +/* + * Postmaster address + */ + +const struct message_address * +sieve_get_postmaster(const struct sieve_script_env *senv) +{ + i_assert(senv->postmaster_address != NULL); + return senv->postmaster_address; +} + +const struct smtp_address * +sieve_get_postmaster_smtp(const struct sieve_script_env *senv) +{ + struct smtp_address *addr; + int ret; + + ret = smtp_address_create_from_msg_temp( + sieve_get_postmaster(senv), &addr); + i_assert(ret >= 0); + return addr; +} + +const char *sieve_get_postmaster_address(const struct sieve_script_env *senv) +{ + const struct message_address *postmaster = + sieve_get_postmaster(senv); + string_t *addr = t_str_new(256); + + message_address_write(addr, postmaster); + return str_c(addr); +} + +/* + * Resource usage + */ + +void sieve_resource_usage_init(struct sieve_resource_usage *rusage_r) +{ + i_zero(rusage_r); +} + +void sieve_resource_usage_add(struct sieve_resource_usage *dst, + const struct sieve_resource_usage *src) +{ + if ((UINT_MAX - dst->cpu_time_msecs) < src->cpu_time_msecs) + dst->cpu_time_msecs = UINT_MAX; + else + dst->cpu_time_msecs += src->cpu_time_msecs; +} + +bool sieve_resource_usage_is_high(struct sieve_instance *svinst ATTR_UNUSED, + const struct sieve_resource_usage *rusage) +{ + return (rusage->cpu_time_msecs > SIEVE_HIGH_CPU_TIME_MSECS); +} + +bool sieve_resource_usage_is_excessive( + struct sieve_instance *svinst, + const struct sieve_resource_usage *rusage) +{ + i_assert(svinst->max_cpu_time_secs <= (UINT_MAX / 1000)); + if (svinst->max_cpu_time_secs == 0) + return FALSE; + return (rusage->cpu_time_msecs > (svinst->max_cpu_time_secs * 1000)); +} + +const char * +sieve_resource_usage_get_summary(const struct sieve_resource_usage *rusage) +{ + if (rusage->cpu_time_msecs == 0) + return "no usage recorded"; + + return t_strdup_printf("cpu time = %u ms", rusage->cpu_time_msecs); +} diff --git a/pigeonhole/src/lib-sieve/sieve.h b/pigeonhole/src/lib-sieve/sieve.h new file mode 100644 index 0000000..66a6d12 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve.h @@ -0,0 +1,261 @@ +#ifndef SIEVE_H +#define SIEVE_H + +struct sieve_script; +struct sieve_binary; + +#include "sieve-config.h" +#include "sieve-types.h" +#include "sieve-error.h" + +/* + * Main Sieve library interface + */ + +/* Initialize the sieve engine. Must be called before any sieve functionality is + used. */ +struct sieve_instance * +sieve_init(const struct sieve_environment *env, + const struct sieve_callbacks *callbacks, void *context, bool debug); + +/* Free all memory allocated by the sieve engine. */ +void sieve_deinit(struct sieve_instance **_svinst); + +/* Get capability string for a particular extension. */ +const char * +sieve_get_capabilities(struct sieve_instance *svinst, const char *name); + +/* Set the supported extensions. The provided string is parsed into a list + of extensions that are to be enabled/disabled. */ +void sieve_set_extensions(struct sieve_instance *svinst, + const char *extensions); + + +/* Get top-level event for this Sieve instance. */ +struct event *sieve_get_event(struct sieve_instance *svinst) ATTR_PURE; + +/* + * Script compilation + */ + +/* Compile a Sieve script from a Sieve script object. Returns Sieve binary upon + success and NULL upon failure. */ +struct sieve_binary * +sieve_compile_script(struct sieve_script *script, + struct sieve_error_handler *ehandler, + enum sieve_compile_flags flags, enum sieve_error *error_r) + ATTR_NULL(2, 4); + +/* Compile a Sieve script from a Sieve script location string. Returns Sieve + binary upon success and NULL upon failure. The provided script_name is used + for the internally created Sieve script object. */ +struct sieve_binary * +sieve_compile(struct sieve_instance *svinst, const char *script_location, + const char *script_name, struct sieve_error_handler *ehandler, + enum sieve_compile_flags flags, enum sieve_error *error_r) + ATTR_NULL(3, 4, 6); + +/* + * Reading/writing Sieve binaries + */ + +/* Loads the sieve binary indicated by the provided path. */ +struct sieve_binary * +sieve_load(struct sieve_instance *svinst, const char *bin_path, + enum sieve_error *error_r); +/* First tries to open the binary version of the specified script and if it does + not exist or if it contains errors, the script is (re-)compiled. Note that + errors in the bytecode are caught only at runtime. + */ +struct sieve_binary * +sieve_open_script(struct sieve_script *script, + struct sieve_error_handler *ehandler, + enum sieve_compile_flags flags, enum sieve_error *error_r); +/* First tries to open the binary version of the specified script and if it does + not exist or if it contains errors, the script is (re-)compiled. Note that + errors in the bytecode are caught only at runtime. + */ +struct sieve_binary * +sieve_open(struct sieve_instance *svinst, const char *script_location, + const char *script_name, struct sieve_error_handler *ehandler, + enum sieve_compile_flags flags, enum sieve_error *error_r); + +/* Record resource usage in the binary cumulatively. The binary is disabled when + resource limits are exceeded within a configured timeout. Returns FALSE when + resource limits are exceeded. */ +bool ATTR_NOWARN_UNUSED_RESULT +sieve_record_resource_usage(struct sieve_binary *sbin, + const struct sieve_resource_usage *rusage) + ATTR_NULL(1); + +/* Check whether Sieve binary is (still) executable. Returns 1 if all is OK, + 0 when an error occurred, and -1 when the error is internal. Sets the Sieve + error code in error_r and a user error message in client_error_r when the + error is not internal. */ +int sieve_check_executable(struct sieve_binary *sbin, + enum sieve_error *error_r, + const char **client_error_r); + +/* Saves the binary as the file indicated by the path parameter. This function + will not write the binary to disk when the provided binary object was loaded + earlier from the indicated bin_path, unless update is TRUE. + */ +int sieve_save_as(struct sieve_binary *sbin, const char *bin_path, bool update, + mode_t save_mode, enum sieve_error *error_r); + +/* Saves the binary to the default location. This function will not overwrite + the binary on disk when the provided binary object was loaded earlier from + the default location, unless update is TRUE. + */ +int sieve_save(struct sieve_binary *sbin, bool update, + enum sieve_error *error_r); + +/* Closes a compiled/opened sieve binary. */ +void sieve_close(struct sieve_binary **_sbin); + +/* Obtains the path the binary was compiled or loaded from. */ +const char *sieve_get_source(struct sieve_binary *sbin); +/* Indicates whether the binary was loaded from a pre-compiled file. */ +bool sieve_is_loaded(struct sieve_binary *sbin); + +/* + * Debugging + */ + +/* Dumps the byte code in human-readable form to the specified ostream. */ +void sieve_dump(struct sieve_binary *sbin, + struct ostream *stream, bool verbose); +/* Dumps the byte code in hexdump form to the specified ostream. */ +void sieve_hexdump(struct sieve_binary *sbin, struct ostream *stream); + +/* Executes the bytecode, but only prints the result to the given stream. */ +int sieve_test(struct sieve_binary *sbin, + const struct sieve_message_data *msgdata, + const struct sieve_script_env *senv, + struct sieve_error_handler *ehandler, struct ostream *stream, + enum sieve_execute_flags flags); + +/* + * Script execution + */ + +/* Initializes the scirpt environment from the given mail_user. */ +int sieve_script_env_init(struct sieve_script_env *senv, struct mail_user *user, + const char **error_r); + +/* Executes the binary, including the result. */ +int sieve_execute(struct sieve_binary *sbin, + const struct sieve_message_data *msgdata, + const struct sieve_script_env *senv, + struct sieve_error_handler *exec_ehandler, + struct sieve_error_handler *action_ehandler, + enum sieve_execute_flags flags); + +/* + * Multiscript support + */ + +struct sieve_multiscript; + +struct sieve_multiscript * +sieve_multiscript_start_execute(struct sieve_instance *svinst, + const struct sieve_message_data *msgdata, + const struct sieve_script_env *senv); +struct sieve_multiscript * +sieve_multiscript_start_test(struct sieve_instance *svinst, + const struct sieve_message_data *msgdata, + const struct sieve_script_env *senv, + struct ostream *stream); + +bool sieve_multiscript_run(struct sieve_multiscript *mscript, + struct sieve_binary *sbin, + struct sieve_error_handler *exec_ehandler, + struct sieve_error_handler *action_ehandler, + enum sieve_execute_flags flags); + +bool sieve_multiscript_will_discard(struct sieve_multiscript *mscript); +void sieve_multiscript_run_discard(struct sieve_multiscript *mscript, + struct sieve_binary *sbin, + struct sieve_error_handler *exec_ehandler, + struct sieve_error_handler *action_ehandler, + enum sieve_execute_flags flags); + +int sieve_multiscript_status(struct sieve_multiscript *mscript); + +int sieve_multiscript_finish(struct sieve_multiscript **_mscript, + struct sieve_error_handler *action_ehandler, + enum sieve_execute_flags flags, int status); + +/* + * Configured limits + */ + +unsigned int sieve_max_redirects(struct sieve_instance *svinst); +unsigned int sieve_max_actions(struct sieve_instance *svinst); +size_t sieve_max_script_size(struct sieve_instance *svinst); + +/* + * User log + */ + +const char *sieve_user_get_log_path(struct sieve_instance *svinst, + struct sieve_script *user_script) + ATTR_NULL(2); + +/* + * Script trace log + */ + +struct sieve_trace_log; + +int sieve_trace_log_create(struct sieve_instance *svinst, const char *path, + struct sieve_trace_log **trace_log_r) ATTR_NULL(2); +int sieve_trace_log_create_dir(struct sieve_instance *svinst, const char *dir, + struct sieve_trace_log **trace_log_r) + ATTR_NULL(3); + +int sieve_trace_log_open(struct sieve_instance *svinst, + struct sieve_trace_log **trace_log_r) ATTR_NULL(2); + +void sieve_trace_log_printf(struct sieve_trace_log *trace_log, + const char *fmt, ...) ATTR_FORMAT(2, 3); + +void sieve_trace_log_free(struct sieve_trace_log **_trace_log); + +int sieve_trace_config_get(struct sieve_instance *svinst, + struct sieve_trace_config *tr_config); + +/* + * Execution exit codes + */ + +const char *sieve_execution_exitcode_to_str(int code); + +/* + * Resource usage + */ + +/* Initialize the resource usage struct, clearing all usage statistics. */ +void sieve_resource_usage_init(struct sieve_resource_usage *rusage_r); + +/* Calculate the sum of the provided resource usage statistics, writing the + result to the first. */ +void sieve_resource_usage_add(struct sieve_resource_usage *dst, + const struct sieve_resource_usage *src); + +/* Returns TRUE if the resource usage is sufficiently high to warrant recording + for checking cumulative resource limits (across several different script + executions). */ +bool sieve_resource_usage_is_high(struct sieve_instance *svinst, + const struct sieve_resource_usage *rusage); +/* Returns TRUE when the provided resource usage statistics exceed a configured + policy limit. */ +bool sieve_resource_usage_is_excessive( + struct sieve_instance *svinst, + const struct sieve_resource_usage *rusage); +/* Returns a string containing a description of the resource usage (to be used + log messages). */ +const char * +sieve_resource_usage_get_summary(const struct sieve_resource_usage *rusage); + +#endif diff --git a/pigeonhole/src/lib-sieve/storage/Makefile.am b/pigeonhole/src/lib-sieve/storage/Makefile.am new file mode 100644 index 0000000..ba39e4a --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/Makefile.am @@ -0,0 +1,5 @@ +SUBDIRS = \ + data \ + file \ + dict \ + ldap diff --git a/pigeonhole/src/lib-sieve/storage/Makefile.in b/pigeonhole/src/lib-sieve/storage/Makefile.in new file mode 100644 index 0000000..d81c0c5 --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/Makefile.in @@ -0,0 +1,697 @@ +# 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 +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 $(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 = +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 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +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 +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +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@ +SUBDIRS = \ + data \ + file \ + dict \ + ldap + +all: all-recursive + +.SUFFIXES: +$(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/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/storage/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): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(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-recursive + +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-recursive + +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 + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +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-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-libtool cscopelist-am ctags \ + ctags-am distclean 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 \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean 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/data/Makefile.am b/pigeonhole/src/lib-sieve/storage/data/Makefile.am new file mode 100644 index 0000000..c17741d --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/data/Makefile.am @@ -0,0 +1,13 @@ +noinst_LTLIBRARIES = libsieve_storage_data.la + +AM_CPPFLAGS = \ + $(LIBDOVECOT_INCLUDE) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/src/lib-sieve + +libsieve_storage_data_la_SOURCES = \ + sieve-data-script.c \ + sieve-data-storage.c + +noinst_HEADERS = \ + sieve-data-storage.h diff --git a/pigeonhole/src/lib-sieve/storage/data/Makefile.in b/pigeonhole/src/lib-sieve/storage/data/Makefile.in new file mode 100644 index 0000000..199f2db --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/data/Makefile.in @@ -0,0 +1,686 @@ +# 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/data +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_data_la_LIBADD = +am_libsieve_storage_data_la_OBJECTS = sieve-data-script.lo \ + sieve-data-storage.lo +libsieve_storage_data_la_OBJECTS = \ + $(am_libsieve_storage_data_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-data-script.Plo \ + ./$(DEPDIR)/sieve-data-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_data_la_SOURCES) +DIST_SOURCES = $(libsieve_storage_data_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_data.la +AM_CPPFLAGS = \ + $(LIBDOVECOT_INCLUDE) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/src/lib-sieve + +libsieve_storage_data_la_SOURCES = \ + sieve-data-script.c \ + sieve-data-storage.c + +noinst_HEADERS = \ + sieve-data-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/data/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/storage/data/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_data.la: $(libsieve_storage_data_la_OBJECTS) $(libsieve_storage_data_la_DEPENDENCIES) $(EXTRA_libsieve_storage_data_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_storage_data_la_OBJECTS) $(libsieve_storage_data_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-data-script.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-data-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-data-script.Plo + -rm -f ./$(DEPDIR)/sieve-data-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-data-script.Plo + -rm -f ./$(DEPDIR)/sieve-data-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/data/sieve-data-script.c b/pigeonhole/src/lib-sieve/storage/data/sieve-data-script.c new file mode 100644 index 0000000..2241bc5 --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/data/sieve-data-script.c @@ -0,0 +1,95 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "strfuncs.h" +#include "istream.h" + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-dump.h" +#include "sieve-binary.h" + +#include "sieve-data-storage.h" + +/* + * Script data implementation + */ + +static struct sieve_data_script *sieve_data_script_alloc(void) +{ + struct sieve_data_script *dscript; + pool_t pool; + + pool = pool_alloconly_create("sieve_data_script", 1024); + dscript = p_new(pool, struct sieve_data_script, 1); + dscript->script = sieve_data_script; + dscript->script.pool = pool; + + return dscript; +} + +struct sieve_script *sieve_data_script_create_from_input +(struct sieve_instance *svinst, const char *name, struct istream *input) +{ + struct sieve_storage *storage; + struct sieve_data_script *dscript = NULL; + + storage = sieve_storage_alloc(svinst, NULL, &sieve_data_storage, + "", 0, FALSE); + + dscript = sieve_data_script_alloc(); + sieve_script_init(&dscript->script, + storage, &sieve_data_script, "data:", name); + + dscript->data = input; + i_stream_ref(dscript->data); + + sieve_storage_unref(&storage); + + dscript->script.open = TRUE; + + return &dscript->script; +} + +static void sieve_data_script_destroy(struct sieve_script *script) +{ + struct sieve_data_script *dscript = + (struct sieve_data_script *)script; + + i_stream_unref(&dscript->data); +} + +static int sieve_data_script_get_stream +(struct sieve_script *script, struct istream **stream_r, + enum sieve_error *error_r) +{ + struct sieve_data_script *dscript = + (struct sieve_data_script *)script; + + i_stream_ref(dscript->data); + i_stream_seek(dscript->data, 0); + + *stream_r = dscript->data; + *error_r = SIEVE_ERROR_NONE; + return 0; +} + +static bool sieve_data_script_equals +(const struct sieve_script *script ATTR_UNUSED, + const struct sieve_script *other ATTR_UNUSED) +{ + return ( script == other ); +} + +const struct sieve_script sieve_data_script = { + .driver_name = SIEVE_DATA_STORAGE_DRIVER_NAME, + .v = { + .destroy = sieve_data_script_destroy, + + .get_stream = sieve_data_script_get_stream, + + .equals = sieve_data_script_equals + } +}; diff --git a/pigeonhole/src/lib-sieve/storage/data/sieve-data-storage.c b/pigeonhole/src/lib-sieve/storage/data/sieve-data-storage.c new file mode 100644 index 0000000..21b4f35 --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/data/sieve-data-storage.c @@ -0,0 +1,47 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" + +#include "sieve-common.h" +#include "sieve-error.h" + +#include "sieve-data-storage.h" + +/* + * Storage class + */ + +static struct sieve_storage *sieve_data_storage_alloc(void) +{ + struct sieve_data_storage *dstorage; + pool_t pool; + + pool = pool_alloconly_create("sieve_data_storage", 1024); + dstorage = p_new(pool, struct sieve_data_storage, 1); + dstorage->storage = sieve_data_storage; + dstorage->storage.pool = pool; + + return &dstorage->storage; +} + +static int sieve_data_storage_init +(struct sieve_storage *storage ATTR_UNUSED, + const char *const *options ATTR_UNUSED, + enum sieve_error *error_r ATTR_UNUSED) +{ + return 0; +} + +/* + * Driver definition + */ + +const struct sieve_storage sieve_data_storage = { + .driver_name = SIEVE_DATA_STORAGE_DRIVER_NAME, + .version = 0, + .v = { + .alloc = sieve_data_storage_alloc, + .init = sieve_data_storage_init, + } +}; diff --git a/pigeonhole/src/lib-sieve/storage/data/sieve-data-storage.h b/pigeonhole/src/lib-sieve/storage/data/sieve-data-storage.h new file mode 100644 index 0000000..a200e25 --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/data/sieve-data-storage.h @@ -0,0 +1,30 @@ +#ifndef SIEVE_DATA_STORAGE_H +#define SIEVE_DATA_STORAGE_H + +#include "sieve.h" +#include "sieve-script-private.h" +#include "sieve-storage-private.h" + +/* + * Storage class + */ + +struct sieve_data_storage { + struct sieve_storage storage; +}; + +/* + * Script class + */ + +struct sieve_data_script { + struct sieve_script script; + + struct istream *data; +}; + +struct sieve_script *sieve_data_script_create_from_input + (struct sieve_instance *svinst, const char *name, + struct istream *input); + +#endif diff --git a/pigeonhole/src/lib-sieve/storage/dict/Makefile.am b/pigeonhole/src/lib-sieve/storage/dict/Makefile.am new file mode 100644 index 0000000..2a73f43 --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/dict/Makefile.am @@ -0,0 +1,13 @@ +noinst_LTLIBRARIES = libsieve_storage_dict.la + +AM_CPPFLAGS = \ + $(LIBDOVECOT_INCLUDE) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/src/lib-sieve + +libsieve_storage_dict_la_SOURCES = \ + sieve-dict-script.c \ + sieve-dict-storage.c + +noinst_HEADERS = \ + sieve-dict-storage.h diff --git a/pigeonhole/src/lib-sieve/storage/dict/Makefile.in b/pigeonhole/src/lib-sieve/storage/dict/Makefile.in new file mode 100644 index 0000000..58fd0c5 --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/dict/Makefile.in @@ -0,0 +1,686 @@ +# 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/dict +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_dict_la_LIBADD = +am_libsieve_storage_dict_la_OBJECTS = sieve-dict-script.lo \ + sieve-dict-storage.lo +libsieve_storage_dict_la_OBJECTS = \ + $(am_libsieve_storage_dict_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-dict-script.Plo \ + ./$(DEPDIR)/sieve-dict-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_dict_la_SOURCES) +DIST_SOURCES = $(libsieve_storage_dict_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_dict.la +AM_CPPFLAGS = \ + $(LIBDOVECOT_INCLUDE) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/src/lib-sieve + +libsieve_storage_dict_la_SOURCES = \ + sieve-dict-script.c \ + sieve-dict-storage.c + +noinst_HEADERS = \ + sieve-dict-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/dict/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/storage/dict/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_dict.la: $(libsieve_storage_dict_la_OBJECTS) $(libsieve_storage_dict_la_DEPENDENCIES) $(EXTRA_libsieve_storage_dict_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_storage_dict_la_OBJECTS) $(libsieve_storage_dict_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-dict-script.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-dict-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-dict-script.Plo + -rm -f ./$(DEPDIR)/sieve-dict-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-dict-script.Plo + -rm -f ./$(DEPDIR)/sieve-dict-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/dict/sieve-dict-script.c b/pigeonhole/src/lib-sieve/storage/dict/sieve-dict-script.c new file mode 100644 index 0000000..eb28f7d --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/dict/sieve-dict-script.c @@ -0,0 +1,343 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "strfuncs.h" +#include "istream.h" +#include "dict.h" + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-dump.h" +#include "sieve-binary.h" + +#include "sieve-dict-storage.h" + +/* + * Script dict implementation + */ + +static struct sieve_dict_script *sieve_dict_script_alloc(void) +{ + struct sieve_dict_script *dscript; + pool_t pool; + + pool = pool_alloconly_create("sieve_dict_script", 1024); + dscript = p_new(pool, struct sieve_dict_script, 1); + dscript->script = sieve_dict_script; + dscript->script.pool = pool; + + return dscript; +} + +struct sieve_dict_script *sieve_dict_script_init +(struct sieve_dict_storage *dstorage, const char *name) +{ + struct sieve_storage *storage = &dstorage->storage; + struct sieve_dict_script *dscript = NULL; + const char *location; + + if ( name == NULL ) { + name = SIEVE_DICT_SCRIPT_DEFAULT; + location = storage->location; + } else { + location = t_strconcat + (storage->location, ";name=", name, NULL); + } + + dscript = sieve_dict_script_alloc(); + sieve_script_init(&dscript->script, + storage, &sieve_dict_script, location, name); + + return dscript; +} + +static void sieve_dict_script_destroy(struct sieve_script *script) +{ + struct sieve_dict_script *dscript = + (struct sieve_dict_script *)script; + + if ( dscript->data_pool != NULL ) + pool_unref(&dscript->data_pool); +} + +static int sieve_dict_script_open +(struct sieve_script *script, enum sieve_error *error_r) +{ + struct sieve_dict_script *dscript = + (struct sieve_dict_script *)script; + struct sieve_storage *storage = script->storage; + struct sieve_dict_storage *dstorage = + (struct sieve_dict_storage *)storage; + const char *name = script->name; + const char *path, *data_id, *error; + int ret; + + if ( sieve_dict_storage_get_dict + (dstorage, &dscript->dict, error_r) < 0 ) + return -1; + + path = t_strconcat + (DICT_SIEVE_NAME_PATH, dict_escape_string(name), NULL); + + struct dict_op_settings set = { + .username = dstorage->username, + }; + ret = dict_lookup + (dscript->dict, &set, script->pool, path, &data_id, &error); + if ( ret <= 0 ) { + if ( ret < 0 ) { + sieve_script_set_critical(script, + "Failed to lookup script id from path %s: %s", path, error); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + } else { + e_debug(script->event, + "Script `%s' not found at path %s", name, path); + sieve_script_set_error(script, + SIEVE_ERROR_NOT_FOUND, + "Sieve script `%s' not found", name); + *error_r = SIEVE_ERROR_NOT_FOUND; + } + return -1; + } + + dscript->data_id = p_strdup(script->pool, data_id); + return 0; +} + +static int sieve_dict_script_get_stream +(struct sieve_script *script, struct istream **stream_r, + enum sieve_error *error_r) +{ + struct sieve_dict_script *dscript = + (struct sieve_dict_script *)script; + struct sieve_dict_storage *dstorage = + (struct sieve_dict_storage *)script->storage; + const char *path, *name = script->name, *data, *error; + int ret; + + dscript->data_pool = + pool_alloconly_create("sieve_dict_script data pool", 1024); + + path = t_strconcat + (DICT_SIEVE_DATA_PATH, dict_escape_string(dscript->data_id), NULL); + + struct dict_op_settings set = { + .username = dstorage->username, + }; + ret = dict_lookup + (dscript->dict, &set, dscript->data_pool, path, &data, &error); + if ( ret <= 0 ) { + if ( ret < 0 ) { + sieve_script_set_critical(script, + "Failed to lookup data with id `%s' " + "for script `%s' from path %s: %s", + dscript->data_id, name, path, error); + } else { + sieve_script_set_critical(script, + "Data with id `%s' for script `%s' " + "not found at path %s", + dscript->data_id, name, path); + } + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + + dscript->data = p_strdup(script->pool, data); + *stream_r = i_stream_create_from_data(dscript->data, strlen(dscript->data)); + return 0; +} + +static int sieve_dict_script_binary_read_metadata +(struct sieve_script *script, struct sieve_binary_block *sblock, + sieve_size_t *offset) +{ + struct sieve_dict_script *dscript = + (struct sieve_dict_script *)script; + struct sieve_binary *sbin = + sieve_binary_block_get_binary(sblock); + string_t *data_id; + + if ( dscript->data_id == NULL && + sieve_script_open(script, NULL) < 0 ) + return 0; + + if ( !sieve_binary_read_string(sblock, offset, &data_id) ) { + e_error(script->event, + "Binary `%s' has invalid metadata for script `%s'", + sieve_binary_path(sbin), sieve_script_location(script)); + return -1; + } + i_assert( dscript->data_id != NULL ); + if ( strcmp(str_c(data_id), dscript->data_id) != 0 ) { + e_debug(script->event, + "Binary `%s' reports different data ID for script `%s' " + "(`%s' rather than `%s')", + sieve_binary_path(sbin), sieve_script_location(script), + str_c(data_id), dscript->data_id); + return 0; + } + return 1; +} + +static void sieve_dict_script_binary_write_metadata +(struct sieve_script *script, struct sieve_binary_block *sblock) +{ + struct sieve_dict_script *dscript = + (struct sieve_dict_script *)script; + + sieve_binary_emit_cstring(sblock, dscript->data_id); +} + +static bool sieve_dict_script_binary_dump_metadata +(struct sieve_script *script ATTR_UNUSED, struct sieve_dumptime_env *denv, + struct sieve_binary_block *sblock, sieve_size_t *offset) +{ + string_t *data_id; + + if ( !sieve_binary_read_string(sblock, offset, &data_id) ) + return FALSE; + sieve_binary_dumpf(denv, "dict.data_id = %s\n", str_c(data_id)); + + return TRUE; +} + +static const char * sieve_dict_script_get_binpath +(struct sieve_dict_script *dscript) +{ + struct sieve_script *script = &dscript->script; + struct sieve_storage *storage = script->storage; + + if ( dscript->binpath == NULL ) { + if ( storage->bin_dir == NULL ) + return NULL; + dscript->binpath = p_strconcat(script->pool, + storage->bin_dir, "/", + sieve_binfile_from_name(script->name), NULL); + } + + return dscript->binpath; +} + +static struct sieve_binary *sieve_dict_script_binary_load +(struct sieve_script *script, enum sieve_error *error_r) +{ + struct sieve_dict_script *dscript = + (struct sieve_dict_script *)script; + + if ( sieve_dict_script_get_binpath(dscript) == NULL ) + return NULL; + + return sieve_binary_open(script->storage->svinst, + dscript->binpath, script, error_r); +} + +static int sieve_dict_script_binary_save +(struct sieve_script *script, struct sieve_binary *sbin, bool update, + enum sieve_error *error_r) +{ + struct sieve_dict_script *dscript = + (struct sieve_dict_script *)script; + + if ( sieve_dict_script_get_binpath(dscript) == NULL ) + return 0; + if ( sieve_storage_setup_bindir(script->storage, 0700) < 0 ) + return -1; + + return sieve_binary_save(sbin, + dscript->binpath, update, 0600, error_r); +} + +static bool sieve_dict_script_equals +(const struct sieve_script *script, const struct sieve_script *other) +{ + struct sieve_storage *storage = script->storage; + struct sieve_storage *sother = other->storage; + + if ( strcmp(storage->location, sother->location) != 0 ) + return FALSE; + + i_assert( script->name != NULL && other->name != NULL ); + + return ( strcmp(script->name, other->name) == 0 ); +} + +const struct sieve_script sieve_dict_script = { + .driver_name = SIEVE_DICT_STORAGE_DRIVER_NAME, + .v = { + .destroy = sieve_dict_script_destroy, + + .open = sieve_dict_script_open, + + .get_stream = sieve_dict_script_get_stream, + + .binary_read_metadata =sieve_dict_script_binary_read_metadata, + .binary_write_metadata = sieve_dict_script_binary_write_metadata, + .binary_dump_metadata = sieve_dict_script_binary_dump_metadata, + .binary_load = sieve_dict_script_binary_load, + .binary_save = sieve_dict_script_binary_save, + + .equals = sieve_dict_script_equals + } +}; + +/* + * Script sequence + */ + +struct sieve_dict_script_sequence { + struct sieve_script_sequence seq; + + bool done:1; +}; + +struct sieve_script_sequence *sieve_dict_storage_get_script_sequence +(struct sieve_storage *storage, enum sieve_error *error_r) +{ + struct sieve_dict_script_sequence *dseq = NULL; + + if ( error_r != NULL ) + *error_r = SIEVE_ERROR_NONE; + + /* Create sequence object */ + dseq = i_new(struct sieve_dict_script_sequence, 1); + sieve_script_sequence_init(&dseq->seq, storage); + + return &dseq->seq; +} + +struct sieve_script *sieve_dict_script_sequence_next +(struct sieve_script_sequence *seq, enum sieve_error *error_r) +{ + struct sieve_dict_script_sequence *dseq = + (struct sieve_dict_script_sequence *)seq; + struct sieve_dict_storage *dstorage = + (struct sieve_dict_storage *)seq->storage; + struct sieve_dict_script *dscript; + + if ( error_r != NULL ) + *error_r = SIEVE_ERROR_NONE; + + if ( dseq->done ) + return NULL; + dseq->done = TRUE; + + dscript = sieve_dict_script_init + (dstorage, seq->storage->script_name); + if ( sieve_script_open(&dscript->script, error_r) < 0 ) { + struct sieve_script *script = &dscript->script; + sieve_script_unref(&script); + return NULL; + } + + return &dscript->script; +} + +void sieve_dict_script_sequence_destroy(struct sieve_script_sequence *seq) +{ + struct sieve_dict_script_sequence *dseq = + (struct sieve_dict_script_sequence *)seq; + i_free(dseq); +} + diff --git a/pigeonhole/src/lib-sieve/storage/dict/sieve-dict-storage.c b/pigeonhole/src/lib-sieve/storage/dict/sieve-dict-storage.c new file mode 100644 index 0000000..7f50816 --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/dict/sieve-dict-storage.c @@ -0,0 +1,193 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "dict.h" + +#include "sieve-common.h" +#include "sieve-error.h" + +#include "sieve-dict-storage.h" + +/* + * Storage class + */ + +static struct sieve_storage *sieve_dict_storage_alloc(void) +{ + struct sieve_dict_storage *dstorage; + pool_t pool; + + pool = pool_alloconly_create("sieve_dict_storage", 1024); + dstorage = p_new(pool, struct sieve_dict_storage, 1); + dstorage->storage = sieve_dict_storage; + dstorage->storage.pool = pool; + + return &dstorage->storage; +} + +static int sieve_dict_storage_init +(struct sieve_storage *storage, const char *const *options, + enum sieve_error *error_r) +{ + struct sieve_dict_storage *dstorage = + (struct sieve_dict_storage *)storage; + struct sieve_instance *svinst = storage->svinst; + const char *uri = storage->location, *username = NULL; + + if ( options != NULL ) { + while ( *options != NULL ) { + const char *option = *options; + + if ( strncasecmp(option, "user=", 5) == 0 && option[5] != '\0' ) { + username = option+5; + } else { + sieve_storage_set_critical(storage, + "Invalid option `%s'", option); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + + options++; + } + } + + if ( username == NULL ) { + if ( svinst->username == NULL ) { + sieve_storage_set_critical(storage, + "No username specified"); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + username = svinst->username; + } + + if ( svinst->base_dir == NULL ) { + sieve_storage_set_critical(storage, + "BUG: Sieve interpreter is initialized without a base_dir"); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + + e_debug(storage->event, "user=%s, uri=%s", username, uri); + + dstorage->uri = p_strdup(storage->pool, uri); + dstorage->username = p_strdup(storage->pool, username); + + storage->location = p_strconcat(storage->pool, + SIEVE_DICT_STORAGE_DRIVER_NAME, ":", storage->location, + ";user=", username, NULL); + + return 0; +} + +int sieve_dict_storage_get_dict +(struct sieve_dict_storage *dstorage, struct dict **dict_r, + enum sieve_error *error_r) +{ + struct sieve_storage *storage = &dstorage->storage; + struct sieve_instance *svinst = storage->svinst; + struct dict_settings dict_set; + const char *error; + int ret; + + if ( dstorage->dict == NULL ) { + i_zero(&dict_set); + dict_set.base_dir = svinst->base_dir; + ret = dict_init(dstorage->uri, &dict_set, &dstorage->dict, &error); + if ( ret < 0 ) { + sieve_storage_set_critical(storage, + "Failed to initialize dict with data `%s' for user `%s': %s", + dstorage->uri, dstorage->username, error); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + } + + *dict_r = dstorage->dict; + return 0; +} + +static void sieve_dict_storage_destroy(struct sieve_storage *storage) +{ + struct sieve_dict_storage *dstorage = + (struct sieve_dict_storage *)storage; + + if ( dstorage->dict != NULL ) + dict_deinit(&dstorage->dict); +} + +/* + * Script access + */ + +static struct sieve_script *sieve_dict_storage_get_script +(struct sieve_storage *storage, const char *name) +{ + struct sieve_dict_storage *dstorage = + (struct sieve_dict_storage *)storage; + struct sieve_dict_script *dscript; + + T_BEGIN { + dscript = sieve_dict_script_init(dstorage, name); + } T_END; + + return &dscript->script; +} + +/* + * Active script + */ + +struct sieve_script *sieve_dict_storage_active_script_open +(struct sieve_storage *storage) +{ + struct sieve_dict_storage *dstorage = + (struct sieve_dict_storage *)storage; + struct sieve_dict_script *dscript; + + dscript = sieve_dict_script_init + (dstorage, storage->script_name); + if ( sieve_script_open(&dscript->script, NULL) < 0 ) { + struct sieve_script *script = &dscript->script; + sieve_script_unref(&script); + return NULL; + } + + return &dscript->script; +} + +int sieve_dict_storage_active_script_get_name +(struct sieve_storage *storage, const char **name_r) +{ + if ( storage->script_name != NULL ) + *name_r = storage->script_name; + else + *name_r = SIEVE_DICT_SCRIPT_DEFAULT; + return 0; +} + +/* + * Driver definition + */ + +const struct sieve_storage sieve_dict_storage = { + .driver_name = SIEVE_DICT_STORAGE_DRIVER_NAME, + .version = 0, + .v = { + .alloc = sieve_dict_storage_alloc, + .destroy = sieve_dict_storage_destroy, + .init = sieve_dict_storage_init, + + .get_script = sieve_dict_storage_get_script, + + .get_script_sequence = sieve_dict_storage_get_script_sequence, + .script_sequence_next = sieve_dict_script_sequence_next, + .script_sequence_destroy = sieve_dict_script_sequence_destroy, + + .active_script_get_name = sieve_dict_storage_active_script_get_name, + .active_script_open = sieve_dict_storage_active_script_open, + + // FIXME: impement management interface + } +}; diff --git a/pigeonhole/src/lib-sieve/storage/dict/sieve-dict-storage.h b/pigeonhole/src/lib-sieve/storage/dict/sieve-dict-storage.h new file mode 100644 index 0000000..1f92221 --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/dict/sieve-dict-storage.h @@ -0,0 +1,66 @@ +#ifndef SIEVE_DICT_STORAGE_H +#define SIEVE_DICT_STORAGE_H + +#include "sieve.h" +#include "sieve-script-private.h" +#include "sieve-storage-private.h" + +#define DICT_SIEVE_PATH DICT_PATH_PRIVATE"sieve/" +#define DICT_SIEVE_NAME_PATH DICT_SIEVE_PATH"name/" +#define DICT_SIEVE_DATA_PATH DICT_SIEVE_PATH"data/" + +#define SIEVE_DICT_SCRIPT_DEFAULT "default" + +/* + * Storage class + */ + +struct sieve_dict_storage { + struct sieve_storage storage; + + const char *username; + const char *uri; + + struct dict *dict; +}; + +int sieve_dict_storage_get_dict + (struct sieve_dict_storage *dstorage, struct dict **dict_r, + enum sieve_error *error_r); + +struct sieve_script *sieve_dict_storage_active_script_open + (struct sieve_storage *storage); +int sieve_dict_storage_active_script_get_name + (struct sieve_storage *storage, const char **name_r); + +/* + * Script class + */ + +struct sieve_dict_script { + struct sieve_script script; + + struct dict *dict; + + pool_t data_pool; + const char *data_id; + const char *data; + + const char *binpath; +}; + +struct sieve_dict_script *sieve_dict_script_init + (struct sieve_dict_storage *dstorage, const char *name); + +/* + * Script sequence + */ + +struct sieve_script_sequence *sieve_dict_storage_get_script_sequence + (struct sieve_storage *storage, enum sieve_error *error_r); + +struct sieve_script *sieve_dict_script_sequence_next + (struct sieve_script_sequence *seq, enum sieve_error *error_r); +void sieve_dict_script_sequence_destroy(struct sieve_script_sequence *seq); + +#endif 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 diff --git a/pigeonhole/src/lib-sieve/storage/ldap/Makefile.am b/pigeonhole/src/lib-sieve/storage/ldap/Makefile.am new file mode 100644 index 0000000..86df92c --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/ldap/Makefile.am @@ -0,0 +1,32 @@ +noinst_LTLIBRARIES = libsieve_storage_ldap.la + +sieve_plugindir = $(dovecot_moduledir)/sieve +sieve_plugin_LTLIBRARIES = + +AM_CPPFLAGS = \ + $(LIBDOVECOT_INCLUDE) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/src/lib-sieve + +ldap_sources = \ + sieve-ldap-db.c \ + sieve-ldap-script.c \ + sieve-ldap-storage.c \ + sieve-ldap-storage-settings.c + +libsieve_storage_ldap_la_SOURCES = $(ldap_sources) +libsieve_storage_ldap_la_LIBADD = $(LDAP_LIBS) + +noinst_HEADERS = \ + sieve-ldap-db.h \ + sieve-ldap-storage.h + +if LDAP_PLUGIN +sieve_plugin_LTLIBRARIES += lib10_sieve_storage_ldap_plugin.la + +lib10_sieve_storage_ldap_plugin_la_LDFLAGS = -module -avoid-version +lib10_sieve_storage_ldap_plugin_la_LIBADD = $(LDAP_LIBS) +lib10_sieve_storage_ldap_plugin_la_DEPENDENCIES = $(LDAP_LIBS) +lib10_sieve_storage_ldap_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) -DPLUGIN_BUILD +lib10_sieve_storage_ldap_plugin_la_SOURCES = $(ldap_sources) +endif diff --git a/pigeonhole/src/lib-sieve/storage/ldap/Makefile.in b/pigeonhole/src/lib-sieve/storage/ldap/Makefile.in new file mode 100644 index 0000000..232f781 --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/ldap/Makefile.in @@ -0,0 +1,843 @@ +# 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@ +@LDAP_PLUGIN_TRUE@am__append_1 = lib10_sieve_storage_ldap_plugin.la +subdir = src/lib-sieve/storage/ldap +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 = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(sieve_plugindir)" +LTLIBRARIES = $(noinst_LTLIBRARIES) $(sieve_plugin_LTLIBRARIES) +am__DEPENDENCIES_1 = +am__lib10_sieve_storage_ldap_plugin_la_SOURCES_DIST = sieve-ldap-db.c \ + sieve-ldap-script.c sieve-ldap-storage.c \ + sieve-ldap-storage-settings.c +am__objects_1 = lib10_sieve_storage_ldap_plugin_la-sieve-ldap-db.lo \ + lib10_sieve_storage_ldap_plugin_la-sieve-ldap-script.lo \ + lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage.lo \ + lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage-settings.lo +@LDAP_PLUGIN_TRUE@am_lib10_sieve_storage_ldap_plugin_la_OBJECTS = \ +@LDAP_PLUGIN_TRUE@ $(am__objects_1) +lib10_sieve_storage_ldap_plugin_la_OBJECTS = \ + $(am_lib10_sieve_storage_ldap_plugin_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 = +lib10_sieve_storage_ldap_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) \ + --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \ + $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(lib10_sieve_storage_ldap_plugin_la_LDFLAGS) $(LDFLAGS) -o $@ +@LDAP_PLUGIN_TRUE@am_lib10_sieve_storage_ldap_plugin_la_rpath = \ +@LDAP_PLUGIN_TRUE@ -rpath $(sieve_plugindir) +libsieve_storage_ldap_la_DEPENDENCIES = $(am__DEPENDENCIES_1) +am__objects_2 = sieve-ldap-db.lo sieve-ldap-script.lo \ + sieve-ldap-storage.lo sieve-ldap-storage-settings.lo +am_libsieve_storage_ldap_la_OBJECTS = $(am__objects_2) +libsieve_storage_ldap_la_OBJECTS = \ + $(am_libsieve_storage_ldap_la_OBJECTS) +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)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-db.Plo \ + ./$(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-script.Plo \ + ./$(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage-settings.Plo \ + ./$(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage.Plo \ + ./$(DEPDIR)/sieve-ldap-db.Plo \ + ./$(DEPDIR)/sieve-ldap-script.Plo \ + ./$(DEPDIR)/sieve-ldap-storage-settings.Plo \ + ./$(DEPDIR)/sieve-ldap-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 = $(lib10_sieve_storage_ldap_plugin_la_SOURCES) \ + $(libsieve_storage_ldap_la_SOURCES) +DIST_SOURCES = $(am__lib10_sieve_storage_ldap_plugin_la_SOURCES_DIST) \ + $(libsieve_storage_ldap_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_ldap.la +sieve_plugindir = $(dovecot_moduledir)/sieve +sieve_plugin_LTLIBRARIES = $(am__append_1) +AM_CPPFLAGS = \ + $(LIBDOVECOT_INCLUDE) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/src/lib-sieve + +ldap_sources = \ + sieve-ldap-db.c \ + sieve-ldap-script.c \ + sieve-ldap-storage.c \ + sieve-ldap-storage-settings.c + +libsieve_storage_ldap_la_SOURCES = $(ldap_sources) +libsieve_storage_ldap_la_LIBADD = $(LDAP_LIBS) +noinst_HEADERS = \ + sieve-ldap-db.h \ + sieve-ldap-storage.h + +@LDAP_PLUGIN_TRUE@lib10_sieve_storage_ldap_plugin_la_LDFLAGS = -module -avoid-version +@LDAP_PLUGIN_TRUE@lib10_sieve_storage_ldap_plugin_la_LIBADD = $(LDAP_LIBS) +@LDAP_PLUGIN_TRUE@lib10_sieve_storage_ldap_plugin_la_DEPENDENCIES = $(LDAP_LIBS) +@LDAP_PLUGIN_TRUE@lib10_sieve_storage_ldap_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) -DPLUGIN_BUILD +@LDAP_PLUGIN_TRUE@lib10_sieve_storage_ldap_plugin_la_SOURCES = $(ldap_sources) +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/ldap/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/storage/ldap/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}; \ + } + +install-sieve_pluginLTLIBRARIES: $(sieve_plugin_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(sieve_plugin_LTLIBRARIES)'; test -n "$(sieve_plugindir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(sieve_plugindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sieve_plugindir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(sieve_plugindir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(sieve_plugindir)"; \ + } + +uninstall-sieve_pluginLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(sieve_plugin_LTLIBRARIES)'; test -n "$(sieve_plugindir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(sieve_plugindir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(sieve_plugindir)/$$f"; \ + done + +clean-sieve_pluginLTLIBRARIES: + -test -z "$(sieve_plugin_LTLIBRARIES)" || rm -f $(sieve_plugin_LTLIBRARIES) + @list='$(sieve_plugin_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}; \ + } + +lib10_sieve_storage_ldap_plugin.la: $(lib10_sieve_storage_ldap_plugin_la_OBJECTS) $(lib10_sieve_storage_ldap_plugin_la_DEPENDENCIES) $(EXTRA_lib10_sieve_storage_ldap_plugin_la_DEPENDENCIES) + $(AM_V_CCLD)$(lib10_sieve_storage_ldap_plugin_la_LINK) $(am_lib10_sieve_storage_ldap_plugin_la_rpath) $(lib10_sieve_storage_ldap_plugin_la_OBJECTS) $(lib10_sieve_storage_ldap_plugin_la_LIBADD) $(LIBS) + +libsieve_storage_ldap.la: $(libsieve_storage_ldap_la_OBJECTS) $(libsieve_storage_ldap_la_DEPENDENCIES) $(EXTRA_libsieve_storage_ldap_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_storage_ldap_la_OBJECTS) $(libsieve_storage_ldap_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-db.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-script.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage-settings.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-ldap-db.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-ldap-script.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-ldap-storage-settings.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-ldap-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 $@ $< + +lib10_sieve_storage_ldap_plugin_la-sieve-ldap-db.lo: sieve-ldap-db.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lib10_sieve_storage_ldap_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lib10_sieve_storage_ldap_plugin_la-sieve-ldap-db.lo -MD -MP -MF $(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-db.Tpo -c -o lib10_sieve_storage_ldap_plugin_la-sieve-ldap-db.lo `test -f 'sieve-ldap-db.c' || echo '$(srcdir)/'`sieve-ldap-db.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-db.Tpo $(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-db.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sieve-ldap-db.c' object='lib10_sieve_storage_ldap_plugin_la-sieve-ldap-db.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lib10_sieve_storage_ldap_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lib10_sieve_storage_ldap_plugin_la-sieve-ldap-db.lo `test -f 'sieve-ldap-db.c' || echo '$(srcdir)/'`sieve-ldap-db.c + +lib10_sieve_storage_ldap_plugin_la-sieve-ldap-script.lo: sieve-ldap-script.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lib10_sieve_storage_ldap_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lib10_sieve_storage_ldap_plugin_la-sieve-ldap-script.lo -MD -MP -MF $(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-script.Tpo -c -o lib10_sieve_storage_ldap_plugin_la-sieve-ldap-script.lo `test -f 'sieve-ldap-script.c' || echo '$(srcdir)/'`sieve-ldap-script.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-script.Tpo $(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-script.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sieve-ldap-script.c' object='lib10_sieve_storage_ldap_plugin_la-sieve-ldap-script.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lib10_sieve_storage_ldap_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lib10_sieve_storage_ldap_plugin_la-sieve-ldap-script.lo `test -f 'sieve-ldap-script.c' || echo '$(srcdir)/'`sieve-ldap-script.c + +lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage.lo: sieve-ldap-storage.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lib10_sieve_storage_ldap_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage.lo -MD -MP -MF $(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage.Tpo -c -o lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage.lo `test -f 'sieve-ldap-storage.c' || echo '$(srcdir)/'`sieve-ldap-storage.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage.Tpo $(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sieve-ldap-storage.c' object='lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lib10_sieve_storage_ldap_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage.lo `test -f 'sieve-ldap-storage.c' || echo '$(srcdir)/'`sieve-ldap-storage.c + +lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage-settings.lo: sieve-ldap-storage-settings.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lib10_sieve_storage_ldap_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage-settings.lo -MD -MP -MF $(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage-settings.Tpo -c -o lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage-settings.lo `test -f 'sieve-ldap-storage-settings.c' || echo '$(srcdir)/'`sieve-ldap-storage-settings.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage-settings.Tpo $(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage-settings.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sieve-ldap-storage-settings.c' object='lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage-settings.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lib10_sieve_storage_ldap_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage-settings.lo `test -f 'sieve-ldap-storage-settings.c' || echo '$(srcdir)/'`sieve-ldap-storage-settings.c + +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: + for dir in "$(DESTDIR)$(sieve_plugindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + clean-sieve_pluginLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-db.Plo + -rm -f ./$(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-script.Plo + -rm -f ./$(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage-settings.Plo + -rm -f ./$(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage.Plo + -rm -f ./$(DEPDIR)/sieve-ldap-db.Plo + -rm -f ./$(DEPDIR)/sieve-ldap-script.Plo + -rm -f ./$(DEPDIR)/sieve-ldap-storage-settings.Plo + -rm -f ./$(DEPDIR)/sieve-ldap-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-sieve_pluginLTLIBRARIES + +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)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-db.Plo + -rm -f ./$(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-script.Plo + -rm -f ./$(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage-settings.Plo + -rm -f ./$(DEPDIR)/lib10_sieve_storage_ldap_plugin_la-sieve-ldap-storage.Plo + -rm -f ./$(DEPDIR)/sieve-ldap-db.Plo + -rm -f ./$(DEPDIR)/sieve-ldap-script.Plo + -rm -f ./$(DEPDIR)/sieve-ldap-storage-settings.Plo + -rm -f ./$(DEPDIR)/sieve-ldap-storage.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-sieve_pluginLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstLTLIBRARIES \ + clean-sieve_pluginLTLIBRARIES 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-sieve_pluginLTLIBRARIES \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-sieve_pluginLTLIBRARIES + +.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/ldap/sieve-ldap-db.c b/pigeonhole/src/lib-sieve/storage/ldap/sieve-ldap-db.c new file mode 100644 index 0000000..0a7b440 --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/ldap/sieve-ldap-db.c @@ -0,0 +1,1378 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" + +#include "sieve-common.h" + +#include "sieve-ldap-storage.h" + +/* FIXME: Imported this from Dovecot auth for now. We're working on a proper + lib-ldap, but, until then, some code is duplicated here. */ + +#if defined(SIEVE_BUILTIN_LDAP) || defined(PLUGIN_BUILD) + +#include "net.h" +#include "ioloop.h" +#include "array.h" +#include "hash.h" +#include "aqueue.h" +#include "str.h" +#include "time-util.h" +#include "env-util.h" +#include "var-expand.h" +#include "istream.h" + +#include <stddef.h> +#include <unistd.h> + +struct db_ldap_result { + int refcount; + LDAPMessage *msg; +}; + +struct db_ldap_result_iterate_context { + pool_t pool; + + struct auth_request *auth_request; + const ARRAY_TYPE(ldap_field) *attr_map; + unsigned int attr_idx; + + /* attribute name => value */ + HASH_TABLE(char *, struct db_ldap_value *) ldap_attrs; + + const char *val_1_arr[2]; + string_t *var, *debug; + + bool skip_null_values; + bool iter_dn_values; +}; + +struct db_ldap_sasl_bind_context { + const char *authcid; + const char *passwd; + const char *realm; + const char *authzid; +}; + +static struct ldap_connection *ldap_connections = NULL; + +static int db_ldap_bind(struct ldap_connection *conn); +static void db_ldap_conn_close(struct ldap_connection *conn); + +int ldap_deref_from_str(const char *str, int *deref_r) +{ + if (strcasecmp(str, "never") == 0) + *deref_r = LDAP_DEREF_NEVER; + else if (strcasecmp(str, "searching") == 0) + *deref_r = LDAP_DEREF_SEARCHING; + else if (strcasecmp(str, "finding") == 0) + *deref_r = LDAP_DEREF_FINDING; + else if (strcasecmp(str, "always") == 0) + *deref_r = LDAP_DEREF_ALWAYS; + else + return -1; + return 0; +} + +int ldap_scope_from_str(const char *str, int *scope_r) +{ + if (strcasecmp(str, "base") == 0) + *scope_r = LDAP_SCOPE_BASE; + else if (strcasecmp(str, "onelevel") == 0) + *scope_r = LDAP_SCOPE_ONELEVEL; + else if (strcasecmp(str, "subtree") == 0) + *scope_r = LDAP_SCOPE_SUBTREE; + else + return -1; + return 0; +} + +#ifdef OPENLDAP_TLS_OPTIONS +int ldap_tls_require_cert_from_str(const char *str, int *opt_x_tls_r) +{ + if (strcasecmp(str, "never") == 0) + *opt_x_tls_r = LDAP_OPT_X_TLS_NEVER; + else if (strcasecmp(str, "hard") == 0) + *opt_x_tls_r = LDAP_OPT_X_TLS_HARD; + else if (strcasecmp(str, "demand") == 0) + *opt_x_tls_r = LDAP_OPT_X_TLS_DEMAND; + else if (strcasecmp(str, "allow") == 0) + *opt_x_tls_r = LDAP_OPT_X_TLS_ALLOW; + else if (strcasecmp(str, "try") == 0) + *opt_x_tls_r = LDAP_OPT_X_TLS_TRY; + else + return -1; + return 0; +} +#endif + + +static int ldap_get_errno(struct ldap_connection *conn) +{ + struct sieve_storage *storage = &conn->lstorage->storage; + int ret, err; + + ret = ldap_get_option(conn->ld, LDAP_OPT_ERROR_NUMBER, (void *) &err); + if (ret != LDAP_SUCCESS) { + e_error(storage->event, "db: " + "Can't get error number: %s", + ldap_err2string(ret)); + return LDAP_UNAVAILABLE; + } + + return err; +} + +const char *ldap_get_error(struct ldap_connection *conn) +{ + const char *ret; + char *str = NULL; + + ret = ldap_err2string(ldap_get_errno(conn)); + + ldap_get_option(conn->ld, LDAP_OPT_ERROR_STRING, (void *)&str); + if (str != NULL) { + ret = t_strconcat(ret, ", ", str, NULL); + ldap_memfree(str); + } + ldap_set_option(conn->ld, LDAP_OPT_ERROR_STRING, NULL); + return ret; +} + +static void ldap_conn_reconnect(struct ldap_connection *conn) +{ + db_ldap_conn_close(conn); + if (sieve_ldap_db_connect(conn) < 0) + db_ldap_conn_close(conn); +} + +static int ldap_handle_error(struct ldap_connection *conn) +{ + int err = ldap_get_errno(conn); + + switch (err) { + case LDAP_SUCCESS: + i_unreached(); + case LDAP_SIZELIMIT_EXCEEDED: + case LDAP_TIMELIMIT_EXCEEDED: + case LDAP_NO_SUCH_ATTRIBUTE: + case LDAP_UNDEFINED_TYPE: + case LDAP_INAPPROPRIATE_MATCHING: + case LDAP_CONSTRAINT_VIOLATION: + case LDAP_TYPE_OR_VALUE_EXISTS: + case LDAP_INVALID_SYNTAX: + case LDAP_NO_SUCH_OBJECT: + case LDAP_ALIAS_PROBLEM: + case LDAP_INVALID_DN_SYNTAX: + case LDAP_IS_LEAF: + case LDAP_ALIAS_DEREF_PROBLEM: + case LDAP_FILTER_ERROR: + /* invalid input */ + return -1; + case LDAP_SERVER_DOWN: + case LDAP_TIMEOUT: + case LDAP_UNAVAILABLE: + case LDAP_BUSY: +#ifdef LDAP_CONNECT_ERROR + case LDAP_CONNECT_ERROR: +#endif + case LDAP_LOCAL_ERROR: + case LDAP_INVALID_CREDENTIALS: + case LDAP_OPERATIONS_ERROR: + default: + /* connection problems */ + ldap_conn_reconnect(conn); + return 0; + } +} + +static int db_ldap_request_search(struct ldap_connection *conn, + struct ldap_request *request) +{ + struct sieve_storage *storage = &conn->lstorage->storage; + + i_assert(conn->conn_state == LDAP_CONN_STATE_BOUND); + i_assert(request->msgid == -1); + + request->msgid = + ldap_search(conn->ld, *request->base == '\0' ? NULL : + request->base, request->scope, + request->filter, request->attributes, 0); + if (request->msgid == -1) { + e_error(storage->event, "db: " + "ldap_search(%s) parsing failed: %s", + request->filter, ldap_get_error(conn)); + if (ldap_handle_error(conn) < 0) { + /* broken request, remove it */ + return 0; + } + return -1; + } + return 1; +} + +static bool db_ldap_request_queue_next(struct ldap_connection *conn) +{ + struct ldap_request *const *requestp, *request; + int ret = -1; + + /* connecting may call db_ldap_connect_finish(), which gets us back + here. so do the connection before checking the request queue. */ + if (sieve_ldap_db_connect(conn) < 0) + return FALSE; + + if (conn->pending_count == aqueue_count(conn->request_queue)) { + /* no non-pending requests */ + return FALSE; + } + if (conn->pending_count > DB_LDAP_MAX_PENDING_REQUESTS) { + /* wait until server has replied to some requests */ + return FALSE; + } + + requestp = array_idx(&conn->request_array, + aqueue_idx(conn->request_queue, + conn->pending_count)); + request = *requestp; + + switch (conn->conn_state) { + case LDAP_CONN_STATE_DISCONNECTED: + case LDAP_CONN_STATE_BINDING: + /* wait until we're in bound state */ + return FALSE; + case LDAP_CONN_STATE_BOUND: + /* we can do anything in this state */ + break; + } + + ret = db_ldap_request_search(conn, request); + if (ret > 0) { + /* success */ + i_assert(request->msgid != -1); + conn->pending_count++; + return TRUE; + } else if (ret < 0) { + /* disconnected */ + return FALSE; + } else { + /* broken request, remove from queue */ + aqueue_delete_tail(conn->request_queue); + request->callback(conn, request, NULL); + return TRUE; + } +} + +static bool +db_ldap_check_limits(struct ldap_connection *conn) +{ + struct sieve_storage *storage = &conn->lstorage->storage; + struct ldap_request *const *first_requestp; + unsigned int count; + time_t secs_diff; + + count = aqueue_count(conn->request_queue); + if (count == 0) + return TRUE; + + first_requestp = array_idx(&conn->request_array, + aqueue_idx(conn->request_queue, 0)); + secs_diff = ioloop_time - (*first_requestp)->create_time; + if (secs_diff > DB_LDAP_REQUEST_LOST_TIMEOUT_SECS) { + e_error(storage->event, "db: " + "Connection appears to be hanging, reconnecting"); + ldap_conn_reconnect(conn); + return TRUE; + } + return TRUE; +} + +void db_ldap_request(struct ldap_connection *conn, + struct ldap_request *request) +{ + request->msgid = -1; + request->create_time = ioloop_time; + + if (!db_ldap_check_limits(conn)) { + request->callback(conn, request, NULL); + return; + } + + aqueue_append(conn->request_queue, &request); + (void)db_ldap_request_queue_next(conn); +} + +static int db_ldap_connect_finish(struct ldap_connection *conn, int ret) +{ + struct sieve_storage *storage = &conn->lstorage->storage; + const struct sieve_ldap_storage_settings *set = &conn->lstorage->set; + + if (ret == LDAP_SERVER_DOWN) { + e_error(storage->event, "db: " + "Can't connect to server: %s", + set->uris != NULL ? + set->uris : set->hosts); + return -1; + } + if (ret != LDAP_SUCCESS) { + e_error(storage->event, "db: " + "binding failed (dn %s): %s", + set->dn == NULL ? "(none)" : set->dn, + ldap_get_error(conn)); + return -1; + } + + timeout_remove(&conn->to); + conn->conn_state = LDAP_CONN_STATE_BOUND; + e_debug(storage->event, "db: " + "Successfully bound (dn %s)", + set->dn == NULL ? "(none)" : set->dn); + while (db_ldap_request_queue_next(conn)) + ; + return 0; +} + +static void db_ldap_default_bind_finished(struct ldap_connection *conn, + struct db_ldap_result *res) +{ + int ret; + + i_assert(conn->pending_count == 0); + conn->default_bind_msgid = -1; + + ret = ldap_result2error(conn->ld, res->msg, FALSE); + if (db_ldap_connect_finish(conn, ret) < 0) { + /* lost connection, close it */ + db_ldap_conn_close(conn); + } +} + +static void db_ldap_abort_requests(struct ldap_connection *conn, + unsigned int max_count, + unsigned int timeout_secs, + bool error, const char *reason) +{ + struct sieve_storage *storage = &conn->lstorage->storage; + struct ldap_request *const *requestp, *request; + time_t diff; + + while (aqueue_count(conn->request_queue) > 0 && max_count > 0) { + requestp = array_idx(&conn->request_array, + aqueue_idx(conn->request_queue, 0)); + request = *requestp; + + diff = ioloop_time - request->create_time; + if (diff < (time_t)timeout_secs) + break; + + /* timed out, abort */ + aqueue_delete_tail(conn->request_queue); + + if (request->msgid != -1) { + i_assert(conn->pending_count > 0); + conn->pending_count--; + } + if (error) + e_error(storage->event, "db: %s", reason); + else + e_debug(storage->event, "db: %s", reason); + request->callback(conn, request, NULL); + max_count--; + } +} + +static struct ldap_request * +db_ldap_find_request(struct ldap_connection *conn, int msgid, + unsigned int *idx_r) +{ + struct ldap_request *const *requests, *request = NULL; + unsigned int i, count; + + count = aqueue_count(conn->request_queue); + if (count == 0) + return NULL; + + requests = array_idx(&conn->request_array, 0); + for (i = 0; i < count; i++) { + request = requests[aqueue_idx(conn->request_queue, i)]; + if (request->msgid == msgid) { + *idx_r = i; + return request; + } + if (request->msgid == -1) + break; + } + return NULL; +} + +static bool +db_ldap_handle_request_result(struct ldap_connection *conn, + struct ldap_request *request, unsigned int idx, + struct db_ldap_result *res) +{ + struct sieve_storage *storage = &conn->lstorage->storage; + int ret; + bool final_result; + + i_assert(conn->pending_count > 0); + + switch (ldap_msgtype(res->msg)) { + case LDAP_RES_SEARCH_ENTRY: + case LDAP_RES_SEARCH_RESULT: + break; + case LDAP_RES_SEARCH_REFERENCE: + /* we're going to ignore this */ + return FALSE; + default: + e_error(storage->event, "db: Reply with unexpected type %d", + ldap_msgtype(res->msg)); + return TRUE; + } + + if (ldap_msgtype(res->msg) == LDAP_RES_SEARCH_ENTRY) { + ret = LDAP_SUCCESS; + final_result = FALSE; + } else { + final_result = TRUE; + ret = ldap_result2error(conn->ld, res->msg, 0); + } + if (ret != LDAP_SUCCESS) { + /* handle search failures here */ + e_error(storage->event, "db: " + "ldap_search(base=%s filter=%s) failed: %s", + request->base, request->filter, + ldap_err2string(ret)); + res = NULL; + } else { + if (!final_result && storage->svinst->debug) { + e_debug(storage->event, + "db: ldap_search(base=%s filter=%s) returned entry: %s", + request->base, request->filter, + ldap_get_dn(conn->ld, res->msg)); + } + } + if (res == NULL && !final_result) { + /* wait for the final reply */ + request->failed = TRUE; + return TRUE; + } + if (request->failed) + res = NULL; + if (final_result) { + conn->pending_count--; + aqueue_delete(conn->request_queue, idx); + } + + T_BEGIN { + request->callback(conn, request, res == NULL ? NULL : res->msg); + } T_END; + + if (idx > 0) { + /* see if there are timed out requests */ + db_ldap_abort_requests(conn, idx, + DB_LDAP_REQUEST_LOST_TIMEOUT_SECS, + TRUE, "Request lost"); + } + return TRUE; +} + +static void db_ldap_result_unref(struct db_ldap_result **_res) +{ + struct db_ldap_result *res = *_res; + + *_res = NULL; + i_assert(res->refcount > 0); + if (--res->refcount == 0) { + ldap_msgfree(res->msg); + i_free(res); + } +} + +static void +db_ldap_request_free(struct ldap_request *request) +{ + if (request->result != NULL) + db_ldap_result_unref(&request->result); +} + +static void +db_ldap_handle_result(struct ldap_connection *conn, struct db_ldap_result *res) +{ + struct sieve_storage *storage = &conn->lstorage->storage; + struct ldap_request *request; + unsigned int idx; + int msgid; + + msgid = ldap_msgid(res->msg); + if (msgid == conn->default_bind_msgid) { + db_ldap_default_bind_finished(conn, res); + return; + } + + request = db_ldap_find_request(conn, msgid, &idx); + if (request == NULL) { + e_error(storage->event, + "db: Reply with unknown msgid %d", msgid); + return; + } + + if (db_ldap_handle_request_result(conn, request, idx, res)) + db_ldap_request_free(request); +} + +static void ldap_input(struct ldap_connection *conn) +{ + struct sieve_storage *storage = &conn->lstorage->storage; + struct timeval timeout; + struct db_ldap_result *res; + LDAPMessage *msg; + time_t prev_reply_diff; + int ret; + + do { + if (conn->ld == NULL) + return; + + i_zero(&timeout); + ret = ldap_result(conn->ld, LDAP_RES_ANY, 0, &timeout, &msg); +#ifdef OPENLDAP_ASYNC_WORKAROUND + if (ret == 0) { + /* try again, there may be another in buffer */ + ret = ldap_result(conn->ld, LDAP_RES_ANY, 0, + &timeout, &msg); + } +#endif + if (ret <= 0) + break; + + res = i_new(struct db_ldap_result, 1); + res->refcount = 1; + res->msg = msg; + db_ldap_handle_result(conn, res); + db_ldap_result_unref(&res); + } while (conn->io != NULL); + + prev_reply_diff = ioloop_time - conn->last_reply_stamp; + conn->last_reply_stamp = ioloop_time; + + if (ret > 0) { + /* input disabled, continue once it's enabled */ + i_assert(conn->io == NULL); + } else if (ret == 0) { + /* send more requests */ + while (db_ldap_request_queue_next(conn)) + ; + } else if (ldap_get_errno(conn) != LDAP_SERVER_DOWN) { + e_error(storage->event, "db: ldap_result() failed: %s", + ldap_get_error(conn)); + ldap_conn_reconnect(conn); + } else if (aqueue_count(conn->request_queue) > 0 || + prev_reply_diff < DB_LDAP_IDLE_RECONNECT_SECS) { + e_error(storage->event, + "db: Connection lost to LDAP server, reconnecting"); + ldap_conn_reconnect(conn); + } else { + /* server probably disconnected an idle connection. don't + reconnect until the next request comes. */ + db_ldap_conn_close(conn); + } +} + +#ifdef HAVE_LDAP_SASL +static int +sasl_interact(LDAP *ld ATTR_UNUSED, unsigned flags ATTR_UNUSED, + void *defaults, void *interact) +{ + struct db_ldap_sasl_bind_context *context = defaults; + sasl_interact_t *in; + const char *str; + + for (in = interact; in->id != SASL_CB_LIST_END; in++) { + switch (in->id) { + case SASL_CB_GETREALM: + str = context->realm; + break; + case SASL_CB_AUTHNAME: + str = context->authcid; + break; + case SASL_CB_USER: + str = context->authzid; + break; + case SASL_CB_PASS: + str = context->passwd; + break; + default: + str = NULL; + break; + } + if (str != NULL) { + in->len = strlen(str); + in->result = str; + } + + } + return LDAP_SUCCESS; +} +#endif + +static void ldap_connection_timeout(struct ldap_connection *conn) +{ + struct sieve_storage *storage = &conn->lstorage->storage; + i_assert(conn->conn_state == LDAP_CONN_STATE_BINDING); + + e_error(storage->event, "db: Initial binding to LDAP server timed out"); + db_ldap_conn_close(conn); +} + +static int db_ldap_bind(struct ldap_connection *conn) +{ + const struct sieve_ldap_storage_settings *set = &conn->lstorage->set; + int msgid; + + i_assert(conn->conn_state != LDAP_CONN_STATE_BINDING); + i_assert(conn->default_bind_msgid == -1); + i_assert(conn->pending_count == 0); + + msgid = ldap_bind(conn->ld, set->dn, set->dnpass, + LDAP_AUTH_SIMPLE); + if (msgid == -1) { + i_assert(ldap_get_errno(conn) != LDAP_SUCCESS); + if (db_ldap_connect_finish(conn, ldap_get_errno(conn)) < 0) { + /* lost connection, close it */ + db_ldap_conn_close(conn); + } + return -1; + } + + conn->conn_state = LDAP_CONN_STATE_BINDING; + conn->default_bind_msgid = msgid; + + timeout_remove(&conn->to); + conn->to = timeout_add(DB_LDAP_REQUEST_LOST_TIMEOUT_SECS*1000, + ldap_connection_timeout, conn); + return 0; +} + +static int db_ldap_get_fd(struct ldap_connection *conn) +{ + struct sieve_storage *storage = &conn->lstorage->storage; + int ret; + + /* get the connection's fd */ + ret = ldap_get_option(conn->ld, LDAP_OPT_DESC, (void *)&conn->fd); + if (ret != LDAP_SUCCESS) { + e_error(storage->event, "db: Can't get connection fd: %s", + ldap_err2string(ret)); + return -1; + } + if (conn->fd <= STDERR_FILENO) { + /* Solaris LDAP library seems to be broken */ + e_error(storage->event, + "db: Buggy LDAP library returned wrong fd: %d", + conn->fd); + return -1; + } + i_assert(conn->fd != -1); + net_set_nonblock(conn->fd, TRUE); + return 0; +} + +static int +db_ldap_set_opt(struct ldap_connection *conn, int opt, const void *value, + const char *optname, const char *value_str) +{ + struct sieve_storage *storage = &conn->lstorage->storage; + int ret; + + ret = ldap_set_option(conn->ld, opt, value); + if (ret != LDAP_SUCCESS) { + e_error(storage->event, "db: Can't set option %s to %s: %s", + optname, value_str, ldap_err2string(ret)); + return -1; + } + return 0; +} + +static int +db_ldap_set_opt_str(struct ldap_connection *conn, int opt, const char *value, + const char *optname) +{ + if (value != NULL) + return db_ldap_set_opt(conn, opt, value, optname, value); + return 0; +} + +static int db_ldap_set_tls_options(struct ldap_connection *conn) +{ + const struct sieve_ldap_storage_settings *set = &conn->lstorage->set; + + if (!set->tls) + return 0; + +#ifdef OPENLDAP_TLS_OPTIONS + if (db_ldap_set_opt_str(conn, LDAP_OPT_X_TLS_CACERTFILE, + set->tls_ca_cert_file, "tls_ca_cert_file") < 0) + return -1; + if (db_ldap_set_opt_str(conn, LDAP_OPT_X_TLS_CACERTDIR, + set->tls_ca_cert_dir, "tls_ca_cert_dir") < 0) + return -1; + if (db_ldap_set_opt_str(conn, LDAP_OPT_X_TLS_CERTFILE, + set->tls_cert_file, "tls_cert_file") < 0) + return -1; + if (db_ldap_set_opt_str(conn, LDAP_OPT_X_TLS_KEYFILE, + set->tls_key_file, "tls_key_file") < 0) + return -1; + if (db_ldap_set_opt_str(conn, LDAP_OPT_X_TLS_CIPHER_SUITE, + set->tls_cipher_suite, "tls_cipher_suite") < 0) + return -1; + if (set->tls_require_cert != NULL) { + if (db_ldap_set_opt(conn, LDAP_OPT_X_TLS_REQUIRE_CERT, + &set->ldap_tls_require_cert, + "tls_require_cert", set->tls_require_cert) < 0) + return -1; + } +#else + if (set->tls_ca_cert_file != NULL || + set->tls_ca_cert_dir != NULL || + set->tls_cert_file != NULL || + set->tls_key_file != NULL || + set->tls_cipher_suite != NULL) { + e_warning(&conn->lstorage->storage, "db: " + "tls_* settings ignored, " + "your LDAP library doesn't seem to support them"); + } +#endif + return 0; +} + +static int db_ldap_set_options(struct ldap_connection *conn) +{ + const struct sieve_ldap_storage_settings *set = &conn->lstorage->set; + struct sieve_storage *storage = &conn->lstorage->storage; + unsigned int ldap_version; + int value; + + if (db_ldap_set_opt(conn, LDAP_OPT_DEREF, &set->ldap_deref, + "deref", set->deref) < 0) + return -1; +#ifdef LDAP_OPT_DEBUG_LEVEL + if (str_to_int(set->debug_level, &value) >= 0 && value != 0) { + if (db_ldap_set_opt(conn, LDAP_OPT_DEBUG_LEVEL, &value, + "debug_level", set->debug_level) < 0) + return -1; + } +#endif + + if (set->ldap_version < 3) { + if (set->sasl_bind) { + e_error(storage->event, + "db: sasl_bind=yes requires ldap_version=3"); + return -1; + } + if (set->tls) { + e_error(storage->event, + "db: tls=yes requires ldap_version=3"); + return -1; + } + } + + ldap_version = set->ldap_version; + if (db_ldap_set_opt(conn, LDAP_OPT_PROTOCOL_VERSION, &ldap_version, + "protocol_version", dec2str(ldap_version)) < 0) + return -1; + if (db_ldap_set_tls_options(conn) < 0) + return -1; + return 0; +} + +int sieve_ldap_db_connect(struct ldap_connection *conn) +{ + const struct sieve_ldap_storage_settings *set = &conn->lstorage->set; + struct sieve_storage *storage = &conn->lstorage->storage; + struct timeval start, end; + int debug_level; + bool debug; +#if defined(HAVE_LDAP_SASL) || defined(LDAP_HAVE_START_TLS_S) + int ret; +#endif + + if (conn->conn_state != LDAP_CONN_STATE_DISCONNECTED) + return 0; + + debug = FALSE; + if (str_to_int(set->debug_level, &debug_level) >= 0) + debug = debug_level > 0; + + if (debug) + i_gettimeofday(&start); + i_assert(conn->pending_count == 0); + if (conn->ld == NULL) { + if (set->uris != NULL) { +#ifdef LDAP_HAVE_INITIALIZE + if (ldap_initialize(&conn->ld, set->uris) != LDAP_SUCCESS) + conn->ld = NULL; +#else + e_error(storage->event, "db: " + "Your LDAP library doesn't support " + "'uris' setting, use 'hosts' instead."); + return -1; +#endif + } else + conn->ld = ldap_init(set->hosts, LDAP_PORT); + + if (conn->ld == NULL) { + e_error(storage->event, "db: " + "ldap_init() failed with hosts: %s", set->hosts); + return -1; + } + + if (db_ldap_set_options(conn) < 0) + return -1; + } + + if (set->tls) { +#ifdef LDAP_HAVE_START_TLS_S + ret = ldap_start_tls_s(conn->ld, NULL, NULL); + if (ret != LDAP_SUCCESS) { + if (ret == LDAP_OPERATIONS_ERROR && + set->uris != NULL && + str_begins(set->uris, "ldaps:")) { + e_error(storage->event, "db: " + "Don't use both tls=yes and ldaps URI"); + } + e_error(storage->event, "db: " + "ldap_start_tls_s() failed: %s", + ldap_err2string(ret)); + return -1; + } +#else + e_error(storage->event, "db: " + "Your LDAP library doesn't support TLS"); + return -1; +#endif + } + + if (set->sasl_bind) { +#ifdef HAVE_LDAP_SASL + struct db_ldap_sasl_bind_context context; + + i_zero(&context); + context.authcid = set->dn; + context.passwd = set->dnpass; + context.realm = set->sasl_realm; + context.authzid = set->sasl_authz_id; + + /* There doesn't seem to be a way to do SASL binding + asynchronously.. */ + ret = ldap_sasl_interactive_bind_s(conn->ld, NULL, + set->sasl_mech, + NULL, NULL, LDAP_SASL_QUIET, + sasl_interact, &context); + if (db_ldap_connect_finish(conn, ret) < 0) + return -1; +#else + e_error(storage->event, "db: " + "sasl_bind=yes but no SASL support compiled in"); + return -1; +#endif + conn->conn_state = LDAP_CONN_STATE_BOUND; + } else { + if (db_ldap_bind(conn) < 0) + return -1; + } + if (debug) { + i_gettimeofday(&end); + int msecs = timeval_diff_msecs(&end, &start); + e_debug(storage->event, "db: " + "Initialization took %d msecs", msecs); + } + + if (db_ldap_get_fd(conn) < 0) + return -1; + conn->io = io_add(conn->fd, IO_READ, ldap_input, conn); + return 0; +} + +void db_ldap_enable_input(struct ldap_connection *conn, bool enable) +{ + if (!enable) { + io_remove(&conn->io); + } else { + if (conn->io == NULL && conn->fd != -1) { + conn->io = io_add(conn->fd, IO_READ, ldap_input, conn); + ldap_input(conn); + } + } +} + +static void db_ldap_disconnect_timeout(struct ldap_connection *conn) +{ + db_ldap_abort_requests(conn, UINT_MAX, + DB_LDAP_REQUEST_DISCONNECT_TIMEOUT_SECS, FALSE, + "Aborting (timeout), we're not connected to LDAP server"); + + if (aqueue_count(conn->request_queue) == 0) { + /* no requests left, remove this timeout handler */ + timeout_remove(&conn->to); + } +} + +static void db_ldap_conn_close(struct ldap_connection *conn) +{ + struct ldap_request *const *requests, *request; + unsigned int i; + + conn->conn_state = LDAP_CONN_STATE_DISCONNECTED; + conn->default_bind_msgid = -1; + + timeout_remove(&conn->to); + + if (conn->pending_count != 0) { + requests = array_idx(&conn->request_array, 0); + for (i = 0; i < conn->pending_count; i++) { + request = requests[aqueue_idx(conn->request_queue, i)]; + + i_assert(request->msgid != -1); + request->msgid = -1; + } + conn->pending_count = 0; + } + + if (conn->ld != NULL) { + ldap_unbind(conn->ld); + conn->ld = NULL; + } + conn->fd = -1; + + /* the fd may have already been closed before ldap_unbind(), + so we'll have to use io_remove_closed(). */ + io_remove_closed(&conn->io); + + if (aqueue_count(conn->request_queue) > 0) { + conn->to = timeout_add(DB_LDAP_REQUEST_DISCONNECT_TIMEOUT_SECS * + 1000/2, db_ldap_disconnect_timeout, conn); + } +} + +struct ldap_field_find_context { + ARRAY_TYPE(string) attr_names; + pool_t pool; +}; + +#define IS_LDAP_ESCAPED_CHAR(c) \ + ((c) == '*' || (c) == '(' || (c) == ')' || (c) == '\\') + +const char *ldap_escape(const char *str) +{ + const char *p; + string_t *ret; + + for (p = str; *p != '\0'; p++) { + if (IS_LDAP_ESCAPED_CHAR(*p)) + break; + } + + if (*p == '\0') + return str; + + ret = t_str_new((size_t) (p - str) + 64); + str_append_data(ret, str, (size_t) (p - str)); + + for (; *p != '\0'; p++) { + if (IS_LDAP_ESCAPED_CHAR(*p)) + str_append_c(ret, '\\'); + str_append_c(ret, *p); + } + return str_c(ret); +} + +struct ldap_connection * +sieve_ldap_db_init(struct sieve_ldap_storage *lstorage) +{ + struct ldap_connection *conn; + pool_t pool; + + pool = pool_alloconly_create("ldap_connection", 1024); + conn = p_new(pool, struct ldap_connection, 1); + conn->pool = pool; + conn->refcount = 1; + conn->lstorage = lstorage; + + conn->conn_state = LDAP_CONN_STATE_DISCONNECTED; + conn->default_bind_msgid = -1; + conn->fd = -1; + + i_array_init(&conn->request_array, 512); + conn->request_queue = aqueue_init(&conn->request_array.arr); + + conn->next = ldap_connections; + ldap_connections = conn; + return conn; +} + +void sieve_ldap_db_unref(struct ldap_connection **_conn) +{ + struct ldap_connection *conn = *_conn; + struct ldap_connection **p; + + *_conn = NULL; + i_assert(conn->refcount >= 0); + if (--conn->refcount > 0) + return; + + for (p = &ldap_connections; *p != NULL; p = &(*p)->next) { + if (*p == conn) { + *p = conn->next; + break; + } + } + + db_ldap_abort_requests(conn, UINT_MAX, 0, FALSE, "Shutting down"); + i_assert(conn->pending_count == 0); + db_ldap_conn_close(conn); + i_assert(conn->to == NULL); + + array_free(&conn->request_array); + aqueue_deinit(&conn->request_queue); + + pool_unref(&conn->pool); +} + +static void db_ldap_switch_ioloop(struct ldap_connection *conn) +{ + if (conn->to != NULL) + conn->to = io_loop_move_timeout(&conn->to); + if (conn->io != NULL) + conn->io = io_loop_move_io(&conn->io); +} + +static void db_ldap_wait(struct ldap_connection *conn) +{ + struct sieve_storage *storage = &conn->lstorage->storage; + struct ioloop *prev_ioloop = current_ioloop; + + i_assert(conn->ioloop == NULL); + + if (aqueue_count(conn->request_queue) == 0) + return; + + conn->ioloop = io_loop_create(); + db_ldap_switch_ioloop(conn); + /* either we're waiting for network I/O or we're getting out of a + callback using timeout_add_short(0) */ + i_assert(io_loop_have_ios(conn->ioloop) || + io_loop_have_immediate_timeouts(conn->ioloop)); + + do { + e_debug(storage->event, "db: " + "Waiting for %d requests to finish", + aqueue_count(conn->request_queue) ); + io_loop_run(conn->ioloop); + } while (aqueue_count(conn->request_queue) > 0); + + e_debug(storage->event, "db: All requests finished"); + + current_ioloop = prev_ioloop; + db_ldap_switch_ioloop(conn); + current_ioloop = conn->ioloop; + io_loop_destroy(&conn->ioloop); +} + +static void sieve_ldap_db_script_free(unsigned char *script) +{ + i_free(script); +} + +static int +sieve_ldap_db_get_script_modattr(struct ldap_connection *conn, + LDAPMessage *entry, pool_t pool, const char **modattr_r) +{ + const struct sieve_ldap_storage_settings *set = &conn->lstorage->set; + struct sieve_storage *storage = &conn->lstorage->storage; + char *attr, **vals; + BerElement *ber; + + *modattr_r = NULL; + + attr = ldap_first_attribute(conn->ld, entry, &ber); + while (attr != NULL) { + if (strcmp(attr, set->sieve_ldap_mod_attr) == 0) { + vals = ldap_get_values(conn->ld, entry, attr); + if (vals == NULL || vals[0] == NULL) + return 0; + + if (vals[1] != NULL) { + e_warning(storage->event, "db: " + "Search returned more than one Sieve modified attribute `%s'; " + "using only the first one.", set->sieve_ldap_mod_attr); + } + + *modattr_r = p_strdup(pool, vals[0]); + + ldap_value_free(vals); + ldap_memfree(attr); + return 1; + } + ldap_memfree(attr); + attr = ldap_next_attribute(conn->ld, entry, ber); + } + ber_free(ber, 0); + + return 0; +} + +static int +sieve_ldap_db_get_script(struct ldap_connection *conn, + LDAPMessage *entry, struct istream **script_r) +{ + const struct sieve_ldap_storage_settings *set = &conn->lstorage->set; + struct sieve_storage *storage = &conn->lstorage->storage; + char *attr; + unsigned char *data; + size_t size; + struct berval **vals; + BerElement *ber; + + attr = ldap_first_attribute(conn->ld, entry, &ber); + while (attr != NULL) { + if (strcmp(attr, set->sieve_ldap_script_attr) == 0) { + vals = ldap_get_values_len(conn->ld, entry, attr); + if (vals == NULL || vals[0] == NULL) + return 0; + + if (vals[1] != NULL) { + e_warning(storage->event, "db: " + "Search returned more than one Sieve script attribute `%s'; " + "using only the first one.", set->sieve_ldap_script_attr); + } + + size = vals[0]->bv_len; + data = i_malloc(size); + + e_debug(storage->event, "db: " + "Found script with length %zu", size); + + memcpy(data, vals[0]->bv_val, size); + + ldap_value_free_len(vals); + ldap_memfree(attr); + + *script_r = i_stream_create_from_data(data, size); + i_stream_add_destroy_callback + (*script_r, sieve_ldap_db_script_free, data); + return 1; + } + ldap_memfree(attr); + attr = ldap_next_attribute(conn->ld, entry, ber); + } + ber_free(ber, 0); + + return 0; +} + +const struct var_expand_table +auth_request_var_expand_static_tab[] = { + { 'u', NULL, "user" }, + { 'n', NULL, "username" }, + { 'd', NULL, "domain" }, + { 'h', NULL, "home" }, + { '\0', NULL, "name" }, + { '\0', NULL, NULL } +}; + +static const struct var_expand_table * +db_ldap_get_var_expand_table(struct ldap_connection *conn, + const char *name) +{ + struct sieve_ldap_storage *lstorage = conn->lstorage; + struct sieve_instance *svinst = lstorage->storage.svinst; + const unsigned int auth_count = + N_ELEMENTS(auth_request_var_expand_static_tab); + struct var_expand_table *tab; + + /* keep the extra fields at the beginning. the last static_tab field + contains the ending NULL-fields. */ + tab = t_malloc_no0((auth_count) * sizeof(*tab)); + + memcpy(tab, auth_request_var_expand_static_tab, + auth_count * sizeof(*tab)); + + tab[0].value = ldap_escape(lstorage->username); + tab[1].value = ldap_escape(t_strcut(lstorage->username, '@')); + tab[2].value = strchr(lstorage->username, '@'); + if (tab[2].value != NULL) + tab[2].value = ldap_escape(tab[2].value+1); + tab[3].value = ldap_escape(svinst->home_dir); + tab[4].value = ldap_escape(name); + return tab; +} + +struct sieve_ldap_script_lookup_request { + struct ldap_request request; + + unsigned int entries; + const char *result_dn; + const char *result_modattr; +}; + +static void +sieve_ldap_lookup_script_callback(struct ldap_connection *conn, + struct ldap_request *request, LDAPMessage *res) +{ + struct sieve_storage *storage = &conn->lstorage->storage; + struct sieve_ldap_script_lookup_request *srequest = + (struct sieve_ldap_script_lookup_request *)request; + + if (res == NULL) { + io_loop_stop(conn->ioloop); + return; + } + + if (ldap_msgtype(res) != LDAP_RES_SEARCH_RESULT) { + if (srequest->result_dn == NULL) { + srequest->result_dn = p_strdup + (request->pool, ldap_get_dn(conn->ld, res)); + (void)sieve_ldap_db_get_script_modattr + (conn, res, request->pool, &srequest->result_modattr); + } else if (srequest->entries++ == 0) { + e_warning(storage->event, "db: " + "Search returned more than one entry for Sieve script; " + "using only the first one."); + } + } else { + io_loop_stop(conn->ioloop); + return; + } +} + +int sieve_ldap_db_lookup_script(struct ldap_connection *conn, + const char *name, const char **dn_r, const char **modattr_r) +{ + struct sieve_ldap_storage *lstorage = conn->lstorage; + struct sieve_storage *storage = &lstorage->storage; + const struct sieve_ldap_storage_settings *set = &lstorage->set; + struct sieve_ldap_script_lookup_request *request; + const struct var_expand_table *tab; + char **attr_names; + const char *error; + string_t *str; + + pool_t pool = pool_alloconly_create + ("sieve_ldap_script_lookup_request", 512); + request = p_new(pool, struct sieve_ldap_script_lookup_request, 1); + request->request.pool = pool; + + tab = db_ldap_get_var_expand_table(conn, name); + + str = t_str_new(512); + if (var_expand(str, set->base, tab, &error) <= 0) { + e_error(storage->event, "db: " + "Failed to expand base=%s: %s", + set->base, error); + return -1; + } + request->request.base = p_strdup(pool, str_c(str)); + + attr_names = p_new(pool, char *, 3); + attr_names[0] = p_strdup(pool, set->sieve_ldap_mod_attr); + + str_truncate(str, 0); + if (var_expand(str, set->sieve_ldap_filter, tab, &error) <= 0) { + e_error(storage->event, "db: " + "Failed to expand sieve_ldap_filter=%s: %s", + set->sieve_ldap_filter, error); + return -1; + } + + request->request.scope = lstorage->set.ldap_scope; + request->request.filter = p_strdup(pool, str_c(str)); + request->request.attributes = attr_names; + + e_debug(storage->event, "base=%s scope=%s filter=%s fields=%s", + request->request.base, lstorage->set.scope, + request->request.filter, + t_strarray_join((const char **)attr_names, ",")); + + request->request.callback = sieve_ldap_lookup_script_callback; + db_ldap_request(conn, &request->request); + db_ldap_wait(conn); + + *dn_r = t_strdup(request->result_dn); + *modattr_r = t_strdup(request->result_modattr); + pool_unref(&request->request.pool); + return (*dn_r == NULL ? 0 : 1); +} + +struct sieve_ldap_script_read_request { + struct ldap_request request; + + unsigned int entries; + struct istream *result; +}; + +static void +sieve_ldap_read_script_callback(struct ldap_connection *conn, + struct ldap_request *request, LDAPMessage *res) +{ + struct sieve_storage *storage = &conn->lstorage->storage; + struct sieve_ldap_script_read_request *srequest = + (struct sieve_ldap_script_read_request *)request; + + if (res == NULL) { + io_loop_stop(conn->ioloop); + return; + } + + if (ldap_msgtype(res) != LDAP_RES_SEARCH_RESULT) { + + if (srequest->result == NULL) { + (void)sieve_ldap_db_get_script(conn, res, &srequest->result); + } else { + e_error(storage->event, "db: " + "Search returned more than one entry for Sieve script DN"); + i_stream_unref(&srequest->result); + } + + } else { + io_loop_stop(conn->ioloop); + return; + } +} + +int sieve_ldap_db_read_script(struct ldap_connection *conn, + const char *dn, struct istream **script_r) +{ + struct sieve_ldap_storage *lstorage = conn->lstorage; + struct sieve_storage *storage = &lstorage->storage; + const struct sieve_ldap_storage_settings *set = &lstorage->set; + struct sieve_ldap_script_read_request *request; + char **attr_names; + + pool_t pool = pool_alloconly_create + ("sieve_ldap_script_read_request", 512); + request = p_new(pool, struct sieve_ldap_script_read_request, 1); + request->request.pool = pool; + request->request.base = p_strdup(pool, dn); + + attr_names = p_new(pool, char *, 3); + attr_names[0] = p_strdup(pool, set->sieve_ldap_script_attr); + + request->request.scope = LDAP_SCOPE_BASE; + request->request.filter = "(objectClass=*)"; + request->request.attributes = attr_names; + + e_debug(storage->event, "base=%s scope=base filter=%s fields=%s", + request->request.base, request->request.filter, + t_strarray_join((const char **)attr_names, ",")); + + request->request.callback = sieve_ldap_read_script_callback; + db_ldap_request(conn, &request->request); + db_ldap_wait(conn); + + *script_r = request->result; + pool_unref(&request->request.pool); + return (*script_r == NULL ? 0 : 1); +} + + +#endif diff --git a/pigeonhole/src/lib-sieve/storage/ldap/sieve-ldap-db.h b/pigeonhole/src/lib-sieve/storage/ldap/sieve-ldap-db.h new file mode 100644 index 0000000..d213026 --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/ldap/sieve-ldap-db.h @@ -0,0 +1,140 @@ +#ifndef DB_LDAP_H +#define DB_LDAP_H + +/* Functions like ldap_bind() have been deprecated in OpenLDAP 2.3 + This define enables them until the code here can be refactored */ +#define LDAP_DEPRECATED 1 + +/* Maximum number of pending requests before delaying new requests. */ +#define DB_LDAP_MAX_PENDING_REQUESTS 8 +/* If LDAP connection is down, fail requests after waiting for this long. */ +#define DB_LDAP_REQUEST_DISCONNECT_TIMEOUT_SECS 4 +/* If request is still in queue after this many seconds and other requests + have been replied, assume the request was lost and abort it. */ +#define DB_LDAP_REQUEST_LOST_TIMEOUT_SECS 60 +/* If server disconnects us, don't reconnect if no requests have been sent + for this many seconds. */ +#define DB_LDAP_IDLE_RECONNECT_SECS 60 + +#include <ldap.h> + +#define HAVE_LDAP_SASL +#ifdef HAVE_SASL_SASL_H +# include <sasl/sasl.h> +#elif defined (HAVE_SASL_H) +# include <sasl.h> +#else +# undef HAVE_LDAP_SASL +#endif +#ifdef LDAP_OPT_X_TLS +# define OPENLDAP_TLS_OPTIONS +#endif +#if !defined(SASL_VERSION_MAJOR) || SASL_VERSION_MAJOR < 2 +# undef HAVE_LDAP_SASL +#endif + +#ifndef LDAP_SASL_QUIET +# define LDAP_SASL_QUIET 0 /* Doesn't exist in Solaris LDAP */ +#endif + +/* Older versions may require calling ldap_result() twice */ +#if LDAP_VENDOR_VERSION <= 20112 +# define OPENLDAP_ASYNC_WORKAROUND +#endif + +/* Solaris LDAP library doesn't have LDAP_OPT_SUCCESS */ +#ifndef LDAP_OPT_SUCCESS +# define LDAP_OPT_SUCCESS LDAP_SUCCESS +#endif + +struct ldap_connection; +struct ldap_request; + +typedef void db_search_callback_t(struct ldap_connection *conn, + struct ldap_request *request, + LDAPMessage *res); +struct ldap_request { + pool_t pool; + + /* msgid for sent requests, -1 if not sent */ + int msgid; + /* timestamp when request was created */ + time_t create_time; + + bool failed; + + db_search_callback_t *callback; + + const char *base; + const char *filter; + int scope; + char **attributes; + + struct db_ldap_result *result; +}; + +enum ldap_connection_state { + /* Not connected */ + LDAP_CONN_STATE_DISCONNECTED, + /* Binding - either to default dn or doing auth bind */ + LDAP_CONN_STATE_BINDING, + /* Bound */ + LDAP_CONN_STATE_BOUND +}; + +struct ldap_connection { + struct ldap_connection *next; + + struct sieve_ldap_storage *lstorage; + + pool_t pool; + int refcount; + + LDAP *ld; + enum ldap_connection_state conn_state; + int default_bind_msgid; + + int fd; + struct io *io; + struct timeout *to; + struct ioloop *ioloop; + + /* Request queue contains sent requests at tail (msgid != -1) and + queued requests at head (msgid == -1). */ + struct aqueue *request_queue; + ARRAY(struct ldap_request *) request_array; + /* Number of messages in queue with msgid != -1 */ + unsigned int pending_count; + + /* Timestamp when we last received a reply */ + time_t last_reply_stamp; +}; + + +int ldap_deref_from_str(const char *str, int *deref_r); +int ldap_scope_from_str(const char *str, int *scope_r); +#ifdef OPENLDAP_TLS_OPTIONS +int ldap_tls_require_cert_from_str(const char *str, int *opt_x_tls_r); +#endif + +/* Send/queue request */ +void db_ldap_request(struct ldap_connection *conn, + struct ldap_request *request); + +void db_ldap_enable_input(struct ldap_connection *conn, bool enable); + +const char *ldap_escape(const char *str); +const char *ldap_get_error(struct ldap_connection *conn); + +int sieve_ldap_db_connect(struct ldap_connection *conn); + +struct ldap_connection * +sieve_ldap_db_init(struct sieve_ldap_storage *lstorage); +void sieve_ldap_db_unref(struct ldap_connection **conn); + +int sieve_ldap_db_lookup_script(struct ldap_connection *conn, + const char *name, const char **dn_r, const char **modattr_r); +int sieve_ldap_db_read_script(struct ldap_connection *conn, + const char *dn, struct istream **script_r); + +#endif diff --git a/pigeonhole/src/lib-sieve/storage/ldap/sieve-ldap-script.c b/pigeonhole/src/lib-sieve/storage/ldap/sieve-ldap-script.c new file mode 100644 index 0000000..1c732db --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/ldap/sieve-ldap-script.c @@ -0,0 +1,369 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "time-util.h" +#include "istream.h" + +#include "sieve-ldap-storage.h" + +#if defined(SIEVE_BUILTIN_LDAP) || defined(PLUGIN_BUILD) + +#include "str.h" +#include "strfuncs.h" + +#include "sieve-error.h" +#include "sieve-dump.h" +#include "sieve-binary.h" + +/* + * Script file implementation + */ + +static struct sieve_ldap_script *sieve_ldap_script_alloc(void) +{ + struct sieve_ldap_script *lscript; + pool_t pool; + + pool = pool_alloconly_create("sieve_ldap_script", 1024); + lscript = p_new(pool, struct sieve_ldap_script, 1); + lscript->script = sieve_ldap_script; + lscript->script.pool = pool; + + return lscript; +} + +struct sieve_ldap_script *sieve_ldap_script_init +(struct sieve_ldap_storage *lstorage, const char *name) +{ + struct sieve_storage *storage = &lstorage->storage; + struct sieve_ldap_script *lscript = NULL; + const char *location; + + if ( name == NULL ) { + name = SIEVE_LDAP_SCRIPT_DEFAULT; + location = storage->location; + } else { + location = t_strconcat + (storage->location, ";name=", name, NULL); + } + + lscript = sieve_ldap_script_alloc(); + sieve_script_init(&lscript->script, + storage, &sieve_ldap_script, location, name); + return lscript; +} + +static int sieve_ldap_script_open +(struct sieve_script *script, enum sieve_error *error_r) +{ + struct sieve_ldap_script *lscript = + (struct sieve_ldap_script *)script; + struct sieve_storage *storage = script->storage; + struct sieve_ldap_storage *lstorage = + (struct sieve_ldap_storage *)storage; + int ret; + + if ( sieve_ldap_db_connect(lstorage->conn) < 0 ) { + sieve_storage_set_critical(storage, + "Failed to connect to LDAP database"); + *error_r = storage->error_code; + return -1; + } + + if ( (ret=sieve_ldap_db_lookup_script(lstorage->conn, + script->name, &lscript->dn, &lscript->modattr)) <= 0 ) { + if ( ret == 0 ) { + e_debug(script->event, "Script entry not found"); + sieve_script_set_error(script, + SIEVE_ERROR_NOT_FOUND, + "Sieve script not found"); + } else { + sieve_script_set_internal_error(script); + } + *error_r = script->storage->error_code; + return -1; + } + + return 0; +} + +static int sieve_ldap_script_get_stream +(struct sieve_script *script, struct istream **stream_r, + enum sieve_error *error_r) +{ + struct sieve_ldap_script *lscript = + (struct sieve_ldap_script *)script; + struct sieve_storage *storage = script->storage; + struct sieve_ldap_storage *lstorage = + (struct sieve_ldap_storage *)storage; + int ret; + + i_assert(lscript->dn != NULL); + + if ( (ret=sieve_ldap_db_read_script( + lstorage->conn, lscript->dn, stream_r)) <= 0 ) { + if ( ret == 0 ) { + e_debug(script->event, "Script attribute not found"); + sieve_script_set_error(script, + SIEVE_ERROR_NOT_FOUND, + "Sieve script not found"); + } else { + sieve_script_set_internal_error(script); + } + *error_r = script->storage->error_code; + return -1; + } + return 0; +} + +static int sieve_ldap_script_binary_read_metadata +(struct sieve_script *script, struct sieve_binary_block *sblock, + sieve_size_t *offset) +{ + struct sieve_ldap_script *lscript = + (struct sieve_ldap_script *)script; + struct sieve_instance *svinst = script->storage->svinst; + struct sieve_ldap_storage *lstorage = + (struct sieve_ldap_storage *)script->storage; + struct sieve_binary *sbin = + sieve_binary_block_get_binary(sblock); + time_t bmtime = sieve_binary_mtime(sbin); + string_t *dn, *modattr; + + /* config file changed? */ + if ( bmtime <= lstorage->set_mtime ) { + if ( svinst->debug ) { + e_debug(script->event, + "Sieve binary `%s' is not newer " + "than the LDAP configuration `%s' (%s <= %s)", + sieve_binary_path(sbin), lstorage->config_file, + t_strflocaltime("%Y-%m-%d %H:%M:%S", bmtime), + t_strflocaltime("%Y-%m-%d %H:%M:%S", lstorage->set_mtime)); + } + return 0; + } + + /* open script if not open already */ + if ( lscript->dn == NULL && + sieve_script_open(script, NULL) < 0 ) + return 0; + + /* if modattr not found, recompile always */ + if ( lscript->modattr == NULL || *lscript->modattr == '\0' ) { + e_error(script->event, + "LDAP entry for script `%s' " + "has no modified attribute `%s'", + sieve_script_location(script), + lstorage->set.sieve_ldap_mod_attr); + return 0; + } + + /* compare DN in binary and from search result */ + if ( !sieve_binary_read_string(sblock, offset, &dn) ) { + e_error(script->event, + "Binary `%s' has invalid metadata for script `%s': " + "Invalid DN", + sieve_binary_path(sbin), sieve_script_location(script)); + return -1; + } + i_assert( lscript->dn != NULL ); + if ( strcmp(str_c(dn), lscript->dn) != 0 ) { + e_debug(script->event, + "Binary `%s' reports different LDAP DN for script `%s' " + "(`%s' rather than `%s')", + sieve_binary_path(sbin), sieve_script_location(script), + str_c(dn), lscript->dn); + return 0; + } + + /* compare modattr in binary and from search result */ + if ( !sieve_binary_read_string(sblock, offset, &modattr) ) { + e_error(script->event, + "Binary `%s' has invalid metadata for script `%s': " + "Invalid modified attribute", + sieve_binary_path(sbin), sieve_script_location(script)); + return -1; + } + if ( strcmp(str_c(modattr), lscript->modattr) != 0 ) { + e_debug(script->event, + "Binary `%s' reports different modified attribute content " + "for script `%s' (`%s' rather than `%s')", + sieve_binary_path(sbin), sieve_script_location(script), + str_c(modattr), lscript->modattr); + return 0; + } + return 1; +} + +static void sieve_ldap_script_binary_write_metadata +(struct sieve_script *script, struct sieve_binary_block *sblock) +{ + struct sieve_ldap_script *lscript = + (struct sieve_ldap_script *)script; + + sieve_binary_emit_cstring(sblock, lscript->dn); + if (lscript->modattr == NULL) + sieve_binary_emit_cstring(sblock, ""); + else + sieve_binary_emit_cstring(sblock, lscript->modattr); +} + +static bool sieve_ldap_script_binary_dump_metadata +(struct sieve_script *script ATTR_UNUSED, struct sieve_dumptime_env *denv, + struct sieve_binary_block *sblock, sieve_size_t *offset) +{ + string_t *dn, *modattr; + + if ( !sieve_binary_read_string(sblock, offset, &dn) ) + return FALSE; + sieve_binary_dumpf(denv, "ldap.dn = %s\n", str_c(dn)); + + if ( !sieve_binary_read_string(sblock, offset, &modattr) ) + return FALSE; + sieve_binary_dumpf(denv, "ldap.mod_attr = %s\n", str_c(modattr)); + + return TRUE; +} + +static const char *sieve_ldap_script_get_binpath +(struct sieve_ldap_script *lscript) +{ + struct sieve_script *script = &lscript->script; + struct sieve_storage *storage = script->storage; + + if ( lscript->binpath == NULL ) { + if ( storage->bin_dir == NULL ) + return NULL; + lscript->binpath = p_strconcat(script->pool, + storage->bin_dir, "/", + sieve_binfile_from_name(script->name), NULL); + } + + return lscript->binpath; +} + +static struct sieve_binary *sieve_ldap_script_binary_load +(struct sieve_script *script, enum sieve_error *error_r) +{ + struct sieve_storage *storage = script->storage; + struct sieve_ldap_script *lscript = + (struct sieve_ldap_script *)script; + + if ( sieve_ldap_script_get_binpath(lscript) == NULL ) + return NULL; + + return sieve_binary_open(storage->svinst, + lscript->binpath, script, error_r); +} + +static int sieve_ldap_script_binary_save +(struct sieve_script *script, struct sieve_binary *sbin, bool update, + enum sieve_error *error_r) +{ + struct sieve_ldap_script *lscript = + (struct sieve_ldap_script *)script; + + if ( sieve_ldap_script_get_binpath(lscript) == NULL ) + return 0; + + if ( sieve_storage_setup_bindir(script->storage, 0700) < 0 ) + return -1; + + return sieve_binary_save + (sbin, lscript->binpath, update, 0600, error_r); +} + +static bool sieve_ldap_script_equals +(const struct sieve_script *script, const struct sieve_script *other) +{ + struct sieve_storage *storage = script->storage; + struct sieve_storage *sother = other->storage; + + if ( strcmp(storage->location, sother->location) != 0 ) + return FALSE; + + i_assert( script->name != NULL && other->name != NULL ); + + return ( strcmp(script->name, other->name) == 0 ); +} + +const struct sieve_script sieve_ldap_script = { + .driver_name = SIEVE_LDAP_STORAGE_DRIVER_NAME, + .v = { + .open = sieve_ldap_script_open, + + .get_stream = sieve_ldap_script_get_stream, + + .binary_read_metadata = sieve_ldap_script_binary_read_metadata, + .binary_write_metadata = sieve_ldap_script_binary_write_metadata, + .binary_dump_metadata = sieve_ldap_script_binary_dump_metadata, + .binary_load = sieve_ldap_script_binary_load, + .binary_save = sieve_ldap_script_binary_save, + + .equals = sieve_ldap_script_equals + } +}; + +/* + * Script sequence + */ + +struct sieve_ldap_script_sequence { + struct sieve_script_sequence seq; + + bool done:1; +}; + +struct sieve_script_sequence *sieve_ldap_storage_get_script_sequence +(struct sieve_storage *storage, enum sieve_error *error_r) +{ + struct sieve_ldap_script_sequence *lsec = NULL; + + if ( error_r != NULL ) + *error_r = SIEVE_ERROR_NONE; + + /* Create sequence object */ + lsec = i_new(struct sieve_ldap_script_sequence, 1); + sieve_script_sequence_init(&lsec->seq, storage); + + return &lsec->seq; +} + +struct sieve_script *sieve_ldap_script_sequence_next +(struct sieve_script_sequence *seq, enum sieve_error *error_r) +{ + struct sieve_ldap_script_sequence *lsec = + (struct sieve_ldap_script_sequence *)seq; + struct sieve_ldap_storage *lstorage = + (struct sieve_ldap_storage *)seq->storage; + struct sieve_ldap_script *lscript; + + if ( error_r != NULL ) + *error_r = SIEVE_ERROR_NONE; + + if ( lsec->done ) + return NULL; + lsec->done = TRUE; + + lscript = sieve_ldap_script_init + (lstorage, seq->storage->script_name); + if ( sieve_script_open(&lscript->script, error_r) < 0 ) { + struct sieve_script *script = &lscript->script; + sieve_script_unref(&script); + return NULL; + } + + return &lscript->script; +} + +void sieve_ldap_script_sequence_destroy +(struct sieve_script_sequence *seq) +{ + struct sieve_ldap_script_sequence *lsec = + (struct sieve_ldap_script_sequence *)seq; + i_free(lsec); +} + + +#endif diff --git a/pigeonhole/src/lib-sieve/storage/ldap/sieve-ldap-storage-settings.c b/pigeonhole/src/lib-sieve/storage/ldap/sieve-ldap-storage-settings.c new file mode 100644 index 0000000..35fe5cb --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/ldap/sieve-ldap-storage-settings.c @@ -0,0 +1,169 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "env-util.h" +#include "settings.h" + +#include "sieve-common.h" + +#include "sieve-ldap-storage.h" + +#if defined(SIEVE_BUILTIN_LDAP) || defined(PLUGIN_BUILD) + +#include "sieve-error.h" + +#include "sieve-ldap-db.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#define DEF_STR(name) DEF_STRUCT_STR(name, sieve_ldap_storage_settings) +#define DEF_INT(name) DEF_STRUCT_INT(name, sieve_ldap_storage_settings) +#define DEF_BOOL(name) DEF_STRUCT_BOOL(name, sieve_ldap_storage_settings) + +static struct setting_def setting_defs[] = { + DEF_STR(hosts), + DEF_STR(uris), + DEF_STR(dn), + DEF_STR(dnpass), + DEF_BOOL(tls), + DEF_BOOL(sasl_bind), + DEF_STR(sasl_mech), + DEF_STR(sasl_realm), + DEF_STR(sasl_authz_id), + DEF_STR(tls_ca_cert_file), + DEF_STR(tls_ca_cert_dir), + DEF_STR(tls_cert_file), + DEF_STR(tls_key_file), + DEF_STR(tls_cipher_suite), + DEF_STR(tls_require_cert), + DEF_STR(deref), + DEF_STR(scope), + DEF_STR(base), + DEF_INT(ldap_version), + DEF_STR(debug_level), + DEF_STR(ldaprc_path), + DEF_STR(sieve_ldap_script_attr), + DEF_STR(sieve_ldap_mod_attr), + DEF_STR(sieve_ldap_filter), + + { 0, NULL, 0 } +}; + +static struct sieve_ldap_storage_settings default_settings = { + .hosts = NULL, + .uris = NULL, + .dn = NULL, + .dnpass = NULL, + .tls = FALSE, + .sasl_bind = FALSE, + .sasl_mech = NULL, + .sasl_realm = NULL, + .sasl_authz_id = NULL, + .tls_ca_cert_file = NULL, + .tls_ca_cert_dir = NULL, + .tls_cert_file = NULL, + .tls_key_file = NULL, + .tls_cipher_suite = NULL, + .tls_require_cert = NULL, + .deref = "never", + .scope = "subtree", + .base = NULL, + .ldap_version = 3, + .debug_level = "0", + .ldaprc_path = "", + .sieve_ldap_script_attr = "mailSieveRuleSource", + .sieve_ldap_mod_attr = "modifyTimestamp", + .sieve_ldap_filter = "(&(objectClass=posixAccount)(uid=%u))", +}; + +static const char *parse_setting(const char *key, const char *value, + struct sieve_ldap_storage *lstorage) +{ + return parse_setting_from_defs + (lstorage->storage.pool, setting_defs, &lstorage->set, key, value); +} + +int sieve_ldap_storage_read_settings +(struct sieve_ldap_storage *lstorage, const char *config_path) +{ + struct sieve_storage *storage = &lstorage->storage; + const char *str, *error; + struct stat st; + + if ( stat(config_path, &st) < 0 ) { + e_error(storage->event, + "Failed to read LDAP storage config: " + "stat(%s) failed: %m", config_path); + return -1; + } + + lstorage->set = default_settings; + lstorage->set_mtime = st.st_mtime; + + if (!settings_read_nosection + (config_path, parse_setting, lstorage, &error)) { + sieve_storage_set_critical(storage, + "Failed to read LDAP storage config `%s': %s", + config_path, error); + return -1; + } + + if (lstorage->set.base == NULL) { + sieve_storage_set_critical(storage, + "Invalid LDAP storage config `%s': " + "No search base given", config_path); + return -1; + } + + if (lstorage->set.uris == NULL && lstorage->set.hosts == NULL) { + sieve_storage_set_critical(storage, + "Invalid LDAP storage config `%s': " + "No uris or hosts set", config_path); + return -1; + } + + if (*lstorage->set.ldaprc_path != '\0') { + str = getenv("LDAPRC"); + if (str != NULL && strcmp(str, lstorage->set.ldaprc_path) != 0) { + sieve_storage_set_critical(storage, + "Invalid LDAP storage config `%s': " + "Multiple different ldaprc_path settings not allowed " + "(%s and %s)", config_path, str, lstorage->set.ldaprc_path); + return -1; + } + env_put("LDAPRC", lstorage->set.ldaprc_path); + } + + if ( ldap_deref_from_str + (lstorage->set.deref, &lstorage->set.ldap_deref) < 0 ) { + sieve_storage_set_critical(storage, + "Invalid LDAP storage config `%s': " + "Invalid deref option `%s'", + config_path, lstorage->set.deref);; + } + + if ( ldap_scope_from_str + (lstorage->set.scope, &lstorage->set.ldap_scope) < 0 ) { + sieve_storage_set_critical(storage, + "Invalid LDAP storage config `%s': " + "Invalid scope option `%s'", + config_path, lstorage->set.scope);; + } + +#ifdef OPENLDAP_TLS_OPTIONS + if ( lstorage->set.tls_require_cert != NULL && + ldap_tls_require_cert_from_str(lstorage->set.tls_require_cert, + &lstorage->set.ldap_tls_require_cert) < 0) { + sieve_storage_set_critical(storage, + "Invalid LDAP storage config `%s': " + "Invalid tls_require_cert option `%s'", + config_path, lstorage->set.tls_require_cert); + } +#endif + return 0; +} + +#endif diff --git a/pigeonhole/src/lib-sieve/storage/ldap/sieve-ldap-storage.c b/pigeonhole/src/lib-sieve/storage/ldap/sieve-ldap-storage.c new file mode 100644 index 0000000..1f7922a --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/ldap/sieve-ldap-storage.c @@ -0,0 +1,230 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +//#include "ldap.h" + +#include "sieve-common.h" + +#include "sieve-ldap-storage.h" + +#if defined(SIEVE_BUILTIN_LDAP) || defined(PLUGIN_BUILD) + +#include "sieve-error.h" + +#ifndef PLUGIN_BUILD +const struct sieve_storage sieve_ldap_storage; +#else +const struct sieve_storage sieve_ldap_storage_plugin; +#endif + +/* + * Storage class + */ + +static struct sieve_storage *sieve_ldap_storage_alloc(void) +{ + struct sieve_ldap_storage *lstorage; + pool_t pool; + + pool = pool_alloconly_create("sieve_ldap_storage", 1024); + lstorage = p_new(pool, struct sieve_ldap_storage, 1); +#ifndef PLUGIN_BUILD + lstorage->storage = sieve_ldap_storage; +#else + lstorage->storage = sieve_ldap_storage_plugin; +#endif + lstorage->storage.pool = pool; + + return &lstorage->storage; +} + +static int sieve_ldap_storage_init +(struct sieve_storage *storage, const char *const *options, + enum sieve_error *error_r) +{ + struct sieve_ldap_storage *lstorage = + (struct sieve_ldap_storage *)storage; + struct sieve_instance *svinst = storage->svinst; + const char *username = NULL; + + if ( options != NULL ) { + while ( *options != NULL ) { + const char *option = *options; + + if ( strncasecmp(option, "user=", 5) == 0 && option[5] != '\0' ) { + username = option+5; + } else { + sieve_storage_set_critical(storage, + "Invalid option `%s'", option); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + + options++; + } + } + + if ( username == NULL ) { + if ( svinst->username == NULL ) { + sieve_storage_set_critical(storage, + "No username specified"); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + username = svinst->username; + } + + e_debug(storage->event, "user=%s, config=%s", + username, storage->location); + + if ( sieve_ldap_storage_read_settings(lstorage, storage->location) < 0 ) + return -1; + + lstorage->username = p_strdup(storage->pool, username); + lstorage->config_file = p_strdup(storage->pool, storage->location); + lstorage->conn = sieve_ldap_db_init(lstorage); + + storage->location = p_strconcat(storage->pool, + SIEVE_LDAP_STORAGE_DRIVER_NAME, ":", storage->location, + ";user=", username, NULL); + + return 0; +} + +static void sieve_ldap_storage_destroy +(struct sieve_storage *storage) +{ + struct sieve_ldap_storage *lstorage = + (struct sieve_ldap_storage *)storage; + + if ( lstorage->conn != NULL ) + sieve_ldap_db_unref(&lstorage->conn); +} + +/* + * Script access + */ + +static struct sieve_script *sieve_ldap_storage_get_script +(struct sieve_storage *storage, const char *name) +{ + struct sieve_ldap_storage *lstorage = + (struct sieve_ldap_storage *)storage; + struct sieve_ldap_script *lscript; + + T_BEGIN { + lscript = sieve_ldap_script_init(lstorage, name); + } T_END; + + return &lscript->script; +} + +/* + * Active script + */ + +struct sieve_script *sieve_ldap_storage_active_script_open +(struct sieve_storage *storage) +{ + struct sieve_ldap_storage *lstorage = + (struct sieve_ldap_storage *)storage; + struct sieve_ldap_script *lscript; + + lscript = sieve_ldap_script_init + (lstorage, storage->script_name); + if ( sieve_script_open(&lscript->script, NULL) < 0 ) { + struct sieve_script *script = &lscript->script; + sieve_script_unref(&script); + return NULL; + } + + return &lscript->script; +} + +int sieve_ldap_storage_active_script_get_name +(struct sieve_storage *storage, const char **name_r) +{ + if ( storage->script_name != NULL ) + *name_r = storage->script_name; + else + *name_r = SIEVE_LDAP_SCRIPT_DEFAULT; + return 0; +} + +/* + * Driver definition + */ + +#ifndef PLUGIN_BUILD +const struct sieve_storage sieve_ldap_storage = { +#else +const struct sieve_storage sieve_ldap_storage_plugin = { +#endif + .driver_name = SIEVE_LDAP_STORAGE_DRIVER_NAME, + .version = 0, + .v = { + .alloc = sieve_ldap_storage_alloc, + .init = sieve_ldap_storage_init, + .destroy = sieve_ldap_storage_destroy, + + .get_script = sieve_ldap_storage_get_script, + + .get_script_sequence = sieve_ldap_storage_get_script_sequence, + .script_sequence_next = sieve_ldap_script_sequence_next, + .script_sequence_destroy = sieve_ldap_script_sequence_destroy, + + .active_script_get_name = sieve_ldap_storage_active_script_get_name, + .active_script_open = sieve_ldap_storage_active_script_open, + + // FIXME: impement management interface + } +}; + +#ifndef SIEVE_BUILTIN_LDAP +/* Building a plugin */ + +const char *sieve_storage_ldap_plugin_version = PIGEONHOLE_ABI_VERSION; + +void sieve_storage_ldap_plugin_load +(struct sieve_instance *svinst, void **context); +void sieve_storage_ldap_plugin_unload +(struct sieve_instance *svinst, void *context); +void sieve_storage_ldap_plugin_init(void); +void sieve_storage_ldap_plugin_deinit(void); + +void sieve_storage_ldap_plugin_load +(struct sieve_instance *svinst, void **context ATTR_UNUSED) +{ + sieve_storage_class_register + (svinst, &sieve_ldap_storage_plugin); + + e_debug(svinst->event, + "Sieve LDAP storage plugin for %s version %s loaded", + PIGEONHOLE_NAME, PIGEONHOLE_VERSION_FULL); +} + +void sieve_storage_ldap_plugin_unload +(struct sieve_instance *svinst ATTR_UNUSED, + void *context ATTR_UNUSED) +{ + sieve_storage_class_unregister + (svinst, &sieve_ldap_storage_plugin); +} + +void sieve_storage_ldap_plugin_init(void) +{ + /* Nothing */ +} + +void sieve_storage_ldap_plugin_deinit(void) +{ + /* Nothing */ +} +#endif + +#else /* !defined(SIEVE_BUILTIN_LDAP) && !defined(PLUGIN_BUILD) */ +const struct sieve_storage sieve_ldap_storage = { + .driver_name = SIEVE_LDAP_STORAGE_DRIVER_NAME +}; +#endif diff --git a/pigeonhole/src/lib-sieve/storage/ldap/sieve-ldap-storage.h b/pigeonhole/src/lib-sieve/storage/ldap/sieve-ldap-storage.h new file mode 100644 index 0000000..f97ceba --- /dev/null +++ b/pigeonhole/src/lib-sieve/storage/ldap/sieve-ldap-storage.h @@ -0,0 +1,108 @@ +#ifndef SIEVE_LDAP_STORAGE_H +#define SIEVE_LDAP_STORAGE_H + +#include "sieve.h" +#include "sieve-script-private.h" +#include "sieve-storage-private.h" + +#define SIEVE_LDAP_SCRIPT_DEFAULT "default" + +#if defined(SIEVE_BUILTIN_LDAP) || defined(PLUGIN_BUILD) + +#include "sieve-ldap-db.h" + +struct sieve_ldap_storage; + +/* + * LDAP settings + */ + +struct sieve_ldap_storage_settings { + const char *hosts; + const char *uris; + const char *dn; + const char *dnpass; + + bool tls; + bool sasl_bind; + const char *sasl_mech; + const char *sasl_realm; + const char *sasl_authz_id; + + const char *tls_ca_cert_file; + const char *tls_ca_cert_dir; + const char *tls_cert_file; + const char *tls_key_file; + const char *tls_cipher_suite; + const char *tls_require_cert; + + const char *deref; + const char *scope; + const char *base; + unsigned int ldap_version; + + const char *ldaprc_path; + const char *debug_level; + + const char *sieve_ldap_script_attr; + const char *sieve_ldap_mod_attr; + const char *sieve_ldap_filter; + + /* ... */ + int ldap_deref, ldap_scope, ldap_tls_require_cert; +}; + +int sieve_ldap_storage_read_settings + (struct sieve_ldap_storage *lstorage, const char *config_path); + +/* + * Storage class + */ + +struct sieve_ldap_storage { + struct sieve_storage storage; + + struct sieve_ldap_storage_settings set; + time_t set_mtime; + + const char *config_file; + const char *username; // FIXME: needed? + + struct ldap_connection *conn; +}; + +struct sieve_script *sieve_ldap_storage_active_script_open + (struct sieve_storage *storage); +int sieve_ldap_storage_active_script_get_name + (struct sieve_storage *storage, const char **name_r); + +/* + * Script class + */ + +struct sieve_ldap_script { + struct sieve_script script; + + const char *dn; + const char *modattr; + + const char *binpath; +}; + +struct sieve_ldap_script *sieve_ldap_script_init + (struct sieve_ldap_storage *lstorage, const char *name); + +/* + * Script sequence + */ + +struct sieve_script_sequence *sieve_ldap_storage_get_script_sequence + (struct sieve_storage *storage, enum sieve_error *error_r); + +struct sieve_script *sieve_ldap_script_sequence_next + (struct sieve_script_sequence *seq, enum sieve_error *error_r); +void sieve_ldap_script_sequence_destroy(struct sieve_script_sequence *seq); + +#endif + +#endif diff --git a/pigeonhole/src/lib-sieve/tst-address.c b/pigeonhole/src/lib-sieve/tst-address.c new file mode 100644 index 0000000..086679d --- /dev/null +++ b/pigeonhole/src/lib-sieve/tst-address.c @@ -0,0 +1,280 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str-sanitize.h" + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-message.h" +#include "sieve-address.h" +#include "sieve-address-parts.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-match.h" + +#include <stdio.h> + +/* + * Address test + * + * Syntax: + * address [ADDRESS-PART] [COMPARATOR] [MATCH-TYPE] + * <header-list: string-list> <key-list: string-list> + */ + +static bool tst_address_registered + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool tst_address_validate + (struct sieve_validator *valdtr, struct sieve_command *tst); +static bool tst_address_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx); + +const struct sieve_command_def tst_address = { + .identifier = "address", + .type = SCT_TEST, + .positional_args = 2, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = tst_address_registered, + .validate = tst_address_validate, + .generate = tst_address_generate +}; + +/* + * Address operation + */ + +static bool tst_address_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int tst_address_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def tst_address_operation = { + .mnemonic = "ADDRESS", + .code = SIEVE_OPERATION_ADDRESS, + .dump = tst_address_operation_dump, + .execute = tst_address_operation_execute +}; + +/* + * Test registration + */ + +static bool tst_address_registered +(struct sieve_validator *valdtr, const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_command_registration *cmd_reg) +{ + /* The order of these is not significant */ + sieve_comparators_link_tag + (valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR ); + sieve_match_types_link_tags + (valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE); + sieve_address_parts_link_tags + (valdtr, cmd_reg, SIEVE_AM_OPT_ADDRESS_PART); + return TRUE; +} + +/* + * Validation + */ + +/* List of valid headers: + * Implementations MUST restrict the address test to headers that + * contain addresses, but MUST include at least From, To, Cc, Bcc, + * Sender, Resent-From, and Resent-To, and it SHOULD include any other + * header that utilizes an "address-list" structured header body. + * + * This list explicitly does not contain the envelope-to and return-path + * headers. The envelope test must be used to test against these addresses. + * + * FIXME: this restriction is somewhat odd. Sieve list advises to allow + * any other header as long as its content matches the address-list + * grammar. + */ +static const char * const _allowed_headers[] = { + /* Required */ + "from", "to", "cc", "bcc", "sender", "resent-from", "resent-to", + + /* Additional (RFC 822 / RFC 2822) */ + "reply-to", "resent-reply-to", "resent-sender", "resent-cc", "resent-bcc", + + /* Non-standard (RFC 2076, draft-palme-mailext-headers-08.txt) */ + "for-approval", "for-handling", "for-comment", "apparently-to", "errors-to", + "delivered-to", "return-receipt-to", "x-admin", "read-receipt-to", + "x-confirm-reading-to", "return-receipt-requested", + "registered-mail-reply-requested-by", "mail-followup-to", "mail-reply-to", + "abuse-reports-to", "x-complaints-to", "x-report-abuse-to", + + /* Undocumented */ + "x-beenthere", "x-original-to", + + NULL +}; + +static int _header_is_allowed +(void *context ATTR_UNUSED, struct sieve_ast_argument *arg) +{ + if ( sieve_argument_is_string_literal(arg) ) { + const char *header = sieve_ast_strlist_strc(arg); + + const char * const *hdsp = _allowed_headers; + while ( *hdsp != NULL ) { + if ( strcasecmp( *hdsp, header ) == 0 ) + return 1; + + hdsp++; + } + + return 0; + } + + return 1; +} + +static bool tst_address_validate +(struct sieve_validator *valdtr, struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + struct sieve_ast_argument *header; + struct sieve_comparator cmp_default = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + struct sieve_match_type mcht_default = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "header list", 1, SAAT_STRING_LIST) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) + return FALSE; + + if ( !sieve_command_verify_headers_argument(valdtr, arg) ) + return FALSE; + + /* Check if supplied header names are allowed + * FIXME: verify dynamic header names at runtime + */ + header = arg; + if ( sieve_ast_stringlist_map + (&header, NULL, _header_is_allowed) <= 0 ) { + i_assert(header != NULL); + sieve_argument_validate_error(valdtr, header, + "specified header '%s' is not allowed for the address test", + str_sanitize(sieve_ast_strlist_strc(header), 64)); + return FALSE; + } + + /* Check key list */ + + arg = sieve_ast_argument_next(arg); + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "key list", 2, SAAT_STRING_LIST) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) + return FALSE; + + /* Validate the key argument to a specified match type */ + return sieve_match_type_validate + (valdtr, tst, arg, &mcht_default, &cmp_default); +} + +/* + * Code generation + */ + +static bool tst_address_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *tst) +{ + sieve_operation_emit(cgenv->sblock, NULL, &tst_address_operation); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, tst, NULL); +} + +/* + * Code dump + */ + +static bool tst_address_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + sieve_code_dumpf(denv, "ADDRESS"); + sieve_code_descend(denv); + + /* Handle any optional arguments */ + if ( sieve_message_opr_optional_dump(denv, address, NULL) != 0 ) + return FALSE; + + return + sieve_opr_stringlist_dump(denv, address, "header list") && + sieve_opr_stringlist_dump(denv, address, "key list"); +} + +/* + * Code execution + */ + +static int tst_address_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + struct sieve_comparator cmp = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + struct sieve_match_type mcht = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + struct sieve_address_part addrp = + SIEVE_ADDRESS_PART_DEFAULT(all_address_part); + struct sieve_stringlist *hdr_list, *hdr_value_list, *value_list, *key_list; + struct sieve_address_list *addr_list; + ARRAY_TYPE(sieve_message_override) svmos; + int match, ret; + + /* Read optional operands */ + i_zero(&svmos); + if ( sieve_message_opr_optional_read + (renv, address, NULL, &ret, &addrp, &mcht, &cmp, &svmos) < 0 ) + return ret; + + /* Read header-list */ + if ( (ret=sieve_opr_stringlist_read(renv, address, "header-list", &hdr_list)) + <= 0 ) + return ret; + + /* Read key-list */ + if ( (ret=sieve_opr_stringlist_read(renv, address, "key-list", &key_list)) + <= 0 ) + return ret; + + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "address test"); + + /* Get header */ + sieve_runtime_trace_descend(renv); + if ( (ret=sieve_message_get_header_fields + (renv, hdr_list, &svmos, FALSE, &hdr_value_list)) <= 0 ) + return ret; + sieve_runtime_trace_ascend(renv); + + /* Create value stringlist */ + addr_list = sieve_header_address_list_create(renv, hdr_value_list); + value_list = sieve_address_part_stringlist_create(renv, &addrp, addr_list); + + /* Perform match */ + if ( (match=sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret)) < 0 ) + return ret; + + /* Set test result for subsequent conditional jump */ + sieve_interpreter_set_test_result(renv->interp, match > 0); + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/tst-allof.c b/pigeonhole/src/lib-sieve/tst-allof.c new file mode 100644 index 0000000..6d278b9 --- /dev/null +++ b/pigeonhole/src/lib-sieve/tst-allof.c @@ -0,0 +1,108 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-binary.h" +#include "sieve-code.h" +#include "sieve-binary.h" + +/* + * Allof test + * + * Syntax + * allof <tests: test-list> + */ + +static bool tst_allof_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx, + struct sieve_jumplist *jumps, bool jump_true); +static bool tst_allof_validate_const + (struct sieve_validator *valdtr, struct sieve_command *tst, + int *const_current, int const_new); + +const struct sieve_command_def tst_allof = { + .identifier = "allof", + .type = SCT_TEST, + .positional_args = 0, + .subtests = 2, + .block_allowed = FALSE, + .block_required = FALSE, + .validate_const = tst_allof_validate_const, + .control_generate = tst_allof_generate +}; + +/* + * Code validation + */ + +static bool tst_allof_validate_const +(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_command *tst ATTR_UNUSED, int *const_current, int const_next) +{ + if ( const_next == 0 ) { + *const_current = 0; + return FALSE; + } + + if ( *const_current != -1 ) + *const_current = const_next; + return TRUE; +} + +/* + * Code generation + */ + +static bool tst_allof_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *ctx, + struct sieve_jumplist *jumps, bool jump_true) +{ + struct sieve_binary_block *sblock = cgenv->sblock; + struct sieve_ast_node *test; + struct sieve_jumplist false_jumps; + + if ( sieve_ast_test_count(ctx->ast_node) > 1 ) { + if ( jump_true ) { + /* Prepare jumplist */ + sieve_jumplist_init_temp(&false_jumps, sblock); + } + + test = sieve_ast_test_first(ctx->ast_node); + while ( test != NULL ) { + bool result; + + /* If this test list must jump on false, all sub-tests can simply add their jumps + * to the caller's jump list, otherwise this test redirects all false jumps to the + * end of the currently generated code. This is just after a final jump to the true + * case + */ + if ( jump_true ) + result = sieve_generate_test(cgenv, test, &false_jumps, FALSE); + else + result = sieve_generate_test(cgenv, test, jumps, FALSE); + + if ( !result ) return FALSE; + + test = sieve_ast_test_next(test); + } + + if ( jump_true ) { + /* All tests succeeded, jump to case TRUE */ + sieve_operation_emit(cgenv->sblock, NULL, &sieve_jmp_operation); + sieve_jumplist_add(jumps, sieve_binary_emit_offset(sblock, 0)); + + /* All false exits jump here */ + sieve_jumplist_resolve(&false_jumps); + } + } else { + /* Script author is being inefficient; we can optimize the allof test away */ + test = sieve_ast_test_first(ctx->ast_node); + sieve_generate_test(cgenv, test, jumps, jump_true); + } + + return TRUE; +} + diff --git a/pigeonhole/src/lib-sieve/tst-anyof.c b/pigeonhole/src/lib-sieve/tst-anyof.c new file mode 100644 index 0000000..84bfedd --- /dev/null +++ b/pigeonhole/src/lib-sieve/tst-anyof.c @@ -0,0 +1,107 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-generator.h" +#include "sieve-validator.h" +#include "sieve-binary.h" +#include "sieve-code.h" +#include "sieve-binary.h" + +/* + * Anyof test + * + * Syntax + * anyof <tests: test-list> + */ + +static bool tst_anyof_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx, + struct sieve_jumplist *jumps, bool jump_true); +static bool tst_anyof_validate_const + (struct sieve_validator *valdtr, struct sieve_command *tst, + int *const_current, int const_next); + +const struct sieve_command_def tst_anyof = { + .identifier = "anyof", + .type = SCT_TEST, + .positional_args = 0, + .subtests = 2, + .block_allowed = FALSE, + .block_required = FALSE, + .validate_const = tst_anyof_validate_const, + .control_generate = tst_anyof_generate +}; + +/* + * Code validation + */ + +static bool tst_anyof_validate_const +(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_command *tst ATTR_UNUSED, int *const_current, int const_next) +{ + if ( const_next > 0 ) { + *const_current = 1; + return FALSE; + } + + if ( *const_current != -1 ) + *const_current = const_next; + return TRUE; +} + +/* + * Code generation + */ + +static bool tst_anyof_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx, + struct sieve_jumplist *jumps, bool jump_true) +{ + struct sieve_binary_block *sblock = cgenv->sblock; + struct sieve_ast_node *test; + struct sieve_jumplist true_jumps; + + if ( sieve_ast_test_count(ctx->ast_node) > 1 ) { + if ( !jump_true ) { + /* Prepare jumplist */ + sieve_jumplist_init_temp(&true_jumps, sblock); + } + + test = sieve_ast_test_first(ctx->ast_node); + while ( test != NULL ) { + bool result; + + /* If this test list must jump on true, all sub-tests can simply add their jumps + * to the caller's jump list, otherwise this test redirects all true jumps to the + * end of the currently generated code. This is just after a final jump to the false + * case + */ + if ( !jump_true ) + result = sieve_generate_test(cgenv, test, &true_jumps, TRUE); + else + result = sieve_generate_test(cgenv, test, jumps, TRUE); + + if ( !result ) return FALSE; + + test = sieve_ast_test_next(test); + } + + if ( !jump_true ) { + /* All tests failed, jump to case FALSE */ + sieve_operation_emit(sblock, NULL, &sieve_jmp_operation); + sieve_jumplist_add(jumps, sieve_binary_emit_offset(sblock, 0)); + + /* All true exits jump here */ + sieve_jumplist_resolve(&true_jumps); + } + } else { + /* Script author is being inefficient; we can optimize the allof test away */ + test = sieve_ast_test_first(ctx->ast_node); + sieve_generate_test(cgenv, test, jumps, jump_true); + } + + return TRUE; +} diff --git a/pigeonhole/src/lib-sieve/tst-exists.c b/pigeonhole/src/lib-sieve/tst-exists.c new file mode 100644 index 0000000..0668c30 --- /dev/null +++ b/pigeonhole/src/lib-sieve/tst-exists.c @@ -0,0 +1,179 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str-sanitize.h" + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-message.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-code-dumper.h" + +/* + * Exists test + * + * Syntax: + * exists <header-names: string-list> + */ + +static bool tst_exists_validate + (struct sieve_validator *valdtr, struct sieve_command *tst); +static bool tst_exists_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *tst); + +const struct sieve_command_def tst_exists = { + .identifier = "exists", + .type = SCT_TEST, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = tst_exists_validate, + .generate = tst_exists_generate +}; + +/* + * Exists operation + */ + +static bool tst_exists_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int tst_exists_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def tst_exists_operation = { + .mnemonic = "EXISTS", + .code = SIEVE_OPERATION_EXISTS, + .dump = tst_exists_operation_dump, + .execute = tst_exists_operation_execute +}; + +/* + * Validation + */ + +static bool tst_exists_validate + (struct sieve_validator *valdtr, struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "header names", 1, SAAT_STRING_LIST) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) + return FALSE; + + return sieve_command_verify_headers_argument(valdtr, arg); +} + +/* + * Code generation + */ + +static bool tst_exists_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *tst) +{ + sieve_operation_emit(cgenv->sblock, NULL, &tst_exists_operation); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, tst, NULL); +} + +/* + * Code dump + */ + +static bool tst_exists_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + sieve_code_dumpf(denv, "EXISTS"); + sieve_code_descend(denv); + + /* Optional operands */ + if ( sieve_message_opr_optional_dump(denv, address, NULL) != 0 ) + return FALSE; + + return sieve_opr_stringlist_dump(denv, address, "header names"); +} + +/* + * Code execution + */ + +static int tst_exists_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + struct sieve_stringlist *hdr_list; + ARRAY_TYPE(sieve_message_override) svmos; + string_t *hdr_item; + bool matched; + int ret; + + /* + * Read operands + */ + + /* Optional operands */ + i_zero(&svmos); + if ( sieve_message_opr_optional_read + (renv, address, NULL, &ret, NULL, NULL, NULL, &svmos) < 0 ) + return ret; + + /* Read header-list */ + if ( (ret=sieve_opr_stringlist_read(renv, address, "header-list", &hdr_list)) + <= 0 ) + return ret; + + /* + * Perfrom test + */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "exists test"); + sieve_runtime_trace_descend(renv); + + /* Iterate through all requested headers to match (must find all specified) */ + hdr_item = NULL; + matched = TRUE; + while ( matched && + (ret=sieve_stringlist_next_item(hdr_list, &hdr_item)) > 0 ) { + struct sieve_stringlist *value_list; + string_t *dummy; + + /* Get header */ + if ( (ret=sieve_message_get_header_fields + (renv, sieve_single_stringlist_create(renv, hdr_item, FALSE), + &svmos, FALSE, &value_list)) <= 0 ) + return ret; + + if ( (ret=sieve_stringlist_next_item(value_list, &dummy)) < 0) + return value_list->exec_status; + + if ( ret == 0 ) + matched = FALSE; + + sieve_runtime_trace(renv, SIEVE_TRLVL_MATCHING, + "header `%s' %s", str_sanitize(str_c(hdr_item), 80), + ( matched ? "exists" : "is missing" )); + } + + if ( matched ) + sieve_runtime_trace(renv, SIEVE_TRLVL_MATCHING, "all headers exist"); + else + sieve_runtime_trace(renv, SIEVE_TRLVL_MATCHING, "headers are missing"); + + /* Set test result for subsequent conditional jump */ + if ( ret >= 0 ) { + sieve_interpreter_set_test_result(renv->interp, matched); + return SIEVE_EXEC_OK; + } + + sieve_runtime_trace_error(renv, "invalid header-list item"); + return SIEVE_EXEC_BIN_CORRUPT; +} diff --git a/pigeonhole/src/lib-sieve/tst-header.c b/pigeonhole/src/lib-sieve/tst-header.c new file mode 100644 index 0000000..6e553a2 --- /dev/null +++ b/pigeonhole/src/lib-sieve/tst-header.c @@ -0,0 +1,204 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str-sanitize.h" + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-code.h" +#include "sieve-message.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-match.h" + +/* + * Header test + * + * Syntax: + * header [COMPARATOR] [MATCH-TYPE] + * <header-names: string-list> <key-list: string-list> + */ + +static bool tst_header_registered + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool tst_header_validate + (struct sieve_validator *valdtr, struct sieve_command *tst); +static bool tst_header_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *tst); + +const struct sieve_command_def tst_header = { + .identifier = "header", + .type = SCT_TEST, + .positional_args = 2, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = tst_header_registered, + .validate = tst_header_validate, + .generate = tst_header_generate +}; + +/* + * Header operation + */ + +static bool tst_header_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int tst_header_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def tst_header_operation = { + .mnemonic = "HEADER", + .code = SIEVE_OPERATION_HEADER, + .dump = tst_header_operation_dump, + .execute = tst_header_operation_execute +}; + +/* + * Test registration + */ + +static bool tst_header_registered +(struct sieve_validator *valdtr, const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_command_registration *cmd_reg) +{ + /* The order of these is not significant */ + sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR); + sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE); + + return TRUE; +} + +/* + * Validation + */ + +static bool tst_header_validate +(struct sieve_validator *valdtr, struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + struct sieve_comparator cmp_default = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + struct sieve_match_type mcht_default = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "header names", 1, SAAT_STRING_LIST) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) + return FALSE; + + if ( !sieve_command_verify_headers_argument(valdtr, arg) ) + return FALSE; + + arg = sieve_ast_argument_next(arg); + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "key list", 2, SAAT_STRING_LIST) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) + return FALSE; + + /* Validate the key argument to a specified match type */ + return sieve_match_type_validate + (valdtr, tst, arg, &mcht_default, &cmp_default); +} + +/* + * Code generation + */ + +static bool tst_header_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *tst) +{ + sieve_operation_emit(cgenv->sblock, NULL, &tst_header_operation); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, tst, NULL); +} + +/* + * Code dump + */ + +static bool tst_header_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + sieve_code_dumpf(denv, "HEADER"); + sieve_code_descend(denv); + + /* Optional operands */ + if ( sieve_message_opr_optional_dump(denv, address, NULL) != 0 ) + return FALSE; + + return + sieve_opr_stringlist_dump(denv, address, "header names") && + sieve_opr_stringlist_dump(denv, address, "key list"); +} + +/* + * Code execution + */ + +static int tst_header_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + struct sieve_comparator cmp = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + struct sieve_match_type mcht = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + struct sieve_stringlist *hdr_list, *key_list, *value_list; + ARRAY_TYPE(sieve_message_override) svmos; + int match, ret; + + /* + * Read operands + */ + + /* Optional operands */ + i_zero(&svmos); + if ( sieve_message_opr_optional_read + (renv, address, NULL, &ret, NULL, &mcht, &cmp, &svmos) < 0 ) + return ret; + + /* Read header-list */ + if ( (ret=sieve_opr_stringlist_read(renv, address, "header-list", &hdr_list)) + <= 0 ) + return ret; + + /* Read key-list */ + if ( (ret=sieve_opr_stringlist_read(renv, address, "key-list", &key_list)) + <= 0 ) + return ret; + + /* + * Perform test + */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "header test"); + + /* Get header */ + sieve_runtime_trace_descend(renv); + if ( (ret=sieve_message_get_header_fields + (renv, hdr_list, &svmos, TRUE, &value_list)) <= 0 ) + return ret; + sieve_runtime_trace_ascend(renv); + + /* Perform match */ + if ( (match=sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret)) < 0 ) + return ret; + + /* Set test result for subsequent conditional jump */ + sieve_interpreter_set_test_result(renv->interp, match > 0); + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/tst-not.c b/pigeonhole/src/lib-sieve/tst-not.c new file mode 100644 index 0000000..9687d49 --- /dev/null +++ b/pigeonhole/src/lib-sieve/tst-not.c @@ -0,0 +1,67 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" + +/* + * Not test + * + * Syntax: + * not <tests: test-list> + */ + +static bool tst_not_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx, + struct sieve_jumplist *jumps, bool jump_true); +static bool tst_not_validate_const + (struct sieve_validator *valdtr, struct sieve_command *tst, + int *const_current, int const_next); + +const struct sieve_command_def tst_not = { + .identifier = "not", + .type = SCT_TEST, + .positional_args = 0, + .subtests = 1, + .block_allowed = FALSE, + .block_required = FALSE, + .validate_const = tst_not_validate_const, + .control_generate = tst_not_generate +}; + +/* + * Code validation + */ + +static bool tst_not_validate_const +(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_command *tst ATTR_UNUSED, int *const_current, int const_next) +{ + if ( const_next < 0 ) + *const_current = -1; + else if ( const_next > 0 ) + *const_current = 0; + else + *const_current = 1; + + return TRUE; +} + +/* + * Code generation + */ + +static bool tst_not_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *ctx, + struct sieve_jumplist *jumps, bool jump_true) +{ + struct sieve_ast_node *test; + + /* Validator verified the existance of the single test already */ + test = sieve_ast_test_first(ctx->ast_node); + + return sieve_generate_test(cgenv, test, jumps, !jump_true); +} + diff --git a/pigeonhole/src/lib-sieve/tst-size.c b/pigeonhole/src/lib-sieve/tst-size.c new file mode 100644 index 0000000..1ed101b --- /dev/null +++ b/pigeonhole/src/lib-sieve/tst-size.c @@ -0,0 +1,318 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "mail-storage.h" + +#include "sieve-common.h" +#include "sieve-code.h" +#include "sieve-message.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +/* + * Size test + * + * Syntax: + * size <":over" / ":under"> <limit: number> + */ + +static bool +tst_size_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool +tst_size_pre_validate(struct sieve_validator *valdtr, + struct sieve_command *tst); +static bool +tst_size_validate(struct sieve_validator *valdtr, struct sieve_command *tst); +static bool +tst_size_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +const struct sieve_command_def tst_size = { + .identifier = "size", + .type = SCT_TEST, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = tst_size_registered, + .pre_validate = tst_size_pre_validate, + .validate = tst_size_validate, + .generate = tst_size_generate +}; + +/* + * Size operations + */ + +static bool +tst_size_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +tst_size_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def tst_size_over_operation = { + .mnemonic = "SIZE-OVER", + .code = SIEVE_OPERATION_SIZE_OVER, + .dump = tst_size_operation_dump, + .execute = tst_size_operation_execute +}; + +const struct sieve_operation_def tst_size_under_operation = { + .mnemonic = "SIZE-UNDER", + .code = SIEVE_OPERATION_SIZE_UNDER, + .dump = tst_size_operation_dump, + .execute = tst_size_operation_execute +}; + +/* + * Context data + */ + +struct tst_size_context_data { + enum { SIZE_UNASSIGNED, SIZE_UNDER, SIZE_OVER } type; +}; + +#define TST_SIZE_ERROR_DUP_TAG \ + "exactly one of the ':under' or ':over' tags must be specified " \ + "for the size test, but more were found" + +/* + * Tag validation + */ + +static bool +tst_size_validate_over_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *tst) +{ + struct tst_size_context_data *ctx_data = + (struct tst_size_context_data *)tst->data; + + if (ctx_data->type != SIZE_UNASSIGNED) { + sieve_argument_validate_error(valdtr, *arg, + TST_SIZE_ERROR_DUP_TAG); + return FALSE; + } + + ctx_data->type = SIZE_OVER; + + /* Delete this tag */ + *arg = sieve_ast_arguments_detach(*arg, 1); + + return TRUE; +} + +static bool +tst_size_validate_under_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg ATTR_UNUSED, + struct sieve_command *tst) +{ + struct tst_size_context_data *ctx_data = + (struct tst_size_context_data *)tst->data; + + if (ctx_data->type != SIZE_UNASSIGNED) { + sieve_argument_validate_error(valdtr, *arg, + TST_SIZE_ERROR_DUP_TAG); + return FALSE; + } + + ctx_data->type = SIZE_UNDER; + + /* Delete this tag */ + *arg = sieve_ast_arguments_detach(*arg, 1); + + return TRUE; +} + +/* + * Test registration + */ + +static const struct sieve_argument_def size_over_tag = { + .identifier = "over", + .validate = tst_size_validate_over_tag +}; + +static const struct sieve_argument_def size_under_tag = { + .identifier = "under", + .validate = tst_size_validate_under_tag, +}; + +static bool +tst_size_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_command_registration *cmd_reg) +{ + /* Register our tags */ + sieve_validator_register_tag(valdtr, cmd_reg, NULL, &size_over_tag, 0); + sieve_validator_register_tag(valdtr, cmd_reg, NULL, &size_under_tag, 0); + + return TRUE; +} + +/* + * Test validation + */ + +static bool +tst_size_pre_validate(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_command *tst) +{ + struct tst_size_context_data *ctx_data; + + /* Assign context */ + ctx_data = p_new(sieve_command_pool(tst), + struct tst_size_context_data, 1); + ctx_data->type = SIZE_UNASSIGNED; + tst->data = ctx_data; + + return TRUE; +} + +static bool +tst_size_validate(struct sieve_validator *valdtr, struct sieve_command *tst) +{ + struct tst_size_context_data *ctx_data = + (struct tst_size_context_data *)tst->data; + struct sieve_ast_argument *arg = tst->first_positional; + + if (ctx_data->type == SIZE_UNASSIGNED) { + sieve_command_validate_error(valdtr, tst, + "the size test requires either the :under or the :over tag " + "to be specified"); + return FALSE; + } + + if (!sieve_validate_positional_argument(valdtr, tst, arg, "limit", 1, + SAAT_NUMBER)) + return FALSE; + + return sieve_validator_argument_activate(valdtr, tst, arg, FALSE); +} + +/* + * Code generation + */ + +bool tst_size_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *tst) +{ + struct tst_size_context_data *ctx_data = + (struct tst_size_context_data *)tst->data; + + if (ctx_data->type == SIZE_OVER) { + sieve_operation_emit(cgenv->sblock, NULL, + &tst_size_over_operation); + } else { + sieve_operation_emit(cgenv->sblock, NULL, + &tst_size_under_operation); + } + + /* Generate arguments */ + if (!sieve_generate_arguments(cgenv, tst, NULL)) + return FALSE; + + return TRUE; +} + +/* + * Code dump + */ + +static bool +tst_size_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + sieve_code_dumpf(denv, "%s", sieve_operation_mnemonic(denv->oprtn)); + sieve_code_descend(denv); + + return sieve_opr_number_dump(denv, address, "limit"); +} + +/* + * Code execution + */ + +static inline bool +tst_size_get(const struct sieve_runtime_env *renv, sieve_number_t *size) +{ + struct mail *mail = sieve_message_get_mail(renv->msgctx); + uoff_t psize; + + if (mail_get_physical_size(mail, &psize) < 0) + return FALSE; + + *size = psize; + return TRUE; +} + +static int +tst_size_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + sieve_number_t mail_size, limit; + int ret; + + /* + * Read operands + */ + + /* Read size limit */ + if ((ret = sieve_opr_number_read(renv, address, "limit", &limit)) <= 0) + return ret; + + /* + * Perform test + */ + + /* Get the size of the message */ + if (!tst_size_get(renv, &mail_size)) { + /* FIXME: improve this error */ + e_error(renv->event, "failed to assess message size"); + return SIEVE_EXEC_FAILURE; + } + + /* Perform the test */ + if (sieve_operation_is(renv->oprtn, tst_size_over_operation)) { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "size :over test"); + + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING)) { + sieve_runtime_trace_descend(renv); + + sieve_runtime_trace( + renv, 0, "comparing message size %llu", + (unsigned long long)mail_size); + sieve_runtime_trace( + renv, 0, "with upper limit %llu", + (unsigned long long)limit); + } + + sieve_interpreter_set_test_result(renv->interp, + (mail_size > limit)); + } else { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "size :under test"); + + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING)) { + sieve_runtime_trace_descend(renv); + + sieve_runtime_trace( + renv, 0, "comparing message size %llu", + (unsigned long long)mail_size); + sieve_runtime_trace( + renv, 0, "with lower limit %llu", + (unsigned long long)limit); + } + + sieve_interpreter_set_test_result(renv->interp, + (mail_size < limit)); + } + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/lib-sieve/tst-truefalse.c b/pigeonhole/src/lib-sieve/tst-truefalse.c new file mode 100644 index 0000000..6373ce3 --- /dev/null +++ b/pigeonhole/src/lib-sieve/tst-truefalse.c @@ -0,0 +1,104 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-ast.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-binary.h" + +#include "sieve-commands.h" +#include "sieve-code.h" +#include "sieve-interpreter.h" + +/* + * True/False test command + */ + +static bool tst_false_validate_const + (struct sieve_validator *valdtr, struct sieve_command *tst, + int *const_current, int const_next); +static bool tst_false_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd, + struct sieve_jumplist *jumps, bool jump_true); + +const struct sieve_command_def tst_false = { + .identifier = "false", + .type = SCT_TEST, + .positional_args = 0, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate_const = tst_false_validate_const, + .control_generate = tst_false_generate +}; + +static bool tst_true_validate_const + (struct sieve_validator *valdtr, struct sieve_command *tst, + int *const_current, int const_next); +static bool tst_true_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd, + struct sieve_jumplist *jumps, bool jump_true); + +const struct sieve_command_def tst_true = { + .identifier = "true", + .type = SCT_TEST, + .positional_args = 0, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate_const = tst_true_validate_const, + .control_generate = tst_true_generate +}; + +/* + * Code validation + */ + +static bool tst_false_validate_const +(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_command *tst ATTR_UNUSED, int *const_current, + int const_next ATTR_UNUSED) +{ + *const_current = 0; + return TRUE; +} + +static bool tst_true_validate_const +(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_command *tst ATTR_UNUSED, int *const_current, + int const_next ATTR_UNUSED) +{ + *const_current = 1; + return TRUE; +} + +/* + * Code generation + */ + +static bool tst_false_generate +(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd ATTR_UNUSED, + struct sieve_jumplist *jumps, bool jump_true) +{ + if ( !jump_true ) { + sieve_operation_emit(cgenv->sblock, NULL, &sieve_jmp_operation); + sieve_jumplist_add(jumps, sieve_binary_emit_offset(cgenv->sblock, 0)); + } + + return TRUE; +} + +static bool tst_true_generate +(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd ATTR_UNUSED, + struct sieve_jumplist *jumps, bool jump_true) +{ + if ( jump_true ) { + sieve_operation_emit(cgenv->sblock, NULL, &sieve_jmp_operation); + sieve_jumplist_add(jumps, sieve_binary_emit_offset(cgenv->sblock, 0)); + } + + return TRUE; +} + diff --git a/pigeonhole/src/lib-sieve/util/Makefile.am b/pigeonhole/src/lib-sieve/util/Makefile.am new file mode 100644 index 0000000..36cad8a --- /dev/null +++ b/pigeonhole/src/lib-sieve/util/Makefile.am @@ -0,0 +1,51 @@ +noinst_LTLIBRARIES = libsieve_util.la + +AM_CPPFLAGS = \ + $(LIBDOVECOT_INCLUDE) \ + $(LIBDOVECOT_SERVICE_INCLUDE) \ + -DMODULEDIR=\""$(dovecot_moduledir)"\" + +libsieve_util_la_DEPENDENCIES = $(LIBDOVECOT_STORAGE_DEPS) $(LIBDOVECOT_DEPS) + +libsieve_util_la_SOURCES = \ + mail-raw.c \ + edit-mail.c \ + rfc2822.c + +headers = \ + mail-raw.h \ + edit-mail.h \ + rfc2822.h + +pkginc_libdir=$(dovecot_pkgincludedir)/sieve +pkginc_lib_HEADERS = $(headers) + +test_programs = \ + test-edit-mail \ + test-rfc2822 + +noinst_PROGRAMS = $(test_programs) + +test_libs = \ + libsieve_util.la \ + $(LIBDOVECOT_STORAGE) \ + $(LIBDOVECOT) +test_deps = \ + libsieve_util.la \ + $(LIBDOVECOT_STORAGE_DEPS) \ + $(LIBDOVECOT_DEPS) + +test_edit_mail_SOURCES = test-edit-mail.c +test_edit_mail_LDADD = $(test_libs) +test_edit_mail_DEPENDENCIES = $(test_deps) + +test_rfc2822_SOURCES = test-rfc2822.c +test_rfc2822_LDADD = $(test_libs) +test_rfc2822_DEPENDENCIES = $(test_deps) + +check: check-am check-test +check-test: all-am + for bin in $(test_programs); do \ + if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ + done + diff --git a/pigeonhole/src/lib-sieve/util/Makefile.in b/pigeonhole/src/lib-sieve/util/Makefile.in new file mode 100644 index 0000000..a62ac20 --- /dev/null +++ b/pigeonhole/src/lib-sieve/util/Makefile.in @@ -0,0 +1,810 @@ +# 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@ +noinst_PROGRAMS = $(am__EXEEXT_1) +subdir = src/lib-sieve/util +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 $(pkginc_lib_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 = +am__EXEEXT_1 = test-edit-mail$(EXEEXT) test-rfc2822$(EXEEXT) +PROGRAMS = $(noinst_PROGRAMS) +LTLIBRARIES = $(noinst_LTLIBRARIES) +libsieve_util_la_LIBADD = +am_libsieve_util_la_OBJECTS = mail-raw.lo edit-mail.lo rfc2822.lo +libsieve_util_la_OBJECTS = $(am_libsieve_util_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_test_edit_mail_OBJECTS = test-edit-mail.$(OBJEXT) +test_edit_mail_OBJECTS = $(am_test_edit_mail_OBJECTS) +am__DEPENDENCIES_1 = +am__DEPENDENCIES_2 = libsieve_util.la $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am_test_rfc2822_OBJECTS = test-rfc2822.$(OBJEXT) +test_rfc2822_OBJECTS = $(am_test_rfc2822_OBJECTS) +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)/edit-mail.Plo \ + ./$(DEPDIR)/mail-raw.Plo ./$(DEPDIR)/rfc2822.Plo \ + ./$(DEPDIR)/test-edit-mail.Po ./$(DEPDIR)/test-rfc2822.Po +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_util_la_SOURCES) $(test_edit_mail_SOURCES) \ + $(test_rfc2822_SOURCES) +DIST_SOURCES = $(libsieve_util_la_SOURCES) $(test_edit_mail_SOURCES) \ + $(test_rfc2822_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(pkginc_libdir)" +HEADERS = $(pkginc_lib_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +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_util.la +AM_CPPFLAGS = \ + $(LIBDOVECOT_INCLUDE) \ + $(LIBDOVECOT_SERVICE_INCLUDE) \ + -DMODULEDIR=\""$(dovecot_moduledir)"\" + +libsieve_util_la_DEPENDENCIES = $(LIBDOVECOT_STORAGE_DEPS) $(LIBDOVECOT_DEPS) +libsieve_util_la_SOURCES = \ + mail-raw.c \ + edit-mail.c \ + rfc2822.c + +headers = \ + mail-raw.h \ + edit-mail.h \ + rfc2822.h + +pkginc_libdir = $(dovecot_pkgincludedir)/sieve +pkginc_lib_HEADERS = $(headers) +test_programs = \ + test-edit-mail \ + test-rfc2822 + +test_libs = \ + libsieve_util.la \ + $(LIBDOVECOT_STORAGE) \ + $(LIBDOVECOT) + +test_deps = \ + libsieve_util.la \ + $(LIBDOVECOT_STORAGE_DEPS) \ + $(LIBDOVECOT_DEPS) + +test_edit_mail_SOURCES = test-edit-mail.c +test_edit_mail_LDADD = $(test_libs) +test_edit_mail_DEPENDENCIES = $(test_deps) +test_rfc2822_SOURCES = test-rfc2822.c +test_rfc2822_LDADD = $(test_libs) +test_rfc2822_DEPENDENCIES = $(test_deps) +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/util/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-sieve/util/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-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +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_util.la: $(libsieve_util_la_OBJECTS) $(libsieve_util_la_DEPENDENCIES) $(EXTRA_libsieve_util_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libsieve_util_la_OBJECTS) $(libsieve_util_la_LIBADD) $(LIBS) + +test-edit-mail$(EXEEXT): $(test_edit_mail_OBJECTS) $(test_edit_mail_DEPENDENCIES) $(EXTRA_test_edit_mail_DEPENDENCIES) + @rm -f test-edit-mail$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_edit_mail_OBJECTS) $(test_edit_mail_LDADD) $(LIBS) + +test-rfc2822$(EXEEXT): $(test_rfc2822_OBJECTS) $(test_rfc2822_DEPENDENCIES) $(EXTRA_test_rfc2822_DEPENDENCIES) + @rm -f test-rfc2822$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_rfc2822_OBJECTS) $(test_rfc2822_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/edit-mail.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-raw.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rfc2822.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-edit-mail.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-rfc2822.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) + @$(NORMAL_INSTALL) + @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ + done + +uninstall-pkginc_libHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + clean-noinstPROGRAMS mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/edit-mail.Plo + -rm -f ./$(DEPDIR)/mail-raw.Plo + -rm -f ./$(DEPDIR)/rfc2822.Plo + -rm -f ./$(DEPDIR)/test-edit-mail.Po + -rm -f ./$(DEPDIR)/test-rfc2822.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pkginc_libHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/edit-mail.Plo + -rm -f ./$(DEPDIR)/mail-raw.Plo + -rm -f ./$(DEPDIR)/rfc2822.Plo + -rm -f ./$(DEPDIR)/test-edit-mail.Po + -rm -f ./$(DEPDIR)/test-rfc2822.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pkginc_libHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstLTLIBRARIES \ + clean-noinstPROGRAMS cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-pkginc_libHEADERS \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am \ + uninstall-pkginc_libHEADERS + +.PRECIOUS: Makefile + + +check: check-am check-test +check-test: all-am + for bin in $(test_programs); do \ + if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ + done + +# 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/util/edit-mail.c b/pigeonhole/src/lib-sieve/util/edit-mail.c new file mode 100644 index 0000000..31d941e --- /dev/null +++ b/pigeonhole/src/lib-sieve/util/edit-mail.c @@ -0,0 +1,2254 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" +#include "str.h" +#include "mempool.h" +#include "llist.h" +#include "istream-private.h" +#include "master-service.h" +#include "master-service-settings.h" +#include "message-parser.h" +#include "message-header-encode.h" +#include "message-header-decode.h" +#include "mail-user.h" +#include "mail-storage-private.h" +#include "index-mail.h" +#include "raw-storage.h" + +#include "rfc2822.h" + +#include "edit-mail.h" + +/* + * Forward declarations + */ + +struct _header_field_index; +struct _header_field; +struct _header_index; +struct _header; + +static struct mail_vfuncs edit_mail_vfuncs; + +struct edit_mail_istream; +struct istream *edit_mail_istream_create(struct edit_mail *edmail); + +static struct _header_index * +edit_mail_header_clone(struct edit_mail *edmail, struct _header *header); + +/* + * Raw storage + */ + +static struct mail_user *edit_mail_user = NULL; +static unsigned int edit_mail_refcount = 0; + +static struct mail_user *edit_mail_raw_storage_get(struct mail_user *mail_user) +{ + if (edit_mail_user == NULL) { + void **sets = + master_service_settings_get_others(master_service); + + edit_mail_user = raw_storage_create_from_set( + mail_user->set_info, sets[0]); + } + + edit_mail_refcount++; + + return edit_mail_user; +} + +static void edit_mail_raw_storage_drop(void) +{ + i_assert(edit_mail_refcount > 0); + + if (--edit_mail_refcount != 0) + return; + + mail_user_unref(&edit_mail_user); + edit_mail_user = NULL; +} + +/* + * Headers + */ + +struct _header_field { + struct _header *header; + + unsigned int refcount; + + char *data; + size_t size; + size_t virtual_size; + uoff_t offset; + unsigned int lines; + + uoff_t body_offset; + + char *utf8_value; +}; + +struct _header_field_index { + struct _header_field_index *prev, *next; + + struct _header_field *field; + struct _header_index *header; +}; + +struct _header { + unsigned int refcount; + + char *name; +}; + +struct _header_index { + struct _header_index *prev, *next; + + struct _header *header; + + struct _header_field_index *first, *last; + + unsigned int count; +}; + +static inline struct _header *_header_create(const char *name) +{ + struct _header *header; + + header = i_new(struct _header, 1); + header->name = i_strdup(name); + header->refcount = 1; + + return header; +} + +static inline void _header_ref(struct _header *header) +{ + header->refcount++; +} + +static inline void _header_unref(struct _header *header) +{ + i_assert(header->refcount > 0); + if (--header->refcount != 0) + return; + + i_free(header->name); + i_free(header); +} + +static inline struct _header_field *_header_field_create(struct _header *header) +{ + struct _header_field *hfield; + + hfield = i_new(struct _header_field, 1); + hfield->refcount = 1; + hfield->header = header; + if (header != NULL) + _header_ref(header); + + return hfield; +} + +static inline void _header_field_ref(struct _header_field *hfield) +{ + hfield->refcount++; +} + +static inline void _header_field_unref(struct _header_field *hfield) +{ + i_assert(hfield->refcount > 0); + if (--hfield->refcount != 0) + return; + + if (hfield->header != NULL) + _header_unref(hfield->header); + + if (hfield->data != NULL) + i_free(hfield->data); + if (hfield->utf8_value != NULL) + i_free(hfield->utf8_value); + i_free(hfield); +} + +/* + * Edit mail object + */ + +struct edit_mail { + struct mail_private mail; + struct mail_private *wrapped; + + struct edit_mail *parent; + unsigned int refcount; + + struct istream *wrapped_stream; + struct istream *stream; + + struct _header_index *headers_head, *headers_tail; + struct _header_field_index *header_fields_head, *header_fields_tail; + struct message_size hdr_size, body_size; + + struct message_size wrapped_hdr_size, wrapped_body_size; + + struct _header_field_index *header_fields_appended; + struct message_size appended_hdr_size; + + bool modified:1; + bool snapshot_modified:1; + bool crlf:1; + bool eoh_crlf:1; + bool headers_parsed:1; + bool destroying_stream:1; +}; + +struct edit_mail *edit_mail_wrap(struct mail *mail) +{ + struct mail_private *mailp = (struct mail_private *) mail; + struct edit_mail *edmail; + struct mail_user *raw_mail_user; + struct mailbox *raw_box = NULL; + struct mailbox_transaction_context *raw_trans; + struct message_size hdr_size, body_size; + struct istream *wrapped_stream; + uoff_t size_diff; + pool_t pool; + + if (mail_get_stream(mail, &hdr_size, &body_size, &wrapped_stream) < 0) + return NULL; + + /* Create dummy raw mailbox for our wrapper */ + + raw_mail_user = edit_mail_raw_storage_get(mail->box->storage->user); + + if (raw_mailbox_alloc_stream(raw_mail_user, wrapped_stream, (time_t)-1, + "editor@example.com", &raw_box) < 0) { + i_error("edit-mail: failed to open raw box: %s", + mailbox_get_last_internal_error(raw_box, NULL)); + mailbox_free(&raw_box); + edit_mail_raw_storage_drop(); + return NULL; + } + + raw_trans = mailbox_transaction_begin(raw_box, 0, __func__); + + /* Create the wrapper mail */ + + pool = pool_alloconly_create("edit_mail", 1024); + edmail = p_new(pool, struct edit_mail, 1); + edmail->refcount = 1; + edmail->mail.pool = pool; + + edmail->wrapped = mailp; + edmail->wrapped_hdr_size = hdr_size; + edmail->wrapped_body_size = body_size; + + edmail->wrapped_stream = wrapped_stream; + i_stream_ref(edmail->wrapped_stream); + + /* Determine whether we should use CRLF or LF for the physical message + */ + size_diff = ((hdr_size.virtual_size + body_size.virtual_size) - + (hdr_size.physical_size + body_size.physical_size)); + if (size_diff == 0 || size_diff <= (hdr_size.lines + body_size.lines)/2) + edmail->crlf = edmail->eoh_crlf = TRUE; + + array_create(&edmail->mail.module_contexts, pool, sizeof(void *), 5); + + edmail->mail.v = edit_mail_vfuncs; + edmail->mail.mail.seq = 1; + edmail->mail.mail.box = raw_box; + edmail->mail.mail.transaction = raw_trans; + edmail->mail.wanted_fields = mailp->wanted_fields; + edmail->mail.wanted_headers = mailp->wanted_headers; + + return edmail; +} + +struct edit_mail *edit_mail_snapshot(struct edit_mail *edmail) +{ + struct _header_field_index *field_idx, *field_idx_new; + struct edit_mail *edmail_new; + pool_t pool; + + if (!edmail->snapshot_modified) + return edmail; + + pool = pool_alloconly_create("edit_mail", 1024); + edmail_new = p_new(pool, struct edit_mail, 1); + edmail_new->refcount = 1; + edmail_new->mail.pool = pool; + + edmail_new->wrapped = edmail->wrapped; + edmail_new->wrapped_hdr_size = edmail->wrapped_hdr_size; + edmail_new->wrapped_body_size = edmail->wrapped_body_size; + edmail_new->hdr_size = edmail->hdr_size; + edmail_new->body_size = edmail->body_size; + edmail_new->appended_hdr_size = edmail->appended_hdr_size; + + edmail_new->wrapped_stream = edmail->wrapped_stream; + i_stream_ref(edmail_new->wrapped_stream); + + edmail_new->crlf = edmail->crlf; + edmail_new->eoh_crlf = edmail->eoh_crlf; + + array_create(&edmail_new->mail.module_contexts, pool, + sizeof(void *), 5); + + edmail_new->mail.v = edit_mail_vfuncs; + edmail_new->mail.mail.seq = 1; + edmail_new->mail.mail.box = edmail->mail.mail.box; + edmail_new->mail.mail.transaction = edmail->mail.mail.transaction; + edmail_new->mail.wanted_fields = edmail->mail.wanted_fields; + edmail_new->mail.wanted_headers = edmail->mail.wanted_headers; + + edmail_new->stream = NULL; + + if (edmail->modified) { + field_idx = edmail->header_fields_head; + while (field_idx != NULL) { + struct _header_field_index *next = field_idx->next; + + field_idx_new = i_new(struct _header_field_index, 1); + + field_idx_new->header = edit_mail_header_clone( + edmail_new, field_idx->header->header); + + field_idx_new->field = field_idx->field; + _header_field_ref(field_idx_new->field); + + DLLIST2_APPEND(&edmail_new->header_fields_head, + &edmail_new->header_fields_tail, + field_idx_new); + + field_idx_new->header->count++; + if (field_idx->header->first == field_idx) + field_idx_new->header->first = field_idx_new; + if (field_idx->header->last == field_idx) + field_idx_new->header->last = field_idx_new; + + if (field_idx == edmail->header_fields_appended) { + edmail_new->header_fields_appended = + field_idx_new; + } + + field_idx = next; + } + + edmail_new->modified = TRUE; + } + + edmail_new->headers_parsed = edmail->headers_parsed; + edmail_new->parent = edmail; + + return edmail_new; +} + +void edit_mail_reset(struct edit_mail *edmail) +{ + struct _header_index *header_idx; + struct _header_field_index *field_idx; + + i_stream_unref(&edmail->stream); + + field_idx = edmail->header_fields_head; + while (field_idx != NULL) { + struct _header_field_index *next = field_idx->next; + + _header_field_unref(field_idx->field); + i_free(field_idx); + + field_idx = next; + } + + header_idx = edmail->headers_head; + while (header_idx != NULL) { + struct _header_index *next = header_idx->next; + + _header_unref(header_idx->header); + i_free(header_idx); + + header_idx = next; + } + + edmail->modified = FALSE; +} + +void edit_mail_unwrap(struct edit_mail **edmail) +{ + struct edit_mail *parent; + + i_assert((*edmail)->refcount > 0); + if (--(*edmail)->refcount != 0) + return; + + edit_mail_reset(*edmail); + i_stream_unref(&(*edmail)->wrapped_stream); + + parent = (*edmail)->parent; + + if (parent == NULL) { + mailbox_transaction_rollback(&(*edmail)->mail.mail.transaction); + mailbox_free(&(*edmail)->mail.mail.box); + edit_mail_raw_storage_drop(); + } + + pool_unref(&(*edmail)->mail.pool); + *edmail = NULL; + + if (parent != NULL) + edit_mail_unwrap(&parent); +} + +struct mail *edit_mail_get_mail(struct edit_mail *edmail) +{ + /* Return wrapped mail when nothing is modified yet */ + if (!edmail->modified) + return &edmail->wrapped->mail; + + return &edmail->mail.mail; +} + +/* + * Editing + */ + +static inline void edit_mail_modify(struct edit_mail *edmail) +{ + edmail->mail.mail.seq++; + edmail->modified = TRUE; + edmail->snapshot_modified = TRUE; +} + +/* Header modification */ + +static inline char *_header_value_unfold(const char *value) +{ + string_t *out; + unsigned int i; + + for (i = 0; value[i] != '\0'; i++) { + if (value[i] == '\r' || value[i] == '\n') + break; + } + if (value[i] == '\0') + return i_strdup(value); + + out = t_str_new(i + strlen(value+i) + 10); + str_append_data(out, value, i); + for (; value[i] != '\0'; i++) { + if (value[i] == '\n') { + i++; + if (value[i] == '\0') + break; + + switch (value[i]) { + case ' ': + str_append_c(out, ' '); + break; + case '\t': + default: + str_append_c(out, '\t'); + } + } else { + if (value[i] != '\r') + str_append_c(out, value[i]); + } + } + + return i_strndup(str_c(out), str_len(out)); +} + +static struct _header_index * +edit_mail_header_find(struct edit_mail *edmail, const char *field_name) +{ + struct _header_index *header_idx; + + header_idx = edmail->headers_head; + while (header_idx != NULL) { + if (strcasecmp(header_idx->header->name, field_name) == 0) + return header_idx; + + header_idx = header_idx->next; + } + + return NULL; +} + +static struct _header_index * +edit_mail_header_create(struct edit_mail *edmail, const char *field_name) +{ + struct _header_index *header_idx; + + header_idx = edit_mail_header_find(edmail, field_name); + if (header_idx == NULL) { + header_idx = i_new(struct _header_index, 1); + header_idx->header = _header_create(field_name); + + DLLIST2_APPEND(&edmail->headers_head, &edmail->headers_tail, + header_idx); + } + + return header_idx; +} + +static struct _header_index * +edit_mail_header_clone(struct edit_mail *edmail, struct _header *header) +{ + struct _header_index *header_idx; + + header_idx = edmail->headers_head; + while (header_idx != NULL) { + if (header_idx->header == header) + return header_idx; + + header_idx = header_idx->next; + } + + header_idx = i_new(struct _header_index, 1); + header_idx->header = header; + _header_ref(header); + DLLIST2_APPEND(&edmail->headers_head, &edmail->headers_tail, + header_idx); + + return header_idx; +} + +static struct _header_field_index * +edit_mail_header_field_create(struct edit_mail *edmail, const char *field_name, + const char *value) +{ + struct _header_index *header_idx; + struct _header *header; + struct _header_field_index *field_idx; + struct _header_field *field; + unsigned int lines; + + /* Get/create header index item */ + header_idx = edit_mail_header_create(edmail, field_name); + header = header_idx->header; + + /* Create new field index item */ + field_idx = i_new(struct _header_field_index, 1); + field_idx->header = header_idx; + field_idx->field = field = _header_field_create(header); + + /* Create header field data (folded if necessary) */ + T_BEGIN { + string_t *enc_value, *data; + + enc_value = t_str_new(strlen(field_name) + strlen(value) + 64); + data = t_str_new(strlen(field_name) + strlen(value) + 128); + + message_header_encode(value, enc_value); + + lines = rfc2822_header_append(data, field_name, + str_c(enc_value), edmail->crlf, + &field->body_offset); + + /* Copy to new field */ + field->data = i_strndup(str_data(data), str_len(data)); + field->size = str_len(data); + field->virtual_size = (edmail->crlf ? + field->size : field->size + lines); + field->lines = lines; + } T_END; + + /* Record original (utf8) value */ + field->utf8_value = _header_value_unfold(value); + + return field_idx; +} + +static void +edit_mail_header_field_delete(struct edit_mail *edmail, + struct _header_field_index *field_idx, + bool update_index) +{ + struct _header_index *header_idx = field_idx->header; + struct _header_field *field = field_idx->field; + + i_assert(header_idx != NULL); + + edmail->hdr_size.physical_size -= field->size; + edmail->hdr_size.virtual_size -= field->virtual_size; + edmail->hdr_size.lines -= field->lines; + + header_idx->count--; + if (update_index) { + if (header_idx->count == 0) { + DLLIST2_REMOVE(&edmail->headers_head, + &edmail->headers_tail, header_idx); + _header_unref(header_idx->header); + i_free(header_idx); + } else if (header_idx->first == field_idx) { + struct _header_field_index *hfield = + header_idx->first->next; + + while (hfield != NULL && hfield->header != header_idx) + hfield = hfield->next; + + i_assert(hfield != NULL); + header_idx->first = hfield; + } else if (header_idx->last == field_idx) { + struct _header_field_index *hfield = + header_idx->last->prev; + + while (hfield != NULL && hfield->header != header_idx) + hfield = hfield->prev; + + i_assert(hfield != NULL); + header_idx->last = hfield; + } + } + + DLLIST2_REMOVE(&edmail->header_fields_head, &edmail->header_fields_tail, + field_idx); + _header_field_unref(field_idx->field); + i_free(field_idx); +} + +static struct _header_field_index * +edit_mail_header_field_replace(struct edit_mail *edmail, + struct _header_field_index *field_idx, + const char *newname, const char *newvalue, + bool update_index) +{ + struct _header_field_index *field_idx_new; + struct _header_index *header_idx = field_idx->header, *header_idx_new; + struct _header_field *field = field_idx->field, *field_new; + + i_assert(header_idx != NULL); + i_assert(newname != NULL || newvalue != NULL); + + if (newname == NULL) + newname = header_idx->header->name; + if (newvalue == NULL) + newvalue = field_idx->field->utf8_value; + field_idx_new = edit_mail_header_field_create( + edmail, newname, newvalue); + field_new = field_idx_new->field; + header_idx_new = field_idx_new->header; + + edmail->hdr_size.physical_size -= field->size; + edmail->hdr_size.virtual_size -= field->virtual_size; + edmail->hdr_size.lines -= field->lines; + + edmail->hdr_size.physical_size += field_new->size; + edmail->hdr_size.virtual_size += field_new->virtual_size; + edmail->hdr_size.lines += field_new->lines; + + /* Replace header field index */ + field_idx_new->prev = field_idx->prev; + field_idx_new->next = field_idx->next; + if (field_idx->prev != NULL) + field_idx->prev->next = field_idx_new; + if (field_idx->next != NULL) + field_idx->next->prev = field_idx_new; + if (edmail->header_fields_head == field_idx) + edmail->header_fields_head = field_idx_new; + if (edmail->header_fields_tail == field_idx) + edmail->header_fields_tail = field_idx_new; + + if (header_idx_new == header_idx) { + if (header_idx->first == field_idx) + header_idx->first = field_idx_new; + if (header_idx->last == field_idx) + header_idx->last = field_idx_new; + } else { + header_idx->count--; + header_idx_new->count++; + + if (update_index) { + if (header_idx->count == 0) { + DLLIST2_REMOVE(&edmail->headers_head, + &edmail->headers_tail, + header_idx); + _header_unref(header_idx->header); + i_free(header_idx); + } else if (header_idx->first == field_idx) { + struct _header_field_index *hfield = + header_idx->first->next; + + while (hfield != NULL && + hfield->header != header_idx) + hfield = hfield->next; + + i_assert(hfield != NULL); + header_idx->first = hfield; + } else if (header_idx->last == field_idx) { + struct _header_field_index *hfield = + header_idx->last->prev; + + while (hfield != NULL && + hfield->header != header_idx) + hfield = hfield->prev; + + i_assert(hfield != NULL); + header_idx->last = hfield; + } + if (header_idx_new->count > 0) { + struct _header_field_index *hfield; + + hfield = edmail->header_fields_head; + while (hfield != NULL && + hfield->header != header_idx_new) + hfield = hfield->next; + + i_assert(hfield != NULL); + header_idx_new->first = hfield; + + hfield = edmail->header_fields_tail; + while (hfield != NULL && + hfield->header != header_idx_new) + hfield = hfield->prev; + + i_assert(hfield != NULL); + header_idx_new->last = hfield; + } + } + } + + _header_field_unref(field_idx->field); + i_free(field_idx); + return field_idx_new; +} + +static inline char * +_header_decode(const unsigned char *hdr_data, size_t hdr_data_len) +{ + string_t *str = t_str_new(512); + + /* hdr_data is already unfolded */ + + /* Decode MIME encoded-words. */ + message_header_decode_utf8((const unsigned char *)hdr_data, + hdr_data_len, str, NULL); + return i_strdup(str_c(str)); +} + +static int edit_mail_headers_parse(struct edit_mail *edmail) +{ + struct message_header_parser_ctx *hparser; + enum message_header_parser_flags hparser_flags = + MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP | + MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE; + struct message_header_line *hdr; + struct _header_index *header_idx; + struct _header_field_index *head = NULL, *tail = NULL, *current; + string_t *hdr_data; + uoff_t offset = 0, body_offset = 0, vsize_diff = 0; + unsigned int lines = 0; + int ret; + + if (edmail->headers_parsed) + return 1; + + i_stream_seek(edmail->wrapped_stream, 0); + hparser = message_parse_header_init(edmail->wrapped_stream, NULL, + hparser_flags); + + T_BEGIN { + hdr_data = t_str_new(1024); + while ((ret = message_parse_header_next(hparser, &hdr)) > 0) { + struct _header_field_index *field_idx_new; + struct _header_field *field; + + if (hdr->eoh) { + /* Record whether header ends in CRLF or LF */ + edmail->eoh_crlf = hdr->crlf_newline; + } + + if (hdr == NULL || hdr->eoh) + break; + + /* Skip bad headers */ + if (hdr->name_len == 0) + continue; + /* We deny the existence of any `Content-Length:' + header. This header is non-standard and it can wreak + havok when the message is modified. + */ + if (strcasecmp(hdr->name, "Content-Length" ) == 0) + continue; + + if (hdr->continued) { + /* Continued line of folded header */ + buffer_append(hdr_data, hdr->value, + hdr->value_len); + } else { + /* First line of header */ + offset = hdr->name_offset; + body_offset = hdr->name_len + hdr->middle_len; + str_truncate(hdr_data, 0); + buffer_append(hdr_data, hdr->name, + hdr->name_len); + buffer_append(hdr_data, hdr->middle, + hdr->middle_len); + buffer_append(hdr_data, hdr->value, + hdr->value_len); + lines = 0; + vsize_diff = 0; + } + + if (!hdr->no_newline) { + lines++; + + if (hdr->crlf_newline) { + buffer_append(hdr_data, "\r\n", 2); + } else { + buffer_append(hdr_data, "\n", 1); + vsize_diff++; + } + } + + if (hdr->continues) { + hdr->use_full_value = TRUE; + continue; + } + + /* Create new header field index entry */ + + field_idx_new = i_new(struct _header_field_index, 1); + + header_idx = edit_mail_header_create(edmail, hdr->name); + header_idx->count++; + field_idx_new->header = header_idx; + field_idx_new->field = field = + _header_field_create(header_idx->header); + + i_assert(body_offset > 0); + field->body_offset = body_offset; + + field->utf8_value = _header_decode(hdr->full_value, + hdr->full_value_len); + + field->size = str_len(hdr_data); + field->virtual_size = field->size + vsize_diff; + field->data = i_strndup(str_data(hdr_data), + field->size); + field->offset = offset; + field->lines = lines; + + DLLIST2_APPEND(&head, &tail, field_idx_new); + + edmail->hdr_size.physical_size += field->size; + edmail->hdr_size.virtual_size += field->virtual_size; + edmail->hdr_size.lines += lines; + } + } T_END; + + message_parse_header_deinit(&hparser); + + /* Blocking i/o required */ + i_assert(ret != 0); + + if (ret < 0 && edmail->wrapped_stream->stream_errno != 0) { + /* Error; clean up */ + i_error("read(%s) failed: %s", + i_stream_get_name(edmail->wrapped_stream), + i_stream_get_error(edmail->wrapped_stream)); + current = head; + while (current != NULL) { + struct _header_field_index *next = current->next; + + _header_field_unref(current->field); + i_free(current); + + current = next; + } + + return ret; + } + + /* Insert header field index items in main list */ + if (head != NULL && tail != NULL) { + if (edmail->header_fields_appended != NULL) { + if (edmail->header_fields_head != + edmail->header_fields_appended) { + edmail->header_fields_appended->prev->next = head; + head->prev = edmail->header_fields_appended->prev; + } else { + edmail->header_fields_head = head; + } + + tail->next = edmail->header_fields_appended; + edmail->header_fields_appended->prev = tail; + } else if (edmail->header_fields_tail != NULL) { + edmail->header_fields_tail->next = head; + head->prev = edmail->header_fields_tail; + edmail->header_fields_tail = tail; + } else { + edmail->header_fields_head = head; + edmail->header_fields_tail = tail; + } + } + + /* Rebuild header index */ + current = edmail->header_fields_head; + while (current != NULL) { + if (current->header->first == NULL) + current->header->first = current; + current->header->last = current; + + current = current->next; + } + + /* Clear appended headers */ + edmail->header_fields_appended = NULL; + edmail->appended_hdr_size.physical_size = 0; + edmail->appended_hdr_size.virtual_size = 0; + edmail->appended_hdr_size.lines = 0; + + /* Do not parse headers again */ + edmail->headers_parsed = TRUE; + + return 1; +} + +void edit_mail_header_add(struct edit_mail *edmail, const char *field_name, + const char *value, bool last) +{ + struct _header_index *header_idx; + struct _header_field_index *field_idx; + struct _header_field *field; + + edit_mail_modify(edmail); + + field_idx = edit_mail_header_field_create(edmail, field_name, value); + header_idx = field_idx->header; + field = field_idx->field; + + /* Add it to the header field index */ + if (last) { + DLLIST2_APPEND(&edmail->header_fields_head, + &edmail->header_fields_tail, field_idx); + + header_idx->last = field_idx; + if (header_idx->first == NULL) + header_idx->first = field_idx; + + if (!edmail->headers_parsed) { + if (edmail->header_fields_appended == NULL) { + /* Record beginning of appended headers */ + edmail->header_fields_appended = field_idx; + } + + edmail->appended_hdr_size.physical_size += field->size; + edmail->appended_hdr_size.virtual_size += field->virtual_size; + edmail->appended_hdr_size.lines += field->lines; + } + } else { + DLLIST2_PREPEND(&edmail->header_fields_head, + &edmail->header_fields_tail, field_idx); + + header_idx->first = field_idx; + if (header_idx->last == NULL) + header_idx->last = field_idx; + } + + header_idx->count++; + + edmail->hdr_size.physical_size += field->size; + edmail->hdr_size.virtual_size += field->virtual_size; + edmail->hdr_size.lines += field->lines; +} + +int edit_mail_header_delete(struct edit_mail *edmail, const char *field_name, + int index) +{ + struct _header_index *header_idx; + struct _header_field_index *field_idx; + int pos = 0; + int ret = 0; + + /* Make sure headers are parsed */ + if (edit_mail_headers_parse(edmail) <= 0) + return -1; + + /* Find the header entry */ + header_idx = edit_mail_header_find(edmail, field_name); + if (header_idx == NULL) { + /* Not found */ + return 0; + } + + /* Signal modification */ + edit_mail_modify(edmail); + + /* Iterate through all header fields and remove those that match */ + field_idx = (index >= 0 ? header_idx->first : header_idx->last); + while (field_idx != NULL) { + struct _header_field_index *next = + (index >= 0 ? field_idx->next : field_idx->prev); + + if (field_idx->field->header == header_idx->header) { + bool final; + + if (index >= 0) { + pos++; + final = (header_idx->last == field_idx); + } else { + pos--; + final = (header_idx->first == field_idx); + } + + if (index == 0 || index == pos) { + if (header_idx->first == field_idx) + header_idx->first = NULL; + if (header_idx->last == field_idx) + header_idx->last = NULL; + edit_mail_header_field_delete( + edmail, field_idx, FALSE); + ret++; + } + + if (final || (index != 0 && index == pos)) + break; + } + + field_idx = next; + } + + if (index == 0 || header_idx->count == 0) { + DLLIST2_REMOVE(&edmail->headers_head, + &edmail->headers_tail, header_idx); + _header_unref(header_idx->header); + i_free(header_idx); + } else if (header_idx->first == NULL || header_idx->last == NULL) { + struct _header_field_index *current = + edmail->header_fields_head; + + while (current != NULL) { + if (current->header == header_idx) { + if (header_idx->first == NULL) + header_idx->first = current; + header_idx->last = current; + } + current = current->next; + } + } + + return ret; +} + +int edit_mail_header_replace(struct edit_mail *edmail, + const char *field_name, int index, + const char *newname, const char *newvalue) +{ + struct _header_index *header_idx, *header_idx_new; + struct _header_field_index *field_idx, *field_idx_new; + int pos = 0; + int ret = 0; + + /* Make sure headers are parsed */ + if (edit_mail_headers_parse(edmail) <= 0) + return -1; + + /* Find the header entry */ + header_idx = edit_mail_header_find(edmail, field_name); + if (header_idx == NULL) { + /* Not found */ + return 0; + } + + /* Signal modification */ + edit_mail_modify(edmail); + + /* Iterate through all header fields and replace those that match */ + field_idx = (index >= 0 ? header_idx->first : header_idx->last); + field_idx_new = NULL; + while (field_idx != NULL) { + struct _header_field_index *next = + (index >= 0 ? field_idx->next : field_idx->prev); + + if (field_idx->field->header == header_idx->header) { + bool final; + + if (index >= 0) { + pos++; + final = (header_idx->last == field_idx); + } else { + pos--; + final = (header_idx->first == field_idx); + } + + if (index == 0 || index == pos) { + if (header_idx->first == field_idx) + header_idx->first = NULL; + if (header_idx->last == field_idx) + header_idx->last = NULL; + field_idx_new = edit_mail_header_field_replace( + edmail, field_idx, newname, newvalue, + FALSE); + ret++; + } + + if (final || (index != 0 && index == pos)) + break; + } + + field_idx = next; + } + + /* Update old header index */ + if (header_idx->count == 0) { + DLLIST2_REMOVE(&edmail->headers_head, &edmail->headers_tail, + header_idx); + _header_unref(header_idx->header); + i_free(header_idx); + } else if (header_idx->first == NULL || header_idx->last == NULL) { + struct _header_field_index *current = + edmail->header_fields_head; + + while (current != NULL) { + if (current->header == header_idx) { + if (header_idx->first == NULL) + header_idx->first = current; + header_idx->last = current; + } + current = current->next; + } + } + + /* Update new header index */ + if (field_idx_new != NULL) { + struct _header_field_index *current = + edmail->header_fields_head; + + header_idx_new = field_idx_new->header; + while (current != NULL) { + if (current->header == header_idx_new) { + if (header_idx_new->first == NULL) + header_idx_new->first = current; + header_idx_new->last = current; + } + current = current->next; + } + } + + return ret; +} + +struct edit_mail_header_iter +{ + struct edit_mail *mail; + struct _header_index *header; + struct _header_field_index *current; + + bool reverse:1; +}; + +int edit_mail_headers_iterate_init(struct edit_mail *edmail, + const char *field_name, bool reverse, + struct edit_mail_header_iter **edhiter_r) +{ + struct edit_mail_header_iter *edhiter; + struct _header_index *header_idx = NULL; + struct _header_field_index *current = NULL; + + /* Make sure headers are parsed */ + if (edit_mail_headers_parse(edmail) <= 0) { + /* Failure */ + return -1; + } + + header_idx = edit_mail_header_find(edmail, field_name); + + if (field_name != NULL && header_idx == NULL) { + current = NULL; + } else if (!reverse) { + current = (header_idx != NULL ? + header_idx->first : edmail->header_fields_head); + } else { + current = (header_idx != NULL ? + header_idx->last : edmail->header_fields_tail); + if (current->header == NULL) + current = current->prev; + } + + if (current == NULL) + return 0; + + edhiter = i_new(struct edit_mail_header_iter, 1); + edhiter->mail = edmail; + edhiter->header = header_idx; + edhiter->reverse = reverse; + edhiter->current = current; + + *edhiter_r = edhiter; + return 1; +} + +void edit_mail_headers_iterate_deinit(struct edit_mail_header_iter **edhiter) +{ + i_free(*edhiter); + *edhiter = NULL; +} + +void edit_mail_headers_iterate_get(struct edit_mail_header_iter *edhiter, + const char **value_r) +{ + const char *raw; + int i; + + i_assert(edhiter->current != NULL && edhiter->current->header != NULL); + + raw = edhiter->current->field->utf8_value; + for (i = strlen(raw)-1; i >= 0; i--) { + if (raw[i] != ' ' && raw[i] != '\t') + break; + } + + *value_r = t_strndup(raw, i+1); +} + +bool edit_mail_headers_iterate_next(struct edit_mail_header_iter *edhiter) +{ + if (edhiter->current == NULL) + return FALSE; + + do { + edhiter->current = (!edhiter->reverse ? + edhiter->current->next : + edhiter->current->prev ); + } while (edhiter->current != NULL && edhiter->current->header != NULL && + edhiter->header != NULL && + edhiter->current->header != edhiter->header); + + return (edhiter->current != NULL && edhiter->current->header != NULL); +} + +bool edit_mail_headers_iterate_remove(struct edit_mail_header_iter *edhiter) +{ + struct _header_field_index *field_idx; + bool next; + + i_assert(edhiter->current != NULL && edhiter->current->header != NULL); + + edit_mail_modify(edhiter->mail); + + field_idx = edhiter->current; + next = edit_mail_headers_iterate_next(edhiter); + edit_mail_header_field_delete(edhiter->mail, field_idx, TRUE); + + return next; +} + +bool edit_mail_headers_iterate_replace(struct edit_mail_header_iter *edhiter, + const char *newname, + const char *newvalue) +{ + struct _header_field_index *field_idx; + bool next; + + i_assert(edhiter->current != NULL && edhiter->current->header != NULL); + + edit_mail_modify(edhiter->mail); + + field_idx = edhiter->current; + next = edit_mail_headers_iterate_next(edhiter); + edit_mail_header_field_replace(edhiter->mail, field_idx, + newname, newvalue, TRUE); + + return next; +} + +/* Body modification */ + +// FIXME: implement + +/* + * Mail API + */ + +static void edit_mail_close(struct mail *mail) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + + edmail->wrapped->v.close(&edmail->wrapped->mail); +} + +static void edit_mail_free(struct mail *mail) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + + edmail->wrapped->v.free(&edmail->wrapped->mail); + + edit_mail_unwrap(&edmail); +} + +static void +edit_mail_set_seq(struct mail *mail ATTR_UNUSED, uint32_t seq ATTR_UNUSED, + bool saving ATTR_UNUSED) +{ + i_panic("edit_mail_set_seq() not implemented"); +} + +static bool ATTR_NORETURN +edit_mail_set_uid(struct mail *mail ATTR_UNUSED, uint32_t uid ATTR_UNUSED) +{ + i_panic("edit_mail_set_uid() not implemented"); +} + +static void edit_mail_set_uid_cache_updates(struct mail *mail, bool set) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + + edmail->wrapped->v.set_uid_cache_updates(&edmail->wrapped->mail, set); +} + +static void +edit_mail_add_temp_wanted_fields( + struct mail *mail ATTR_UNUSED, enum mail_fetch_field fields ATTR_UNUSED, + struct mailbox_header_lookup_ctx *headers ATTR_UNUSED) +{ + /* Nothing */ +} + +static enum mail_flags edit_mail_get_flags(struct mail *mail) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + + return edmail->wrapped->v.get_flags(&edmail->wrapped->mail); +} + +static const char *const *edit_mail_get_keywords(struct mail *mail) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + + return edmail->wrapped->v.get_keywords(&edmail->wrapped->mail); +} + +static const ARRAY_TYPE(keyword_indexes) * +edit_mail_get_keyword_indexes(struct mail *mail) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + + return edmail->wrapped->v.get_keyword_indexes(&edmail->wrapped->mail); +} + +static uint64_t edit_mail_get_modseq(struct mail *mail) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + + return edmail->wrapped->v.get_modseq(&edmail->wrapped->mail); +} + +static uint64_t edit_mail_get_pvt_modseq(struct mail *mail) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + + return edmail->wrapped->v.get_pvt_modseq(&edmail->wrapped->mail); +} + +static int edit_mail_get_parts(struct mail *mail, struct message_part **parts_r) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + + return edmail->wrapped->v.get_parts(&edmail->wrapped->mail, parts_r); +} + +static int +edit_mail_get_date(struct mail *mail, time_t *date_r, int *timezone_r) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + + return edmail->wrapped->v.get_date(&edmail->wrapped->mail, + date_r, timezone_r); +} + +static int edit_mail_get_received_date(struct mail *mail, time_t *date_r) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + + return edmail->wrapped->v.get_received_date(&edmail->wrapped->mail, + date_r); +} + +static int edit_mail_get_save_date(struct mail *mail, time_t *date_r) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + + return edmail->wrapped->v.get_save_date(&edmail->wrapped->mail, date_r); +} + +static int edit_mail_get_virtual_size(struct mail *mail, uoff_t *size_r) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + + if (!edmail->headers_parsed) { + *size_r = (edmail->wrapped_hdr_size.virtual_size + + edmail->wrapped_body_size.virtual_size); + + if (!edmail->modified) + return 0; + } else { + *size_r = edmail->wrapped_body_size.virtual_size + 2; + } + + *size_r += (edmail->hdr_size.virtual_size + + edmail->body_size.virtual_size); + return 0; +} + +static int edit_mail_get_physical_size(struct mail *mail, uoff_t *size_r) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + + *size_r = 0; + if (!edmail->headers_parsed) { + *size_r = (edmail->wrapped_hdr_size.physical_size + + edmail->wrapped_body_size.physical_size); + + if (!edmail->modified) + return 0; + } else { + *size_r = (edmail->wrapped_body_size.physical_size + + (edmail->eoh_crlf ? 2 : 1)); + } + + *size_r += (edmail->hdr_size.physical_size + + edmail->body_size.physical_size); + return 0; +} + +static int +edit_mail_get_first_header(struct mail *mail, const char *field_name, + bool decode_to_utf8, const char **value_r) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + struct _header_index *header_idx; + struct _header_field *field; + int ret; + + /* Check whether mail headers were modified at all */ + if (!edmail->modified || edmail->headers_head == NULL) { + /* Unmodified */ + return edmail->wrapped->v.get_first_header( + &edmail->wrapped->mail, field_name, decode_to_utf8, + value_r); + } + + /* Try to find modified header */ + header_idx = edit_mail_header_find(edmail, field_name); + if (header_idx == NULL || header_idx->count == 0 ) { + if (!edmail->headers_parsed) { + /* No new header */ + return edmail->wrapped->v.get_first_header( + &edmail->wrapped->mail, field_name, + decode_to_utf8, value_r); + } + + *value_r = NULL; + return 0; + } + + /* Get the first occurrence */ + if (edmail->header_fields_appended == NULL) { + /* There are no appended headers, so first is found directly */ + field = header_idx->first->field; + } else { + struct _header_field_index *field_idx; + + /* Scan prepended headers */ + field_idx = edmail->header_fields_head; + while (field_idx != NULL) { + if (field_idx->header == header_idx) + break; + + if (field_idx == edmail->header_fields_appended) { + field_idx = NULL; + break; + } + field_idx = field_idx->next; + } + + if (field_idx == NULL) { + /* Check original message */ + ret = edmail->wrapped->v.get_first_header( + &edmail->wrapped->mail, field_name, + decode_to_utf8, value_r); + if (ret != 0) + return ret; + + /* Use first (apparently appended) header */ + field = header_idx->first->field; + } else { + field = field_idx->field; + } + } + + if (decode_to_utf8) + *value_r = field->utf8_value; + else + *value_r = (const char *)(field->data + field->body_offset); + return 1; +} + +static int +edit_mail_get_headers(struct mail *mail, const char *field_name, + bool decode_to_utf8, const char *const **value_r) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + struct _header_index *header_idx; + struct _header_field_index *field_idx; + const char *const *headers; + ARRAY(const char *) header_values; + + if (!edmail->modified || edmail->headers_head == NULL) { + /* Unmodified */ + return edmail->wrapped->v.get_headers( + &edmail->wrapped->mail, field_name, decode_to_utf8, + value_r); + } + + header_idx = edit_mail_header_find(edmail, field_name); + if (header_idx == NULL || header_idx->count == 0 ) { + if (!edmail->headers_parsed) { + /* No new header */ + return edmail->wrapped->v.get_headers( + &edmail->wrapped->mail, field_name, + decode_to_utf8, value_r); + } + + p_array_init(&header_values, edmail->mail.pool, 1); + (void)array_append_space(&header_values); + *value_r = array_idx(&header_values, 0); + return 0; + } + + /* Merge */ + + /* Read original headers too if message headers are not parsed */ + headers = NULL; + if (!edmail->headers_parsed && + edmail->wrapped->v.get_headers(&edmail->wrapped->mail, field_name, + decode_to_utf8, &headers) < 0) + return -1; + + /* Fill result array */ + p_array_init(&header_values, edmail->mail.pool, 32); + field_idx = header_idx->first; + while (field_idx != NULL) { + /* If current field is the first appended one, we need to add + original headers first. + */ + if (field_idx == edmail->header_fields_appended && + headers != NULL) { + while (*headers != NULL) { + array_append(&header_values, headers, 1); + headers++; + } + } + + /* Add modified header to the list */ + if (field_idx->field->header == header_idx->header) { + struct _header_field *field = field_idx->field; + + const char *value; + if (decode_to_utf8) + value = field->utf8_value; + else { + value = (const char *)(field->data + + field->body_offset); + } + + array_append(&header_values, &value, 1); + + if (field_idx == header_idx->last) + break; + } + + field_idx = field_idx->next; + } + + /* Add original headers if necessary */ + if (headers != NULL) { + while (*headers != NULL) { + array_append(&header_values, headers, 1); + headers++; + } + } + + (void)array_append_space(&header_values); + *value_r = array_idx(&header_values, 0); + return 1; +} + +static int ATTR_NORETURN +edit_mail_get_header_stream( + struct mail *mail ATTR_UNUSED, + struct mailbox_header_lookup_ctx *headers ATTR_UNUSED, + struct istream **stream_r ATTR_UNUSED) +{ + // FIXME: implement! + i_panic("edit_mail_get_header_stream() not implemented"); +} + +static int +edit_mail_get_stream(struct mail *mail, bool get_body ATTR_UNUSED, + struct message_size *hdr_size, + struct message_size *body_size, struct istream **stream_r) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + + if (edmail->stream == NULL) + edmail->stream = edit_mail_istream_create(edmail); + + if (hdr_size != NULL) { + *hdr_size = edmail->wrapped_hdr_size; + hdr_size->physical_size += edmail->hdr_size.physical_size; + hdr_size->virtual_size += edmail->hdr_size.virtual_size; + hdr_size->lines += edmail->hdr_size.lines; + } + + if (body_size != NULL) + *body_size = edmail->wrapped_body_size; + + *stream_r = edmail->stream; + i_stream_seek(edmail->stream, 0); + + return 0; +} + +static int +edit_mail_get_special(struct mail *mail, enum mail_fetch_field field, + const char **value_r) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + + if (edmail->modified) { + /* Block certain fields when modified */ + + switch (field) { + case MAIL_FETCH_GUID: + /* This is in essence a new message */ + *value_r = ""; + return 0; + case MAIL_FETCH_STORAGE_ID: + /* Prevent hardlink copying */ + *value_r = ""; + return 0; + default: + break; + } + } + + return edmail->wrapped->v.get_special(&edmail->wrapped->mail, + field, value_r); +} + +static int +edit_mail_get_backend_mail(struct mail *mail, struct mail **real_mail_r) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + + *real_mail_r = edit_mail_get_mail(edmail); + return 0; +} + +static void +edit_mail_update_flags(struct mail *mail, enum modify_type modify_type, + enum mail_flags flags) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + + edmail->wrapped->v.update_flags(&edmail->wrapped->mail, + modify_type, flags); +} + +static void +edit_mail_update_keywords(struct mail *mail, enum modify_type modify_type, + struct mail_keywords *keywords) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + + edmail->wrapped->v.update_keywords(&edmail->wrapped->mail, + modify_type, keywords); +} + +static void edit_mail_update_modseq(struct mail *mail, uint64_t min_modseq) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + + edmail->wrapped->v.update_modseq(&edmail->wrapped->mail, min_modseq); +} + +static void +edit_mail_update_pvt_modseq(struct mail *mail, uint64_t min_pvt_modseq) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + + edmail->wrapped->v.update_pvt_modseq(&edmail->wrapped->mail, + min_pvt_modseq); +} + +static void edit_mail_update_pop3_uidl(struct mail *mail, const char *uidl) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + + if (edmail->wrapped->v.update_pop3_uidl != NULL) { + edmail->wrapped->v.update_pop3_uidl( + &edmail->wrapped->mail, uidl); + } +} + +static void edit_mail_expunge(struct mail *mail ATTR_UNUSED) +{ + /* NOOP */ +} + +static void +edit_mail_set_cache_corrupted(struct mail *mail, enum mail_fetch_field field, + const char *reason) +{ + struct edit_mail *edmail = (struct edit_mail *)mail; + + edmail->wrapped->v.set_cache_corrupted(&edmail->wrapped->mail, + field, reason); +} + +static struct mail_vfuncs edit_mail_vfuncs = { + edit_mail_close, + edit_mail_free, + edit_mail_set_seq, + edit_mail_set_uid, + edit_mail_set_uid_cache_updates, + NULL, + NULL, + edit_mail_add_temp_wanted_fields, + edit_mail_get_flags, + edit_mail_get_keywords, + edit_mail_get_keyword_indexes, + edit_mail_get_modseq, + edit_mail_get_pvt_modseq, + edit_mail_get_parts, + edit_mail_get_date, + edit_mail_get_received_date, + edit_mail_get_save_date, + edit_mail_get_virtual_size, + edit_mail_get_physical_size, + edit_mail_get_first_header, + edit_mail_get_headers, + edit_mail_get_header_stream, + edit_mail_get_stream, + index_mail_get_binary_stream, + edit_mail_get_special, + edit_mail_get_backend_mail, + edit_mail_update_flags, + edit_mail_update_keywords, + edit_mail_update_modseq, + edit_mail_update_pvt_modseq, + edit_mail_update_pop3_uidl, + edit_mail_expunge, + edit_mail_set_cache_corrupted, + NULL, +}; + +/* + * Edit Mail Stream + */ + +struct edit_mail_istream { + struct istream_private istream; + pool_t pool; + + struct edit_mail *mail; + + struct _header_field_index *cur_header; + uoff_t cur_header_v_offset; + + bool parent_buffer:1; + bool header_read:1; + bool eof:1; +}; + +static void edit_mail_istream_destroy(struct iostream_private *stream) +{ + struct edit_mail_istream *edstream = + (struct edit_mail_istream *)stream; + + i_stream_unref(&edstream->istream.parent); + i_stream_free_buffer(&edstream->istream); + pool_unref(&edstream->pool); +} + +static ssize_t +merge_from_parent(struct edit_mail_istream *edstream, uoff_t parent_v_offset, + uoff_t parent_end_v_offset, uoff_t copy_v_offset) +{ + struct istream_private *stream = &edstream->istream; + uoff_t v_offset, append_v_offset; + const unsigned char *data; + size_t pos, cur_pos, parent_bytes_left; + bool parent_buffer = edstream->parent_buffer; + ssize_t ret; + + i_assert(parent_v_offset <= parent_end_v_offset); + edstream->parent_buffer = FALSE; + + v_offset = stream->istream.v_offset; + if (v_offset >= copy_v_offset) { + i_assert((v_offset - copy_v_offset) <= parent_end_v_offset); + if ((v_offset - copy_v_offset) == parent_end_v_offset) { + /* Parent data is all read */ + return 0; + } + } + + /* Determine where we are appending more data to the stream */ + append_v_offset = v_offset + (stream->pos - stream->skip); + + if (v_offset >= copy_v_offset) { + /* Parent buffer used */ + cur_pos = (stream->pos - stream->skip); + parent_v_offset += (v_offset - copy_v_offset); + } else { + cur_pos = 0; + i_assert(append_v_offset >= copy_v_offset); + parent_v_offset += (append_v_offset - copy_v_offset); + } + + /* Seek parent to required position */ + i_stream_seek(stream->parent, parent_v_offset); + + /* Read from parent */ + data = i_stream_get_data(stream->parent, &pos); + if (pos > cur_pos) + ret = 0; + else do { + /* Use normal read here, since parent data can be returned + directly to caller. */ + ret = i_stream_read(stream->parent); + + stream->istream.stream_errno = stream->parent->stream_errno; + stream->istream.eof = stream->parent->eof; + edstream->eof = stream->parent->eof; + data = i_stream_get_data(stream->parent, &pos); + /* Check again, in case the parent stream had been seeked + backwards and the previous read() didn't get us far + enough. */ + } while (pos <= cur_pos && ret > 0); + + /* Don't read beyond parent end offset */ + if (parent_end_v_offset != (uoff_t)-1) { + parent_bytes_left = (size_t)(parent_end_v_offset - + parent_v_offset); + if (pos >= parent_bytes_left) { + pos = parent_bytes_left; + } + } + + if (v_offset < copy_v_offset || ret == -2 || + (parent_buffer && (append_v_offset + 1) >= parent_end_v_offset)) { + /* Merging with our local buffer; copying data from parent */ + if (pos > 0) { + size_t avail; + + if (parent_buffer) { + stream->pos = stream->skip = 0; + stream->buffer = NULL; + } + if (!i_stream_try_alloc(stream, pos, &avail)) + return -2; + pos = (pos > avail ? avail : pos); + + memcpy(stream->w_buffer + stream->pos, data, pos); + stream->pos += pos; + stream->buffer = stream->w_buffer; + + if (cur_pos >= pos) + ret = 0; + else + ret = (ssize_t)(pos - cur_pos); + } else { + ret = (ret == 0 ? 0 : -1); + } + } else { + /* Just passing buffers from parent; no copying */ + ret = (pos > cur_pos ? + (ssize_t)(pos - cur_pos) : (ret == 0 ? 0 : -1)); + stream->buffer = data; + stream->pos = pos; + stream->skip = 0; + edstream->parent_buffer = TRUE; + } + + i_assert(ret != -1 || stream->istream.eof || + stream->istream.stream_errno != 0); + return ret; +} + +static ssize_t merge_modified_headers(struct edit_mail_istream *edstream) +{ + struct istream_private *stream = &edstream->istream; + struct edit_mail *edmail = edstream->mail; + uoff_t v_offset = stream->istream.v_offset, append_v_offset; + size_t appended, written, avail, size; + + if (edstream->cur_header == NULL) { + /* No (more) headers */ + return 0; + } + + /* Caller must already have committed remaining parent data to + our stream buffer. */ + i_assert(!edstream->parent_buffer); + + /* Add modified headers to buffer */ + written = 0; + while (edstream->cur_header != NULL) { + size_t wsize; + + /* Determine what part of the header was already buffered */ + append_v_offset = v_offset + (stream->pos - stream->skip); + i_assert(append_v_offset >= edstream->cur_header_v_offset); + if (append_v_offset >= edstream->cur_header_v_offset) + appended = (size_t)(append_v_offset - + edstream->cur_header_v_offset); + else + appended = 0; + i_assert(appended <= edstream->cur_header->field->size); + + /* Determine how much we want to write */ + size = edstream->cur_header->field->size - appended; + if (size > 0) { + /* Determine how much we can write */ + if (!i_stream_try_alloc(stream, size, &avail)) { + if (written == 0) + return -2; + break; + } + wsize = (size >= avail ? avail : size); + + /* Write (part of) the header to buffer */ + memcpy(stream->w_buffer + stream->pos, + edstream->cur_header->field->data + appended, + wsize); + stream->pos += wsize; + stream->buffer = stream->w_buffer; + written += wsize; + + if (wsize < size) { + /* Could not write whole header; finish here */ + break; + } + } + + /* Skip to next header */ + edstream->cur_header_v_offset += + edstream->cur_header->field->size; + edstream->cur_header = edstream->cur_header->next; + + /* Stop at end of prepended headers if original header is left + unparsed */ + if (!edmail->headers_parsed && + edstream->cur_header == edmail->header_fields_appended) + edstream->cur_header = NULL; + } + + if (edstream->cur_header == NULL) { + /* Clear offset too, just to be tidy */ + edstream->cur_header_v_offset = 0; + } + + i_assert(written > 0); + return (ssize_t)written; +} + +static ssize_t edit_mail_istream_read(struct istream_private *stream) +{ + struct edit_mail_istream *edstream = + (struct edit_mail_istream *)stream; + struct edit_mail *edmail = edstream->mail; + uoff_t v_offset, append_v_offset; + uoff_t parent_v_offset, parent_end_v_offset, copy_v_offset; + uoff_t prep_hdr_size, hdr_size; + ssize_t ret = 0; + + if (edstream->eof) { + stream->istream.eof = TRUE; + return -1; + } + + if (edstream->parent_buffer && stream->skip == stream->pos) { + edstream->parent_buffer = FALSE; + stream->pos = stream->skip = 0; + stream->buffer = NULL; + } + + /* Merge prepended headers */ + if (!edstream->parent_buffer) { + ret = merge_modified_headers(edstream); + if (ret != 0) + return ret; + } + v_offset = stream->istream.v_offset; + append_v_offset = v_offset + (stream->pos - stream->skip); + + if (!edmail->headers_parsed && edmail->header_fields_appended != NULL && + !edstream->header_read) { + /* Output headers from original stream */ + + /* Size of the prepended header */ + i_assert(edmail->hdr_size.physical_size >= + edmail->appended_hdr_size.physical_size); + prep_hdr_size = (edmail->hdr_size.physical_size - + edmail->appended_hdr_size.physical_size); + + /* Calculate offset of header end or appended header. Any final + CR is dealt with later. + */ + hdr_size = (prep_hdr_size + + edmail->wrapped_hdr_size.physical_size); + i_assert(hdr_size > 0); + if (append_v_offset <= (hdr_size - 1) && + edmail->wrapped_hdr_size.physical_size > 0) { + parent_v_offset = stream->parent_start_offset; + parent_end_v_offset = + (stream->parent_start_offset + + edmail->wrapped_hdr_size.physical_size - 1); + copy_v_offset = prep_hdr_size; + + ret = merge_from_parent(edstream, parent_v_offset, + parent_end_v_offset, + copy_v_offset); + if (ret < 0) + return ret; + append_v_offset = (v_offset + + (stream->pos - stream->skip)); + i_assert(append_v_offset <= hdr_size - 1); + + if (append_v_offset == hdr_size - 1) { + /* Strip final CR too when it is present */ + if (stream->buffer != NULL && + stream->buffer[stream->pos-1] == '\r') { + stream->pos--; + append_v_offset--; + ret--; + } + + i_assert(ret >= 0); + edstream->cur_header = + edmail->header_fields_appended; + edstream->cur_header_v_offset = append_v_offset; + if (!edstream->parent_buffer) + edstream->header_read = TRUE; + } + + if (ret != 0) + return ret; + } else { + edstream->header_read = TRUE; + } + + /* Merge appended headers */ + ret = merge_modified_headers(edstream); + if (ret != 0) + return ret; + } + + /* Header does not come from original mail at all */ + if (edmail->headers_parsed) { + parent_v_offset = (stream->parent_start_offset + + edmail->wrapped_hdr_size.physical_size - + (edmail->eoh_crlf ? 2 : 1)); + copy_v_offset = edmail->hdr_size.physical_size; + /* Header comes partially from original mail and headers are added + between header and body. */ + } else if (edmail->header_fields_appended != NULL) { + parent_v_offset = (stream->parent_start_offset + + edmail->wrapped_hdr_size.physical_size - + (edmail->eoh_crlf ? 2 : 1)); + copy_v_offset = (edmail->hdr_size.physical_size + + edmail->wrapped_hdr_size.physical_size - + (edmail->eoh_crlf ? 2 : 1)); + /* Header comes partially from original mail, but headers are only + prepended. */ + } else { + parent_v_offset = stream->parent_start_offset; + copy_v_offset = edmail->hdr_size.physical_size; + } + + return merge_from_parent(edstream, parent_v_offset, (uoff_t)-1, + copy_v_offset); +} + +static void +stream_reset_to(struct edit_mail_istream *edstream, uoff_t v_offset) +{ + edstream->istream.istream.v_offset = v_offset; + edstream->istream.skip = 0; + edstream->istream.pos = 0; + edstream->istream.buffer = NULL; + edstream->parent_buffer = FALSE; + edstream->eof = FALSE; + i_stream_seek(edstream->istream.parent, 0); +} + +static void +edit_mail_istream_seek(struct istream_private *stream, uoff_t v_offset, + bool mark ATTR_UNUSED) +{ + struct edit_mail_istream *edstream = + (struct edit_mail_istream *)stream; + struct _header_field_index *cur_header; + struct edit_mail *edmail = edstream->mail; + uoff_t offset; + + edstream->header_read = FALSE; + edstream->cur_header = NULL; + edstream->cur_header_v_offset = 0; + + /* The beginning */ + if (v_offset == 0) { + stream_reset_to(edstream, 0); + + if (edmail->header_fields_head != + edmail->header_fields_appended) + edstream->cur_header = edmail->header_fields_head; + return; + } + + /* Inside (prepended) headers */ + if (edmail->headers_parsed) { + offset = edmail->hdr_size.physical_size; + } else { + offset = (edmail->hdr_size.physical_size - + edmail->appended_hdr_size.physical_size); + } + + if (v_offset < offset) { + stream_reset_to(edstream, v_offset); + + /* Find the header */ + cur_header = edmail->header_fields_head; + i_assert(cur_header != NULL && + cur_header != edmail->header_fields_appended); + edstream->cur_header_v_offset = 0; + offset = cur_header->field->size; + while (v_offset > offset) { + cur_header = cur_header->next; + i_assert(cur_header != NULL && + cur_header != edmail->header_fields_appended); + + edstream->cur_header_v_offset = offset; + offset += cur_header->field->size; + } + + edstream->cur_header = cur_header; + return; + } + + if (!edmail->headers_parsed) { + /* Inside original header */ + offset = (edmail->hdr_size.physical_size - + edmail->appended_hdr_size.physical_size + + edmail->wrapped_hdr_size.physical_size); + if (v_offset < offset) { + stream_reset_to(edstream, v_offset); + return; + } + + edstream->header_read = TRUE; + + /* Inside appended header */ + offset = (edmail->hdr_size.physical_size + + edmail->wrapped_hdr_size.physical_size); + if (v_offset < offset) { + stream_reset_to(edstream, v_offset); + + offset -= edmail->appended_hdr_size.physical_size; + + cur_header = edmail->header_fields_appended; + i_assert(cur_header != NULL); + edstream->cur_header_v_offset = offset; + offset += cur_header->field->size; + + while (v_offset > offset) { + cur_header = cur_header->next; + i_assert(cur_header != NULL); + + edstream->cur_header_v_offset = offset; + offset += cur_header->field->size; + } + + edstream->cur_header = cur_header; + return; + } + } + + stream_reset_to(edstream, v_offset); + edstream->cur_header = NULL; +} + +static void ATTR_NORETURN +edit_mail_istream_sync(struct istream_private *stream ATTR_UNUSED) +{ + i_panic("edit-mail istream sync() not implemented"); +} + +static int +edit_mail_istream_stat(struct istream_private *stream, bool exact) +{ + struct edit_mail_istream *edstream = + (struct edit_mail_istream *)stream; + struct edit_mail *edmail = edstream->mail; + const struct stat *st; + + /* Stat the original stream */ + if (i_stream_stat(stream->parent, exact, &st) < 0) + return -1; + + stream->statbuf = *st; + if (st->st_size == -1 || !exact) + return 0; + + if (!edmail->headers_parsed) { + if (!edmail->modified) + return 0; + } else { + stream->statbuf.st_size = + (edmail->wrapped_body_size.physical_size + + (edmail->eoh_crlf ? 2 : 1)); + } + + stream->statbuf.st_size += (edmail->hdr_size.physical_size + + edmail->body_size.physical_size); + return 0; +} + +struct istream *edit_mail_istream_create(struct edit_mail *edmail) +{ + struct edit_mail_istream *edstream; + struct istream *wrapped = edmail->wrapped_stream; + + edstream = i_new(struct edit_mail_istream, 1); + edstream->pool = pool_alloconly_create(MEMPOOL_GROWING + "edit mail stream", 4096); + edstream->mail = edmail; + + edstream->istream.max_buffer_size = + wrapped->real_stream->max_buffer_size; + + edstream->istream.iostream.destroy = edit_mail_istream_destroy; + edstream->istream.read = edit_mail_istream_read; + edstream->istream.seek = edit_mail_istream_seek; + edstream->istream.sync = edit_mail_istream_sync; + edstream->istream.stat = edit_mail_istream_stat; + + edstream->istream.istream.readable_fd = FALSE; + edstream->istream.istream.blocking = wrapped->blocking; + edstream->istream.istream.seekable = wrapped->seekable; + + if (edmail->header_fields_head != edmail->header_fields_appended) + edstream->cur_header = edmail->header_fields_head; + + i_stream_seek(wrapped, 0); + + return i_stream_create(&edstream->istream, wrapped, -1, 0); +} diff --git a/pigeonhole/src/lib-sieve/util/edit-mail.h b/pigeonhole/src/lib-sieve/util/edit-mail.h new file mode 100644 index 0000000..14d2eaa --- /dev/null +++ b/pigeonhole/src/lib-sieve/util/edit-mail.h @@ -0,0 +1,47 @@ +#ifndef EDIT_MAIL_H +#define EDIT_MAIL_H + +struct edit_mail; + +struct edit_mail *edit_mail_wrap(struct mail *mail); +void edit_mail_unwrap(struct edit_mail **edmail); +struct edit_mail *edit_mail_snapshot(struct edit_mail *edmail); + +void edit_mail_reset(struct edit_mail *edmail); + +struct mail *edit_mail_get_mail(struct edit_mail *edmail); + +/* + * Header modification + */ + +/* Simple API */ + +void edit_mail_header_add(struct edit_mail *edmail, const char *field_name, + const char *value, bool last); +int edit_mail_header_delete(struct edit_mail *edmail, + const char *field_name, int index); +int edit_mail_header_replace(struct edit_mail *edmail, + const char *field_name, int index, + const char *newname, const char *newvalue); + +/* Iterator */ + +struct edit_mail_header_iter; + +int edit_mail_headers_iterate_init(struct edit_mail *edmail, + const char *field_name, bool reverse, + struct edit_mail_header_iter **edhiter_r); +void edit_mail_headers_iterate_deinit(struct edit_mail_header_iter **edhiter); + +void edit_mail_headers_iterate_get(struct edit_mail_header_iter *edhiter, + const char **value_r); + +bool edit_mail_headers_iterate_next(struct edit_mail_header_iter *edhiter); + +bool edit_mail_headers_iterate_remove(struct edit_mail_header_iter *edhiter); +bool edit_mail_headers_iterate_replace(struct edit_mail_header_iter *edhiter, + const char *newname, + const char *newvalue); + +#endif diff --git a/pigeonhole/src/lib-sieve/util/mail-raw.c b/pigeonhole/src/lib-sieve/util/mail-raw.c new file mode 100644 index 0000000..b357fe1 --- /dev/null +++ b/pigeonhole/src/lib-sieve/util/mail-raw.c @@ -0,0 +1,247 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "istream.h" +#include "istream-seekable.h" +#include "str.h" +#include "str-sanitize.h" +#include "strescape.h" +#include "safe-mkstemp.h" +#include "path-util.h" +#include "message-address.h" +#include "mbox-from.h" +#include "raw-storage.h" +#include "mail-namespace.h" +#include "master-service.h" +#include "master-service-settings.h" +#include "settings-parser.h" +#include "mail-raw.h" + +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <pwd.h> + +/* + * Configuration + */ + +#define DEFAULT_ENVELOPE_SENDER "MAILER-DAEMON" + +/* After buffer grows larger than this, create a temporary file to /tmp + where to read the mail. */ +#define MAIL_MAX_MEMORY_BUFFER (1024*128) + +static const char *wanted_headers[] = { + "From", "Message-ID", "Subject", "Return-Path", + NULL +}; + +/* + * Global data + */ + +struct mail_raw_user { + struct mail_namespace *ns; + struct mail_user *mail_user; +}; + +/* + * Raw mail implementation + */ + +static int seekable_fd_callback +(const char **path_r, void *context) +{ + struct mail_user *ruser = (struct mail_user *)context; + string_t *path; + int fd; + + path = t_str_new(128); + mail_user_set_get_temp_prefix(path, ruser->set); + fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1); + if (fd == -1) { + i_error("safe_mkstemp(%s) failed: %m", str_c(path)); + return -1; + } + + /* we just want the fd, unlink it */ + if (i_unlink(str_c(path)) < 0) { + /* shouldn't happen.. */ + i_close_fd(&fd); + return -1; + } + + *path_r = str_c(path); + return fd; +} + +static struct istream *mail_raw_create_stream +(struct mail_user *ruser, int fd, time_t *mtime_r, const char **sender) +{ + struct istream *input, *input2, *input_list[2]; + const unsigned char *data; + size_t i, size; + int ret, tz; + char *env_sender = NULL; + + *mtime_r = (time_t)-1; + fd_set_nonblock(fd, FALSE); + + input = i_stream_create_fd(fd, 4096); + input->blocking = TRUE; + /* If input begins with a From-line, drop it */ + ret = i_stream_read_bytes(input, &data, &size, 5); + if (ret > 0 && memcmp(data, "From ", 5) == 0) { + /* skip until the first LF */ + i_stream_skip(input, 5); + while ( i_stream_read_more(input, &data, &size) > 0 ) { + for (i = 0; i < size; i++) { + if (data[i] == '\n') + break; + } + if (i != size) { + (void)mbox_from_parse(data, i, mtime_r, &tz, &env_sender); + i_stream_skip(input, i + 1); + break; + } + i_stream_skip(input, size); + } + } + + if (env_sender != NULL && sender != NULL) { + *sender = t_strdup(env_sender); + } + i_free(env_sender); + + if (input->v_offset == 0) { + input2 = input; + i_stream_ref(input2); + } else { + input2 = i_stream_create_limit(input, (uoff_t)-1); + } + i_stream_unref(&input); + + input_list[0] = input2; input_list[1] = NULL; + input = i_stream_create_seekable(input_list, MAIL_MAX_MEMORY_BUFFER, + seekable_fd_callback, (void*)ruser); + i_stream_unref(&input2); + return input; +} + +/* + * Init/Deinit + */ + +struct mail_user *mail_raw_user_create +(struct master_service *service, struct mail_user *mail_user) +{ + void **sets = master_service_settings_get_others(service); + + return raw_storage_create_from_set(mail_user->set_info, sets[0]); +} + +/* + * Open raw mail data + */ + +static struct mail_raw *mail_raw_create +(struct mail_user *ruser, struct istream *input, + const char *mailfile, const char *sender, time_t mtime) +{ + struct mail_raw *mailr; + struct mailbox_header_lookup_ctx *headers_ctx; + const char *envelope_sender, *error; + int ret; + + if ( mailfile != NULL && *mailfile != '/' ) + if (t_abspath(mailfile, &mailfile, &error) < 0) + i_fatal("t_abspath(%s) failed: %s", + mailfile, error); + + mailr = i_new(struct mail_raw, 1); + + envelope_sender = sender != NULL ? sender : DEFAULT_ENVELOPE_SENDER; + if ( mailfile == NULL ) { + ret = raw_mailbox_alloc_stream(ruser, input, mtime, + envelope_sender, &mailr->box); + } else { + ret = raw_mailbox_alloc_path(ruser, mailfile, (time_t)-1, + envelope_sender, &mailr->box); + } + + if ( ret < 0 ) { + if ( mailfile == NULL ) { + i_fatal("Can't open delivery mail as raw: %s", + mailbox_get_last_internal_error(mailr->box, NULL)); + } else { + i_fatal("Can't open delivery mail as raw (file=%s): %s", + mailfile, mailbox_get_last_internal_error(mailr->box, NULL)); + } + } + + mailr->trans = mailbox_transaction_begin(mailr->box, 0, __func__); + headers_ctx = mailbox_header_lookup_init(mailr->box, wanted_headers); + mailr->mail = mail_alloc(mailr->trans, 0, headers_ctx); + mailbox_header_lookup_unref(&headers_ctx); + mail_set_seq(mailr->mail, 1); + + return mailr; +} + +struct mail_raw *mail_raw_open_stream +(struct mail_user *ruser, struct istream *input) +{ + struct mail_raw *mailr; + + i_assert(input->seekable); + i_stream_set_name(input, "data"); + mailr = mail_raw_create(ruser, input, NULL, NULL, (time_t)-1); + + return mailr; +} + +struct mail_raw *mail_raw_open_data +(struct mail_user *ruser, string_t *mail_data) +{ + struct mail_raw *mailr; + struct istream *input; + + input = i_stream_create_from_data(str_data(mail_data), str_len(mail_data)); + + mailr = mail_raw_open_stream(ruser, input); + + i_stream_unref(&input); + return mailr; +} + +struct mail_raw *mail_raw_open_file +(struct mail_user *ruser, const char *path) +{ + struct mail_raw *mailr; + struct istream *input = NULL; + time_t mtime = (time_t)-1; + const char *sender = NULL; + + if ( path == NULL || strcmp(path, "-") == 0 ) { + path = NULL; + input = mail_raw_create_stream(ruser, 0, &mtime, &sender); + } + + mailr = mail_raw_create(ruser, input, path, sender, mtime); + i_stream_unref(&input); + + return mailr; +} + +void mail_raw_close(struct mail_raw **mailr) +{ + mail_free(&(*mailr)->mail); + mailbox_transaction_rollback(&(*mailr)->trans); + mailbox_free(&(*mailr)->box); + + i_free(*mailr); + *mailr = NULL; +} + diff --git a/pigeonhole/src/lib-sieve/util/mail-raw.h b/pigeonhole/src/lib-sieve/util/mail-raw.h new file mode 100644 index 0000000..a942d06 --- /dev/null +++ b/pigeonhole/src/lib-sieve/util/mail-raw.h @@ -0,0 +1,27 @@ +#ifndef MAIL_RAW_H +#define MAIL_RAW_H + +#include "lib.h" +#include "master-service.h" + +struct mail_raw { + pool_t pool; + struct mail *mail; + + struct mailbox *box; + struct mailbox_transaction_context *trans; +}; + +struct mail_user *mail_raw_user_create + (struct master_service *service, struct mail_user *mail_user); + +struct mail_raw *mail_raw_open_stream + (struct mail_user *ruser, struct istream *input); +struct mail_raw *mail_raw_open_file + (struct mail_user *ruser, const char *path); +struct mail_raw *mail_raw_open_data + (struct mail_user *ruser, string_t *mail_data); +void mail_raw_close(struct mail_raw **mailr); + + +#endif diff --git a/pigeonhole/src/lib-sieve/util/rfc2822.c b/pigeonhole/src/lib-sieve/util/rfc2822.c new file mode 100644 index 0000000..ff3a9ad --- /dev/null +++ b/pigeonhole/src/lib-sieve/util/rfc2822.c @@ -0,0 +1,277 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* NOTE: much of the functionality implemented here should eventually appear + * somewhere in Dovecot itself. + */ + +#include "lib.h" +#include "str.h" +#include "unichar.h" + +#include "rfc2822.h" + +#include "message-header-encode.h" + +#include <stdio.h> +#include <ctype.h> + +bool rfc2822_header_field_name_verify +(const char *field_name, unsigned int len) +{ + const char *p = field_name; + const char *pend = p + len; + + /* field-name = 1*ftext + * ftext = %d33-57 / ; Any character except + * %d59-126 ; controls, SP, and + * ; ":". + */ + + while ( p < pend ) { + if ( *p < 33 || *p == ':' ) + return FALSE; + + p++; + } + + return TRUE; +} + +bool rfc2822_header_field_body_verify +(const char *field_body, unsigned int len, bool allow_crlf, bool allow_utf8) +{ + const unsigned char *p = (const unsigned char *)field_body; + const unsigned char *pend = p + len; + bool is8bit = FALSE; + + /* RFC5322: + * + * unstructured = (*([FWS] VCHAR) *WSP) + * VCHAR = %x21-7E + * FWS = ([*WSP CRLF] 1*WSP) / ; Folding white space + * WSP = SP / HTAB ; White space + */ + + while ( p < pend ) { + if ( *p < 0x20 ) { + if ( (*p == '\r' || *p == '\n') ) { + if ( !allow_crlf ) + return FALSE; + } else if ( *p != '\t' ) { + return FALSE; + } + } + + if ( !is8bit && *p > 127 ) { + if ( !allow_utf8 ) + return FALSE; + + is8bit = TRUE; + } + + p++; + } + + if ( is8bit && !uni_utf8_str_is_valid(field_body) ) { + return FALSE; + } + + return TRUE; +} + +/* + * + */ + +const char *rfc2822_header_field_name_sanitize(const char *name) +{ + char *result = t_strdup_noconst(name); + char *p; + + /* Make the whole name lower case ... */ + result = str_lcase(result); + + /* ... except for the first letter and those that follow '-' */ + p = result; + *p = i_toupper(*p); + while ( *p != '\0' ) { + if ( *p == '-' ) { + p++; + + if ( *p != '\0' ) + *p = i_toupper(*p); + + continue; + } + + p++; + } + + return result; +} + +/* + * Message construction + */ + +/* FIXME: This should be collected into a Dovecot API for composing internet + * mail messages. + */ + +unsigned int rfc2822_header_append +(string_t *header, const char *name, const char *body, bool crlf, + uoff_t *body_offset_r) +{ + static const unsigned int max_line = 80; + + const char *bp = body; /* Pointer */ + const char *sp = body; /* Start pointer */ + const char *wp = NULL; /* Whitespace pointer */ + const char *nlp = NULL; /* New-line pointer */ + unsigned int line_len = strlen(name); + unsigned int lines = 0; + + /* Write header field name first */ + str_append(header, name); + str_append(header, ": "); + + if ( body_offset_r != NULL ) + *body_offset_r = str_len(header); + + line_len += 2; + + /* Add field body; fold it if necessary and account for existing folding */ + while ( *bp != '\0' ) { + bool ws_first = TRUE; + + while ( *bp != '\0' && nlp == NULL && + (wp == NULL || line_len < max_line) ) { + if ( *bp == ' ' || *bp == '\t' ) { + if (ws_first) + wp = bp; + ws_first = FALSE; + } else if ( *bp == '\r' || *bp == '\n' ) { + if (ws_first) + nlp = bp; + else + nlp = wp; + break; + } else { + ws_first = TRUE; + } + + bp++; line_len++; + } + + if ( *bp == '\0' ) break; + + /* Existing newline ? */ + if ( nlp != NULL ) { + /* Replace any consecutive newline and whitespace for + consistency */ + while ( *bp == ' ' || *bp == '\t' || *bp == '\r' || *bp == '\n' ) + bp++; + + str_append_data(header, sp, nlp-sp); + + if ( crlf ) + str_append(header, "\r\n"); + else + str_append(header, "\n"); + + while ( *bp == ' ' || *bp == '\t' ) + bp++; + if ( *bp != '\0' ) { + /* Continued line; replace leading whitespace with single TAB */ + str_append_c(header, '\t'); + } + + sp = bp; + } else { + /* Insert newline at last whitespace within the max_line limit */ + i_assert(wp >= sp); + str_append_data(header, sp, wp-sp); + + /* Force continued line; drop any existing whitespace */ + while ( *wp == ' ' || *wp == '\t' ) + wp++; + + if ( crlf ) + str_append(header, "\r\n"); + else + str_append(header, "\n"); + + /* Insert single TAB instead of the original whitespace */ + str_append_c(header, '\t'); + + sp = wp; + if (sp > bp) + bp = sp; + } + + lines++; + + line_len = bp - sp; + wp = NULL; + nlp = NULL; + } + + if ( bp != sp || lines == 0 ) { + str_append_data(header, sp, bp-sp); + if ( crlf ) + str_append(header, "\r\n"); + else + str_append(header, "\n"); + lines++; + } + + return lines; +} + +void rfc2822_header_printf +(string_t *header, const char *name, const char *fmt, ...) +{ + const char *body; + va_list args; + + va_start(args, fmt); + body = t_strdup_vprintf(fmt, args); + va_end(args); + + rfc2822_header_write(header, name, body); +} + +void rfc2822_header_utf8_printf +(string_t *header, const char *name, const char *fmt, ...) +{ + string_t *body = t_str_new(256); + va_list args; + + va_start(args, fmt); + message_header_encode(t_strdup_vprintf(fmt, args), body); + va_end(args); + + rfc2822_header_write(header, name, str_c(body)); +} + + +void rfc2822_header_write_address(string_t *header, + const char *name, const char *address) +{ + bool has_8bit = FALSE; + const char *p; + + for (p = address; *p != '\0'; p++) { + if ((*p & 0x80) != 0) + has_8bit = TRUE; + } + + if (!has_8bit) { + rfc2822_header_write(header, name, address); + } else { + string_t *body = t_str_new(256); + message_header_encode(address, body); + rfc2822_header_write(header, name, str_c(body)); + } +} diff --git a/pigeonhole/src/lib-sieve/util/rfc2822.h b/pigeonhole/src/lib-sieve/util/rfc2822.h new file mode 100644 index 0000000..02266a9 --- /dev/null +++ b/pigeonhole/src/lib-sieve/util/rfc2822.h @@ -0,0 +1,46 @@ +#ifndef RFC2822_H +#define RFC2822_H + +#include "lib.h" + +#include <stdio.h> + +/* + * Verification + */ + +bool rfc2822_header_field_name_verify + (const char *field_name, unsigned int len); +bool rfc2822_header_field_body_verify + (const char *field_body, unsigned int len, bool allow_crlf, bool allow_utf8); + +/* + * + */ + +const char *rfc2822_header_field_name_sanitize(const char *name); + +/* + * Message composition + */ + +unsigned int rfc2822_header_append + (string_t *header, const char *name, const char *body, bool crlf, + uoff_t *body_offset_r); + +static inline void rfc2822_header_write +(string_t *header, const char *name, const char *body) +{ + (void)rfc2822_header_append(header, name, body, TRUE, NULL); +} + +void rfc2822_header_printf + (string_t *header, const char *name, const char *fmt, ...) ATTR_FORMAT(3, 4); +void rfc2822_header_utf8_printf + (string_t *header, const char *name, const char *fmt, ...) ATTR_FORMAT(3, 4); + +void rfc2822_header_write_address(string_t *header, + const char *name, const char *address); + + +#endif diff --git a/pigeonhole/src/lib-sieve/util/test-edit-mail.c b/pigeonhole/src/lib-sieve/util/test-edit-mail.c new file mode 100644 index 0000000..0e263a2 --- /dev/null +++ b/pigeonhole/src/lib-sieve/util/test-edit-mail.c @@ -0,0 +1,842 @@ +/* Copyright (c) 2018 Pigeonhole authors, see the included COPYING file */ + +#include "lib.h" +#include "test-common.h" +#include "path-util.h" +#include "buffer.h" +#include "str.h" +#include "istream.h" +#include "istream-concat.h" +#include "istream-crlf.h" +#include "unlink-directory.h" +#include "master-service.h" +#include "istream-header-filter.h" +#include "mail-storage.h" +#include "mail-storage-service.h" +#include "mail-user.h" + +#include "mail-raw.h" +#include "edit-mail.h" + +#include <time.h> + +static pool_t test_pool; + +static struct mail_storage_service_ctx *mail_storage_service = NULL; +static struct mail_user *test_mail_user = NULL; +static struct mail_storage_service_user *test_service_user = NULL; +static const char *mail_home; +static char *test_dir; + +static struct mail_user *test_raw_mail_user = NULL; + +static void str_append_no_cr(string_t *str, const char *cstr) +{ + const char *p, *poff; + + poff = p = cstr; + while (*p != '\0') { + if (*p == '\r') { + str_append_data(str, poff, (p - poff)); + poff = p+1; + } + p++; + } + str_append_data(str, poff, (p - poff)); +} + +static int test_init_mail_user(void) +{ + const char *error; + + mail_home = p_strdup_printf(test_pool, "%s/test_user.%ld.%ld", + test_dir, (long)time(NULL), (long)getpid()); + + struct mail_storage_service_input input = { + .userdb_fields = (const char*const[]){ + t_strdup_printf("mail=maildir:~/"), + t_strdup_printf("home=%s", mail_home), + NULL + }, + .username = "test@example.com", + .no_userdb_lookup = TRUE, + .debug = TRUE, + }; + + mail_storage_service = mail_storage_service_init( + master_service, NULL, + (MAIL_STORAGE_SERVICE_FLAG_NO_RESTRICT_ACCESS | + MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT | + MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS)); + + if (mail_storage_service_lookup(mail_storage_service, &input, + &test_service_user, &error) < 0) + { + i_error("Cannot lookup test user: %s", error); + return -1; + } + + if (mail_storage_service_next(mail_storage_service, test_service_user, + &test_mail_user, &error) < 0) + { + i_error("Cannot lookup test user: %s", error); + return -1; + } + + return 0; +} + +static void test_deinit_mail_user() +{ + const char *error; + mail_user_unref(&test_mail_user); + mail_storage_service_user_unref(&test_service_user); + mail_storage_service_deinit(&mail_storage_service); + if (unlink_directory(mail_home, UNLINK_DIRECTORY_FLAG_RMDIR, + &error) < 0) + i_error("unlink_directory(%s) failed: %s", mail_home, error); +} + +static void test_init(void) +{ + test_pool = pool_alloconly_create(MEMPOOL_GROWING"test pool", 128); + + test_init_mail_user(); + test_raw_mail_user = + mail_raw_user_create(master_service, test_mail_user); +} + +static void test_deinit(void) +{ + mail_user_unref(&test_raw_mail_user); + test_deinit_mail_user(); + pool_unref(&test_pool); +} + +static void test_stream_data(struct istream *input, buffer_t *buffer) +{ + const unsigned char *data; + size_t size; + + while (i_stream_read_more(input, &data, &size) > 0) { + buffer_append(buffer, data, size); + i_stream_skip(input, size); + } + + test_assert(!i_stream_have_bytes_left(input)); + test_assert(input->stream_errno == 0); +} + +static void test_stream_data_slow(struct istream *input, buffer_t *buffer) +{ + const unsigned char *data; + size_t size; + int ret; + + ret = i_stream_read(input); + while (ret > 0 || i_stream_have_bytes_left(input) || ret == -2) { + data = i_stream_get_data(input, &size); + buffer_append(buffer, data, 1); + i_stream_skip(input, 1); + + ret = i_stream_read(input); + } + + test_assert(!i_stream_have_bytes_left(input)); + test_assert(input->stream_errno == 0); +} + +static void test_edit_mail_concatenated(void) +{ + static const char *hide_headers[] = + { "Return-Path", "X-Sieve", "X-Sieve-Redirected-From" }; + static const char *msg_part1 = + "Received: from example.com ([127.0.0.1] helo=example.com)\r\n" + " by example.org with LMTP (Dovecot)\r\n" + " (envelope-from <frop-bounces@example.com>)\r\n" + " id 1er3e8-0015df-QO\r\n" + " for timo@example.org;\r\n" + " Sat, 03 Mar 2018 10:40:05 +0100\r\n"; + static const char *msg_part2 = + "Return-Path: <stephan@example.com>\r\n"; + static const char *msg_part3 = + "Delivered-To: <timo@example.org>\r\n"; + static const char *msg_part4 = + "From: <stephan@example.com>\r\n" + "To: <timo@example.org>\r\n" + "Subject: Sieve editheader breaks with LMTP\r\n" + "\r\n" + "Hi,\r\n" + "\r\n" + "Sieve editheader seems to be broken when used from LMTP\r\n" + "\r\n" + "Regards,\r\n" + "\r\n" + "Stephan.\r\n"; + static const char *msg_added = + "X-Filter-Junk-Type: NONE\r\n" + "X-Filter-Junk-Flag: NO\r\n"; + struct istream *inputs[5], *input_msg, *input_filt, *input_mail, *input; + buffer_t *buffer; + struct mail_raw *rawmail; + struct edit_mail *edmail; + struct mail *mail; + string_t *expected; + const char *value; + + test_begin("edit-mail - concatenated"); + test_init(); + + /* Compose the message */ + + inputs[0] = i_stream_create_from_data(msg_part1, strlen(msg_part1)); + inputs[1] = i_stream_create_from_data(msg_part2, strlen(msg_part2)); + inputs[2] = i_stream_create_from_data(msg_part3, strlen(msg_part3)); + inputs[3] = i_stream_create_from_data(msg_part4, strlen(msg_part4)); + inputs[4] = NULL; + + input_msg = i_stream_create_concat(inputs); + + i_stream_unref(&inputs[0]); + i_stream_unref(&inputs[1]); + i_stream_unref(&inputs[2]); + i_stream_unref(&inputs[3]); + + rawmail = mail_raw_open_stream(test_raw_mail_user, input_msg); + + /* Add headers */ + + edmail = edit_mail_wrap(rawmail->mail); + + edit_mail_header_add(edmail, "X-Filter-Junk-Flag", "NO", FALSE); + edit_mail_header_add(edmail, "X-Filter-Junk-Type", "NONE", FALSE); + + mail = edit_mail_get_mail(edmail); + + /* Evaluate modified header */ + + test_assert(mail_get_first_header_utf8(mail, "Subject", &value) > 0); + test_assert(strcmp(value, "Sieve editheader breaks with LMTP") == 0); + + test_assert(mail_get_first_header_utf8(mail, "X-Filter-Junk-Flag", + &value) > 0); + test_assert(strcmp(value, "NO") == 0); + test_assert(mail_get_first_header_utf8(mail, "X-Filter-Junk-Type", + &value) > 0); + test_assert(strcmp(value, "NONE") == 0); + + test_assert(mail_get_first_header_utf8(mail, "Delivered-To", + &value) > 0); + + /* Prepare tests */ + + if (mail_get_stream(mail, NULL, NULL, &input_mail) < 0) { + i_fatal("Failed to open mail stream: %s", + mailbox_get_last_internal_error(mail->box, NULL)); + } + + buffer = buffer_create_dynamic(default_pool, 1024); + expected = t_str_new(1024); + + /* Added */ + + i_stream_seek(input_mail, 0); + buffer_set_used_size(buffer, 0); + input = input_mail; + + test_stream_data(input_mail, buffer); + + str_truncate(expected, 0); + str_append(expected, msg_added); + str_append(expected, msg_part1); + str_append(expected, msg_part2); + str_append(expected, msg_part3); + str_append(expected, msg_part4); + + test_out("added", strcmp(str_c(buffer), str_c(expected)) == 0); + + /* Added, slow */ + + i_stream_seek(input_mail, 0); + buffer_set_used_size(buffer, 0); + + test_stream_data_slow(input_mail, buffer); + + str_truncate(expected, 0); + str_append(expected, msg_added); + str_append(expected, msg_part1); + str_append(expected, msg_part2); + str_append(expected, msg_part3); + str_append(expected, msg_part4); + + test_out("added, slow", strcmp(str_c(buffer), str_c(expected)) == 0); + + /* Added, filtered */ + + i_stream_seek(input_mail, 0); + buffer_set_used_size(buffer, 0); + + input_filt = i_stream_create_header_filter( + input_mail, (HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR), + hide_headers, N_ELEMENTS(hide_headers), + *null_header_filter_callback, (void *)NULL); + input = i_stream_create_lf(input_filt); + i_stream_unref(&input_filt); + + test_stream_data(input, buffer); + test_assert(!i_stream_have_bytes_left(input_mail)); + test_assert(input_mail->stream_errno == 0); + + str_truncate(expected, 0); + str_append_no_cr(expected, msg_added); + str_append_no_cr(expected, msg_part1); + str_append_no_cr(expected, msg_part3); + str_append_no_cr(expected, msg_part4); + + test_out("added, filtered", + strcmp(str_c(buffer), str_c(expected)) == 0); + + i_stream_unref(&input); + + /* Added, filtered, slow */ + + i_stream_seek(input_mail, 0); + buffer_set_used_size(buffer, 0); + + input_filt = i_stream_create_header_filter( + input_mail, (HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR), + hide_headers, N_ELEMENTS(hide_headers), + *null_header_filter_callback, (void *)NULL); + input = i_stream_create_lf(input_filt); + i_stream_unref(&input_filt); + + test_stream_data_slow(input, buffer); + test_assert(!i_stream_have_bytes_left(input_mail)); + test_assert(input_mail->stream_errno == 0); + + str_truncate(expected, 0); + str_append_no_cr(expected, msg_added); + str_append_no_cr(expected, msg_part1); + str_append_no_cr(expected, msg_part3); + str_append_no_cr(expected, msg_part4); + + test_out("added, filtered, slow", + strcmp(str_c(buffer), str_c(expected)) == 0); + + i_stream_unref(&input); + + /* Delete header */ + + edit_mail_header_delete(edmail, "Delivered-To", 0); + + /* Evaluate modified header */ + + test_assert(mail_get_first_header_utf8(mail, "Subject", &value) > 0); + test_assert(strcmp(value, "Sieve editheader breaks with LMTP") == 0); + + test_assert(mail_get_first_header_utf8(mail, "X-Filter-Junk-Flag", + &value) > 0); + test_assert(strcmp(value, "NO") == 0); + test_assert(mail_get_first_header_utf8(mail, "X-Filter-Junk-Type", + &value) > 0); + test_assert(strcmp(value, "NONE") == 0); + + test_assert(mail_get_first_header_utf8(mail, "Delivered-To", + &value) == 0); + + /* Deleted */ + + i_stream_seek(input_mail, 0); + buffer_set_used_size(buffer, 0); + input = input_mail; + + test_stream_data(input_mail, buffer); + + str_truncate(expected, 0); + str_append(expected, msg_added); + str_append(expected, msg_part1); + str_append(expected, msg_part2); + str_append(expected, msg_part4); + + test_out("deleted", strcmp(str_c(buffer), str_c(expected)) == 0); + + /* Deleted, slow */ + + i_stream_seek(input_mail, 0); + buffer_set_used_size(buffer, 0); + + test_stream_data_slow(input_mail, buffer); + + str_truncate(expected, 0); + str_append(expected, msg_added); + str_append(expected, msg_part1); + str_append(expected, msg_part2); + str_append(expected, msg_part4); + + test_out("deleted, slow", strcmp(str_c(buffer), str_c(expected)) == 0); + + /* Deleted, filtered */ + + i_stream_seek(input_mail, 0); + buffer_set_used_size(buffer, 0); + + input_filt = i_stream_create_header_filter( + input_mail, (HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR), + hide_headers, N_ELEMENTS(hide_headers), + *null_header_filter_callback, (void *)NULL); + input = i_stream_create_lf(input_filt); + i_stream_unref(&input_filt); + + test_stream_data(input, buffer); + test_assert(!i_stream_have_bytes_left(input_mail)); + test_assert(input_mail->stream_errno == 0); + + str_truncate(expected, 0); + str_append_no_cr(expected, msg_added); + str_append_no_cr(expected, msg_part1); + str_append_no_cr(expected, msg_part4); + + test_out("deleted, filtered", + strcmp(str_c(buffer), str_c(expected)) == 0); + + i_stream_unref(&input); + + /* Deleted, filtered, slow */ + + i_stream_seek(input_mail, 0); + buffer_set_used_size(buffer, 0); + + input_filt = i_stream_create_header_filter(input_mail, + HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR, hide_headers, + N_ELEMENTS(hide_headers), *null_header_filter_callback, + (void *)NULL); + input = i_stream_create_lf(input_filt); + i_stream_unref(&input_filt); + + test_stream_data_slow(input, buffer); + test_assert(!i_stream_have_bytes_left(input_mail)); + test_assert(input_mail->stream_errno == 0); + + str_truncate(expected, 0); + str_append_no_cr(expected, msg_added); + str_append_no_cr(expected, msg_part1); + str_append_no_cr(expected, msg_part4); + + test_out("deleted, filtered, slow", + strcmp(str_c(buffer), str_c(expected)) == 0); + + i_stream_unref(&input); + + /* clean up */ + + buffer_free(&buffer); + edit_mail_unwrap(&edmail); + mail_raw_close(&rawmail); + i_stream_unref(&input_msg); + test_deinit(); + test_end(); +} + +static const char *big_header = + "X-A: AAAA\n" + "X-Big-One: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " AAAAAAAAAAAAAAAAAAAAAAAAA\n" + "X-B: BBBB\n" + "\n" + "Frop!\n"; + +static void test_edit_mail_big_header(void) +{ + struct istream *input_msg, *input_mail; + buffer_t *buffer; + struct mail_raw *rawmail; + struct edit_mail *edmail; + struct mail *mail; + const char *value; + + test_begin("edit-mail - big header"); + test_init(); + + /* compose the message */ + + input_msg = i_stream_create_from_data(big_header, strlen(big_header)); + + rawmail = mail_raw_open_stream(test_raw_mail_user, input_msg); + + edmail = edit_mail_wrap(rawmail->mail); + + /* delete header */ + + edit_mail_header_delete(edmail, "X-B", 0); + mail = edit_mail_get_mail(edmail); + + /* prepare tests */ + + if (mail_get_stream(mail, NULL, NULL, &input_mail) < 0) { + i_fatal("Failed to open mail stream: %s", + mailbox_get_last_internal_error(mail->box, NULL)); + } + + buffer = buffer_create_dynamic(default_pool, 1024); + + /* evaluate modified header */ + + test_assert(mail_get_first_header_utf8(mail, "X-B", &value) == 0); + + /* deleted */ + + i_stream_seek(input_mail, 0); + buffer_set_used_size(buffer, 0); + + test_stream_data(input_mail, buffer); + + /* clean up */ + + buffer_free(&buffer); + edit_mail_unwrap(&edmail); + mail_raw_close(&rawmail); + i_stream_unref(&input_msg); + test_deinit(); + test_end(); +} + +static void test_edit_mail_small_buffer(void) +{ + static const char *message = + "X-A: AAAA\n" + "X-B: BBBB\n" + "\n" + "Frop!\n"; + struct istream *input_msg, *input_mail; + buffer_t *buffer; + struct mail_raw *rawmail; + struct edit_mail *edmail; + struct mail *mail; + const char *value; + unsigned int i; + + test_begin("edit-mail - small buffer"); + test_init(); + + /* compose the message */ + + input_msg = i_stream_create_from_data(message, strlen(message)); + i_stream_set_max_buffer_size(input_msg, 16); + + rawmail = mail_raw_open_stream(test_raw_mail_user, input_msg); + + edmail = edit_mail_wrap(rawmail->mail); + + /* add headers */ + + for (i = 0; i < 16; i++) { + edit_mail_header_add(edmail, "X-F", "FF", FALSE); + edit_mail_header_add(edmail, "X-L", "LL", TRUE); + } + + mail = edit_mail_get_mail(edmail); + + /* prepare tests */ + + if (mail_get_stream(mail, NULL, NULL, &input_mail) < 0) { + i_fatal("Failed to open mail stream: %s", + mailbox_get_last_internal_error(mail->box, NULL)); + } + + buffer = buffer_create_dynamic(default_pool, 1024); + + /* evaluate modified header */ + + test_assert(mail_get_first_header_utf8(mail, "X-F", &value) > 0); + test_assert(mail_get_first_header_utf8(mail, "X-A", &value) > 0); + test_assert(mail_get_first_header_utf8(mail, "X-B", &value) > 0); + test_assert(mail_get_first_header_utf8(mail, "X-L", &value) > 0); + + /* check stream read */ + + i_stream_seek(input_mail, 0); + buffer_set_used_size(buffer, 0); + + test_stream_data(input_mail, buffer); + + /* clean up */ + + buffer_free(&buffer); + edit_mail_unwrap(&edmail); + mail_raw_close(&rawmail); + i_stream_unref(&input_msg); + test_deinit(); + test_end(); +} + +int main(int argc, char *argv[]) +{ + static void (*test_functions[])(void) = { + test_edit_mail_concatenated, + test_edit_mail_big_header, + test_edit_mail_small_buffer, + NULL + }; + const enum master_service_flags service_flags = + MASTER_SERVICE_FLAG_STANDALONE | + MASTER_SERVICE_FLAG_DONT_SEND_STATS | + MASTER_SERVICE_FLAG_NO_CONFIG_SETTINGS; + const char *cwd, *error; + int ret; + + master_service = master_service_init("test-edit-header", service_flags, + &argc, &argv, ""); + master_service_init_finish(master_service); + + if (t_get_working_dir(&cwd, &error) < 0) + i_fatal("getcwd() failed: %s", error); + test_dir = i_strdup(cwd); + + ret = test_run(test_functions); + + i_free(test_dir); + master_service_deinit(&master_service); + + return ret; +} + diff --git a/pigeonhole/src/lib-sieve/util/test-rfc2822.c b/pigeonhole/src/lib-sieve/util/test-rfc2822.c new file mode 100644 index 0000000..66e8ee5 --- /dev/null +++ b/pigeonhole/src/lib-sieve/util/test-rfc2822.c @@ -0,0 +1,197 @@ +/* Copyright (c) 2018 Pigeonhole authors, see the included COPYING file */ + +#include "lib.h" +#include "test-common.h" +#include "str.h" + +#include "rfc2822.h" + +struct test_header_write { + const char *name; + const char *body; + const char *output; +}; + +static const struct test_header_write header_write_tests[] = { + { + .name = "Frop", + .body = "Bladiebla", + .output = "Frop: Bladiebla\r\n" + },{ + .name = "Subject", + .body = "This is a very long subject that well exceeds the " + "boundary of 80 characters. It should therefore " + "trigger the header folding algorithm.", + .output = + "Subject: This is a very long subject that well " + "exceeds the boundary of 80\r\n" + "\tcharacters. It should therefore trigger the header " + "folding algorithm.\r\n" + },{ + .name = "Subject", + .body = "This\tis\ta\tvery\tlong\tsubject\tthat\twell\texceeds" + "\tthe\tboundary\tof\t80\tcharacters.\tIt\tshould\t" + "therefore\ttrigger\tthe\theader\tfolding\talgorithm.", + .output = + "Subject: This\tis\ta\tvery\tlong\tsubject\tthat\twell" + "\texceeds\tthe\tboundary\tof\t80\r\n" + "\tcharacters.\tIt\tshould\ttherefore\ttrigger\tthe\t" + "header\tfolding\talgorithm.\r\n" + },{ + .name = "Comment", + .body = "This header already contains newlines.\n" + "The header folding algorithm should respect these.\n" + "It also should convert between CRLF and LF when " + "needed.", + .output = "Comment: This header already contains newlines.\r\n" + "\tThe header folding algorithm should respect " + "these.\r\n" + "\tIt also should convert between CRLF and LF when " + "needed.\r\n" + },{ + .name = "References", + .body = "<messageid1@example.com> <messageid2@example.com> " + "<extremelylonglonglonglonglonglonglonglonglonglong" + "longlongmessageid3@example.com> " + "<messageid4@example.com>", + .output = "References: <messageid1@example.com> " + "<messageid2@example.com>\r\n" + "\t<extremelylonglonglonglonglonglonglonglonglonglong" + "longlongmessageid3@example.com>\r\n" + "\t<messageid4@example.com>\r\n", + },{ + .name = "Cc", + .body = "\"Richard Edgar Cipient\" " + "<r.e.cipient@example.com>, \"Albert Buser\" " + "<a.buser@example.com>, \"Steven Pammer\" " + "<s.pammer@example.com>", + .output = "Cc: \"Richard Edgar Cipient\" " + "<r.e.cipient@example.com>, \"Albert Buser\"\r\n" + "\t<a.buser@example.com>, \"Steven Pammer\" " + "<s.pammer@example.com>\r\n" + },{ + .name = "References", + .body = "<00fd01d31b6c$33d98e30$9b8caa90$@karel@aa.example.org" + "> <00f201d31c36$afbfa320$0f3ee960$@karel@aa.example.o" + "rg> <015c01d32023$fe3840c0$faa8c240$@karel@aa.examp" + "le.org> <014601d325a4$ece1ed90$c6a5c8b0$@karel@aa." + "example.org> <012801d32b24$7734c380$659e4a80$@karel" + "@aa.example.org> <00da01d32be9$2d9944b0$88cbce10$@kar" + "el@aa.example.org> <006a01d336ef$6825d5b0$387181" + "10$@karel@aa.example.org> <018501d33880$58b654f0$0a2" + "2fed0$@frederik@aa.example.org> <00e601d33ba3$be50f10" + "0$3af2d300$@frederik@aa.example.org> <016501d341ee$e" + "678e1a0$b36aa4e0$@frederik@aa.example.org> <00ab01" + "d348f9$ae2e1ab0$0a8a5010$@karel@aa.example.org> <0086" + "01d349c1$98ff4ba0$cafde2e0$@frederik@aa.example.org> " + " <019301d357e6$a2190680$e64b1380$@frederik@aa.example" + ".org> <025f01d384b0$24d2c" + "660$6e785320$@karel@aa.example.org> <01cf01d3889e$7" + "280cb90$578262b0$@karel@aa.example.org> <013701d38" + "bc2$9164b950$b42e2bf0$@karel@aa.example.org> " + " <014f01d3a5b1$a51afc80$ef\n" + " \n" + "\t \t \t \t \t \t \t \t5\t0\tf\t5\t8\t0\t$\t@\tk\ta\t" + "r\te\tl\t@\taa.example.org> <01cb01d3af29$dd7d" + "1b40$987751c0$@karel@aa.example.org> " + " <00b401d3f2bc$6ad8c180$408a4480" + "$@karel@aa.example.org> <011a01d3f6ab$0eeb0480$2cc1" + "0d80$@frederik@aa.example.org> <005c01d3f774$37f1b210" + "$a7d51630$@richard@aa.example.org> <01a801d3fc2d$59" + "0f7730$0b2e6590$@frederik@aa.example.org> <007501d3fc" + "f5$23d75ce0$6b8616a0$@frederik@aa.example.org> <015d0" + "1d3fdbf$136da510$3a48ef30$@frederik@aa.example.org> <" + "021a01d3fe87$556d68b0$00483a10$@frederik@aa.example.o" + "rg> <013f01d3ff4e$a2d13d30$e873b790$@frederik@aa.exam" + "ple.org> <001f01d401ab$31e7b090$95b711b0$@frederik@aa" + ".example.org> <017201d40273$a118d200$e34a7600$@freder" + "ik@aa.example.org> <017401d4033e$ca3602e0$5ea208a0$@f" + "rederik@aa.example.org> <02a601d40404$608b9e10$21a2da" + "30$@frederik@aa.example.org> <014301d404d0$b65269b0$2" + "2f73d10$@frederik@aa.example.org> <015901d4072b$b5a1b" + "950$20e52bf0$@frederik@aa.example.org> <01b401d407f3$" + "bef52050$3cdf\n" + " 60 \n" + "\tf0 \t$@ \tfr \ted \teri\tk@aa.example.org> <012801d" + "408bd$6602fce0$3208f6a0$@frederik@aa.example.org> <01" + "c801d40984$ae4b23c0$0ae16b40$@frederik@aa.example.org" + "> <00ec01d40a4d$12859190$3790b4b0$@frederik@aa.exampl" + "e.org> <02af01d40d74$589c9050$09d5b0f0$@frederik@aa.e" + "xample.org> <000d01d40ec8$d3d337b0$7b79a710$@richard@" + "aa.example.org>\n", + .output = "References: <00fd01d31b6c$33d98e30$9b8caa90$@karel@aa.example.org>\r\n" + "\t<00f201d31c36$afbfa320$0f3ee960$@karel@aa.example.org>\r\n" + "\t<015c01d32023$fe3840c0$faa8c240$@karel@aa.example.org>\r\n" + "\t<014601d325a4$ece1ed90$c6a5c8b0$@karel@aa.example.org>\r\n" + "\t<012801d32b24$7734c380$659e4a80$@karel@aa.example.org>\r\n" + "\t<00da01d32be9$2d9944b0$88cbce10$@karel@aa.example.org>\r\n" + "\t<006a01d336ef$6825d5b0$38718110$@karel@aa.example.org>\r\n" + "\t<018501d33880$58b654f0$0a22fed0$@frederik@aa.example.org>\r\n" + "\t<00e601d33ba3$be50f100$3af2d300$@frederik@aa.example.org>\r\n" + "\t<016501d341ee$e678e1a0$b36aa4e0$@frederik@aa.example.org>\r\n" + "\t<00ab01d348f9$ae2e1ab0$0a8a5010$@karel@aa.example.org>\r\n" + "\t<008601d349c1$98ff4ba0$cafde2e0$@frederik@aa.example.org>\r\n" + "\t<019301d357e6$a2190680$e64b1380$@frederik@aa.example.org>\r\n" + "\t<025f01d384b0$24d2c660$6e785320$@karel@aa.example.org>\r\n" + "\t<01cf01d3889e$7280cb90$578262b0$@karel@aa.example.org>\r\n" + "\t<013701d38bc2$9164b950$b42e2bf0$@karel@aa.example.org>\r\n" + "\t<014f01d3a5b1$a51afc80$ef\r\n" + "\t5\t0\tf\t5\t8\t0\t$\t@\tk\ta\tr\te\tl\t@\taa.example.org>\r\n" + "\t<01cb01d3af29$dd7d1b40$987751c0$@karel@aa.example.org>\r\n" + "\t<00b401d3f2bc$6ad8c180$408a4480$@karel@aa.example.org>\r\n" + "\t<011a01d3f6ab$0eeb0480$2cc10d80$@frederik@aa.example.org>\r\n" + "\t<005c01d3f774$37f1b210$a7d51630$@richard@aa.example.org>\r\n" + "\t<01a801d3fc2d$590f7730$0b2e6590$@frederik@aa.example.org>\r\n" + "\t<007501d3fcf5$23d75ce0$6b8616a0$@frederik@aa.example.org>\r\n" + "\t<015d01d3fdbf$136da510$3a48ef30$@frederik@aa.example.org>\r\n" + "\t<021a01d3fe87$556d68b0$00483a10$@frederik@aa.example.org>\r\n" + "\t<013f01d3ff4e$a2d13d30$e873b790$@frederik@aa.example.org>\r\n" + "\t<001f01d401ab$31e7b090$95b711b0$@frederik@aa.example.org>\r\n" + "\t<017201d40273$a118d200$e34a7600$@frederik@aa.example.org>\r\n" + "\t<017401d4033e$ca3602e0$5ea208a0$@frederik@aa.example.org>\r\n" + "\t<02a601d40404$608b9e10$21a2da30$@frederik@aa.example.org>\r\n" + "\t<014301d404d0$b65269b0$22f73d10$@frederik@aa.example.org>\r\n" + "\t<015901d4072b$b5a1b950$20e52bf0$@frederik@aa.example.org>\r\n" + "\t<01b401d407f3$bef52050$3cdf\r\n" + "\t60\r\n" + "\tf0 \t$@ \tfr \ted \teri\tk@aa.example.org>\r\n" + "\t<012801d408bd$6602fce0$3208f6a0$@frederik@aa.example.org>\r\n" + "\t<01c801d40984$ae4b23c0$0ae16b40$@frederik@aa.example.org>\r\n" + "\t<00ec01d40a4d$12859190$3790b4b0$@frederik@aa.example.org>\r\n" + "\t<02af01d40d74$589c9050$09d5b0f0$@frederik@aa.example.org>\r\n" + "\t<000d01d40ec8$d3d337b0$7b79a710$@richard@aa.example.org>\r\n" + } +}; + +static const unsigned int header_write_tests_count = + N_ELEMENTS(header_write_tests); + +static void test_rfc2822_header_write(void) +{ + string_t *header; + unsigned int i; + + test_begin("rfc2822 - header write"); + + header = t_str_new(1024); + for (i = 0; i < header_write_tests_count; i++) { + const struct test_header_write *test = &header_write_tests[i]; + + str_truncate(header, 0); + rfc2822_header_write(header, test->name, test->body); + + test_assert_idx(strcmp(str_c(header), test->output) == 0, i); + } + + test_end(); +} + +int main(void) +{ + static void (*test_functions[])(void) = { + test_rfc2822_header_write, + NULL + }; + return test_run(test_functions); +} + diff --git a/pigeonhole/src/managesieve-login/Makefile.am b/pigeonhole/src/managesieve-login/Makefile.am new file mode 100644 index 0000000..3bfbc75 --- /dev/null +++ b/pigeonhole/src/managesieve-login/Makefile.am @@ -0,0 +1,43 @@ +settingsdir = $(dovecot_moduledir)/settings + +dovecot_pkglibexec_PROGRAMS = managesieve-login + +AM_CPPFLAGS = \ + $(LIBDOVECOT_INCLUDE) \ + $(LIBDOVECOT_SERVICE_INCLUDE) \ + $(LIBDOVECOT_LOGIN_INCLUDE) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/src/lib-managesieve + +libmanagesieve_login_settings_la_LDFLAGS = -module -avoid-version + +settings_LTLIBRARIES = \ + libmanagesieve_login_settings.la + +libmanagesieve_login_settings_la_SOURCES = \ + managesieve-login-settings.c \ + managesieve-login-settings-plugin.c + +libmanagesieve_login_settings_la_CFLAGS = \ + $(AM_CFLAGS) $(LIBDOVECOT_CONFIG_INCLUDE) -DPKG_LIBEXECDIR=\""$(dovecot_pkglibexecdir)"\" + +libs = \ + $(top_builddir)/src/lib-managesieve/libmanagesieve.la + +managesieve_login_CPPFLAGS = $(AM_CPPFLAGS) $(BINARY_CFLAGS) +managesieve_login_LDFLAGS = $(BINARY_LDFLAGS) +managesieve_login_LDADD = $(libs) $(LIBDOVECOT_LOGIN) $(LIBDOVECOT) +managesieve_login_DEPENDENCIES = $(libs) $(LIBDOVECOT_LOGIN_DEPS) $(LIBDOVECOT_DEPS) + +managesieve_login_SOURCES = \ + client.c \ + client-authenticate.c \ + managesieve-login-settings.c \ + managesieve-proxy.c + +noinst_HEADERS = \ + client.h \ + client-authenticate.h \ + managesieve-login-settings.h \ + managesieve-login-settings-plugin.h \ + managesieve-proxy.h diff --git a/pigeonhole/src/managesieve-login/Makefile.in b/pigeonhole/src/managesieve-login/Makefile.in new file mode 100644 index 0000000..14d7b78 --- /dev/null +++ b/pigeonhole/src/managesieve-login/Makefile.in @@ -0,0 +1,931 @@ +# 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@ +dovecot_pkglibexec_PROGRAMS = managesieve-login$(EXEEXT) +subdir = src/managesieve-login +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 = +am__installdirs = "$(DESTDIR)$(dovecot_pkglibexecdir)" \ + "$(DESTDIR)$(settingsdir)" +PROGRAMS = $(dovecot_pkglibexec_PROGRAMS) +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +LTLIBRARIES = $(settings_LTLIBRARIES) +libmanagesieve_login_settings_la_LIBADD = +am_libmanagesieve_login_settings_la_OBJECTS = libmanagesieve_login_settings_la-managesieve-login-settings.lo \ + libmanagesieve_login_settings_la-managesieve-login-settings-plugin.lo +libmanagesieve_login_settings_la_OBJECTS = \ + $(am_libmanagesieve_login_settings_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 = +libmanagesieve_login_settings_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(libmanagesieve_login_settings_la_CFLAGS) $(CFLAGS) \ + $(libmanagesieve_login_settings_la_LDFLAGS) $(LDFLAGS) -o $@ +am_managesieve_login_OBJECTS = managesieve_login-client.$(OBJEXT) \ + managesieve_login-client-authenticate.$(OBJEXT) \ + managesieve_login-managesieve-login-settings.$(OBJEXT) \ + managesieve_login-managesieve-proxy.$(OBJEXT) +managesieve_login_OBJECTS = $(am_managesieve_login_OBJECTS) +am__DEPENDENCIES_1 = +managesieve_login_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(managesieve_login_LDFLAGS) $(LDFLAGS) \ + -o $@ +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)/libmanagesieve_login_settings_la-managesieve-login-settings-plugin.Plo \ + ./$(DEPDIR)/libmanagesieve_login_settings_la-managesieve-login-settings.Plo \ + ./$(DEPDIR)/managesieve_login-client-authenticate.Po \ + ./$(DEPDIR)/managesieve_login-client.Po \ + ./$(DEPDIR)/managesieve_login-managesieve-login-settings.Po \ + ./$(DEPDIR)/managesieve_login-managesieve-proxy.Po +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 = $(libmanagesieve_login_settings_la_SOURCES) \ + $(managesieve_login_SOURCES) +DIST_SOURCES = $(libmanagesieve_login_settings_la_SOURCES) \ + $(managesieve_login_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@ +settingsdir = $(dovecot_moduledir)/settings +AM_CPPFLAGS = \ + $(LIBDOVECOT_INCLUDE) \ + $(LIBDOVECOT_SERVICE_INCLUDE) \ + $(LIBDOVECOT_LOGIN_INCLUDE) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/src/lib-managesieve + +libmanagesieve_login_settings_la_LDFLAGS = -module -avoid-version +settings_LTLIBRARIES = \ + libmanagesieve_login_settings.la + +libmanagesieve_login_settings_la_SOURCES = \ + managesieve-login-settings.c \ + managesieve-login-settings-plugin.c + +libmanagesieve_login_settings_la_CFLAGS = \ + $(AM_CFLAGS) $(LIBDOVECOT_CONFIG_INCLUDE) -DPKG_LIBEXECDIR=\""$(dovecot_pkglibexecdir)"\" + +libs = \ + $(top_builddir)/src/lib-managesieve/libmanagesieve.la + +managesieve_login_CPPFLAGS = $(AM_CPPFLAGS) $(BINARY_CFLAGS) +managesieve_login_LDFLAGS = $(BINARY_LDFLAGS) +managesieve_login_LDADD = $(libs) $(LIBDOVECOT_LOGIN) $(LIBDOVECOT) +managesieve_login_DEPENDENCIES = $(libs) $(LIBDOVECOT_LOGIN_DEPS) $(LIBDOVECOT_DEPS) +managesieve_login_SOURCES = \ + client.c \ + client-authenticate.c \ + managesieve-login-settings.c \ + managesieve-proxy.c + +noinst_HEADERS = \ + client.h \ + client-authenticate.h \ + managesieve-login-settings.h \ + managesieve-login-settings-plugin.h \ + managesieve-proxy.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/managesieve-login/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/managesieve-login/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): +install-dovecot_pkglibexecPROGRAMS: $(dovecot_pkglibexec_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(dovecot_pkglibexec_PROGRAMS)'; test -n "$(dovecot_pkglibexecdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(dovecot_pkglibexecdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(dovecot_pkglibexecdir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(dovecot_pkglibexecdir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(dovecot_pkglibexecdir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-dovecot_pkglibexecPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(dovecot_pkglibexec_PROGRAMS)'; test -n "$(dovecot_pkglibexecdir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(dovecot_pkglibexecdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(dovecot_pkglibexecdir)" && rm -f $$files + +clean-dovecot_pkglibexecPROGRAMS: + @list='$(dovecot_pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +install-settingsLTLIBRARIES: $(settings_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(settings_LTLIBRARIES)'; test -n "$(settingsdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(settingsdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(settingsdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(settingsdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(settingsdir)"; \ + } + +uninstall-settingsLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(settings_LTLIBRARIES)'; test -n "$(settingsdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(settingsdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(settingsdir)/$$f"; \ + done + +clean-settingsLTLIBRARIES: + -test -z "$(settings_LTLIBRARIES)" || rm -f $(settings_LTLIBRARIES) + @list='$(settings_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}; \ + } + +libmanagesieve_login_settings.la: $(libmanagesieve_login_settings_la_OBJECTS) $(libmanagesieve_login_settings_la_DEPENDENCIES) $(EXTRA_libmanagesieve_login_settings_la_DEPENDENCIES) + $(AM_V_CCLD)$(libmanagesieve_login_settings_la_LINK) -rpath $(settingsdir) $(libmanagesieve_login_settings_la_OBJECTS) $(libmanagesieve_login_settings_la_LIBADD) $(LIBS) + +managesieve-login$(EXEEXT): $(managesieve_login_OBJECTS) $(managesieve_login_DEPENDENCIES) $(EXTRA_managesieve_login_DEPENDENCIES) + @rm -f managesieve-login$(EXEEXT) + $(AM_V_CCLD)$(managesieve_login_LINK) $(managesieve_login_OBJECTS) $(managesieve_login_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmanagesieve_login_settings_la-managesieve-login-settings-plugin.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmanagesieve_login_settings_la-managesieve-login-settings.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/managesieve_login-client-authenticate.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/managesieve_login-client.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/managesieve_login-managesieve-login-settings.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/managesieve_login-managesieve-proxy.Po@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 $@ $< + +libmanagesieve_login_settings_la-managesieve-login-settings.lo: managesieve-login-settings.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libmanagesieve_login_settings_la_CFLAGS) $(CFLAGS) -MT libmanagesieve_login_settings_la-managesieve-login-settings.lo -MD -MP -MF $(DEPDIR)/libmanagesieve_login_settings_la-managesieve-login-settings.Tpo -c -o libmanagesieve_login_settings_la-managesieve-login-settings.lo `test -f 'managesieve-login-settings.c' || echo '$(srcdir)/'`managesieve-login-settings.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmanagesieve_login_settings_la-managesieve-login-settings.Tpo $(DEPDIR)/libmanagesieve_login_settings_la-managesieve-login-settings.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='managesieve-login-settings.c' object='libmanagesieve_login_settings_la-managesieve-login-settings.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libmanagesieve_login_settings_la_CFLAGS) $(CFLAGS) -c -o libmanagesieve_login_settings_la-managesieve-login-settings.lo `test -f 'managesieve-login-settings.c' || echo '$(srcdir)/'`managesieve-login-settings.c + +libmanagesieve_login_settings_la-managesieve-login-settings-plugin.lo: managesieve-login-settings-plugin.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libmanagesieve_login_settings_la_CFLAGS) $(CFLAGS) -MT libmanagesieve_login_settings_la-managesieve-login-settings-plugin.lo -MD -MP -MF $(DEPDIR)/libmanagesieve_login_settings_la-managesieve-login-settings-plugin.Tpo -c -o libmanagesieve_login_settings_la-managesieve-login-settings-plugin.lo `test -f 'managesieve-login-settings-plugin.c' || echo '$(srcdir)/'`managesieve-login-settings-plugin.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmanagesieve_login_settings_la-managesieve-login-settings-plugin.Tpo $(DEPDIR)/libmanagesieve_login_settings_la-managesieve-login-settings-plugin.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='managesieve-login-settings-plugin.c' object='libmanagesieve_login_settings_la-managesieve-login-settings-plugin.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libmanagesieve_login_settings_la_CFLAGS) $(CFLAGS) -c -o libmanagesieve_login_settings_la-managesieve-login-settings-plugin.lo `test -f 'managesieve-login-settings-plugin.c' || echo '$(srcdir)/'`managesieve-login-settings-plugin.c + +managesieve_login-client.o: client.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_login_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve_login-client.o -MD -MP -MF $(DEPDIR)/managesieve_login-client.Tpo -c -o managesieve_login-client.o `test -f 'client.c' || echo '$(srcdir)/'`client.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve_login-client.Tpo $(DEPDIR)/managesieve_login-client.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='client.c' object='managesieve_login-client.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_login_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve_login-client.o `test -f 'client.c' || echo '$(srcdir)/'`client.c + +managesieve_login-client.obj: client.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_login_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve_login-client.obj -MD -MP -MF $(DEPDIR)/managesieve_login-client.Tpo -c -o managesieve_login-client.obj `if test -f 'client.c'; then $(CYGPATH_W) 'client.c'; else $(CYGPATH_W) '$(srcdir)/client.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve_login-client.Tpo $(DEPDIR)/managesieve_login-client.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='client.c' object='managesieve_login-client.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_login_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve_login-client.obj `if test -f 'client.c'; then $(CYGPATH_W) 'client.c'; else $(CYGPATH_W) '$(srcdir)/client.c'; fi` + +managesieve_login-client-authenticate.o: client-authenticate.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_login_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve_login-client-authenticate.o -MD -MP -MF $(DEPDIR)/managesieve_login-client-authenticate.Tpo -c -o managesieve_login-client-authenticate.o `test -f 'client-authenticate.c' || echo '$(srcdir)/'`client-authenticate.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve_login-client-authenticate.Tpo $(DEPDIR)/managesieve_login-client-authenticate.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='client-authenticate.c' object='managesieve_login-client-authenticate.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_login_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve_login-client-authenticate.o `test -f 'client-authenticate.c' || echo '$(srcdir)/'`client-authenticate.c + +managesieve_login-client-authenticate.obj: client-authenticate.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_login_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve_login-client-authenticate.obj -MD -MP -MF $(DEPDIR)/managesieve_login-client-authenticate.Tpo -c -o managesieve_login-client-authenticate.obj `if test -f 'client-authenticate.c'; then $(CYGPATH_W) 'client-authenticate.c'; else $(CYGPATH_W) '$(srcdir)/client-authenticate.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve_login-client-authenticate.Tpo $(DEPDIR)/managesieve_login-client-authenticate.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='client-authenticate.c' object='managesieve_login-client-authenticate.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_login_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve_login-client-authenticate.obj `if test -f 'client-authenticate.c'; then $(CYGPATH_W) 'client-authenticate.c'; else $(CYGPATH_W) '$(srcdir)/client-authenticate.c'; fi` + +managesieve_login-managesieve-login-settings.o: managesieve-login-settings.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_login_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve_login-managesieve-login-settings.o -MD -MP -MF $(DEPDIR)/managesieve_login-managesieve-login-settings.Tpo -c -o managesieve_login-managesieve-login-settings.o `test -f 'managesieve-login-settings.c' || echo '$(srcdir)/'`managesieve-login-settings.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve_login-managesieve-login-settings.Tpo $(DEPDIR)/managesieve_login-managesieve-login-settings.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='managesieve-login-settings.c' object='managesieve_login-managesieve-login-settings.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_login_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve_login-managesieve-login-settings.o `test -f 'managesieve-login-settings.c' || echo '$(srcdir)/'`managesieve-login-settings.c + +managesieve_login-managesieve-login-settings.obj: managesieve-login-settings.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_login_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve_login-managesieve-login-settings.obj -MD -MP -MF $(DEPDIR)/managesieve_login-managesieve-login-settings.Tpo -c -o managesieve_login-managesieve-login-settings.obj `if test -f 'managesieve-login-settings.c'; then $(CYGPATH_W) 'managesieve-login-settings.c'; else $(CYGPATH_W) '$(srcdir)/managesieve-login-settings.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve_login-managesieve-login-settings.Tpo $(DEPDIR)/managesieve_login-managesieve-login-settings.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='managesieve-login-settings.c' object='managesieve_login-managesieve-login-settings.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_login_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve_login-managesieve-login-settings.obj `if test -f 'managesieve-login-settings.c'; then $(CYGPATH_W) 'managesieve-login-settings.c'; else $(CYGPATH_W) '$(srcdir)/managesieve-login-settings.c'; fi` + +managesieve_login-managesieve-proxy.o: managesieve-proxy.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_login_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve_login-managesieve-proxy.o -MD -MP -MF $(DEPDIR)/managesieve_login-managesieve-proxy.Tpo -c -o managesieve_login-managesieve-proxy.o `test -f 'managesieve-proxy.c' || echo '$(srcdir)/'`managesieve-proxy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve_login-managesieve-proxy.Tpo $(DEPDIR)/managesieve_login-managesieve-proxy.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='managesieve-proxy.c' object='managesieve_login-managesieve-proxy.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_login_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve_login-managesieve-proxy.o `test -f 'managesieve-proxy.c' || echo '$(srcdir)/'`managesieve-proxy.c + +managesieve_login-managesieve-proxy.obj: managesieve-proxy.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_login_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve_login-managesieve-proxy.obj -MD -MP -MF $(DEPDIR)/managesieve_login-managesieve-proxy.Tpo -c -o managesieve_login-managesieve-proxy.obj `if test -f 'managesieve-proxy.c'; then $(CYGPATH_W) 'managesieve-proxy.c'; else $(CYGPATH_W) '$(srcdir)/managesieve-proxy.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve_login-managesieve-proxy.Tpo $(DEPDIR)/managesieve_login-managesieve-proxy.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='managesieve-proxy.c' object='managesieve_login-managesieve-proxy.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_login_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve_login-managesieve-proxy.obj `if test -f 'managesieve-proxy.c'; then $(CYGPATH_W) 'managesieve-proxy.c'; else $(CYGPATH_W) '$(srcdir)/managesieve-proxy.c'; fi` + +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 $(PROGRAMS) $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(dovecot_pkglibexecdir)" "$(DESTDIR)$(settingsdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-dovecot_pkglibexecPROGRAMS clean-generic clean-libtool \ + clean-settingsLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/libmanagesieve_login_settings_la-managesieve-login-settings-plugin.Plo + -rm -f ./$(DEPDIR)/libmanagesieve_login_settings_la-managesieve-login-settings.Plo + -rm -f ./$(DEPDIR)/managesieve_login-client-authenticate.Po + -rm -f ./$(DEPDIR)/managesieve_login-client.Po + -rm -f ./$(DEPDIR)/managesieve_login-managesieve-login-settings.Po + -rm -f ./$(DEPDIR)/managesieve_login-managesieve-proxy.Po + -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-settingsLTLIBRARIES + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-dovecot_pkglibexecPROGRAMS + +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)/libmanagesieve_login_settings_la-managesieve-login-settings-plugin.Plo + -rm -f ./$(DEPDIR)/libmanagesieve_login_settings_la-managesieve-login-settings.Plo + -rm -f ./$(DEPDIR)/managesieve_login-client-authenticate.Po + -rm -f ./$(DEPDIR)/managesieve_login-client.Po + -rm -f ./$(DEPDIR)/managesieve_login-managesieve-login-settings.Po + -rm -f ./$(DEPDIR)/managesieve_login-managesieve-proxy.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-dovecot_pkglibexecPROGRAMS \ + uninstall-settingsLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-dovecot_pkglibexecPROGRAMS clean-generic clean-libtool \ + clean-settingsLTLIBRARIES 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-dovecot_pkglibexecPROGRAMS 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-settingsLTLIBRARIES install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am \ + uninstall-dovecot_pkglibexecPROGRAMS \ + uninstall-settingsLTLIBRARIES + +.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/managesieve-login/client-authenticate.c b/pigeonhole/src/managesieve-login/client-authenticate.c new file mode 100644 index 0000000..b186f84 --- /dev/null +++ b/pigeonhole/src/managesieve-login/client-authenticate.c @@ -0,0 +1,308 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "login-common.h" +#include "base64.h" +#include "buffer.h" +#include "ioloop.h" +#include "istream.h" +#include "ostream.h" +#include "safe-memset.h" +#include "str.h" +#include "str-sanitize.h" +#include "auth-client.h" + +#include "managesieve-parser.h" +#include "managesieve-quote.h" +#include "client.h" + +#include "client-authenticate.h" +#include "managesieve-proxy.h" + + +const char *client_authenticate_get_capabilities +(struct client *client) +{ + const struct auth_mech_desc *mech; + unsigned int i, count; + string_t *str; + + str = t_str_new(128); + mech = sasl_server_get_advertised_mechs(client, &count); + + for (i = 0; i < count; i++) { + if (i > 0) + str_append_c(str, ' '); + str_append(str, mech[i].name); + } + + return str_c(str); +} + +void managesieve_client_auth_result(struct client *client, + enum client_auth_result result, + const struct client_auth_reply *reply, const char *text) +{ + struct managesieve_client *msieve_client = + (struct managesieve_client *)client; + string_t *referral; + + switch (result) { + case CLIENT_AUTH_RESULT_SUCCESS: + /* nothing to be done for IMAP */ + break; + case CLIENT_AUTH_RESULT_REFERRAL_SUCCESS: + case CLIENT_AUTH_RESULT_REFERRAL_NOLOGIN: + /* MANAGESIEVE referral + + [nologin] referral host=.. [port=..] [destuser=..] + [reason=..] + + NO [REFERRAL sieve://user;AUTH=mech@host:port/] "Can't login." + OK [...] "Logged in, but you should use this server instead." + .. [REFERRAL ..] Reason from auth server + */ + referral = t_str_new(128); + str_printfa(referral, "REFERRAL sieve://%s;AUTH=%s@%s", + reply->destuser, client->auth_mech_name, reply->host); + if ( reply->port != 4190 ) + str_printfa(referral, ":%u", reply->port); + + if ( result == CLIENT_AUTH_RESULT_REFERRAL_SUCCESS ) { + client_send_okresp(client, str_c(referral), text);; + } else { + client_send_noresp(client, str_c(referral), text); + } + break; + case CLIENT_AUTH_RESULT_ABORTED: + case CLIENT_AUTH_RESULT_AUTHFAILED_REASON: + case CLIENT_AUTH_RESULT_AUTHZFAILED: + client_send_no(client, text); + break; + case CLIENT_AUTH_RESULT_TEMPFAIL: + client_send_noresp(client, "TRYLATER", text); + break; + case CLIENT_AUTH_RESULT_SSL_REQUIRED: + client_send_noresp(client, "ENCRYPT-NEEDED", text); + break; + case CLIENT_AUTH_RESULT_AUTHFAILED: + default: + client_send_no(client, text); + break; + } + + msieve_client->auth_response_input = NULL; + managesieve_parser_reset(msieve_client->parser); +} + +void managesieve_client_auth_send_challenge +(struct client *client, const char *data) +{ + struct managesieve_client *msieve_client = + (struct managesieve_client *) client; + + T_BEGIN { + string_t *str = t_str_new(256); + + managesieve_quote_append_string(str, data, TRUE); + str_append(str, "\r\n"); + + client_send_raw_data(client, str_c(str), str_len(str)); + } T_END; + + msieve_client->auth_response_input = NULL; + managesieve_parser_reset(msieve_client->parser); +} + +static int managesieve_client_auth_read_response +(struct managesieve_client *msieve_client, bool initial, const char **error_r) +{ + struct client *client = &msieve_client->common; + const struct managesieve_arg *args; + const char *error; + bool fatal; + const unsigned char *data; + size_t size; + uoff_t resp_size; + int ret; + + *error_r = NULL; + + if ( i_stream_read(client->input) == -1 ) { + /* disconnected */ + client_destroy_iostream_error(client); + return -1; + } + + if ( msieve_client->auth_response_input == NULL ) { + + if ( msieve_client->skip_line ) { + if ( i_stream_next_line(client->input) == NULL ) + return 0; + + msieve_client->skip_line = FALSE; + } + + switch ( managesieve_parser_read_args(msieve_client->parser, 0, + MANAGESIEVE_PARSE_FLAG_STRING_STREAM, &args) ) { + case -1: + error = managesieve_parser_get_error(msieve_client->parser, &fatal); + if (fatal) { + client_send_bye(client, error); + client_destroy(client, t_strconcat( + "parse error during auth: ", error, NULL)); + } else { + *error_r = error; + } + msieve_client->skip_line = TRUE; + return -1; + + case -2: + /* not enough data */ + return 0; + + default: + break; + } + + if ( MANAGESIEVE_ARG_IS_EOL(&args[0]) ) { + if (!initial) { + *error_r = "Received empty AUTHENTICATE client response line."; + msieve_client->skip_line = TRUE; + return -1; + } + msieve_client->skip_line = TRUE; + return 1; + } + + if ( !managesieve_arg_get_string_stream + (&args[0], &msieve_client->auth_response_input) + || !MANAGESIEVE_ARG_IS_EOL(&args[1]) ) { + if ( !initial ) + *error_r = "Invalid AUTHENTICATE client response."; + else + *error_r = "Invalid AUTHENTICATE initial response."; + msieve_client->skip_line = TRUE; + return -1; + } + + if ( i_stream_get_size + (msieve_client->auth_response_input, FALSE, &resp_size) <= 0 ) + resp_size = 0; + + if (client->auth_response == NULL) + client->auth_response = str_new(default_pool, I_MAX(resp_size+1, 256)); + } + + while ( (ret=i_stream_read_more + (msieve_client->auth_response_input, &data, &size) ) > 0 ) { + + if (str_len(client->auth_response) + size > LOGIN_MAX_AUTH_BUF_SIZE) { + client_destroy(client, "Authentication response too large"); + return -1; + } + + str_append_data(client->auth_response, data, size); + i_stream_skip(msieve_client->auth_response_input, size); + } + + if ( ret == 0 ) return 0; + + if ( msieve_client->auth_response_input->stream_errno != 0 ) { + if ( !client->input->eof && + msieve_client->auth_response_input->stream_errno == EINVAL ) { + msieve_client->skip_line = TRUE; + *error_r = t_strconcat + ("Error in AUTHENTICATE response string: ", + i_stream_get_error(msieve_client->auth_response_input), NULL); + return -1; + } + + client_destroy_iostream_error(client); + return -1; + } + + if ( i_stream_next_line(client->input) == NULL ) + return 0; + + return 1; +} + +void managesieve_client_auth_parse_response(struct client *client) +{ + struct managesieve_client *msieve_client = + (struct managesieve_client *) client; + const char *error = NULL; + int ret; + + if ( (ret=managesieve_client_auth_read_response(msieve_client, FALSE, &error)) + < 0 ) { + if ( error != NULL ) + client_auth_fail(client, error); + return; + } + + if ( ret == 0 ) return; + + if ( strcmp(str_c(client->auth_response), "*") == 0 ) { + client_auth_abort(client); + return; + } + + client_auth_respond(client, str_c(client->auth_response)); + + memset(str_c_modifiable(client->auth_response), 0, + str_len(client->auth_response)); +} + +int cmd_authenticate +(struct managesieve_client *msieve_client, const struct managesieve_arg *args) +{ + /* NOTE: This command's input is handled specially because the + SASL-IR can be large. */ + struct client *client = &msieve_client->common; + const char *mech_name, *init_response; + const char *error; + int ret; + + if (!msieve_client->auth_mech_name_parsed) { + i_assert(args != NULL); + + /* one mandatory argument: authentication mechanism name */ + if ( !managesieve_arg_get_string(&args[0], &mech_name) ) + return -1; + + if (*mech_name == '\0') + return -1; + + i_free(client->auth_mech_name); + client->auth_mech_name = i_strdup(mech_name); + msieve_client->auth_mech_name_parsed = TRUE; + + msieve_client->auth_response_input = NULL; + managesieve_parser_reset(msieve_client->parser); + } + + msieve_client->skip_line = FALSE; + if ( (ret=managesieve_client_auth_read_response(msieve_client, TRUE, &error)) + < 0 ) { + msieve_client->auth_mech_name_parsed = FALSE; + if ( error != NULL ) { + client_send_no(client, error); + } + return 1; + } + + if ( ret == 0 ) return 0; + + init_response = ( client->auth_response == NULL ? NULL : + t_strdup(str_c(client->auth_response)) ); + msieve_client->auth_mech_name_parsed = FALSE; + if ( (ret=client_auth_begin + (client, t_strdup(client->auth_mech_name), init_response)) < 0 ) + return ret; + + msieve_client->cmd_finished = TRUE; + return 0; +} + diff --git a/pigeonhole/src/managesieve-login/client-authenticate.h b/pigeonhole/src/managesieve-login/client-authenticate.h new file mode 100644 index 0000000..1280ad5 --- /dev/null +++ b/pigeonhole/src/managesieve-login/client-authenticate.h @@ -0,0 +1,21 @@ +#ifndef CLIENT_AUTHENTICATE_H +#define CLIENT_AUTHENTICATE_H + +struct managesieve_arg; + +const char *client_authenticate_get_capabilities + (struct client *client); + +void managesieve_client_auth_result + (struct client *client, enum client_auth_result result, + const struct client_auth_reply *reply, const char *text); + +void managesieve_client_auth_send_challenge + (struct client *client, const char *data); +void managesieve_client_auth_parse_response + (struct client *client); + +int cmd_authenticate + (struct managesieve_client *client, const struct managesieve_arg *args); + +#endif diff --git a/pigeonhole/src/managesieve-login/client.c b/pigeonhole/src/managesieve-login/client.c new file mode 100644 index 0000000..fe8acff --- /dev/null +++ b/pigeonhole/src/managesieve-login/client.c @@ -0,0 +1,574 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "login-common.h" +#include "buffer.h" +#include "ioloop.h" +#include "istream.h" +#include "ostream.h" +#include "safe-memset.h" +#include "str.h" +#include "strescape.h" +#include "base64.h" +#include "master-service.h" +#include "master-auth.h" +#include "auth-client.h" + +#include "managesieve-parser.h" +#include "managesieve-quote.h" + +#include "client.h" +#include "client-authenticate.h" + +#include "managesieve-login-settings.h" +#include "managesieve-proxy.h" + +/* Disconnect client when it sends too many bad commands */ +#define CLIENT_MAX_BAD_COMMANDS 3 + +struct managesieve_command { + const char *name; + int (*func)(struct managesieve_client *client, + const struct managesieve_arg *args); + int preparsed_args; +}; + +/* Skip incoming data until newline is found, + returns TRUE if newline was found. */ +bool client_skip_line(struct managesieve_client *client) +{ + const unsigned char *data; + size_t i, data_size; + + data = i_stream_get_data(client->common.input, &data_size); + + for (i = 0; i < data_size; i++) { + if (data[i] == '\n') { + i_stream_skip(client->common.input, i+1); + return TRUE; + } + } + + return FALSE; +} + +static void client_send_capabilities(struct client *client) +{ + struct managesieve_client *msieve_client = + (struct managesieve_client *)client; + const char *saslcap; + + T_BEGIN { + saslcap = client_authenticate_get_capabilities(client); + + /* Default capabilities */ + client_send_raw(client, t_strconcat( + "\"IMPLEMENTATION\" \"", + msieve_client->set->managesieve_implementation_string, + "\"\r\n", NULL)); + client_send_raw(client, t_strconcat( + "\"SIEVE\" \"", + msieve_client->set->managesieve_sieve_capability, + "\"\r\n", NULL)); + if (msieve_client->set->managesieve_notify_capability != NULL) { + client_send_raw(client, t_strconcat( + "\"NOTIFY\" \"", + msieve_client->set->managesieve_notify_capability, + "\"\r\n", NULL)); + } + client_send_raw(client, t_strconcat("\"SASL\" \"", saslcap, + "\"\r\n", NULL)); + + /* STARTTLS */ + if (login_ssl_initialized && !client->tls) + client_send_raw(client, "\"STARTTLS\"\r\n"); + + /* Protocol version */ + client_send_raw(client, "\"VERSION\" \"1.0\"\r\n"); + + /* XCLIENT */ + if (client->trusted) + client_send_raw(client, "\"XCLIENT\"\r\n"); + } T_END; +} + +static int +cmd_capability(struct managesieve_client *client, + const struct managesieve_arg *args ATTR_UNUSED) +{ + o_stream_cork(client->common.output); + + client_send_capabilities(&client->common); + client_send_ok(&client->common, "Capability completed."); + + o_stream_uncork(client->common.output); + + return 1; +} + +static int +cmd_starttls(struct managesieve_client *client, + const struct managesieve_arg *args ATTR_UNUSED) +{ + client_cmd_starttls(&client->common); + return 1; +} + +static void +managesieve_client_notify_starttls(struct client *client, bool success, + const char *text) +{ + if (success) + client_send_ok(client, text); + else + client_send_no(client, text); +} + +static int +cmd_noop(struct managesieve_client *client, const struct managesieve_arg *args) +{ + const char *text; + string_t *resp_code; + + if (MANAGESIEVE_ARG_IS_EOL(&args[0])) { + client_send_ok(&client->common, "NOOP Completed"); + return 1; + } + if (!MANAGESIEVE_ARG_IS_EOL(&args[1])) + return -1; + if (!managesieve_arg_get_string(&args[0], &text)) { + client_send_no(&client->common, "Invalid echo tag."); + return 1; + } + + resp_code = t_str_new(256); + str_append(resp_code, "TAG "); + managesieve_quote_append_string(resp_code, text, FALSE); + + client_send_okresp(&client->common, str_c(resp_code), "Done"); + return 1; +} + +static int +cmd_logout(struct managesieve_client *client, + const struct managesieve_arg *args ATTR_UNUSED) +{ + client_send_ok(&client->common, "Logout completed."); + client_destroy(&client->common, CLIENT_UNAUTHENTICATED_LOGOUT_MSG); + return 1; +} + +static int +cmd_xclient_parse_forward(struct managesieve_client *client, const char *value) +{ + size_t value_len = strlen(value); + + if (client->common.forward_fields != NULL) + str_truncate(client->common.forward_fields, 0); + else { + client->common.forward_fields = str_new( + client->common.preproxy_pool, + MAX_BASE64_DECODED_SIZE(value_len)); + } + + if (base64_decode(value, value_len, NULL, + client->common.forward_fields) < 0) + return -1; + + return 0; +} + +static int +cmd_xclient(struct managesieve_client *client, + const struct managesieve_arg *args) +{ + const char *arg; + bool args_ok = TRUE; + + if (!client->common.trusted) { + client_send_no(&client->common, "You are not from trusted IP"); + return 1; + } + while (!MANAGESIEVE_ARG_IS_EOL(&args[0]) && + managesieve_arg_get_atom(&args[0], &arg)) { + if (strncasecmp(arg, "ADDR=", 5) == 0) { + if (net_addr2ip(arg + 5, &client->common.ip) < 0) + args_ok = FALSE; + } else if (strncasecmp(arg, "FORWARD=", 8) == 0) { + if (cmd_xclient_parse_forward(client, arg + 8) < 0) + args_ok = FALSE; + } else if (strncasecmp(arg, "PORT=", 5) == 0) { + if (net_str2port(arg + 5, + &client->common.remote_port) < 0) + args_ok = FALSE; + } else if (strncasecmp(arg, "SESSION=", 8) == 0) { + const char *value = arg + 8; + + if (strlen(value) <= LOGIN_MAX_SESSION_ID_LEN) { + client->common.session_id = + p_strdup(client->common.pool, value); + } + } else if (strncasecmp(arg, "TTL=", 4) == 0) { + if (str_to_uint(arg + 4, &client->common.proxy_ttl) < 0) + args_ok = FALSE; + } + args++; + } + if (!args_ok || !MANAGESIEVE_ARG_IS_EOL(&args[0])) + return -1; + + client_send_ok(&client->common, "Updated"); + return 1; +} + +static struct managesieve_command commands[] = { + { "AUTHENTICATE", cmd_authenticate, 1 }, + { "CAPABILITY", cmd_capability, -1 }, + { "STARTTLS", cmd_starttls, -1 }, + { "NOOP", cmd_noop, 0 }, + { "LOGOUT", cmd_logout, -1 }, + { "XCLIENT", cmd_xclient, 0 }, + { NULL, NULL, 0 } +}; + +static bool client_handle_input(struct managesieve_client *client) +{ + i_assert(!client->common.authenticating); + + if (client->cmd_finished) { + /* Clear the previous command from memory */ + client->cmd_name = NULL; + client->cmd_parsed_args = FALSE; + client->cmd = NULL; + managesieve_parser_reset(client->parser); + + /* Remove \r\n */ + if (client->skip_line) { + if (!client_skip_line(client)) + return FALSE; + client->skip_line = FALSE; + } + + client->cmd_finished = FALSE; + } + + if (client->cmd == NULL) { + struct managesieve_command *cmd; + const char *cmd_name; + + client->cmd_name = managesieve_parser_read_word(client->parser); + if (client->cmd_name == NULL) + return FALSE; /* Need more data */ + + cmd_name = t_str_ucase(client->cmd_name); + cmd = commands; + while (cmd->name != NULL) { + if (strcmp(cmd->name, cmd_name) == 0) + break; + cmd++; + } + + if (cmd->name != NULL) + client->cmd = cmd; + else + client->skip_line = TRUE; + } + return client->common.v.input_next_cmd(&client->common); +} + +static bool managesieve_client_input_next_cmd(struct client *_client) +{ + struct managesieve_client *client = + (struct managesieve_client *)_client; + const struct managesieve_arg *args = NULL; + const char *msg; + int ret = 1; + bool fatal; + + if (client->cmd == NULL) { + /* Unknown command */ + ret = -1; + } else if (!client->cmd_parsed_args) { + unsigned int arg_count = + (client->cmd->preparsed_args > 0 ? + client->cmd->preparsed_args : 0); + + switch (managesieve_parser_read_args(client->parser, arg_count, + 0, &args)) { + case -1: + /* Error */ + msg = managesieve_parser_get_error(client->parser, + &fatal); + if (fatal) { + client_send_bye(&client->common, msg); + client_destroy(&client->common, msg); + return FALSE; + } + client_send_no(&client->common, msg); + client->cmd_finished = TRUE; + client->skip_line = TRUE; + return TRUE; + case -2: + /* Not enough data */ + return FALSE; + } + i_assert(args != NULL); + + if (arg_count == 0) { + /* We read the entire line - skip over the CRLF */ + if (!client_skip_line(client)) + i_unreached(); + } else { + /* Get rid of it later */ + client->skip_line = TRUE; + } + + client->cmd_parsed_args = TRUE; + + if (client->cmd->preparsed_args == -1) { + /* Check absence of arguments */ + if (args[0].type != MANAGESIEVE_ARG_EOL) + ret = -1; + } + } + if (ret > 0) { + i_assert(client->cmd != NULL); + ret = client->cmd->func(client, args); + } + + if (ret != 0) + client->cmd_finished = TRUE; + if (ret < 0) { + if (++client->common.bad_counter >= CLIENT_MAX_BAD_COMMANDS) { + client_send_bye(&client->common, + "Too many invalid MANAGESIEVE commands."); + client_destroy(&client->common, + "Too many invalid commands."); + return FALSE; + } + client_send_no(&client->common, + "Error in MANAGESIEVE command received by server."); + } + + return ret != 0 && !client->common.destroyed; +} + +static void managesieve_client_input(struct client *client) +{ + struct managesieve_client *managesieve_client = + (struct managesieve_client *)client; + + if (!client_read(client)) + return; + + client_ref(client); + o_stream_cork(managesieve_client->common.output); + for (;;) { + if (!auth_client_is_connected(auth_client)) { + /* We're not currently connected to auth process - + don't allow any commands */ + /* FIXME: Can't do untagged responses with managesieve. + Any other ways? + client_send_ok(client, AUTH_SERVER_WAITING_MSG); + */ + timeout_remove(&client->to_auth_waiting); + + client->input_blocked = TRUE; + break; + } else { + if (!client_handle_input(managesieve_client)) + break; + } + } + o_stream_uncork(managesieve_client->common.output); + client_unref(&client); +} + +static struct client *managesieve_client_alloc(pool_t pool) +{ + struct managesieve_client *msieve_client; + + msieve_client = p_new(pool, struct managesieve_client, 1); + return &msieve_client->common; +} + +static void managesieve_client_create(struct client *client, void **other_sets) +{ + struct managesieve_client *msieve_client = + (struct managesieve_client *)client; + + msieve_client->set = other_sets[0]; + msieve_client->parser = managesieve_parser_create( + msieve_client->common.input, MAX_MANAGESIEVE_LINE); + client->io = io_add(client->fd, IO_READ, client_input, client); +} + +static void managesieve_client_destroy(struct client *client) +{ + struct managesieve_client *managesieve_client = + (struct managesieve_client *)client; + + managesieve_parser_destroy(&managesieve_client->parser); +} + +static void managesieve_client_notify_auth_ready(struct client *client) +{ + /* Cork the stream to send the capability data as a single tcp frame + Some naive clients break if we don't. + */ + o_stream_cork(client->output); + + /* Send initial capabilities */ + client_send_capabilities(client); + client_send_ok(client, client->set->login_greeting); + + o_stream_uncork(client->output); + + client->banner_sent = TRUE; +} + +static void managesieve_client_starttls(struct client *client) +{ + struct managesieve_client *msieve_client = + (struct managesieve_client *)client; + + managesieve_parser_destroy(&msieve_client->parser); + msieve_client->parser = managesieve_parser_create( + msieve_client->common.input, MAX_MANAGESIEVE_LINE); + + /* CRLF is lost from buffer when streams are reopened. */ + msieve_client->skip_line = FALSE; + + /* Cork the stream to send the capability data as a single tcp frame + Some naive clients break if we don't. + */ + o_stream_cork(client->output); + + client_send_capabilities(client); + client_send_ok(client, "TLS negotiation successful."); + + o_stream_uncork(client->output); +} + +static void +client_send_reply_raw(struct client *client, const char *prefix, + const char *resp_code, const char *text) +{ + T_BEGIN { + string_t *line = t_str_new(256); + + str_append(line, prefix); + + if (resp_code != NULL) { + str_append(line, " ("); + str_append(line, resp_code); + str_append_c(line, ')'); + } + + if (text != NULL) { + str_append_c(line, ' '); + managesieve_quote_append_string(line, text, TRUE); + } + + str_append(line, "\r\n"); + + client_send_raw_data(client, str_data(line), str_len(line)); + } T_END; +} + +void client_send_reply_code(struct client *client, + enum managesieve_cmd_reply reply, + const char *resp_code, const char *text) +{ + const char *prefix = "NO"; + + switch (reply) { + case MANAGESIEVE_CMD_REPLY_OK: + prefix = "OK"; + break; + case MANAGESIEVE_CMD_REPLY_NO: + break; + case MANAGESIEVE_CMD_REPLY_BYE: + prefix = "BYE"; + break; + } + + client_send_reply_raw(client, prefix, resp_code, text); +} + +void client_send_reply(struct client *client, enum managesieve_cmd_reply reply, + const char *text) +{ + client_send_reply_code(client, reply, NULL, text); +} + +static void +managesieve_client_notify_disconnect(struct client *client, + enum client_disconnect_reason reason, + const char *text) +{ + if (reason == CLIENT_DISCONNECT_SYSTEM_SHUTDOWN) { + client_send_reply_code(client, MANAGESIEVE_CMD_REPLY_BYE, + "TRYLATER", text); + } else { + client_send_reply_code(client, MANAGESIEVE_CMD_REPLY_BYE, + NULL, text); + } +} + +static void managesieve_login_preinit(void) +{ + login_set_roots = managesieve_login_settings_set_roots; +} + +static void managesieve_login_init(void) +{ +} + +static void managesieve_login_deinit(void) +{ + clients_destroy_all(); +} + +static struct client_vfuncs managesieve_client_vfuncs = { + .alloc = managesieve_client_alloc, + .create = managesieve_client_create, + .destroy = managesieve_client_destroy, + .notify_auth_ready = managesieve_client_notify_auth_ready, + .notify_disconnect = managesieve_client_notify_disconnect, + .notify_starttls = managesieve_client_notify_starttls, + .starttls = managesieve_client_starttls, + .input = managesieve_client_input, + .auth_send_challenge = managesieve_client_auth_send_challenge, + .auth_parse_response = managesieve_client_auth_parse_response, + .auth_result = managesieve_client_auth_result, + .proxy_reset = managesieve_proxy_reset, + .proxy_parse_line = managesieve_proxy_parse_line, + .proxy_failed = managesieve_proxy_failed, + .proxy_get_state = managesieve_proxy_get_state, + .send_raw_data = client_common_send_raw_data, + .input_next_cmd = managesieve_client_input_next_cmd, + .free = client_common_default_free, +}; + +static struct login_binary managesieve_login_binary = { + .protocol = "sieve", + .process_name = "managesieve-login", + .default_port = 4190, + + .event_category = { + .name = "managesieve", + }, + + .client_vfuncs = &managesieve_client_vfuncs, + .preinit = managesieve_login_preinit, + .init = managesieve_login_init, + .deinit = managesieve_login_deinit, + + .anonymous_login_acceptable = FALSE, +}; + +int main(int argc, char *argv[]) +{ + return login_binary_run(&managesieve_login_binary, argc, argv); +} diff --git a/pigeonhole/src/managesieve-login/client.h b/pigeonhole/src/managesieve-login/client.h new file mode 100644 index 0000000..a04190d --- /dev/null +++ b/pigeonhole/src/managesieve-login/client.h @@ -0,0 +1,76 @@ +#ifndef CLIENT_H +#define CLIENT_H + +#include "net.h" +#include "client-common.h" + +/* maximum length for managesieve command line. */ +#define MAX_MANAGESIEVE_LINE 8192 + +enum managesieve_proxy_state { + MSIEVE_PROXY_STATE_NONE, + MSIEVE_PROXY_STATE_TLS_START, + MSIEVE_PROXY_STATE_TLS_READY, + MSIEVE_PROXY_STATE_XCLIENT, + MSIEVE_PROXY_STATE_AUTH, + + MSIEVE_PROXY_STATE_COUNT +}; +struct managesieve_command; + +struct managesieve_client { + struct client common; + + const struct managesieve_login_settings *set; + struct managesieve_parser *parser; + + enum managesieve_proxy_state proxy_state; + + const char *cmd_name; + struct managesieve_command *cmd; + + struct istream *auth_response_input; + + bool cmd_finished:1; + bool cmd_parsed_args:1; + bool skip_line:1; + bool auth_mech_name_parsed:1; + + bool proxy_starttls:1; + bool proxy_sasl:1; + bool proxy_xclient:1; +}; + +bool client_skip_line(struct managesieve_client *client); + +enum managesieve_cmd_reply { + MANAGESIEVE_CMD_REPLY_OK, + MANAGESIEVE_CMD_REPLY_NO, + MANAGESIEVE_CMD_REPLY_BYE +}; + +void client_send_reply(struct client *client, enum managesieve_cmd_reply reply, + const char *text); + +void client_send_reply_code(struct client *client, + enum managesieve_cmd_reply reply, + const char *resp_code, const char *text); + +#define client_send_ok(client, text) \ + client_send_reply(client, MANAGESIEVE_CMD_REPLY_OK, text) +#define client_send_no(client, text) \ + client_send_reply(client, MANAGESIEVE_CMD_REPLY_NO, text) +#define client_send_bye(client, text) \ + client_send_reply(client, MANAGESIEVE_CMD_REPLY_BYE, text) + +#define client_send_okresp(client, resp_code, text) \ + client_send_reply_code(client, MANAGESIEVE_CMD_REPLY_OK, \ + resp_code, text) +#define client_send_noresp(client, resp_code, text) \ + client_send_reply_code(client, MANAGESIEVE_CMD_REPLY_NO, \ + resp_code, text) +#define client_send_byeresp(client, resp_code, text) \ + client_send_reply_code(client, MANAGESIEVE_CMD_REPLY_BYE, \ + resp_code, text) + +#endif diff --git a/pigeonhole/src/managesieve-login/managesieve-login-settings-plugin.c b/pigeonhole/src/managesieve-login/managesieve-login-settings-plugin.c new file mode 100644 index 0000000..12515cd --- /dev/null +++ b/pigeonhole/src/managesieve-login/managesieve-login-settings-plugin.c @@ -0,0 +1,223 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "buffer.h" +#include "env-util.h" +#include "execv-const.h" +#include "master-service.h" +#include "settings-parser.h" +#include "config-parser-private.h" +#include "managesieve-login-settings-plugin.h" + +#include <stddef.h> +#include <unistd.h> +#include <sys/wait.h> +#include <sysexits.h> + +typedef enum { CAP_SIEVE, CAP_NOTIFY } capability_type_t; + +bool capability_dumped = FALSE; +static char *capability_sieve = NULL; +static char *capability_notify = NULL; + +static void (*next_hook_config_parser_begin)(struct config_parser_context *ctx) = NULL; + +static void managesieve_login_config_parser_begin(struct config_parser_context *ctx); + +const char *managesieve_login_settings_version = DOVECOT_ABI_VERSION; + +void managesieve_login_settings_init(struct module *module ATTR_UNUSED) +{ + next_hook_config_parser_begin = hook_config_parser_begin; + hook_config_parser_begin = managesieve_login_config_parser_begin; +} + +void managesieve_login_settings_deinit(void) +{ + hook_config_parser_begin = next_hook_config_parser_begin; + + if ( capability_sieve != NULL ) + i_free(capability_sieve); + + if ( capability_notify != NULL ) + i_free(capability_notify); +} + +static void capability_store(capability_type_t cap_type, const char *value) +{ + switch ( cap_type ) { + case CAP_SIEVE: + capability_sieve = i_strdup(value); + break; + case CAP_NOTIFY: + capability_notify = i_strdup(value); + break; + } +} + +static void capability_parse(const char *cap_string) +{ + capability_type_t cap_type = CAP_SIEVE; + const char *p = cap_string; + string_t *part = t_str_new(256); + + if ( cap_string == NULL || *cap_string == '\0' ) { + i_warning("managesieve-login: capability string is empty."); + return; + } + + while ( *p != '\0' ) { + if ( *p == '\\' ) { + p++; + if ( *p != '\0' ) { + str_append_c(part, *p); + p++; + } else break; + } else if ( *p == ':' ) { + if ( strcasecmp(str_c(part), "SIEVE") == 0 ) + cap_type = CAP_SIEVE; + else if ( strcasecmp(str_c(part), "NOTIFY") == 0 ) + cap_type = CAP_NOTIFY; + else + i_warning("managesieve-login: unknown capability '%s' listed in " + "capability string (ignored).", str_c(part)); + str_truncate(part, 0); + } else if ( *p == ',' ) { + capability_store(cap_type, str_c(part)); + str_truncate(part, 0); + } else { + /* Append character, but omit leading spaces */ + if ( str_len(part) > 0 || *p != ' ' ) + str_append_c(part, *p); + } + p++; + } + + if ( str_len(part) > 0 ) { + capability_store(cap_type, str_c(part)); + } +} + +static bool capability_dump(void) +{ + char buf[4096]; + int fd[2], status = 0; + ssize_t ret; + unsigned int pos; + pid_t pid; + + if ( getenv("DUMP_CAPABILITY") != NULL ) + return TRUE; + + if ( pipe(fd) < 0 ) { + i_error("managesieve-login: dump-capability pipe() failed: %m"); + return FALSE; + } + fd_close_on_exec(fd[0], TRUE); + fd_close_on_exec(fd[1], TRUE); + + if ( (pid = fork()) == (pid_t)-1 ) { + (void)close(fd[0]); (void)close(fd[1]); + i_error("managesieve-login: dump-capability fork() failed: %m"); + return FALSE; + } + + if ( pid == 0 ) { + const char *argv[5]; + + /* Child */ + (void)close(fd[0]); + + if (dup2(fd[1], STDOUT_FILENO) < 0) + i_fatal("managesieve-login: dump-capability dup2() failed: %m"); + + env_put("DUMP_CAPABILITY", "1"); + + argv[0] = PKG_LIBEXECDIR"/managesieve"; + argv[1] = "-k"; + argv[2] = "-c"; + argv[3] = master_service_get_config_path(master_service); + argv[4] = NULL; + execv_const(argv[0], argv); + + i_fatal("managesieve-login: dump-capability execv(%s) failed: %m", argv[0]); + } + + (void)close(fd[1]); + + alarm(60); + if (wait(&status) == -1) { + i_error("managesieve-login: dump-capability failed: process %d got stuck", + (int)pid); + return FALSE; + } + alarm(0); + + if (status != 0) { + (void)close(fd[0]); + if (WIFSIGNALED(status)) { + i_error("managesieve-login: dump-capability process " + "killed with signal %d", WTERMSIG(status)); + } else { + i_error("managesieve-login: dump-capability process returned %d", + WIFEXITED(status) ? WEXITSTATUS(status) : status); + } + return FALSE; + } + + pos = 0; + while ((ret = read(fd[0], buf + pos, sizeof(buf) - pos)) > 0) + pos += ret; + + if (ret < 0) { + i_error("managesieve-login: read(dump-capability process) failed: %m"); + (void)close(fd[0]); + return FALSE; + } + (void)close(fd[0]); + + if (pos == 0 || buf[pos-1] != '\n') { + i_error("managesieve-login: dump-capability: Couldn't read capability " + "(got %u bytes)", pos); + return FALSE; + } + buf[pos-1] = '\0'; + + capability_parse(buf); + + return TRUE; +} + +static void managesieve_login_config_set +(struct config_parser_context *ctx, const char *key, const char *value) +{ + config_apply_line(ctx, key, t_strdup_printf("%s=%s", key, value), NULL); +} + +static void managesieve_login_config_parser_begin(struct config_parser_context *ctx) +{ + const char *const *module = ctx->modules; + + if ( module != NULL && *module != NULL ) { + while ( *module != NULL ) { + if ( strcmp(*module, "managesieve-login") == 0 ) + break; + module++; + } + if ( *module == NULL ) + return; + } + + if ( !capability_dumped ) { + (void)capability_dump(); + capability_dumped = TRUE; + } + + if ( capability_sieve != NULL ) + managesieve_login_config_set(ctx, "managesieve_sieve_capability", capability_sieve); + + if ( capability_notify != NULL ) + managesieve_login_config_set(ctx, "managesieve_notify_capability", capability_notify); +} diff --git a/pigeonhole/src/managesieve-login/managesieve-login-settings-plugin.h b/pigeonhole/src/managesieve-login/managesieve-login-settings-plugin.h new file mode 100644 index 0000000..ebefe1a --- /dev/null +++ b/pigeonhole/src/managesieve-login/managesieve-login-settings-plugin.h @@ -0,0 +1,9 @@ +#ifndef MANAGESIEVE_LOGIN_SETTINGS_PLUGIN_H +#define MANAGESIEVE_LOGIN_SETTINGS_PLUGIN_H + +#include "lib.h" + +void managesieve_login_settings_init(struct module *module); +void managesieve_login_settings_deinit(void); + +#endif diff --git a/pigeonhole/src/managesieve-login/managesieve-login-settings.c b/pigeonhole/src/managesieve-login/managesieve-login-settings.c new file mode 100644 index 0000000..9146d03 --- /dev/null +++ b/pigeonhole/src/managesieve-login/managesieve-login-settings.c @@ -0,0 +1,104 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "buffer.h" +#include "env-util.h" +#include "execv-const.h" +#include "settings-parser.h" +#include "service-settings.h" +#include "login-settings.h" + +#include "pigeonhole-config.h" + +#include "managesieve-login-settings.h" + +#include <stddef.h> +#include <unistd.h> +#include <sys/wait.h> +#include <sysexits.h> + +/* <settings checks> */ + +static struct inet_listener_settings managesieve_login_inet_listeners_array[] = { + { .name = "sieve", .address = "", .port = 4190 }, +}; +static struct inet_listener_settings *managesieve_login_inet_listeners[] = { + &managesieve_login_inet_listeners_array[0] +}; +static buffer_t managesieve_login_inet_listeners_buf = { + { { managesieve_login_inet_listeners, + sizeof(managesieve_login_inet_listeners) } } +}; +/* </settings checks> */ + +struct service_settings managesieve_login_settings_service_settings = { + .name = "managesieve-login", + .protocol = "sieve", + .type = "login", + .executable = "managesieve-login", + .user = "$default_login_user", + .group = "", + .privileged_group = "", + .extra_groups = "", + .chroot = "login", + + .drop_priv_before_exec = FALSE, + + .process_min_avail = 0, + .process_limit = 0, + .client_limit = 0, + .service_count = 1, + .idle_kill = 0, + .vsz_limit = (uoff_t)-1, + + .unix_listeners = ARRAY_INIT, + .fifo_listeners = ARRAY_INIT, + .inet_listeners = { { &managesieve_login_inet_listeners_buf, + sizeof(managesieve_login_inet_listeners[0]) } } +}; + +#undef DEF +#define DEF(type, name) \ + SETTING_DEFINE_STRUCT_##type(#name, name, struct managesieve_login_settings) + +static const struct setting_define managesieve_login_setting_defines[] = { + DEF(STR, managesieve_implementation_string), + DEF(STR, managesieve_sieve_capability), + DEF(STR, managesieve_notify_capability), + + SETTING_DEFINE_LIST_END +}; + +static const struct managesieve_login_settings managesieve_login_default_settings = { + .managesieve_implementation_string = DOVECOT_NAME " " PIGEONHOLE_NAME, + .managesieve_sieve_capability = "", + .managesieve_notify_capability = NULL +}; + +static const struct setting_parser_info *managesieve_login_setting_dependencies[] = { + &login_setting_parser_info, + NULL +}; + +static const struct setting_parser_info managesieve_login_setting_parser_info = { + .module_name = "managesieve-login", + .defines = managesieve_login_setting_defines, + .defaults = &managesieve_login_default_settings, + + .type_offset = (size_t)-1, + .struct_size = sizeof(struct managesieve_login_settings), + + .parent_offset = (size_t)-1, + .parent = NULL, + + .dependencies = managesieve_login_setting_dependencies +}; + +const struct setting_parser_info *managesieve_login_settings_set_roots[] = { + &login_setting_parser_info, + &managesieve_login_setting_parser_info, + NULL +}; + diff --git a/pigeonhole/src/managesieve-login/managesieve-login-settings.h b/pigeonhole/src/managesieve-login/managesieve-login-settings.h new file mode 100644 index 0000000..c6f4826 --- /dev/null +++ b/pigeonhole/src/managesieve-login/managesieve-login-settings.h @@ -0,0 +1,17 @@ +#ifndef MANAGESIEVE_LOGIN_SETTINGS_H +#define MANAGESIEVE_LOGIN_SETTINGS_H + +struct managesieve_login_settings { + const char *managesieve_implementation_string; + const char *managesieve_sieve_capability; + const char *managesieve_notify_capability; +}; + +extern const struct setting_parser_info *managesieve_login_settings_set_roots[]; + +#ifdef _CONFIG_PLUGIN +void managesieve_login_settings_init(void); +void managesieve_login_settings_deinit(void); +#endif + +#endif diff --git a/pigeonhole/src/managesieve-login/managesieve-proxy.c b/pigeonhole/src/managesieve-login/managesieve-proxy.c new file mode 100644 index 0000000..50e31ae --- /dev/null +++ b/pigeonhole/src/managesieve-login/managesieve-proxy.c @@ -0,0 +1,686 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include <string.h> +#include "login-common.h" +#include "ioloop.h" +#include "istream.h" +#include "ostream.h" +#include "str.h" +#include "str-sanitize.h" +#include "strescape.h" +#include "safe-memset.h" +#include "buffer.h" +#include "base64.h" +#include "dsasl-client.h" + +#include "client.h" +#include "client-authenticate.h" + +#include "managesieve-quote.h" +#include "managesieve-proxy.h" +#include "managesieve-parser.h" + +typedef enum { + MANAGESIEVE_RESPONSE_NONE, + MANAGESIEVE_RESPONSE_OK, + MANAGESIEVE_RESPONSE_NO, + MANAGESIEVE_RESPONSE_BYE +} managesieve_response_t; + +static const char *managesieve_proxy_state_names[MSIEVE_PROXY_STATE_COUNT] = { + "none", "tls-start", "tls-ready", "xclient", "auth" +}; + +static string_t * +proxy_compose_xclient_forward(struct managesieve_client *client) +{ + const char *const *arg; + string_t *str; + + if (*client->common.auth_passdb_args == NULL) + return NULL; + + str = t_str_new(128); + for (arg = client->common.auth_passdb_args; *arg != NULL; arg++) { + if (strncasecmp(*arg, "forward_", 8) == 0) { + if (str_len(str) > 0) + str_append_c(str, '\t'); + str_append_tabescaped(str, (*arg)+8); + } + } + if (str_len(str) == 0) + return NULL; + + return str; +} + +static void +proxy_write_xclient(struct managesieve_client *client, string_t *str) +{ + string_t *fwd = proxy_compose_xclient_forward(client); + + str_printfa(str, "XCLIENT ADDR=%s PORT=%u SESSION=%s TTL=%u", + net_ip2addr(&client->common.ip), client->common.remote_port, + client_get_session_id(&client->common), + client->common.proxy_ttl - 1); + if (fwd != NULL) { + str_append(str, " FORWARD="); + base64_encode(str_data(fwd), str_len(fwd), str); + } + str_append(str, "\r\n"); +} + +static void +proxy_write_auth_data(const unsigned char *data, unsigned int data_len, + string_t *str) +{ + if (data_len == 0) + str_append(str, "\"\""); + else { + string_t *data_str = t_str_new(128); + base64_encode(data, data_len, data_str); + managesieve_quote_append_string(str, str_c(data_str), FALSE); + } +} + +static int +proxy_write_auth(struct managesieve_client *client, string_t *str) +{ + struct dsasl_client_settings sasl_set; + const unsigned char *output; + size_t len; + const char *mech_name, *error; + + i_assert(client->common.proxy_ttl > 1); + + if (!client->proxy_sasl) { + /* Prevent sending credentials to a server that has login + disabled; i.e., due to the lack of TLS */ + login_proxy_failed(client->common.login_proxy, + login_proxy_get_event(client->common.login_proxy), + LOGIN_PROXY_FAILURE_TYPE_REMOTE_CONFIG, + "Server has disabled authentication (TLS required?)"); + return -1; + } + + if (client->common.proxy_mech == NULL) + client->common.proxy_mech = &dsasl_client_mech_plain; + + i_assert(client->common.proxy_sasl_client == NULL); + i_zero(&sasl_set); + sasl_set.authid = (client->common.proxy_master_user != NULL ? + client->common.proxy_master_user : + client->common.proxy_user); + sasl_set.authzid = client->common.proxy_user; + sasl_set.password = client->common.proxy_password; + client->common.proxy_sasl_client = + dsasl_client_new(client->common.proxy_mech, &sasl_set); + mech_name = dsasl_client_mech_get_name(client->common.proxy_mech); + + str_append(str, "AUTHENTICATE "); + managesieve_quote_append_string(str, mech_name, FALSE); + if (dsasl_client_output(client->common.proxy_sasl_client, + &output, &len, &error) < 0) { + const char *reason = t_strdup_printf( + "SASL mechanism %s init failed: %s", + mech_name, error); + login_proxy_failed(client->common.login_proxy, + login_proxy_get_event(client->common.login_proxy), + LOGIN_PROXY_FAILURE_TYPE_INTERNAL, reason); + return -1; + } + if (len > 0) { + str_append_c(str, ' '); + proxy_write_auth_data(output, len, str); + } + str_append(str, "\r\n"); + return 0; +} + +static int +proxy_input_auth_challenge(struct managesieve_client *client, const char *line, + const char **challenge_r) +{ + struct istream *input; + struct managesieve_parser *parser; + const struct managesieve_arg *args; + const char *challenge; + bool fatal = FALSE; + int ret; + + i_assert(client->common.proxy_sasl_client != NULL); + *challenge_r = NULL; + + /* Build an input stream for the managesieve parser. + FIXME: Ugly, see proxy_input_capability(). + */ + line = t_strconcat(line, "\r\n", NULL); + input = i_stream_create_from_data(line, strlen(line)); + parser = managesieve_parser_create(input, MAX_MANAGESIEVE_LINE); + managesieve_parser_reset(parser); + + (void)i_stream_read(input); + ret = managesieve_parser_read_args(parser, 1, 0, &args); + + if (ret >= 0) { + if (ret > 0 && + managesieve_arg_get_string(&args[0], &challenge)) { + *challenge_r = t_strdup(challenge); + } else { + const char *reason = t_strdup_printf( + "Server sent invalid SASL challenge line: %s", + str_sanitize(line,160)); + login_proxy_failed(client->common.login_proxy, + login_proxy_get_event(client->common.login_proxy), + LOGIN_PROXY_FAILURE_TYPE_PROTOCOL, reason); + fatal = TRUE; + } + + } else if (ret == -2) { + /* Parser needs more data (not possible on mem stream) */ + i_unreached(); + + } else { + const char *error_str = + managesieve_parser_get_error(parser, &fatal); + error_str = (error_str != NULL ? error_str : "unknown (bug)"); + + /* Do not accept faulty server */ + const char *reason = t_strdup_printf( + "Protocol parse error(%d) int SASL challenge line: %s " + "(line=`%s')", ret, error_str, line); + login_proxy_failed(client->common.login_proxy, + login_proxy_get_event(client->common.login_proxy), + LOGIN_PROXY_FAILURE_TYPE_PROTOCOL, reason); + fatal = TRUE; + } + + + /* Cleanup parser */ + managesieve_parser_destroy(&parser); + i_stream_destroy(&input); + + /* Time to exit if greeting was not accepted */ + if (fatal) + return -1; + return 0; +} + +static int +proxy_write_auth_response(struct managesieve_client *client, + const char *challenge, string_t *str) +{ + const unsigned char *data; + size_t data_len; + const char *error; + int ret; + + if (base64_decode(challenge, strlen(challenge), NULL, str) < 0) { + login_proxy_failed(client->common.login_proxy, + login_proxy_get_event(client->common.login_proxy), + LOGIN_PROXY_FAILURE_TYPE_PROTOCOL, + "Server sent invalid base64 data in AUTHENTICATE response"); + return -1; + } + ret = dsasl_client_input(client->common.proxy_sasl_client, + str_data(str), str_len(str), &error); + if (ret == 0) { + ret = dsasl_client_output(client->common.proxy_sasl_client, + &data, &data_len, &error); + } + if (ret < 0) { + const char *reason = t_strdup_printf( + "Server sent invalid authentication data: %s", error); + login_proxy_failed(client->common.login_proxy, + login_proxy_get_event(client->common.login_proxy), + LOGIN_PROXY_FAILURE_TYPE_PROTOCOL, reason); + return -1; + } + i_assert(ret == 0); + + str_truncate(str, 0); + proxy_write_auth_data(data, data_len, str); + str_append(str, "\r\n"); + return 0; +} + +static managesieve_response_t +proxy_read_response(const struct managesieve_arg *args) +{ + const char *response; + + if (managesieve_arg_get_atom(&args[0], &response)) { + if (strcasecmp(response, "OK") == 0) { + /* Received OK response; greeting is finished */ + return MANAGESIEVE_RESPONSE_OK; + + } else if (strcasecmp(response, "NO") == 0) { + /* Received OK response; greeting is finished */ + return MANAGESIEVE_RESPONSE_NO; + + } else if (strcasecmp(response, "BYE") == 0) { + /* Received OK response; greeting is finished */ + return MANAGESIEVE_RESPONSE_BYE; + } + } + return MANAGESIEVE_RESPONSE_NONE; +} + +static int +proxy_input_capability(struct managesieve_client *client, const char *line, + managesieve_response_t *resp_r) +{ + struct istream *input; + struct managesieve_parser *parser; + const struct managesieve_arg *args; + const char *capability; + int ret; + bool fatal = FALSE; + + *resp_r = MANAGESIEVE_RESPONSE_NONE; + + /* Build an input stream for the managesieve parser + + FIXME: It would be nice if the line-wise parsing could be substituded + by something similar to the command line interpreter. However, + the current login_proxy structure does not make streams known + until inside proxy_input handler. + */ + line = t_strconcat(line, "\r\n", NULL); + input = i_stream_create_from_data(line, strlen(line)); + parser = managesieve_parser_create(input, MAX_MANAGESIEVE_LINE); + managesieve_parser_reset(parser); + + /* Parse input + + FIXME: Theoretically the OK response could include a response code + which could be rejected by the parser. + */ + (void)i_stream_read(input); + ret = managesieve_parser_read_args(parser, 2, 0, &args); + + if (ret == 0) { + const char *reason = t_strdup_printf( + "Remote returned with invalid capability/greeting line: %s", + str_sanitize(line,160)); + login_proxy_failed(client->common.login_proxy, + login_proxy_get_event(client->common.login_proxy), + LOGIN_PROXY_FAILURE_TYPE_PROTOCOL, reason); + fatal = TRUE; + } else if (ret > 0) { + if (args[0].type == MANAGESIEVE_ARG_ATOM) { + *resp_r = proxy_read_response(args); + + if (*resp_r == MANAGESIEVE_RESPONSE_NONE) { + const char *reason = t_strdup_printf( + "Remote sent invalid response: %s", + str_sanitize(line,160)); + login_proxy_failed(client->common.login_proxy, + login_proxy_get_event(client->common.login_proxy), + LOGIN_PROXY_FAILURE_TYPE_PROTOCOL, + reason); + + fatal = TRUE; + } + } else if (managesieve_arg_get_string(&args[0], &capability)) { + if (strcasecmp(capability, "SASL") == 0) { + const char *sasl_mechs; + + /* Check whether the server supports the SASL mechanism + we are going to use (currently only PLAIN supported). + */ + if (ret == 2 && + managesieve_arg_get_string(&args[1], &sasl_mechs)) { + const char *const *mechs = t_strsplit(sasl_mechs, " "); + + if (*mechs != NULL) { + /* At least one SASL mechanism is supported */ + client->proxy_sasl = TRUE; + } + + } else { + login_proxy_failed(client->common.login_proxy, + login_proxy_get_event(client->common.login_proxy), + LOGIN_PROXY_FAILURE_TYPE_PROTOCOL, + "Server returned erroneous SASL capability"); + fatal = TRUE; + } + + } else if (strcasecmp(capability, "STARTTLS") == 0) { + client->proxy_starttls = TRUE; + } else if (strcasecmp(capability, "XCLIENT") == 0) { + client->proxy_xclient = TRUE; + } + + } else { + /* Do not accept faulty server */ + const char *reason = t_strdup_printf( + "Remote returned with invalid capability/greeting line: %s", + str_sanitize(line,160)); + login_proxy_failed(client->common.login_proxy, + login_proxy_get_event(client->common.login_proxy), + LOGIN_PROXY_FAILURE_TYPE_PROTOCOL, reason); + fatal = TRUE; + } + + } else if (ret == -2) { + /* Parser needs more data (not possible on mem stream) */ + i_unreached(); + + } else { + const char *error_str = + managesieve_parser_get_error(parser, &fatal); + error_str = (error_str != NULL ? error_str : "unknown (bug)"); + + /* Do not accept faulty server */ + const char *reason = t_strdup_printf( + "Protocol parse error(%d) in capability/greeting line: %s " + "(line=`%s')", ret, error_str, line); + login_proxy_failed(client->common.login_proxy, + login_proxy_get_event(client->common.login_proxy), + LOGIN_PROXY_FAILURE_TYPE_PROTOCOL, reason); + fatal = TRUE; + } + + /* Cleanup parser */ + managesieve_parser_destroy(&parser); + i_stream_destroy(&input); + + /* Time to exit if greeting was not accepted */ + if (fatal) + return -1; + + /* Wait until greeting is received completely */ + if (*resp_r == MANAGESIEVE_RESPONSE_NONE) + return 1; + + return 0; +} + +static void +managesieve_proxy_parse_auth_reply(const char *line, + const char **reason_r, bool *trylater_r) +{ + struct managesieve_parser *parser; + const struct managesieve_arg *args; + struct istream *input; + const char *reason; + int ret; + + *trylater_r = FALSE; + + if (strncasecmp(line, "NO ", 3) != 0) { + *reason_r = line; + return; + } + line += 3; + *reason_r = line; + + if (line[0] == '(') { + /* Parse optional resp-code. FIXME: The current + managesieve-parser can't really handle this properly, so + we'll just assume that there aren't any strings with ')' + in them. */ + if (strncasecmp(line, "(TRYLATER) ", 11) == 0) { + *trylater_r = TRUE; + line += 11; + } else { + line = strstr(line, ") "); + if (line == NULL) + return; + line += 2; + } + } + + /* Parse the string */ + input = i_stream_create_from_data(line, strlen(line)); + parser = managesieve_parser_create(input, (size_t)-1); + (void)i_stream_read(input); + ret = managesieve_parser_finish_line(parser, 0, 0, &args); + if (ret == 1 && managesieve_arg_get_string(&args[0], &reason)) + *reason_r = t_strdup(reason); + managesieve_parser_destroy(&parser); + i_stream_destroy(&input); +} + +int managesieve_proxy_parse_line(struct client *client, const char *line) +{ + struct managesieve_client *msieve_client = + (struct managesieve_client *)client; + struct ostream *output; + enum login_proxy_ssl_flags ssl_flags; + managesieve_response_t response = MANAGESIEVE_RESPONSE_NONE; + string_t *command; + int ret = 0; + + i_assert(!client->destroyed); + + output = login_proxy_get_ostream(client->login_proxy); + switch (msieve_client->proxy_state) { + case MSIEVE_PROXY_STATE_NONE: + ret = proxy_input_capability(msieve_client, line, &response); + if (ret < 0) + return -1; + if (ret == 0) { + if (response != MANAGESIEVE_RESPONSE_OK) { + login_proxy_failed(client->login_proxy, + login_proxy_get_event(client->login_proxy), + LOGIN_PROXY_FAILURE_TYPE_PROTOCOL, + "Remote sent unexpected NO/BYE instead of capability response"); + return -1; + } + + command = t_str_new(128); + + ssl_flags = login_proxy_get_ssl_flags(client->login_proxy); + if ((ssl_flags & PROXY_SSL_FLAG_STARTTLS) != 0) { + if (!msieve_client->proxy_starttls) { + login_proxy_failed(client->login_proxy, + login_proxy_get_event(client->login_proxy), + LOGIN_PROXY_FAILURE_TYPE_REMOTE_CONFIG, + "Remote doesn't support STARTTLS"); + return -1; + } + + str_append(command, "STARTTLS\r\n"); + msieve_client->proxy_state = MSIEVE_PROXY_STATE_TLS_START; + } else if (msieve_client->proxy_xclient) { + proxy_write_xclient(msieve_client, command); + msieve_client->proxy_state = MSIEVE_PROXY_STATE_XCLIENT; + } else { + if (proxy_write_auth(msieve_client, command) < 0) + return -1; + msieve_client->proxy_state = MSIEVE_PROXY_STATE_AUTH; + } + + o_stream_nsend(output, str_data(command), str_len(command)); + } + return 0; + case MSIEVE_PROXY_STATE_TLS_START: + if (strncasecmp(line, "OK", 2) == 0 && + (strlen(line) == 2 || line[2] == ' ')) { + /* STARTTLS successful, begin TLS negotiation. */ + if (login_proxy_starttls(client->login_proxy) < 0) + return -1; + + msieve_client->proxy_sasl = FALSE; + msieve_client->proxy_xclient = FALSE; + msieve_client->proxy_state = MSIEVE_PROXY_STATE_TLS_READY; + return 1; + } + + login_proxy_failed(client->login_proxy, + login_proxy_get_event(client->login_proxy), + LOGIN_PROXY_FAILURE_TYPE_REMOTE, + "Remote refused STARTTLS command"); + return -1; + case MSIEVE_PROXY_STATE_TLS_READY: + ret = proxy_input_capability(msieve_client, line, &response); + if (ret < 0) + return -1; + if (ret == 0) { + if (response != MANAGESIEVE_RESPONSE_OK) { + /* STARTTLS failed */ + const char *reason = t_strdup_printf( + "Remote STARTTLS failed: %s", + str_sanitize(line, 160)); + login_proxy_failed(client->login_proxy, + login_proxy_get_event(client->login_proxy), + LOGIN_PROXY_FAILURE_TYPE_REMOTE, reason); + return -1; + } + + command = t_str_new(128); + if (msieve_client->proxy_xclient) { + proxy_write_xclient(msieve_client, command); + msieve_client->proxy_state = MSIEVE_PROXY_STATE_XCLIENT; + } else { + if (proxy_write_auth(msieve_client, command) < 0) + return -1; + msieve_client->proxy_state = MSIEVE_PROXY_STATE_AUTH; + } + o_stream_nsend(output, str_data(command), str_len(command)); + } + return 0; + case MSIEVE_PROXY_STATE_XCLIENT: + if (strncasecmp(line, "OK", 2) == 0 && + (strlen(line) == 2 || line[2] == ' ')) { + command = t_str_new(128); + if (proxy_write_auth(msieve_client, command) < 0) + return -1; + o_stream_nsend(output, str_data(command), str_len(command)); + msieve_client->proxy_state = MSIEVE_PROXY_STATE_AUTH; + return 0; + } + + const char *reason = t_strdup_printf( + "Remote XCLIENT failed: %s", + str_sanitize(line, 160)); + login_proxy_failed(client->login_proxy, + login_proxy_get_event(client->login_proxy), + LOGIN_PROXY_FAILURE_TYPE_REMOTE, reason); + return -1; + case MSIEVE_PROXY_STATE_AUTH: + /* Challenge? */ + if (*line == '"') { + const char *challenge; + + if (proxy_input_auth_challenge(msieve_client, line, + &challenge) < 0) + return -1; + command = t_str_new(128); + if (proxy_write_auth_response(msieve_client, challenge, + command) < 0) + return -1; + o_stream_nsend(output, str_data(command), + str_len(command)); + return 0; + } + + /* Check login status */ + if (strncasecmp(line, "OK", 2) == 0 && + (strlen(line) == 2 || line[2] == ' ')) { + string_t *str = t_str_new(128); + + /* Login successful */ + + /* FIXME: Some SASL mechanisms cause a capability + response to be sent. + */ + + /* Send this line to client. */ + str_append(str, line); + str_append(str, "\r\n"); + o_stream_nsend(client->output, str_data(str), + str_len(str)); + + client_proxy_finish_destroy_client(client); + return 1; + } + + /* Authentication failed */ + bool try_later; + (void)managesieve_proxy_parse_auth_reply(line, &reason, + &try_later); + + /* Login failed. Send our own failure reply so client can't + figure out if user exists or not just by looking at the reply + string. + */ + enum login_proxy_failure_type failure_type; + if (try_later) + failure_type = LOGIN_PROXY_FAILURE_TYPE_AUTH_TEMPFAIL; + else { + failure_type = LOGIN_PROXY_FAILURE_TYPE_AUTH; + client_send_no(client, AUTH_FAILED_MSG); + } + + login_proxy_failed(client->login_proxy, + login_proxy_get_event(client->login_proxy), + failure_type, reason); + return -1; + default: + /* Not supposed to happen */ + break; + } + + i_unreached(); + return -1; +} + +void managesieve_proxy_reset(struct client *client) +{ + struct managesieve_client *msieve_client = + (struct managesieve_client *)client; + + msieve_client->proxy_starttls = FALSE; + msieve_client->proxy_sasl = FALSE; + msieve_client->proxy_xclient = FALSE; + msieve_client->proxy_state = MSIEVE_PROXY_STATE_NONE; +} + +static void +managesieve_proxy_send_failure_reply(struct client *client, + enum login_proxy_failure_type type, + const char *reason) +{ + switch (type) { + case LOGIN_PROXY_FAILURE_TYPE_CONNECT: + case LOGIN_PROXY_FAILURE_TYPE_INTERNAL: + case LOGIN_PROXY_FAILURE_TYPE_REMOTE: + case LOGIN_PROXY_FAILURE_TYPE_PROTOCOL: + client_send_reply_code(client, MANAGESIEVE_CMD_REPLY_NO, + "TRYLATER", LOGIN_PROXY_FAILURE_MSG); + break; + case LOGIN_PROXY_FAILURE_TYPE_INTERNAL_CONFIG: + case LOGIN_PROXY_FAILURE_TYPE_REMOTE_CONFIG: + client_send_reply_code(client, MANAGESIEVE_CMD_REPLY_NO, + NULL, LOGIN_PROXY_FAILURE_MSG); + break; + case LOGIN_PROXY_FAILURE_TYPE_AUTH_TEMPFAIL: + client_send_reply_code(client, MANAGESIEVE_CMD_REPLY_NO, + "TRYLATER", reason); + break; + case LOGIN_PROXY_FAILURE_TYPE_AUTH: + /* reply was already sent */ + break; + } +} + +void managesieve_proxy_failed(struct client *client, + enum login_proxy_failure_type type, + const char *reason, bool reconnecting) +{ + if (!reconnecting) + managesieve_proxy_send_failure_reply(client, type, reason); + client_common_proxy_failed(client, type, reason, reconnecting); +} + +const char *managesieve_proxy_get_state(struct client *client) +{ + struct managesieve_client *msieve_client = + (struct managesieve_client *)client; + + return managesieve_proxy_state_names[msieve_client->proxy_state]; +} diff --git a/pigeonhole/src/managesieve-login/managesieve-proxy.h b/pigeonhole/src/managesieve-login/managesieve-proxy.h new file mode 100644 index 0000000..946f074 --- /dev/null +++ b/pigeonhole/src/managesieve-login/managesieve-proxy.h @@ -0,0 +1,12 @@ +#ifndef MANAGESIEVE_PROXY_H +#define MANAGESIEVE_PROXY_H + +void managesieve_proxy_reset(struct client *client); +int managesieve_proxy_parse_line(struct client *client, const char *line); + +void managesieve_proxy_failed(struct client *client, + enum login_proxy_failure_type type, + const char *reason, bool reconnecting); +const char *managesieve_proxy_get_state(struct client *client); + +#endif diff --git a/pigeonhole/src/managesieve/Makefile.am b/pigeonhole/src/managesieve/Makefile.am new file mode 100644 index 0000000..17b5790 --- /dev/null +++ b/pigeonhole/src/managesieve/Makefile.am @@ -0,0 +1,59 @@ +settingsdir = $(dovecot_moduledir)/settings + +dovecot_pkglibexec_PROGRAMS = managesieve + +AM_CPPFLAGS = \ + $(LIBDOVECOT_INCLUDE) \ + $(LIBDOVECOT_SERVICE_INCLUDE) \ + -DMODULEDIR=\""$(dovecot_moduledir)"\" \ + -I$(top_srcdir) \ + -I$(top_srcdir)/src/lib-sieve \ + -I$(top_srcdir)/src/lib-managesieve + +libmanagesieve_settings_la_LDFLAGS = -module -avoid-version + +settings_LTLIBRARIES = \ + libmanagesieve_settings.la + +libmanagesieve_settings_la_SOURCES = \ + managesieve-settings.c + +libs = \ + managesieve-settings.lo \ + $(top_builddir)/src/lib-managesieve/libmanagesieve.la \ + $(top_builddir)/src/lib-sieve/libdovecot-sieve.la + +managesieve_CPPFLAGS = $(AM_CPPFLAGS) $(BINARY_CFLAGS) +managesieve_LDFLAGS = -export-dynamic $(BINARY_LDFLAGS) + +managesieve_LDADD = $(libs) $(LIBDOVECOT_STORAGE) $(LIBDOVECOT_LDA) $(LIBDOVECOT) + +managesieve_DEPENDENCIES = $(libs) $(LIBDOVECOT_STORAGE_DEPS) $(LIBDOVECOT_LDA_DEPS) $(LIBDOVECOT_DEPS) + +cmds = \ + cmd-capability.c \ + cmd-logout.c \ + cmd-putscript.c \ + cmd-getscript.c \ + cmd-setactive.c \ + cmd-deletescript.c \ + cmd-listscripts.c \ + cmd-havespace.c \ + cmd-renamescript.c \ + cmd-noop.c + +managesieve_SOURCES = \ + $(cmds) \ + managesieve-quota.c \ + managesieve-client.c \ + managesieve-commands.c \ + managesieve-capabilities.c \ + main.c + +noinst_HEADERS = \ + managesieve-quota.h \ + managesieve-client.h \ + managesieve-commands.h \ + managesieve-capabilities.h \ + managesieve-settings.h \ + managesieve-common.h diff --git a/pigeonhole/src/managesieve/Makefile.in b/pigeonhole/src/managesieve/Makefile.in new file mode 100644 index 0000000..d8f9faa --- /dev/null +++ b/pigeonhole/src/managesieve/Makefile.in @@ -0,0 +1,1134 @@ +# 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@ +dovecot_pkglibexec_PROGRAMS = managesieve$(EXEEXT) +subdir = src/managesieve +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 = +am__installdirs = "$(DESTDIR)$(dovecot_pkglibexecdir)" \ + "$(DESTDIR)$(settingsdir)" +PROGRAMS = $(dovecot_pkglibexec_PROGRAMS) +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +LTLIBRARIES = $(settings_LTLIBRARIES) +libmanagesieve_settings_la_LIBADD = +am_libmanagesieve_settings_la_OBJECTS = managesieve-settings.lo +libmanagesieve_settings_la_OBJECTS = \ + $(am_libmanagesieve_settings_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 = +libmanagesieve_settings_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(libmanagesieve_settings_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +am__objects_1 = managesieve-cmd-capability.$(OBJEXT) \ + managesieve-cmd-logout.$(OBJEXT) \ + managesieve-cmd-putscript.$(OBJEXT) \ + managesieve-cmd-getscript.$(OBJEXT) \ + managesieve-cmd-setactive.$(OBJEXT) \ + managesieve-cmd-deletescript.$(OBJEXT) \ + managesieve-cmd-listscripts.$(OBJEXT) \ + managesieve-cmd-havespace.$(OBJEXT) \ + managesieve-cmd-renamescript.$(OBJEXT) \ + managesieve-cmd-noop.$(OBJEXT) +am_managesieve_OBJECTS = $(am__objects_1) \ + managesieve-managesieve-quota.$(OBJEXT) \ + managesieve-managesieve-client.$(OBJEXT) \ + managesieve-managesieve-commands.$(OBJEXT) \ + managesieve-managesieve-capabilities.$(OBJEXT) \ + managesieve-main.$(OBJEXT) +managesieve_OBJECTS = $(am_managesieve_OBJECTS) +am__DEPENDENCIES_1 = +managesieve_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(managesieve_LDFLAGS) $(LDFLAGS) -o $@ +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)/managesieve-cmd-capability.Po \ + ./$(DEPDIR)/managesieve-cmd-deletescript.Po \ + ./$(DEPDIR)/managesieve-cmd-getscript.Po \ + ./$(DEPDIR)/managesieve-cmd-havespace.Po \ + ./$(DEPDIR)/managesieve-cmd-listscripts.Po \ + ./$(DEPDIR)/managesieve-cmd-logout.Po \ + ./$(DEPDIR)/managesieve-cmd-noop.Po \ + ./$(DEPDIR)/managesieve-cmd-putscript.Po \ + ./$(DEPDIR)/managesieve-cmd-renamescript.Po \ + ./$(DEPDIR)/managesieve-cmd-setactive.Po \ + ./$(DEPDIR)/managesieve-main.Po \ + ./$(DEPDIR)/managesieve-managesieve-capabilities.Po \ + ./$(DEPDIR)/managesieve-managesieve-client.Po \ + ./$(DEPDIR)/managesieve-managesieve-commands.Po \ + ./$(DEPDIR)/managesieve-managesieve-quota.Po \ + ./$(DEPDIR)/managesieve-settings.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 = $(libmanagesieve_settings_la_SOURCES) $(managesieve_SOURCES) +DIST_SOURCES = $(libmanagesieve_settings_la_SOURCES) \ + $(managesieve_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@ +settingsdir = $(dovecot_moduledir)/settings +AM_CPPFLAGS = \ + $(LIBDOVECOT_INCLUDE) \ + $(LIBDOVECOT_SERVICE_INCLUDE) \ + -DMODULEDIR=\""$(dovecot_moduledir)"\" \ + -I$(top_srcdir) \ + -I$(top_srcdir)/src/lib-sieve \ + -I$(top_srcdir)/src/lib-managesieve + +libmanagesieve_settings_la_LDFLAGS = -module -avoid-version +settings_LTLIBRARIES = \ + libmanagesieve_settings.la + +libmanagesieve_settings_la_SOURCES = \ + managesieve-settings.c + +libs = \ + managesieve-settings.lo \ + $(top_builddir)/src/lib-managesieve/libmanagesieve.la \ + $(top_builddir)/src/lib-sieve/libdovecot-sieve.la + +managesieve_CPPFLAGS = $(AM_CPPFLAGS) $(BINARY_CFLAGS) +managesieve_LDFLAGS = -export-dynamic $(BINARY_LDFLAGS) +managesieve_LDADD = $(libs) $(LIBDOVECOT_STORAGE) $(LIBDOVECOT_LDA) $(LIBDOVECOT) +managesieve_DEPENDENCIES = $(libs) $(LIBDOVECOT_STORAGE_DEPS) $(LIBDOVECOT_LDA_DEPS) $(LIBDOVECOT_DEPS) +cmds = \ + cmd-capability.c \ + cmd-logout.c \ + cmd-putscript.c \ + cmd-getscript.c \ + cmd-setactive.c \ + cmd-deletescript.c \ + cmd-listscripts.c \ + cmd-havespace.c \ + cmd-renamescript.c \ + cmd-noop.c + +managesieve_SOURCES = \ + $(cmds) \ + managesieve-quota.c \ + managesieve-client.c \ + managesieve-commands.c \ + managesieve-capabilities.c \ + main.c + +noinst_HEADERS = \ + managesieve-quota.h \ + managesieve-client.h \ + managesieve-commands.h \ + managesieve-capabilities.h \ + managesieve-settings.h \ + managesieve-common.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/managesieve/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/managesieve/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): +install-dovecot_pkglibexecPROGRAMS: $(dovecot_pkglibexec_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(dovecot_pkglibexec_PROGRAMS)'; test -n "$(dovecot_pkglibexecdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(dovecot_pkglibexecdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(dovecot_pkglibexecdir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(dovecot_pkglibexecdir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(dovecot_pkglibexecdir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-dovecot_pkglibexecPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(dovecot_pkglibexec_PROGRAMS)'; test -n "$(dovecot_pkglibexecdir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(dovecot_pkglibexecdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(dovecot_pkglibexecdir)" && rm -f $$files + +clean-dovecot_pkglibexecPROGRAMS: + @list='$(dovecot_pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +install-settingsLTLIBRARIES: $(settings_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(settings_LTLIBRARIES)'; test -n "$(settingsdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(settingsdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(settingsdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(settingsdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(settingsdir)"; \ + } + +uninstall-settingsLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(settings_LTLIBRARIES)'; test -n "$(settingsdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(settingsdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(settingsdir)/$$f"; \ + done + +clean-settingsLTLIBRARIES: + -test -z "$(settings_LTLIBRARIES)" || rm -f $(settings_LTLIBRARIES) + @list='$(settings_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}; \ + } + +libmanagesieve_settings.la: $(libmanagesieve_settings_la_OBJECTS) $(libmanagesieve_settings_la_DEPENDENCIES) $(EXTRA_libmanagesieve_settings_la_DEPENDENCIES) + $(AM_V_CCLD)$(libmanagesieve_settings_la_LINK) -rpath $(settingsdir) $(libmanagesieve_settings_la_OBJECTS) $(libmanagesieve_settings_la_LIBADD) $(LIBS) + +managesieve$(EXEEXT): $(managesieve_OBJECTS) $(managesieve_DEPENDENCIES) $(EXTRA_managesieve_DEPENDENCIES) + @rm -f managesieve$(EXEEXT) + $(AM_V_CCLD)$(managesieve_LINK) $(managesieve_OBJECTS) $(managesieve_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/managesieve-cmd-capability.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/managesieve-cmd-deletescript.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/managesieve-cmd-getscript.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/managesieve-cmd-havespace.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/managesieve-cmd-listscripts.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/managesieve-cmd-logout.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/managesieve-cmd-noop.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/managesieve-cmd-putscript.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/managesieve-cmd-renamescript.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/managesieve-cmd-setactive.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/managesieve-main.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/managesieve-managesieve-capabilities.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/managesieve-managesieve-client.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/managesieve-managesieve-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/managesieve-managesieve-quota.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/managesieve-settings.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 $@ $< + +managesieve-cmd-capability.o: cmd-capability.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-cmd-capability.o -MD -MP -MF $(DEPDIR)/managesieve-cmd-capability.Tpo -c -o managesieve-cmd-capability.o `test -f 'cmd-capability.c' || echo '$(srcdir)/'`cmd-capability.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-cmd-capability.Tpo $(DEPDIR)/managesieve-cmd-capability.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cmd-capability.c' object='managesieve-cmd-capability.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-cmd-capability.o `test -f 'cmd-capability.c' || echo '$(srcdir)/'`cmd-capability.c + +managesieve-cmd-capability.obj: cmd-capability.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-cmd-capability.obj -MD -MP -MF $(DEPDIR)/managesieve-cmd-capability.Tpo -c -o managesieve-cmd-capability.obj `if test -f 'cmd-capability.c'; then $(CYGPATH_W) 'cmd-capability.c'; else $(CYGPATH_W) '$(srcdir)/cmd-capability.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-cmd-capability.Tpo $(DEPDIR)/managesieve-cmd-capability.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cmd-capability.c' object='managesieve-cmd-capability.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-cmd-capability.obj `if test -f 'cmd-capability.c'; then $(CYGPATH_W) 'cmd-capability.c'; else $(CYGPATH_W) '$(srcdir)/cmd-capability.c'; fi` + +managesieve-cmd-logout.o: cmd-logout.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-cmd-logout.o -MD -MP -MF $(DEPDIR)/managesieve-cmd-logout.Tpo -c -o managesieve-cmd-logout.o `test -f 'cmd-logout.c' || echo '$(srcdir)/'`cmd-logout.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-cmd-logout.Tpo $(DEPDIR)/managesieve-cmd-logout.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cmd-logout.c' object='managesieve-cmd-logout.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-cmd-logout.o `test -f 'cmd-logout.c' || echo '$(srcdir)/'`cmd-logout.c + +managesieve-cmd-logout.obj: cmd-logout.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-cmd-logout.obj -MD -MP -MF $(DEPDIR)/managesieve-cmd-logout.Tpo -c -o managesieve-cmd-logout.obj `if test -f 'cmd-logout.c'; then $(CYGPATH_W) 'cmd-logout.c'; else $(CYGPATH_W) '$(srcdir)/cmd-logout.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-cmd-logout.Tpo $(DEPDIR)/managesieve-cmd-logout.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cmd-logout.c' object='managesieve-cmd-logout.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-cmd-logout.obj `if test -f 'cmd-logout.c'; then $(CYGPATH_W) 'cmd-logout.c'; else $(CYGPATH_W) '$(srcdir)/cmd-logout.c'; fi` + +managesieve-cmd-putscript.o: cmd-putscript.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-cmd-putscript.o -MD -MP -MF $(DEPDIR)/managesieve-cmd-putscript.Tpo -c -o managesieve-cmd-putscript.o `test -f 'cmd-putscript.c' || echo '$(srcdir)/'`cmd-putscript.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-cmd-putscript.Tpo $(DEPDIR)/managesieve-cmd-putscript.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cmd-putscript.c' object='managesieve-cmd-putscript.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-cmd-putscript.o `test -f 'cmd-putscript.c' || echo '$(srcdir)/'`cmd-putscript.c + +managesieve-cmd-putscript.obj: cmd-putscript.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-cmd-putscript.obj -MD -MP -MF $(DEPDIR)/managesieve-cmd-putscript.Tpo -c -o managesieve-cmd-putscript.obj `if test -f 'cmd-putscript.c'; then $(CYGPATH_W) 'cmd-putscript.c'; else $(CYGPATH_W) '$(srcdir)/cmd-putscript.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-cmd-putscript.Tpo $(DEPDIR)/managesieve-cmd-putscript.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cmd-putscript.c' object='managesieve-cmd-putscript.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-cmd-putscript.obj `if test -f 'cmd-putscript.c'; then $(CYGPATH_W) 'cmd-putscript.c'; else $(CYGPATH_W) '$(srcdir)/cmd-putscript.c'; fi` + +managesieve-cmd-getscript.o: cmd-getscript.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-cmd-getscript.o -MD -MP -MF $(DEPDIR)/managesieve-cmd-getscript.Tpo -c -o managesieve-cmd-getscript.o `test -f 'cmd-getscript.c' || echo '$(srcdir)/'`cmd-getscript.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-cmd-getscript.Tpo $(DEPDIR)/managesieve-cmd-getscript.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cmd-getscript.c' object='managesieve-cmd-getscript.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-cmd-getscript.o `test -f 'cmd-getscript.c' || echo '$(srcdir)/'`cmd-getscript.c + +managesieve-cmd-getscript.obj: cmd-getscript.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-cmd-getscript.obj -MD -MP -MF $(DEPDIR)/managesieve-cmd-getscript.Tpo -c -o managesieve-cmd-getscript.obj `if test -f 'cmd-getscript.c'; then $(CYGPATH_W) 'cmd-getscript.c'; else $(CYGPATH_W) '$(srcdir)/cmd-getscript.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-cmd-getscript.Tpo $(DEPDIR)/managesieve-cmd-getscript.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cmd-getscript.c' object='managesieve-cmd-getscript.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-cmd-getscript.obj `if test -f 'cmd-getscript.c'; then $(CYGPATH_W) 'cmd-getscript.c'; else $(CYGPATH_W) '$(srcdir)/cmd-getscript.c'; fi` + +managesieve-cmd-setactive.o: cmd-setactive.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-cmd-setactive.o -MD -MP -MF $(DEPDIR)/managesieve-cmd-setactive.Tpo -c -o managesieve-cmd-setactive.o `test -f 'cmd-setactive.c' || echo '$(srcdir)/'`cmd-setactive.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-cmd-setactive.Tpo $(DEPDIR)/managesieve-cmd-setactive.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cmd-setactive.c' object='managesieve-cmd-setactive.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-cmd-setactive.o `test -f 'cmd-setactive.c' || echo '$(srcdir)/'`cmd-setactive.c + +managesieve-cmd-setactive.obj: cmd-setactive.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-cmd-setactive.obj -MD -MP -MF $(DEPDIR)/managesieve-cmd-setactive.Tpo -c -o managesieve-cmd-setactive.obj `if test -f 'cmd-setactive.c'; then $(CYGPATH_W) 'cmd-setactive.c'; else $(CYGPATH_W) '$(srcdir)/cmd-setactive.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-cmd-setactive.Tpo $(DEPDIR)/managesieve-cmd-setactive.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cmd-setactive.c' object='managesieve-cmd-setactive.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-cmd-setactive.obj `if test -f 'cmd-setactive.c'; then $(CYGPATH_W) 'cmd-setactive.c'; else $(CYGPATH_W) '$(srcdir)/cmd-setactive.c'; fi` + +managesieve-cmd-deletescript.o: cmd-deletescript.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-cmd-deletescript.o -MD -MP -MF $(DEPDIR)/managesieve-cmd-deletescript.Tpo -c -o managesieve-cmd-deletescript.o `test -f 'cmd-deletescript.c' || echo '$(srcdir)/'`cmd-deletescript.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-cmd-deletescript.Tpo $(DEPDIR)/managesieve-cmd-deletescript.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cmd-deletescript.c' object='managesieve-cmd-deletescript.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-cmd-deletescript.o `test -f 'cmd-deletescript.c' || echo '$(srcdir)/'`cmd-deletescript.c + +managesieve-cmd-deletescript.obj: cmd-deletescript.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-cmd-deletescript.obj -MD -MP -MF $(DEPDIR)/managesieve-cmd-deletescript.Tpo -c -o managesieve-cmd-deletescript.obj `if test -f 'cmd-deletescript.c'; then $(CYGPATH_W) 'cmd-deletescript.c'; else $(CYGPATH_W) '$(srcdir)/cmd-deletescript.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-cmd-deletescript.Tpo $(DEPDIR)/managesieve-cmd-deletescript.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cmd-deletescript.c' object='managesieve-cmd-deletescript.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-cmd-deletescript.obj `if test -f 'cmd-deletescript.c'; then $(CYGPATH_W) 'cmd-deletescript.c'; else $(CYGPATH_W) '$(srcdir)/cmd-deletescript.c'; fi` + +managesieve-cmd-listscripts.o: cmd-listscripts.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-cmd-listscripts.o -MD -MP -MF $(DEPDIR)/managesieve-cmd-listscripts.Tpo -c -o managesieve-cmd-listscripts.o `test -f 'cmd-listscripts.c' || echo '$(srcdir)/'`cmd-listscripts.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-cmd-listscripts.Tpo $(DEPDIR)/managesieve-cmd-listscripts.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cmd-listscripts.c' object='managesieve-cmd-listscripts.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-cmd-listscripts.o `test -f 'cmd-listscripts.c' || echo '$(srcdir)/'`cmd-listscripts.c + +managesieve-cmd-listscripts.obj: cmd-listscripts.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-cmd-listscripts.obj -MD -MP -MF $(DEPDIR)/managesieve-cmd-listscripts.Tpo -c -o managesieve-cmd-listscripts.obj `if test -f 'cmd-listscripts.c'; then $(CYGPATH_W) 'cmd-listscripts.c'; else $(CYGPATH_W) '$(srcdir)/cmd-listscripts.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-cmd-listscripts.Tpo $(DEPDIR)/managesieve-cmd-listscripts.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cmd-listscripts.c' object='managesieve-cmd-listscripts.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-cmd-listscripts.obj `if test -f 'cmd-listscripts.c'; then $(CYGPATH_W) 'cmd-listscripts.c'; else $(CYGPATH_W) '$(srcdir)/cmd-listscripts.c'; fi` + +managesieve-cmd-havespace.o: cmd-havespace.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-cmd-havespace.o -MD -MP -MF $(DEPDIR)/managesieve-cmd-havespace.Tpo -c -o managesieve-cmd-havespace.o `test -f 'cmd-havespace.c' || echo '$(srcdir)/'`cmd-havespace.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-cmd-havespace.Tpo $(DEPDIR)/managesieve-cmd-havespace.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cmd-havespace.c' object='managesieve-cmd-havespace.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-cmd-havespace.o `test -f 'cmd-havespace.c' || echo '$(srcdir)/'`cmd-havespace.c + +managesieve-cmd-havespace.obj: cmd-havespace.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-cmd-havespace.obj -MD -MP -MF $(DEPDIR)/managesieve-cmd-havespace.Tpo -c -o managesieve-cmd-havespace.obj `if test -f 'cmd-havespace.c'; then $(CYGPATH_W) 'cmd-havespace.c'; else $(CYGPATH_W) '$(srcdir)/cmd-havespace.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-cmd-havespace.Tpo $(DEPDIR)/managesieve-cmd-havespace.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cmd-havespace.c' object='managesieve-cmd-havespace.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-cmd-havespace.obj `if test -f 'cmd-havespace.c'; then $(CYGPATH_W) 'cmd-havespace.c'; else $(CYGPATH_W) '$(srcdir)/cmd-havespace.c'; fi` + +managesieve-cmd-renamescript.o: cmd-renamescript.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-cmd-renamescript.o -MD -MP -MF $(DEPDIR)/managesieve-cmd-renamescript.Tpo -c -o managesieve-cmd-renamescript.o `test -f 'cmd-renamescript.c' || echo '$(srcdir)/'`cmd-renamescript.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-cmd-renamescript.Tpo $(DEPDIR)/managesieve-cmd-renamescript.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cmd-renamescript.c' object='managesieve-cmd-renamescript.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-cmd-renamescript.o `test -f 'cmd-renamescript.c' || echo '$(srcdir)/'`cmd-renamescript.c + +managesieve-cmd-renamescript.obj: cmd-renamescript.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-cmd-renamescript.obj -MD -MP -MF $(DEPDIR)/managesieve-cmd-renamescript.Tpo -c -o managesieve-cmd-renamescript.obj `if test -f 'cmd-renamescript.c'; then $(CYGPATH_W) 'cmd-renamescript.c'; else $(CYGPATH_W) '$(srcdir)/cmd-renamescript.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-cmd-renamescript.Tpo $(DEPDIR)/managesieve-cmd-renamescript.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cmd-renamescript.c' object='managesieve-cmd-renamescript.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-cmd-renamescript.obj `if test -f 'cmd-renamescript.c'; then $(CYGPATH_W) 'cmd-renamescript.c'; else $(CYGPATH_W) '$(srcdir)/cmd-renamescript.c'; fi` + +managesieve-cmd-noop.o: cmd-noop.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-cmd-noop.o -MD -MP -MF $(DEPDIR)/managesieve-cmd-noop.Tpo -c -o managesieve-cmd-noop.o `test -f 'cmd-noop.c' || echo '$(srcdir)/'`cmd-noop.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-cmd-noop.Tpo $(DEPDIR)/managesieve-cmd-noop.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cmd-noop.c' object='managesieve-cmd-noop.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-cmd-noop.o `test -f 'cmd-noop.c' || echo '$(srcdir)/'`cmd-noop.c + +managesieve-cmd-noop.obj: cmd-noop.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-cmd-noop.obj -MD -MP -MF $(DEPDIR)/managesieve-cmd-noop.Tpo -c -o managesieve-cmd-noop.obj `if test -f 'cmd-noop.c'; then $(CYGPATH_W) 'cmd-noop.c'; else $(CYGPATH_W) '$(srcdir)/cmd-noop.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-cmd-noop.Tpo $(DEPDIR)/managesieve-cmd-noop.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cmd-noop.c' object='managesieve-cmd-noop.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-cmd-noop.obj `if test -f 'cmd-noop.c'; then $(CYGPATH_W) 'cmd-noop.c'; else $(CYGPATH_W) '$(srcdir)/cmd-noop.c'; fi` + +managesieve-managesieve-quota.o: managesieve-quota.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-managesieve-quota.o -MD -MP -MF $(DEPDIR)/managesieve-managesieve-quota.Tpo -c -o managesieve-managesieve-quota.o `test -f 'managesieve-quota.c' || echo '$(srcdir)/'`managesieve-quota.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-managesieve-quota.Tpo $(DEPDIR)/managesieve-managesieve-quota.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='managesieve-quota.c' object='managesieve-managesieve-quota.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-managesieve-quota.o `test -f 'managesieve-quota.c' || echo '$(srcdir)/'`managesieve-quota.c + +managesieve-managesieve-quota.obj: managesieve-quota.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-managesieve-quota.obj -MD -MP -MF $(DEPDIR)/managesieve-managesieve-quota.Tpo -c -o managesieve-managesieve-quota.obj `if test -f 'managesieve-quota.c'; then $(CYGPATH_W) 'managesieve-quota.c'; else $(CYGPATH_W) '$(srcdir)/managesieve-quota.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-managesieve-quota.Tpo $(DEPDIR)/managesieve-managesieve-quota.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='managesieve-quota.c' object='managesieve-managesieve-quota.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-managesieve-quota.obj `if test -f 'managesieve-quota.c'; then $(CYGPATH_W) 'managesieve-quota.c'; else $(CYGPATH_W) '$(srcdir)/managesieve-quota.c'; fi` + +managesieve-managesieve-client.o: managesieve-client.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-managesieve-client.o -MD -MP -MF $(DEPDIR)/managesieve-managesieve-client.Tpo -c -o managesieve-managesieve-client.o `test -f 'managesieve-client.c' || echo '$(srcdir)/'`managesieve-client.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-managesieve-client.Tpo $(DEPDIR)/managesieve-managesieve-client.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='managesieve-client.c' object='managesieve-managesieve-client.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-managesieve-client.o `test -f 'managesieve-client.c' || echo '$(srcdir)/'`managesieve-client.c + +managesieve-managesieve-client.obj: managesieve-client.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-managesieve-client.obj -MD -MP -MF $(DEPDIR)/managesieve-managesieve-client.Tpo -c -o managesieve-managesieve-client.obj `if test -f 'managesieve-client.c'; then $(CYGPATH_W) 'managesieve-client.c'; else $(CYGPATH_W) '$(srcdir)/managesieve-client.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-managesieve-client.Tpo $(DEPDIR)/managesieve-managesieve-client.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='managesieve-client.c' object='managesieve-managesieve-client.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-managesieve-client.obj `if test -f 'managesieve-client.c'; then $(CYGPATH_W) 'managesieve-client.c'; else $(CYGPATH_W) '$(srcdir)/managesieve-client.c'; fi` + +managesieve-managesieve-commands.o: managesieve-commands.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-managesieve-commands.o -MD -MP -MF $(DEPDIR)/managesieve-managesieve-commands.Tpo -c -o managesieve-managesieve-commands.o `test -f 'managesieve-commands.c' || echo '$(srcdir)/'`managesieve-commands.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-managesieve-commands.Tpo $(DEPDIR)/managesieve-managesieve-commands.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='managesieve-commands.c' object='managesieve-managesieve-commands.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-managesieve-commands.o `test -f 'managesieve-commands.c' || echo '$(srcdir)/'`managesieve-commands.c + +managesieve-managesieve-commands.obj: managesieve-commands.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-managesieve-commands.obj -MD -MP -MF $(DEPDIR)/managesieve-managesieve-commands.Tpo -c -o managesieve-managesieve-commands.obj `if test -f 'managesieve-commands.c'; then $(CYGPATH_W) 'managesieve-commands.c'; else $(CYGPATH_W) '$(srcdir)/managesieve-commands.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-managesieve-commands.Tpo $(DEPDIR)/managesieve-managesieve-commands.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='managesieve-commands.c' object='managesieve-managesieve-commands.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-managesieve-commands.obj `if test -f 'managesieve-commands.c'; then $(CYGPATH_W) 'managesieve-commands.c'; else $(CYGPATH_W) '$(srcdir)/managesieve-commands.c'; fi` + +managesieve-managesieve-capabilities.o: managesieve-capabilities.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-managesieve-capabilities.o -MD -MP -MF $(DEPDIR)/managesieve-managesieve-capabilities.Tpo -c -o managesieve-managesieve-capabilities.o `test -f 'managesieve-capabilities.c' || echo '$(srcdir)/'`managesieve-capabilities.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-managesieve-capabilities.Tpo $(DEPDIR)/managesieve-managesieve-capabilities.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='managesieve-capabilities.c' object='managesieve-managesieve-capabilities.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-managesieve-capabilities.o `test -f 'managesieve-capabilities.c' || echo '$(srcdir)/'`managesieve-capabilities.c + +managesieve-managesieve-capabilities.obj: managesieve-capabilities.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-managesieve-capabilities.obj -MD -MP -MF $(DEPDIR)/managesieve-managesieve-capabilities.Tpo -c -o managesieve-managesieve-capabilities.obj `if test -f 'managesieve-capabilities.c'; then $(CYGPATH_W) 'managesieve-capabilities.c'; else $(CYGPATH_W) '$(srcdir)/managesieve-capabilities.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-managesieve-capabilities.Tpo $(DEPDIR)/managesieve-managesieve-capabilities.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='managesieve-capabilities.c' object='managesieve-managesieve-capabilities.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-managesieve-capabilities.obj `if test -f 'managesieve-capabilities.c'; then $(CYGPATH_W) 'managesieve-capabilities.c'; else $(CYGPATH_W) '$(srcdir)/managesieve-capabilities.c'; fi` + +managesieve-main.o: main.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-main.o -MD -MP -MF $(DEPDIR)/managesieve-main.Tpo -c -o managesieve-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-main.Tpo $(DEPDIR)/managesieve-main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='main.c' object='managesieve-main.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c + +managesieve-main.obj: main.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT managesieve-main.obj -MD -MP -MF $(DEPDIR)/managesieve-main.Tpo -c -o managesieve-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/managesieve-main.Tpo $(DEPDIR)/managesieve-main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='main.c' object='managesieve-main.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(managesieve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o managesieve-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi` + +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 $(PROGRAMS) $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(dovecot_pkglibexecdir)" "$(DESTDIR)$(settingsdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-dovecot_pkglibexecPROGRAMS clean-generic clean-libtool \ + clean-settingsLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/managesieve-cmd-capability.Po + -rm -f ./$(DEPDIR)/managesieve-cmd-deletescript.Po + -rm -f ./$(DEPDIR)/managesieve-cmd-getscript.Po + -rm -f ./$(DEPDIR)/managesieve-cmd-havespace.Po + -rm -f ./$(DEPDIR)/managesieve-cmd-listscripts.Po + -rm -f ./$(DEPDIR)/managesieve-cmd-logout.Po + -rm -f ./$(DEPDIR)/managesieve-cmd-noop.Po + -rm -f ./$(DEPDIR)/managesieve-cmd-putscript.Po + -rm -f ./$(DEPDIR)/managesieve-cmd-renamescript.Po + -rm -f ./$(DEPDIR)/managesieve-cmd-setactive.Po + -rm -f ./$(DEPDIR)/managesieve-main.Po + -rm -f ./$(DEPDIR)/managesieve-managesieve-capabilities.Po + -rm -f ./$(DEPDIR)/managesieve-managesieve-client.Po + -rm -f ./$(DEPDIR)/managesieve-managesieve-commands.Po + -rm -f ./$(DEPDIR)/managesieve-managesieve-quota.Po + -rm -f ./$(DEPDIR)/managesieve-settings.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-settingsLTLIBRARIES + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-dovecot_pkglibexecPROGRAMS + +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)/managesieve-cmd-capability.Po + -rm -f ./$(DEPDIR)/managesieve-cmd-deletescript.Po + -rm -f ./$(DEPDIR)/managesieve-cmd-getscript.Po + -rm -f ./$(DEPDIR)/managesieve-cmd-havespace.Po + -rm -f ./$(DEPDIR)/managesieve-cmd-listscripts.Po + -rm -f ./$(DEPDIR)/managesieve-cmd-logout.Po + -rm -f ./$(DEPDIR)/managesieve-cmd-noop.Po + -rm -f ./$(DEPDIR)/managesieve-cmd-putscript.Po + -rm -f ./$(DEPDIR)/managesieve-cmd-renamescript.Po + -rm -f ./$(DEPDIR)/managesieve-cmd-setactive.Po + -rm -f ./$(DEPDIR)/managesieve-main.Po + -rm -f ./$(DEPDIR)/managesieve-managesieve-capabilities.Po + -rm -f ./$(DEPDIR)/managesieve-managesieve-client.Po + -rm -f ./$(DEPDIR)/managesieve-managesieve-commands.Po + -rm -f ./$(DEPDIR)/managesieve-managesieve-quota.Po + -rm -f ./$(DEPDIR)/managesieve-settings.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-dovecot_pkglibexecPROGRAMS \ + uninstall-settingsLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-dovecot_pkglibexecPROGRAMS clean-generic clean-libtool \ + clean-settingsLTLIBRARIES 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-dovecot_pkglibexecPROGRAMS 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-settingsLTLIBRARIES install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am \ + uninstall-dovecot_pkglibexecPROGRAMS \ + uninstall-settingsLTLIBRARIES + +.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/managesieve/cmd-capability.c b/pigeonhole/src/managesieve/cmd-capability.c new file mode 100644 index 0000000..1d26eb5 --- /dev/null +++ b/pigeonhole/src/managesieve/cmd-capability.c @@ -0,0 +1,76 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "strfuncs.h" +#include "ostream.h" + +#include "sieve.h" + +#include "managesieve-common.h" +#include "managesieve-commands.h" + +static void send_capability(struct client_command_context *cmd) +{ + struct client *client = cmd->client; + const char *sievecap, *notifycap; + unsigned int max_redirects; + + /* Get capabilities */ + sievecap = sieve_get_capabilities(client->svinst, NULL); + notifycap = sieve_get_capabilities(client->svinst, "notify"); + max_redirects = sieve_max_redirects(client->svinst); + + /* Default capabilities */ + client_send_line(client, + t_strconcat( + "\"IMPLEMENTATION\" \"", + client->set->managesieve_implementation_string, + "\"", NULL)); + client_send_line(client, + t_strconcat( + "\"SIEVE\" \"", + (sievecap == NULL ? "" : sievecap), + "\"", NULL)); + + /* Maximum number of redirects (if limited) */ + if (max_redirects > 0) { + client_send_line(client, + t_strdup_printf("\"MAXREDIRECTS\" \"%u\"", + max_redirects)); + } + + /* Notify methods */ + if (notifycap != NULL) { + client_send_line(client, + t_strconcat("\"NOTIFY\" \"", notifycap, "\"", + NULL)); + } + + /* Protocol version */ + client_send_line(client, "\"VERSION\" \"1.0\""); +} + +bool cmd_capability(struct client_command_context *cmd) +{ + struct client *client = cmd->client; + + /* no arguments */ + if (!client_read_no_args(cmd)) + return FALSE; + + o_stream_cork(client->output); + + T_BEGIN { + send_capability(cmd); + } T_END; + + client_send_line(client, "OK \"Capability completed.\""); + + o_stream_uncork(client->output); + + return TRUE; + +} + diff --git a/pigeonhole/src/managesieve/cmd-deletescript.c b/pigeonhole/src/managesieve/cmd-deletescript.c new file mode 100644 index 0000000..a8be5c9 --- /dev/null +++ b/pigeonhole/src/managesieve/cmd-deletescript.c @@ -0,0 +1,43 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" + +#include "sieve.h" +#include "sieve-script.h" +#include "sieve-storage.h" + +#include "managesieve-common.h" +#include "managesieve-commands.h" + +bool cmd_deletescript(struct client_command_context *cmd) +{ + struct client *client = cmd->client; + struct sieve_storage *storage = client->storage; + const char *scriptname; + struct sieve_script *script; + + /* <script name>*/ + if (!client_read_string_args(cmd, TRUE, 1, &scriptname)) + return FALSE; + + event_add_str(cmd->event, "script_name", scriptname); + + script = sieve_storage_open_script(storage, scriptname, NULL); + if (script == NULL || sieve_script_delete(script, FALSE) < 0) { + client_command_storage_error( + cmd, "Failed to delete script `%s'", scriptname); + sieve_script_unref(&script); + return TRUE; + } + + struct event_passthrough *e = + client_command_create_finish_event(cmd); + e_debug(e->event(), "Deleted script `%s'", scriptname); + + client->deleted_count++; + client_send_ok(client, "Deletescript completed."); + + sieve_script_unref(&script); + return TRUE; +} diff --git a/pigeonhole/src/managesieve/cmd-getscript.c b/pigeonhole/src/managesieve/cmd-getscript.c new file mode 100644 index 0000000..2f0a5f9 --- /dev/null +++ b/pigeonhole/src/managesieve/cmd-getscript.c @@ -0,0 +1,157 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "ostream.h" +#include "istream.h" +#include "iostream.h" + +#include "sieve-script.h" +#include "sieve-storage.h" + +#include "managesieve-common.h" +#include "managesieve-commands.h" + +struct cmd_getscript_context { + struct client *client; + struct client_command_context *cmd; + struct sieve_storage *storage; + uoff_t script_size; + + const char *scriptname; + struct sieve_script *script; + struct istream *script_stream; + + bool failed:1; +}; + +static bool cmd_getscript_finish(struct cmd_getscript_context *ctx) +{ + struct client_command_context *cmd = ctx->cmd; + struct client *client = ctx->client; + + if (ctx->script != NULL) + sieve_script_unref(&ctx->script); + + if (ctx->failed) { + if (client->output->closed) { + client_disconnect(client, NULL); + return TRUE; + } + + client_command_storage_error( + cmd, "Failed to retrieve script `%s'", ctx->scriptname); + return TRUE; + } + + client->get_count++; + client->get_bytes += ctx->script_size; + + struct event_passthrough *e = + client_command_create_finish_event(cmd); + e_debug(e->event(), "Retrieved script `%s'", ctx->scriptname); + + client_send_line(client, ""); + client_send_ok(client, "Getscript completed."); + return TRUE; +} + +static bool cmd_getscript_continue(struct client_command_context *cmd) +{ + struct client *client = cmd->client; + struct cmd_getscript_context *ctx = cmd->context; + + switch (o_stream_send_istream(client->output, ctx->script_stream)) { + case OSTREAM_SEND_ISTREAM_RESULT_FINISHED: + if (ctx->script_stream->v_offset != ctx->script_size && + !ctx->failed) { + /* Input stream gave less data than expected */ + sieve_storage_set_critical( + ctx->storage, "GETSCRIPT for script `%s' " + "from %s got too little data: " + "%"PRIuUOFF_T" vs %"PRIuUOFF_T, + sieve_script_name(ctx->script), + sieve_script_location(ctx->script), + ctx->script_stream->v_offset, ctx->script_size); + client_disconnect(ctx->client, "GETSCRIPT failed"); + ctx->failed = TRUE; + } + break; + case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT: + i_unreached(); + case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT: + return FALSE; + case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT: + sieve_storage_set_critical(ctx->storage, + "o_stream_send_istream() failed for script `%s' " + "from %s: %s", + sieve_script_name(ctx->script), + sieve_script_location(ctx->script), + i_stream_get_error(ctx->script_stream)); + ctx->failed = TRUE; + break; + case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT: + client_disconnect(ctx->client, NULL); + ctx->failed = TRUE; + break; + } + return cmd_getscript_finish(ctx); +} + +bool cmd_getscript(struct client_command_context *cmd) +{ + struct client *client = cmd->client; + struct cmd_getscript_context *ctx; + const char *scriptname; + enum sieve_error error; + + /* <scriptname> */ + if (!client_read_string_args(cmd, TRUE, 1, &scriptname)) + return FALSE; + + event_add_str(cmd->event, "script_name", scriptname); + + ctx = p_new(cmd->pool, struct cmd_getscript_context, 1); + ctx->cmd = cmd; + ctx->client = client; + ctx->scriptname = p_strdup(cmd->pool, scriptname); + ctx->storage = client->storage; + ctx->failed = FALSE; + + ctx->script = sieve_storage_open_script(client->storage, scriptname, + NULL); + if (ctx->script == NULL) { + ctx->failed = TRUE; + return cmd_getscript_finish(ctx); + } + + if (sieve_script_get_stream(ctx->script, &ctx->script_stream, + &error) < 0 ) { + if (error == SIEVE_ERROR_NOT_FOUND) { + sieve_storage_set_error(client->storage, error, + "Script does not exist."); + } + ctx->failed = TRUE; + return cmd_getscript_finish(ctx); + } + + if (sieve_script_get_size(ctx->script, &ctx->script_size) <= 0) { + sieve_storage_set_critical(ctx->storage, + "failed to obtain script size for script `%s' from %s", + sieve_script_name(ctx->script), + sieve_script_location(ctx->script)); + ctx->failed = TRUE; + return cmd_getscript_finish(ctx); + } + + i_assert(ctx->script_stream->v_offset == 0); + + client_send_line(client, t_strdup_printf("{%"PRIuUOFF_T"}", + ctx->script_size)); + + client->command_pending = TRUE; + cmd->func = cmd_getscript_continue; + cmd->context = ctx; + + return cmd_getscript_continue(cmd); +} diff --git a/pigeonhole/src/managesieve/cmd-havespace.c b/pigeonhole/src/managesieve/cmd-havespace.c new file mode 100644 index 0000000..5af2bb1 --- /dev/null +++ b/pigeonhole/src/managesieve/cmd-havespace.c @@ -0,0 +1,60 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" + +#include "managesieve-common.h" +#include "managesieve-commands.h" + +#include "sieve.h" +#include "sieve-script.h" +#include "sieve-storage.h" + +#include "managesieve-client.h" +#include "managesieve-quota.h" + +bool cmd_havespace(struct client_command_context *cmd) +{ + struct client *client = cmd->client; + const struct managesieve_arg *args; + const char *scriptname; + uoff_t size; + + /* <scriptname> <size> */ + if (!client_read_args(cmd, 2, 0, TRUE, &args)) + return FALSE; + + if (!managesieve_arg_get_string(&args[0], &scriptname)) { + client_send_no(client, "Invalid string for scriptname."); + return TRUE; + } + + if (!managesieve_arg_get_number(&args[1], &size)) { + client_send_no(client, "Invalid scriptsize argument."); + return TRUE; + } + + if (!sieve_script_name_is_valid(scriptname)) { + client_send_no(client, "Invalid script name."); + return TRUE; + } + + if (size == 0) { + client_send_no(client, "Cannot upload empty script."); + return TRUE; + } + + event_add_str(cmd->event, "script_name", scriptname); + event_add_int(cmd->event, "script_size", size); + + if (!managesieve_quota_check_all(cmd, scriptname, size)) + return TRUE; + + struct event_passthrough *e = + client_command_create_finish_event(cmd); + e_debug(e->event(), "Quota is within limits for script `%s' " + "with size %"PRIuUOFF_T, scriptname, size); + + client_send_ok(client, "Putscript would succeed."); + return TRUE; +} diff --git a/pigeonhole/src/managesieve/cmd-listscripts.c b/pigeonhole/src/managesieve/cmd-listscripts.c new file mode 100644 index 0000000..9e6abd2 --- /dev/null +++ b/pigeonhole/src/managesieve/cmd-listscripts.c @@ -0,0 +1,65 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" + +#include "sieve.h" +#include "sieve-storage.h" + +#include "managesieve-quote.h" + +#include "managesieve-common.h" +#include "managesieve-commands.h" + +bool cmd_listscripts(struct client_command_context *cmd) +{ + struct client *client = cmd->client; + struct sieve_storage_list_context *ctx; + const char *scriptname; + unsigned int script_count = 0; + bool active; + string_t *str; + + /* no arguments */ + if (!client_read_no_args(cmd)) + return FALSE; + + if ((ctx = sieve_storage_list_init(client->storage)) == NULL) { + client_command_storage_error( + cmd, "Failed to list scripts"); + return TRUE; + } + + /* FIXME: This will be quite slow for large script lists. Implement + * some buffering to fix this. Wont truely be an issue with managesieve + * though. + */ + while ((scriptname = sieve_storage_list_next(ctx, &active)) != NULL) { + T_BEGIN { + str = t_str_new(128); + + managesieve_quote_append_string(str, scriptname, FALSE); + + if (active) + str_append(str, " ACTIVE"); + + client_send_line(client, str_c(str)); + } T_END; + + script_count++; + } + + if (sieve_storage_list_deinit(&ctx) < 0) { + client_command_storage_error( + cmd, "Failed to list scripts"); + return TRUE; + } + + struct event_passthrough *e = + client_command_create_finish_event(cmd); + e_debug(e->event(), "Listed %u scripts", script_count); + + client_send_ok(client, "Listscripts completed."); + return TRUE; +} diff --git a/pigeonhole/src/managesieve/cmd-logout.c b/pigeonhole/src/managesieve/cmd-logout.c new file mode 100644 index 0000000..9e0847d --- /dev/null +++ b/pigeonhole/src/managesieve/cmd-logout.c @@ -0,0 +1,21 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "ostream.h" + +#include "managesieve-common.h" +#include "managesieve-commands.h" + +bool cmd_logout(struct client_command_context *cmd) +{ + struct client *client = cmd->client; + + /* no arguments */ + if (!client_read_no_args(cmd)) + return FALSE; + + client_send_line(client, "OK \"Logout completed.\""); + client_disconnect(client, "Logged out"); + return TRUE; +} diff --git a/pigeonhole/src/managesieve/cmd-noop.c b/pigeonhole/src/managesieve/cmd-noop.c new file mode 100644 index 0000000..76d96c3 --- /dev/null +++ b/pigeonhole/src/managesieve/cmd-noop.c @@ -0,0 +1,46 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" + +#include "managesieve-quote.h" + +#include "managesieve-common.h" +#include "managesieve-commands.h" + + +bool cmd_noop(struct client_command_context *cmd) +{ + struct client *client = cmd->client; + const struct managesieve_arg *args; + const char *text; + string_t *resp_code; + + /* [<echo string>] */ + if (!client_read_args(cmd, 0, 0, FALSE, &args)) + return FALSE; + + if (MANAGESIEVE_ARG_IS_EOL(&args[0])) { + client_send_ok(client, "NOOP Completed"); + return TRUE; + } + + if (!managesieve_arg_get_string(&args[0], &text)) { + client_send_no(client, "Invalid echo tag."); + return TRUE; + } + + if (!MANAGESIEVE_ARG_IS_EOL(&args[1])) { + client_send_command_error(cmd, "Too many arguments."); + return TRUE; + } + + resp_code = t_str_new(256); + str_append(resp_code, "TAG "); + managesieve_quote_append_string(resp_code, text, FALSE); + + client_send_okresp(client, str_c(resp_code), "Done"); + return TRUE; +} + diff --git a/pigeonhole/src/managesieve/cmd-putscript.c b/pigeonhole/src/managesieve/cmd-putscript.c new file mode 100644 index 0000000..1998cd9 --- /dev/null +++ b/pigeonhole/src/managesieve/cmd-putscript.c @@ -0,0 +1,578 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* NOTE: this file also contains the checkscript command due to its obvious + * similarities. + */ + +#include "lib.h" +#include "ioloop.h" +#include "istream.h" +#include "ostream.h" +#include "iostream.h" +#include "str.h" + +#include "sieve.h" +#include "sieve-script.h" +#include "sieve-storage.h" + +#include "managesieve-parser.h" + +#include "managesieve-common.h" +#include "managesieve-client.h" +#include "managesieve-commands.h" +#include "managesieve-quota.h" + +#include <sys/time.h> + +struct cmd_putscript_context { + struct client *client; + struct client_command_context *cmd; + struct sieve_storage *storage; + + struct istream *input; + + const char *scriptname; + uoff_t script_size, max_script_size; + + struct managesieve_parser *save_parser; + struct sieve_storage_save_context *save_ctx; + + bool script_size_valid:1; +}; + +static void cmd_putscript_finish(struct cmd_putscript_context *ctx); +static bool cmd_putscript_continue_script(struct client_command_context *cmd); + +static void client_input_putscript(struct client *client) +{ + struct client_command_context *cmd = &client->cmd; + + i_assert(!client->destroyed); + + client->last_input = ioloop_time; + timeout_reset(client->to_idle); + + switch (i_stream_read(client->input)) { + case -1: + /* disconnected */ + cmd_putscript_finish(cmd->context); + /* Reset command so that client_destroy() doesn't try to call + cmd_putscript_continue_script() anymore. */ + _client_reset_command(client); + client_destroy(client, "Disconnected in PUTSCRIPT/CHECKSCRIPT"); + return; + case -2: + cmd_putscript_finish(cmd->context); + if (client->command_pending) { + /* uploaded script data, this is handled internally by + mailbox_save_continue() */ + break; + } + + /* parameter word is longer than max. input buffer size. + this is most likely an error, so skip the new data + until newline is found. */ + client->input_skip_line = TRUE; + + client_send_command_error(cmd, "Too long argument."); + cmd->param_error = TRUE; + _client_reset_command(client); + return; + } + + if (cmd->func(cmd)) { + /* command execution was finished. Note that if cmd_sync() + didn't finish, we didn't get here but the input handler + has already been moved. So don't do anything important + here.. + + reset command once again to reset cmd_sync()'s changes. */ + _client_reset_command(client); + + if (client->input_pending) + client_input(client); + } +} + +static void cmd_putscript_finish(struct cmd_putscript_context *ctx) +{ + managesieve_parser_destroy(&ctx->save_parser); + + io_remove(&ctx->client->io); + o_stream_set_flush_callback(ctx->client->output, + client_output, ctx->client); + + if (ctx->save_ctx != NULL) { + ctx->client->input_skip_line = TRUE; + sieve_storage_save_cancel(&ctx->save_ctx); + } +} + +static bool cmd_putscript_continue_cancel(struct client_command_context *cmd) +{ + struct cmd_putscript_context *ctx = cmd->context; + size_t size; + + (void)i_stream_read(ctx->input); + (void)i_stream_get_data(ctx->input, &size); + i_stream_skip(ctx->input, size); + + if (cmd->client->input->closed || ctx->input->eof || + ctx->input->v_offset == ctx->script_size) { + cmd_putscript_finish(ctx); + return TRUE; + } + return FALSE; +} + +static bool cmd_putscript_cancel(struct cmd_putscript_context *ctx, bool skip) +{ + ctx->client->input_skip_line = TRUE; + + if (!skip) { + cmd_putscript_finish(ctx); + return TRUE; + } + + /* we have to read the nonsynced literal so we don't treat the uploaded + script as commands. */ + ctx->client->command_pending = TRUE; + ctx->cmd->func = cmd_putscript_continue_cancel; + ctx->cmd->context = ctx; + return cmd_putscript_continue_cancel(ctx->cmd); +} + +static void cmd_putscript_storage_error(struct cmd_putscript_context *ctx) +{ + struct client_command_context *cmd = ctx->cmd; + + if (ctx->scriptname == NULL) { + client_command_storage_error(cmd, "Failed to check script"); + } else { + client_command_storage_error(cmd, "Failed to store script `%s'", + ctx->scriptname); + } +} + +static bool cmd_putscript_save(struct cmd_putscript_context *ctx) +{ + /* Commit to save only when this is a putscript command */ + if (ctx->scriptname == NULL) + return TRUE; + + /* Check commit */ + if (sieve_storage_save_commit(&ctx->save_ctx) < 0) { + cmd_putscript_storage_error(ctx); + return FALSE; + } + return TRUE; +} + +static void +cmd_putscript_finish_script(struct cmd_putscript_context *ctx, + struct sieve_script *script) +{ + struct client *client = ctx->client; + struct client_command_context *cmd = ctx->cmd; + struct sieve_error_handler *ehandler; + enum sieve_compile_flags cpflags = + SIEVE_COMPILE_FLAG_NOGLOBAL | SIEVE_COMPILE_FLAG_UPLOADED; + struct sieve_binary *sbin; + bool success = TRUE; + enum sieve_error error; + string_t *errors; + + /* Mark this as an activation when we are replacing the + active script */ + if (sieve_storage_save_will_activate(ctx->save_ctx)) + cpflags |= SIEVE_COMPILE_FLAG_ACTIVATED; + + /* Prepare error handler */ + errors = str_new(default_pool, 1024); + ehandler = sieve_strbuf_ehandler_create( + client->svinst, errors, TRUE, + client->set->managesieve_max_compile_errors); + + /* Compile */ + sbin = sieve_compile_script(script, ehandler, cpflags, &error); + if (sbin == NULL) { + const char *errormsg = NULL, *action; + + if (error != SIEVE_ERROR_NOT_VALID) { + errormsg = sieve_script_get_last_error(script, &error); + if (error == SIEVE_ERROR_NONE) + errormsg = NULL; + } + + action = (ctx->scriptname != NULL ? + t_strdup_printf("store script `%s'", + ctx->scriptname) : + "check script"); + + if (errormsg == NULL) { + struct event_passthrough *e = + client_command_create_finish_event(cmd)-> + add_str("error", "Compilation failed")-> + add_int("compile_errors", + sieve_get_errors(ehandler))-> + add_int("compile_warnings", + sieve_get_warnings(ehandler)); + e_debug(e->event(), "Failed to %s: " + "Compilation failed (%u errors, %u warnings)", + action, sieve_get_errors(ehandler), + sieve_get_warnings(ehandler)); + + client_send_no(client, str_c(errors)); + } else { + struct event_passthrough *e = + client_command_create_finish_event(cmd)-> + add_str("error", errormsg); + e_debug(e->event(), "Failed to %s: %s", + action, errormsg); + + client_send_no(client, errormsg); + } + + success = FALSE; + } else { + sieve_close(&sbin); + + if (!cmd_putscript_save(ctx)) + success = FALSE; + } + + /* Finish up */ + cmd_putscript_finish(ctx); + + /* Report result to user */ + if (success) { + if (ctx->scriptname != NULL) { + client->put_count++; + client->put_bytes += ctx->script_size; + } else { + client->check_count++; + client->check_bytes += ctx->script_size; + } + + struct event_passthrough *e = + client_command_create_finish_event(cmd)-> + add_int("compile_warnings", + sieve_get_warnings(ehandler)); + if (ctx->scriptname != NULL) { + e_debug(e->event(), "Stored script `%s' successfully " + "(%u warnings)", ctx->scriptname, + sieve_get_warnings(ehandler)); + } else { + e_debug(e->event(), "Checked script successfully " + "(%u warnings)", sieve_get_warnings(ehandler)); + } + + if (sieve_get_warnings(ehandler) > 0) + client_send_okresp(client, "WARNINGS", str_c(errors)); + else if (ctx->scriptname != NULL) + client_send_ok(client, "PUTSCRIPT completed."); + else + client_send_ok(client, "Script checked successfully."); + } + + sieve_error_handler_unref(&ehandler); + str_free(&errors); +} + +static void cmd_putscript_handle_script(struct cmd_putscript_context *ctx) +{ + struct client_command_context *cmd = ctx->cmd; + struct sieve_script *script; + + /* Obtain script object for uploaded script */ + script = sieve_storage_save_get_tempscript(ctx->save_ctx); + + /* Check result */ + if (script == NULL) { + cmd_putscript_storage_error(ctx); + cmd_putscript_finish(ctx); + return; + } + + /* If quoted string, the size was not known until now */ + if (!ctx->script_size_valid) { + if (sieve_script_get_size(script, &ctx->script_size) < 0) { + cmd_putscript_storage_error(ctx); + cmd_putscript_finish(ctx); + return; + } + ctx->script_size_valid = TRUE; + + /* Check quota; max size is already checked */ + if (ctx->scriptname != NULL && + !managesieve_quota_check_all(cmd, ctx->scriptname, + ctx->script_size)) { + cmd_putscript_finish(ctx); + return; + } + } + + /* Try to compile and store the script */ + T_BEGIN { + cmd_putscript_finish_script(ctx, script); + } T_END; +} + +static bool cmd_putscript_finish_parsing(struct client_command_context *cmd) +{ + struct client *client = cmd->client; + struct cmd_putscript_context *ctx = cmd->context; + const struct managesieve_arg *args; + int ret; + + /* if error occurs, the CRLF is already read. */ + client->input_skip_line = FALSE; + + /* <script literal> */ + ret = managesieve_parser_read_args(ctx->save_parser, 0, 0, &args); + if (ret == -1 || client->output->closed) { + if (ctx->storage != NULL) { + const char *msg; + bool fatal ATTR_UNUSED; + + msg = managesieve_parser_get_error( + ctx->save_parser, &fatal); + client_send_command_error(cmd, msg); + } + cmd_putscript_finish(ctx); + return TRUE; + } + if (ret < 0) { + /* need more data */ + return FALSE; + } + + if (MANAGESIEVE_ARG_IS_EOL(&args[0])) { + /* Eat away the trailing CRLF */ + client->input_skip_line = TRUE; + + cmd_putscript_handle_script(ctx); + return TRUE; + } + + client_send_command_error(cmd, "Too many command arguments."); + cmd_putscript_finish(ctx); + return TRUE; +} + +static bool cmd_putscript_continue_parsing(struct client_command_context *cmd) +{ + struct client *client = cmd->client; + struct cmd_putscript_context *ctx = cmd->context; + const struct managesieve_arg *args; + int ret; + + /* if error occurs, the CRLF is already read. */ + client->input_skip_line = FALSE; + + /* <script literal> */ + ret = managesieve_parser_read_args( + ctx->save_parser, 0, MANAGESIEVE_PARSE_FLAG_STRING_STREAM, + &args); + if (ret == -1 || client->output->closed) { + cmd_putscript_finish(ctx); + client_send_command_error(cmd, "Invalid arguments."); + client->input_skip_line = TRUE; + return TRUE; + } + if (ret < 0) { + /* need more data */ + return FALSE; + } + + /* Validate the script argument */ + if (!managesieve_arg_get_string_stream(args,&ctx->input)) { + client_send_command_error(cmd, "Invalid arguments."); + return cmd_putscript_cancel(ctx, FALSE); + } + + if (i_stream_get_size(ctx->input, FALSE, &ctx->script_size) > 0) { + ctx->script_size_valid = TRUE; + + /* Check quota */ + if (ctx->scriptname == NULL) { + if (!managesieve_quota_check_validsize( + cmd, ctx->script_size)) + return cmd_putscript_cancel(ctx, TRUE); + } else { + if (!managesieve_quota_check_all( + cmd, ctx->scriptname, ctx->script_size)) + return cmd_putscript_cancel(ctx, TRUE); + } + + } else { + ctx->max_script_size = + managesieve_quota_max_script_size(client); + } + + /* save the script */ + ctx->save_ctx = sieve_storage_save_init(ctx->storage, ctx->scriptname, + ctx->input); + + if (ctx->save_ctx == NULL) { + /* save initialization failed */ + cmd_putscript_storage_error(ctx); + return cmd_putscript_cancel(ctx, TRUE); + } + + /* after literal comes CRLF, if we fail make sure we eat it away */ + client->input_skip_line = TRUE; + + client->command_pending = TRUE; + cmd->func = cmd_putscript_continue_script; + return cmd_putscript_continue_script(cmd); +} + +static bool cmd_putscript_continue_script(struct client_command_context *cmd) +{ + struct client *client = cmd->client; + struct cmd_putscript_context *ctx = cmd->context; + size_t size; + int ret; + + if (ctx->save_ctx != NULL) { + for (;;) { + i_assert(!ctx->script_size_valid || + ctx->input->v_offset <= ctx->script_size); + if (ctx->max_script_size > 0 && + ctx->input->v_offset > ctx->max_script_size) { + (void)managesieve_quota_check_validsize( + cmd, ctx->input->v_offset); + cmd_putscript_finish(ctx); + return TRUE; + } + + ret = i_stream_read(ctx->input); + if ((ret != -1 || ctx->input->stream_errno != EINVAL || + client->input->eof) && + sieve_storage_save_continue(ctx->save_ctx) < 0) { + /* we still have to finish reading the script + from client */ + sieve_storage_save_cancel(&ctx->save_ctx); + break; + } + if (ret == -1 || ret == 0) + break; + } + } + + if (ctx->save_ctx == NULL) { + (void)i_stream_read(ctx->input); + (void)i_stream_get_data(ctx->input, &size); + i_stream_skip(ctx->input, size); + } + + if (ctx->input->eof || client->input->closed) { + bool failed = FALSE; + bool all_written = FALSE; + + if (!ctx->script_size_valid) { + if (!client->input->eof && + ctx->input->stream_errno == EINVAL) { + client_send_command_error( + cmd, t_strdup_printf( + "Invalid input: %s", + i_stream_get_error(ctx->input))); + client->input_skip_line = TRUE; + failed = TRUE; + } + all_written = (ctx->input->eof && + ctx->input->stream_errno == 0); + + } else { + all_written = (ctx->input->v_offset == ctx->script_size); + } + + /* finished */ + ctx->input = NULL; + + if (!failed) { + if (ctx->save_ctx == NULL) { + /* failed above */ + cmd_putscript_storage_error(ctx); + failed = TRUE; + } else if (!all_written) { + /* client disconnected before it finished sending the + whole script. */ + failed = TRUE; + sieve_storage_save_cancel(&ctx->save_ctx); + const char *reason = t_strdup_printf( + "%s (While appending in PUTSCRIPT/CHECKSCRIPT)", + io_stream_get_disconnect_reason(client->input, + client->output)); + client_disconnect(client, reason); + } else if (sieve_storage_save_finish(ctx->save_ctx) < 0) { + failed = TRUE; + cmd_putscript_storage_error(ctx); + } else { + failed = client->input->closed; + } + } + + if (failed) { + cmd_putscript_finish(ctx); + return TRUE; + } + + /* finish */ + client->command_pending = FALSE; + managesieve_parser_reset(ctx->save_parser); + cmd->func = cmd_putscript_finish_parsing; + return cmd_putscript_finish_parsing(cmd); + } + + return FALSE; +} + +static bool +cmd_putscript_start(struct client_command_context *cmd, const char *scriptname) +{ + struct cmd_putscript_context *ctx; + struct client *client = cmd->client; + + ctx = p_new(cmd->pool, struct cmd_putscript_context, 1); + ctx->cmd = cmd; + ctx->client = client; + ctx->storage = client->storage; + ctx->scriptname = scriptname; + + io_remove(&client->io); + client->io = io_add(i_stream_get_fd(client->input), IO_READ, + client_input_putscript, client); + /* putscript is special because we're only waiting on client input, not + client output, so disable the standard output handler until we're + finished */ + o_stream_unset_flush_callback(client->output); + + ctx->save_parser = managesieve_parser_create( + client->input, client->set->managesieve_max_line_length); + + cmd->func = cmd_putscript_continue_parsing; + cmd->context = ctx; + return cmd_putscript_continue_parsing(cmd); + +} + +bool cmd_putscript(struct client_command_context *cmd) +{ + const char *scriptname; + + /* <scriptname> */ + if (!client_read_string_args(cmd, FALSE, 1, &scriptname)) + return FALSE; + + event_add_str(cmd->event, "script_name", scriptname); + + return cmd_putscript_start(cmd, scriptname); +} + +bool cmd_checkscript(struct client_command_context *cmd) +{ + return cmd_putscript_start(cmd, NULL); +} diff --git a/pigeonhole/src/managesieve/cmd-renamescript.c b/pigeonhole/src/managesieve/cmd-renamescript.c new file mode 100644 index 0000000..bc7a99d --- /dev/null +++ b/pigeonhole/src/managesieve/cmd-renamescript.c @@ -0,0 +1,54 @@ +/* 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-storage.h" + +#include "managesieve-common.h" +#include "managesieve-commands.h" + +bool cmd_renamescript(struct client_command_context *cmd) +{ + struct client *client = cmd->client; + struct sieve_storage *storage = client->storage; + const char *scriptname, *newname; + struct sieve_script *script; + + /* <oldname> <newname> */ + if (!client_read_string_args(cmd, TRUE, 2, &scriptname, &newname)) + return FALSE; + + event_add_str(cmd->event, "old_script_name", scriptname); + event_add_str(cmd->event, "new_script_name", newname); + + script = sieve_storage_open_script(storage, scriptname, NULL); + if (script == NULL) { + client_command_storage_error( + cmd, "Failed to open script `%s' for rename to `%s'", + scriptname, newname); + return TRUE; + } + + if (sieve_script_rename(script, newname) < 0) { + client_command_storage_error( + cmd, "Failed to rename script `%s' to `%s'", + scriptname, newname); + } else { + client->renamed_count++; + + struct event_passthrough *e = + client_command_create_finish_event(cmd); + e_debug(e->event(), "Renamed script `%s' to `%s'", + scriptname, newname); + + client_send_ok(client, "Renamescript completed."); + } + + sieve_script_unref(&script); + return TRUE; +} + diff --git a/pigeonhole/src/managesieve/cmd-setactive.c b/pigeonhole/src/managesieve/cmd-setactive.c new file mode 100644 index 0000000..07844c5 --- /dev/null +++ b/pigeonhole/src/managesieve/cmd-setactive.c @@ -0,0 +1,165 @@ +/* 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-storage.h" + +#include "managesieve-common.h" +#include "managesieve-commands.h" + +static void +cmd_setactive_activate(struct client_command_context *cmd, + const char *scriptname) +{ + struct client *client = cmd->client; + struct sieve_storage *storage = client->storage; + struct sieve_script *script; + string_t *errors = NULL; + const char *errormsg = NULL; + unsigned int warning_count = 0, error_count = 0; + bool success = TRUE; + int ret; + + event_add_str(cmd->event, "script_name", scriptname); + + script = sieve_storage_open_script(storage, scriptname, NULL); + if (script == NULL) { + client_command_storage_error( + cmd, "Failed to open script `%s' for activation", + scriptname); + return; + } + + if (sieve_script_is_active(script) <= 0) T_BEGIN { + /* Script is first being activated; compile it again without the + UPLOAD flag. */ + struct sieve_error_handler *ehandler; + enum sieve_compile_flags cpflags = + SIEVE_COMPILE_FLAG_NOGLOBAL | + SIEVE_COMPILE_FLAG_ACTIVATED; + struct sieve_binary *sbin; + enum sieve_error error; + + /* Prepare error handler */ + errors = str_new(default_pool, 1024); + ehandler = sieve_strbuf_ehandler_create( + client->svinst, errors, TRUE, + client->set->managesieve_max_compile_errors); + + /* Compile */ + sbin = sieve_compile_script(script, ehandler, cpflags, &error); + if (sbin == NULL) { + if (error != SIEVE_ERROR_NOT_VALID) { + errormsg = sieve_script_get_last_error( + script, &error); + if (error == SIEVE_ERROR_NONE) + errormsg = NULL; + } + success = FALSE; + } else { + sieve_close(&sbin); + } + + warning_count = sieve_get_warnings(ehandler); + error_count = sieve_get_errors(ehandler); + sieve_error_handler_unref(&ehandler); + } T_END; + + /* Activate only when script is valid (or already active) */ + if (success) { + /* Refresh activation no matter what; this can also + resolve some erroneous situations. */ + ret = sieve_script_activate(script, (time_t)-1); + if (ret < 0) { + client_command_storage_error( + cmd, "Failed to activate script `%s'", + scriptname); + } else { + struct event_passthrough *e = + client_command_create_finish_event(cmd)-> + add_int("compile_warnings", warning_count); + e_debug(e->event(), "Activated script `%s' " + " (%u warnings%s)", + scriptname, warning_count, + (ret == 0 ? ", redundant" : "")); + + if (warning_count > 0) { + client_send_okresp( + client, "WARNINGS", + str_c(errors)); + } else { + client_send_ok(client, + (ret > 0 ? + "Setactive completed." : + "Script is already active.")); + } + } + } else if (errormsg == NULL) { + struct event_passthrough *e = + client_command_create_finish_event(cmd)-> + add_str("error", "Compilation failed")-> + add_int("compile_errors", error_count)-> + add_int("compile_warnings", warning_count); + e_debug(e->event(), "Failed to activate script `%s': " + "Compilation failed (%u errors, %u warnings)", + scriptname, error_count, warning_count); + + client_send_no(client, str_c(errors)); + } else { + struct event_passthrough *e = + client_command_create_finish_event(cmd)-> + add_str("error", errormsg); + e_debug(e->event(), "Failed to activate script `%s': %s", + scriptname, errormsg); + + client_send_no(client, errormsg); + } + + if (errors != NULL) + str_free(&errors); + sieve_script_unref(&script); +} + +static void +cmd_setactive_deactivate(struct client_command_context *cmd) +{ + struct client *client = cmd->client; + struct sieve_storage *storage = client->storage; + int ret; + + ret = sieve_storage_deactivate(storage, (time_t)-1); + if (ret < 0) { + client_command_storage_error( + cmd, "Failed to deactivate script"); + return; + } + + struct event_passthrough *e = + client_command_create_finish_event(cmd); + e_debug(e->event(), "Deactivated script"); + + client_send_ok(client, (ret > 0 ? + "Active script is now deactivated." : + "No scripts currently active.")); +} + +bool cmd_setactive(struct client_command_context *cmd) +{ + const char *scriptname; + + /* <scriptname> */ + if (!client_read_string_args(cmd, TRUE, 1, &scriptname)) + return FALSE; + + /* Activate, or deactivate */ + if (*scriptname != '\0') + cmd_setactive_activate(cmd, scriptname); + else + cmd_setactive_deactivate(cmd); + + return TRUE; +} diff --git a/pigeonhole/src/managesieve/main.c b/pigeonhole/src/managesieve/main.c new file mode 100644 index 0000000..321ebe3 --- /dev/null +++ b/pigeonhole/src/managesieve/main.c @@ -0,0 +1,401 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "buffer.h" +#include "ioloop.h" +#include "istream.h" +#include "istream-concat.h" +#include "ostream.h" +#include "path-util.h" +#include "str.h" +#include "base64.h" +#include "process-title.h" +#include "restrict-access.h" +#include "settings-parser.h" +#include "master-interface.h" +#include "master-service.h" +#include "master-login.h" +#include "mail-user.h" +#include "mail-storage-service.h" + +#include "managesieve-common.h" +#include "managesieve-commands.h" +#include "managesieve-capabilities.h" + +#include <stdio.h> +#include <unistd.h> + +#define IS_STANDALONE() \ + (getenv(MASTER_IS_PARENT_ENV) == NULL) + +#define MANAGESIEVE_DIE_IDLE_SECS 10 + +static bool verbose_proctitle = FALSE; +static struct mail_storage_service_ctx *storage_service; +static struct master_login *master_login = NULL; + +void (*hook_client_created)(struct client **client) = NULL; + +struct event_category event_category_managesieve = { + .name = "managesieve", +}; + +void managesieve_refresh_proctitle(void) +{ +#define MANAGESIEVE_PROCTITLE_PREFERRED_LEN 80 + struct client *client; + string_t *title = t_str_new(128); + + if (!verbose_proctitle) + return; + + str_append_c(title, '['); + switch (managesieve_client_count) { + case 0: + str_append(title, "idling"); + break; + case 1: + client = managesieve_clients; + str_append(title, client->user->username); + if (client->user->conn.remote_ip != NULL) { + str_append_c(title, ' '); + str_append(title, + net_ip2addr(client->user->conn.remote_ip)); + } + + if (client->cmd.name != NULL && + str_len(title) <= MANAGESIEVE_PROCTITLE_PREFERRED_LEN) { + str_append_c(title, ' '); + str_append(title, client->cmd.name); + } + break; + default: + str_printfa(title, "%u connections", managesieve_client_count); + break; + } + str_append_c(title, ']'); + process_title_set(str_c(title)); +} + +static void client_kill_idle(struct client *client) +{ + mail_storage_service_io_activate_user(client->service_user); + client_send_bye(client, "Server shutting down."); + client_destroy(client, "Server shutting down."); +} + +static void managesieve_die(void) +{ + struct client *client, *next; + time_t last_io, now = time(NULL); + time_t stop_timestamp = now - MANAGESIEVE_DIE_IDLE_SECS; + unsigned int stop_msecs; + + for (client = managesieve_clients; client != NULL; client = next) { + next = client->next; + + last_io = I_MAX(client->last_input, client->last_output); + if (last_io <= stop_timestamp) + client_kill_idle(client); + else { + timeout_remove(&client->to_idle); + stop_msecs = (last_io - stop_timestamp) * 1000; + client->to_idle = timeout_add(stop_msecs, + client_kill_idle, client); + } + } +} + +static void client_add_istream_prefix(struct client *client, + const buffer_t *input) +{ + struct istream *inputs[] = { + i_stream_create_copy_from_data(input->data, input->used), + client->input, + NULL + }; + client->input = i_stream_create_concat(inputs); + i_stream_copy_fd(client->input, inputs[1]); + i_stream_unref(&inputs[0]); + i_stream_unref(&inputs[1]); + + i_stream_set_input_pending(client->input, TRUE); +} + +static void client_logged_in(struct client *client) +{ + struct ostream *output; + + output = client->output; + o_stream_ref(output); + o_stream_cork(output); + if (!IS_STANDALONE()) + client_send_ok(client, "Logged in."); + (void)client_input(client); + o_stream_uncork(output); + o_stream_unref(&output); +} + +static int +client_create_from_input(const struct mail_storage_service_input *input, + int fd_in, int fd_out, const buffer_t *input_buf, + const char **error_r) +{ + struct mail_storage_service_input service_input; + struct mail_storage_service_user *user; + struct mail_user *mail_user; + struct client *client; + struct managesieve_settings *set; + struct event *event; + const char *error; + + event = event_create(NULL); + event_add_category(event, &event_category_managesieve); + event_add_fields(event, (const struct event_add_field []){ + { .key = "user", .value = input->username }, + { .key = "session", .value = input->session_id }, + { .key = NULL } + }); + + service_input = *input; + service_input.event_parent = event; + if (mail_storage_service_lookup_next(storage_service, &service_input, + &user, &mail_user, error_r) <= 0) { + event_unref(&event); + return -1; + } + restrict_access_allow_coredumps(TRUE); + + set = mail_storage_service_user_get_set(user)[1]; + if (set->verbose_proctitle) + verbose_proctitle = TRUE; + + if (settings_var_expand(&managesieve_setting_parser_info, set, + mail_user->pool, + mail_user_var_expand_table(mail_user), + &error) <= 0) { + i_error("Failed to expand settings: %s", error); + mail_storage_service_user_unref(&user); + mail_user_unref(&mail_user); + event_unref(&event); + return -1; + } + + client = client_create(fd_in, fd_out, input->session_id, + event, mail_user, user, set); + if (input_buf != NULL && input_buf->used > 0) + client_add_istream_prefix(client, input_buf); + client_create_finish(client); + T_BEGIN { + client_logged_in(client); + } T_END; + event_unref(&event); + return 0; +} + +static void main_stdio_run(const char *username) +{ + struct mail_storage_service_input input; + const char *value, *error, *input_base64; + buffer_t *input_buf; + + i_zero(&input); + input.module = "managesieve"; + input.service = "sieve"; + input.username = username != NULL ? username : getenv("USER"); + if (input.username == NULL && IS_STANDALONE()) + input.username = getlogin(); + if (input.username == NULL) + i_fatal("USER environment missing"); + if ((value = getenv("IP")) != NULL) + net_addr2ip(value, &input.remote_ip); + if ((value = getenv("LOCAL_IP")) != NULL) + net_addr2ip(value, &input.local_ip); + + input_base64 = getenv("CLIENT_INPUT"); + input_buf = (input_base64 == NULL ? + NULL : t_base64_decode_str(input_base64)); + + if (client_create_from_input(&input, STDIN_FILENO, STDOUT_FILENO, + input_buf, &error) < 0) + i_fatal("%s", error); +} + +static void +login_client_connected(const struct master_login_client *client, + const char *username, const char *const *extra_fields) +{ +#define MSG_BYE_INTERNAL_ERROR "BYE \""CRITICAL_MSG"\"\r\n" + struct mail_storage_service_input input; + enum mail_auth_request_flags flags = client->auth_req.flags; + const char *error; + buffer_t input_buf; + + i_zero(&input); + input.module = "managesieve"; + input.service = "sieve"; + input.local_ip = client->auth_req.local_ip; + input.remote_ip = client->auth_req.remote_ip; + input.local_port = client->auth_req.local_port; + input.remote_port = client->auth_req.remote_port; + input.username = username; + input.userdb_fields = extra_fields; + input.session_id = client->session_id; + if ((flags & MAIL_AUTH_REQUEST_FLAG_CONN_SECURED) != 0) + input.conn_secured = TRUE; + if ((flags & MAIL_AUTH_REQUEST_FLAG_CONN_SSL_SECURED) != 0) + input.conn_ssl_secured = TRUE; + + buffer_create_from_const_data(&input_buf, client->data, + client->auth_req.data_size); + if (client_create_from_input(&input, client->fd, client->fd, + &input_buf, &error) < 0) { + int fd = client->fd; + + if (write(fd, MSG_BYE_INTERNAL_ERROR, + strlen(MSG_BYE_INTERNAL_ERROR)) < 0) { + if (errno != EAGAIN && errno != EPIPE) + i_error("write(client) failed: %m"); + } + i_error("%s", error); + i_close_fd(&fd); + master_service_client_connection_destroyed(master_service); + } +} + +static void +login_client_failed(const struct master_login_client *client, + const char *errormsg) +{ + const char *msg; + + msg = t_strdup_printf("NO \"%s\"\r\n", errormsg); + if (write(client->fd, msg, strlen(msg)) < 0) { + /* ignored */ + } +} + +static void client_connected(struct master_service_connection *conn) +{ + /* when running standalone, we shouldn't even get here */ + i_assert(master_login != NULL); + + master_service_client_connection_accept(conn); + master_login_add(master_login, conn->fd); +} + +int main(int argc, char *argv[]) +{ + static const struct setting_parser_info *set_roots[] = { + &managesieve_setting_parser_info, + NULL + }; + struct master_login_settings login_set; + enum master_service_flags service_flags = 0; + enum mail_storage_service_flags storage_service_flags = 0; + const char *username = NULL, *error = NULL; + int c; + + i_zero(&login_set); + login_set.postlogin_timeout_secs = MASTER_POSTLOGIN_TIMEOUT_DEFAULT; + + if (IS_STANDALONE() && getuid() == 0 && + net_getpeername(1, NULL, NULL) == 0) { + printf("NO \"managesieve binary must not be started from " + "inetd, use managesieve-login instead.\"\n"); + return 1; + } + + if (IS_STANDALONE() || getenv("DUMP_CAPABILITY") != NULL) { + service_flags |= MASTER_SERVICE_FLAG_STANDALONE | + MASTER_SERVICE_FLAG_STD_CLIENT; + } else { + service_flags |= MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN; + } + if (getenv("DUMP_CAPABILITY") != NULL) { + service_flags |= MASTER_SERVICE_FLAG_DONT_SEND_STATS | + MASTER_SERVICE_FLAG_DISABLE_SSL_SET; + } + + master_service = master_service_init("managesieve", service_flags, + &argc, &argv, "t:u:"); + while ((c = master_getopt(master_service)) > 0) { + switch (c) { + case 't': + if (str_to_uint(optarg, + &login_set.postlogin_timeout_secs) < 0 || + login_set.postlogin_timeout_secs == 0) + i_fatal("Invalid -t parameter: %s", optarg); + break; + case 'u': + storage_service_flags |= + MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP; + username = optarg; + break; + default: + return FATAL_DEFAULT; + } + } + + master_service_set_die_callback(master_service, managesieve_die); + + /* plugins may want to add commands, so this needs to be called early */ + commands_init(); + + /* Dump capabilities if requested */ + if (getenv("DUMP_CAPABILITY") != NULL) { + i_set_debug_file("/dev/null"); + managesieve_capabilities_dump(); + commands_deinit(); + master_service_deinit(&master_service); + exit(0); + } + + if (t_abspath("auth-master", &login_set.auth_socket_path, &error) < 0) + i_fatal("t_abspath(%s) failed: %s", "auth-master", error); + + if (argv[optind] != NULL && + t_abspath(argv[optind], &login_set.postlogin_socket_path, + &error) < 0) { + i_fatal("t_abspath(%s) failed: %s", argv[optind], error); + } + + login_set.callback = login_client_connected; + login_set.failure_callback = login_client_failed; + + if (!IS_STANDALONE()) + master_login = master_login_init(master_service, &login_set); + + storage_service = + mail_storage_service_init(master_service, + set_roots, storage_service_flags); + master_service_init_finish(master_service); + /* NOTE: login_set.*_socket_path are now invalid due to data stack + having been freed */ + + /* fake that we're running, so we know if client was destroyed + while handling its initial input */ + io_loop_set_running(current_ioloop); + + if (IS_STANDALONE()) { + T_BEGIN { + main_stdio_run(username); + } T_END; + } else { + io_loop_set_running(current_ioloop); + } + + if (io_loop_is_running(current_ioloop)) + master_service_run(master_service, client_connected); + clients_destroy_all(); + + if (master_login != NULL) + master_login_deinit(&master_login); + mail_storage_service_deinit(&storage_service); + + commands_deinit(); + + master_service_deinit(&master_service); + return 0; +} diff --git a/pigeonhole/src/managesieve/managesieve-capabilities.c b/pigeonhole/src/managesieve/managesieve-capabilities.c new file mode 100644 index 0000000..c72558f --- /dev/null +++ b/pigeonhole/src/managesieve/managesieve-capabilities.c @@ -0,0 +1,136 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" +#include "hostpid.h" +#include "var-expand.h" +#include "settings-parser.h" +#include "master-service.h" +#include "master-service-settings.h" +#include "master-service-settings-cache.h" + +#include "sieve.h" + +#include "managesieve-capabilities.h" + +#include <stddef.h> +#include <unistd.h> + +/* + * Global plugin settings + */ + +struct plugin_settings { + ARRAY(const char *) plugin_envs; +}; + +static const struct setting_parser_info **plugin_set_roots; + +static const struct setting_define plugin_setting_defines[] = { + { .type = SET_STRLIST, .key = "plugin", + .offset = offsetof(struct plugin_settings, plugin_envs) }, + + SETTING_DEFINE_LIST_END +}; + +static const struct setting_parser_info plugin_setting_parser_info = { + .module_name = "managesieve", + .defines = plugin_setting_defines, + + .type_offset = (size_t)-1, + .struct_size = sizeof(struct plugin_settings), + + .parent_offset = (size_t)-1, +}; + +static const struct setting_parser_info *default_plugin_set_roots[] = { + &plugin_setting_parser_info, + NULL +}; + +static const struct setting_parser_info **plugin_set_roots = + default_plugin_set_roots; + +static struct plugin_settings *plugin_settings_read(void) +{ + const char *error; + + if (master_service_settings_read_simple( + master_service, plugin_set_roots, &error) < 0) + i_fatal("Error reading configuration: %s", error); + + return (struct plugin_settings *) + master_service_settings_get_others(master_service)[0]; +} + +static const char * +plugin_settings_get(const struct plugin_settings *set, const char *identifier) +{ + const char *const *envs; + unsigned int i, count; + + if ( !array_is_created(&set->plugin_envs) ) + return NULL; + + envs = array_get(&set->plugin_envs, &count); + for ( i = 0; i < count; i += 2 ) { + if ( strcmp(envs[i], identifier) == 0 ) + return envs[i+1]; + } + return NULL; +} + +/* + * Sieve environment + */ + +static const char *sieve_get_setting(void *context, const char *identifier) +{ + const struct plugin_settings *set = context; + + return plugin_settings_get(set, identifier); +} + +static const struct sieve_callbacks sieve_callbacks = { + NULL, + sieve_get_setting +}; + +/* + * Capability dumping + */ + +void managesieve_capabilities_dump(void) +{ + const struct plugin_settings *global_plugin_settings; + struct sieve_environment svenv; + struct sieve_instance *svinst; + const char *notify_cap; + + /* Read plugin settings */ + + global_plugin_settings = plugin_settings_read(); + + /* Initialize Sieve engine */ + + memset((void*)&svenv, 0, sizeof(svenv)); + svenv.home_dir = "/tmp"; + + svinst = sieve_init(&svenv, &sieve_callbacks, + (void *) global_plugin_settings, FALSE); + + /* Dump capabilities */ + + notify_cap = sieve_get_capabilities(svinst, "notify"); + + if (notify_cap == NULL) + printf("SIEVE: %s\n", sieve_get_capabilities(svinst, NULL)); + else { + printf("SIEVE: %s, NOTIFY: %s\n", + sieve_get_capabilities(svinst, NULL), + sieve_get_capabilities(svinst, "notify")); + } + + sieve_deinit(&svinst); +} diff --git a/pigeonhole/src/managesieve/managesieve-capabilities.h b/pigeonhole/src/managesieve/managesieve-capabilities.h new file mode 100644 index 0000000..b7503a9 --- /dev/null +++ b/pigeonhole/src/managesieve/managesieve-capabilities.h @@ -0,0 +1,6 @@ +#ifndef MANAGESIEVE_CAPABILITIES_H +#define MANAGESIEVE_CAPABILITIES_H + +void managesieve_capabilities_dump(void); + +#endif diff --git a/pigeonhole/src/managesieve/managesieve-client.c b/pigeonhole/src/managesieve/managesieve-client.c new file mode 100644 index 0000000..bacf460 --- /dev/null +++ b/pigeonhole/src/managesieve/managesieve-client.c @@ -0,0 +1,792 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "ioloop.h" +#include "llist.h" +#include "str.h" +#include "hostpid.h" +#include "net.h" +#include "istream.h" +#include "ostream.h" +#include "iostream.h" +#include "iostream-rawlog.h" +#include "var-expand.h" +#include "time-util.h" +#include "master-service.h" +#include "mail-storage-service.h" +#include "mail-namespace.h" + +#include "sieve.h" +#include "sieve-storage.h" + +#include "managesieve-quote.h" +#include "managesieve-common.h" +#include "managesieve-commands.h" +#include "managesieve-client.h" + +#include <unistd.h> + +extern struct mail_storage_callbacks mail_storage_callbacks; +struct managesieve_module_register managesieve_module_register = { 0 }; + +struct client *managesieve_clients = NULL; +unsigned int managesieve_client_count = 0; + +static const char * +managesieve_sieve_get_setting(void *context, const char *identifier) +{ + struct mail_user *mail_user = (struct mail_user *) context; + + if (mail_user == NULL) + return NULL; + + return mail_user_plugin_getenv(mail_user, identifier); +} + +static const struct sieve_callbacks managesieve_sieve_callbacks = { + NULL, + managesieve_sieve_get_setting +}; + +static void client_idle_timeout(struct client *client) +{ + if (client->cmd.func != NULL) { + client_destroy(client, + "Disconnected for inactivity in reading our output"); + } else { + client_send_bye(client, "Disconnected for inactivity"); + client_destroy(client, "Disconnected for inactivity"); + } +} + +static struct sieve_storage * +client_get_storage(struct sieve_instance *svinst, struct mail_user *user, + int fd_out) +{ + struct sieve_storage *storage; + enum sieve_error error; + const char *errormsg, *byemsg; + + /* Open personal script storage */ + + storage = sieve_storage_create_main(svinst, user, + SIEVE_STORAGE_FLAG_READWRITE, + &error); + if (storage == NULL) { + switch (error) { + case SIEVE_ERROR_NOT_POSSIBLE: + byemsg = "BYE \"Sieve processing is disabled for this user.\"\r\n"; + errormsg = "Failed to open Sieve storage: " + "Sieve is disabled for this user"; + break; + case SIEVE_ERROR_NOT_FOUND: + byemsg = "BYE \"This user cannot manage personal Sieve scripts.\"\r\n"; + errormsg = "Failed to open Sieve storage: " + "Personal script storage disabled or not found."; + break; + default: + byemsg = t_strflocaltime( + "BYE \""CRITICAL_MSG_STAMP"\"\r\n", ioloop_time); + errormsg = "Failed to open Sieve storage."; + } + + if (write(fd_out, byemsg, strlen(byemsg)) < 0) { + if (errno != EAGAIN && errno != EPIPE) + i_error("write(client) failed: %m"); + } + i_fatal("%s", errormsg); + } + + return storage; +} + +struct client * +client_create(int fd_in, int fd_out, const char *session_id, + struct event *event, struct mail_user *user, + struct mail_storage_service_user *service_user, + const struct managesieve_settings *set) +{ + struct client *client; + const char *ident; + struct sieve_environment svenv; + struct sieve_instance *svinst; + struct sieve_storage *storage; + pool_t pool; + + /* Initialize Sieve */ + + memset((void*)&svenv, 0, sizeof(svenv)); + svenv.username = user->username; + (void)mail_user_get_home(user, &svenv.home_dir); + svenv.base_dir = user->set->base_dir; + svenv.event_parent = event; + svenv.flags = SIEVE_FLAG_HOME_RELATIVE; + + svinst = sieve_init(&svenv, &managesieve_sieve_callbacks, + (void *) user, set->mail_debug); + + /* Get Sieve storage */ + + storage = client_get_storage(svinst, user, fd_out); + + /* always use nonblocking I/O */ + net_set_nonblock(fd_in, TRUE); + net_set_nonblock(fd_out, TRUE); + + pool = pool_alloconly_create("managesieve client", 1024); + client = p_new(pool, struct client, 1); + client->pool = pool; + client->event = event; + event_ref(client->event); + client->set = set; + client->service_user = service_user; + client->session_id = p_strdup(pool, session_id); + client->fd_in = fd_in; + client->fd_out = fd_out; + client->input = i_stream_create_fd( + fd_in, set->managesieve_max_line_length); + client->output = o_stream_create_fd(fd_out, (size_t)-1); + + o_stream_set_no_error_handling(client->output, TRUE); + i_stream_set_name(client->input, "<managesieve client>"); + o_stream_set_name(client->output, "<managesieve client>"); + + o_stream_set_flush_callback(client->output, client_output, client); + + client->last_input = ioloop_time; + client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS, + client_idle_timeout, client); + + client->cmd.pool = pool_alloconly_create( + MEMPOOL_GROWING"client command", 1024*12); + client->cmd.client = client; + client->cmd.event = event_create(client->event); + client->user = user; + + client->svinst = svinst; + client->storage = storage; + + ident = mail_user_get_anvil_userip_ident(client->user); + if (ident != NULL) { + master_service_anvil_send( + master_service, t_strconcat("CONNECT\t", my_pid, + "\tsieve/", ident, + "\n", NULL)); + client->anvil_sent = TRUE; + } + + managesieve_client_count++; + DLLIST_PREPEND(&managesieve_clients, client); + if (hook_client_created != NULL) + hook_client_created(&client); + + managesieve_refresh_proctitle(); + return client; +} + +void client_create_finish(struct client *client) +{ + if (client->set->rawlog_dir[0] != '\0') { + (void)iostream_rawlog_create(client->set->rawlog_dir, + &client->input, &client->output); + } + client->parser = managesieve_parser_create( + client->input, client->set->managesieve_max_line_length); + client->io = io_add_istream(client->input, client_input, client); +} + +static const char *client_stats(struct client *client) +{ + const struct var_expand_table logout_tab[] = { + { 'i', dec2str(i_stream_get_absolute_offset(client->input)), + "input" }, + { 'o', dec2str(client->output->offset), "output" }, + { '\0', dec2str(client->put_bytes), "put_bytes" }, + { '\0', dec2str(client->put_count), "put_count" }, + { '\0', dec2str(client->get_bytes), "get_bytes" }, + { '\0', dec2str(client->get_count), "get_count" }, + { '\0', dec2str(client->check_bytes), "check_bytes" }, + { '\0', dec2str(client->check_count), "check_count" }, + { '\0', dec2str(client->deleted_count), "deleted_count" }, + { '\0', dec2str(client->renamed_count), "renamed_count" }, + { '\0', client->session_id, "session" }, + { '\0', NULL, NULL } + }; + const struct var_expand_table *user_tab = + mail_user_var_expand_table(client->user); + const struct var_expand_table *tab = + t_var_expand_merge_tables(logout_tab, user_tab); + string_t *str; + const char *error; + + str = t_str_new(128); + if (var_expand_with_funcs(str, client->set->managesieve_logout_format, + tab, mail_user_var_expand_func_table, + client->user, &error) < 0) { + i_error("Failed to expand managesieve_logout_format=%s: %s", + client->set->managesieve_logout_format, error); + } + return str_c(str); +} + +void client_destroy(struct client *client, const char *reason) +{ + bool ret; + + i_assert(!client->handling_input); + i_assert(!client->destroyed); + client->destroyed = TRUE; + + client_disconnect(client, reason); + + if (client->command_pending) { + /* try to deinitialize the command */ + i_assert(client->cmd.func != NULL); + + i_stream_close(client->input); + o_stream_close(client->output); + + client->input_pending = FALSE; + + ret = client->cmd.func(&client->cmd); + i_assert(ret); + } + + if (client->anvil_sent) { + master_service_anvil_send( + master_service, t_strconcat( + "DISCONNECT\t", my_pid, "\tsieve/", + mail_user_get_anvil_userip_ident(client->user), + "\n", NULL)); + } + + managesieve_parser_destroy(&client->parser); + io_remove(&client->io); + timeout_remove(&client->to_idle_output); + timeout_remove(&client->to_idle); + + /* i/ostreams are already closed at this stage, so fd can be closed */ + net_disconnect(client->fd_in); + if (client->fd_in != client->fd_out) + net_disconnect(client->fd_out); + + /* Free the user after client is already disconnected. It may start + some background work like autoexpunging. */ + mail_user_unref(&client->user); + + /* free the i/ostreams after mail_user_unref(), which could trigger + mail_storage_callbacks notifications that write to the ostream. */ + i_stream_destroy(&client->input); + o_stream_destroy(&client->output); + + sieve_storage_unref(&client->storage); + sieve_deinit(&client->svinst); + + event_unref(&client->cmd.event); + pool_unref(&client->cmd.pool); + mail_storage_service_user_unref(&client->service_user); + + managesieve_client_count--; + DLLIST_REMOVE(&managesieve_clients, client); + event_unref(&client->event); + pool_unref(&client->pool); + + master_service_client_connection_destroyed(master_service); + managesieve_refresh_proctitle(); +} + +static void client_destroy_timeout(struct client *client) +{ + client_destroy(client, NULL); +} + +void client_disconnect(struct client *client, const char *reason) +{ + if (client->disconnected) + return; + + if (reason == NULL) { + reason = io_stream_get_disconnect_reason(client->input, + client->output); + i_info("%s %s", reason, client_stats(client)); + } else { + i_info("Disconnected: %s %s", reason, client_stats(client)); + } + client->disconnected = TRUE; + o_stream_flush(client->output); + o_stream_uncork(client->output); + + i_stream_close(client->input); + o_stream_close(client->output); + + timeout_remove(&client->to_idle); + if (!client->destroyed) + client->to_idle = timeout_add(0, client_destroy_timeout, client); +} + +void client_disconnect_with_error(struct client *client, const char *msg) +{ + client_send_bye(client, msg); + client_disconnect(client, msg); +} + +int client_send_line(struct client *client, const char *data) +{ + struct const_iovec iov[2]; + + if (client->output->closed) + return -1; + + iov[0].iov_base = data; + iov[0].iov_len = strlen(data); + iov[1].iov_base = "\r\n"; + iov[1].iov_len = 2; + + if (o_stream_sendv(client->output, iov, 2) < 0) + return -1; + client->last_output = ioloop_time; + + if (o_stream_get_buffer_used_size(client->output) >= + CLIENT_OUTPUT_OPTIMAL_SIZE) { + /* buffer full, try flushing */ + return o_stream_flush(client->output); + } + return 1; +} + +void client_send_response(struct client *client, const char *oknobye, + const char *resp_code, const char *msg) +{ + string_t *str; + + str = t_str_new(128); + str_append(str, oknobye); + + if (resp_code != NULL) { + str_append(str, " ("); + str_append(str, resp_code); + str_append_c(str, ')'); + } + + if (msg != NULL) { + str_append_c(str, ' '); + managesieve_quote_append_string(str, msg, TRUE); + } + + client_send_line(client, str_c(str)); +} + +struct event_passthrough * +client_command_create_finish_event(struct client_command_context *cmd) +{ + struct event_passthrough *e = + event_create_passthrough(cmd->event)-> + set_name("managesieve_command_finished"); + return e; +} + +void client_send_command_error(struct client_command_context *cmd, + const char *msg) +{ + struct client *client = cmd->client; + const char *error, *cmd_name; + bool fatal; + + if (msg == NULL) { + msg = managesieve_parser_get_error(client->parser, &fatal); + if (fatal) { + client_disconnect_with_error(client, msg); + return; + } + } + + if (cmd->name == NULL) { + error = t_strconcat("Error in MANAGESIEVE command: ", + msg, NULL); + } else { + cmd_name = t_str_ucase(cmd->name); + error = t_strconcat("Error in MANAGESIEVE command ", + cmd_name, ": ", msg, NULL); + } + + client_send_no(client, error); + + if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) { + client_disconnect_with_error( + client, "Too many invalid MANAGESIEVE commands."); + } + + /* client_read_args() failures rely on this being set, so that the + command processing is stopped even while command function returns + FALSE. */ + cmd->param_error = TRUE; +} + +#undef client_command_storage_error +void client_command_storage_error(struct client_command_context *cmd, + const char *source_filename, + unsigned int source_linenum, + const char *log_prefix, ...) +{ + struct event_log_params params = { + .log_type = LOG_TYPE_INFO, + .source_filename = source_filename, + .source_linenum = source_linenum, + }; + struct client *client = cmd->client; + struct sieve_storage *storage = client->storage; + enum sieve_error error_code; + const char *error; + va_list args; + + error = sieve_storage_get_last_error(storage, &error_code); + + switch (error_code) { + case SIEVE_ERROR_TEMP_FAILURE: + client_send_noresp(client, "TRYLATER", error); + break; + case SIEVE_ERROR_NO_QUOTA: + client_send_noresp(client, "QUOTA", error); + break; + case SIEVE_ERROR_NOT_FOUND: + client_send_noresp(client, "NONEXISTENT", error); + break; + case SIEVE_ERROR_ACTIVE: + client_send_noresp(client, "ACTIVE", error); + break; + case SIEVE_ERROR_EXISTS: + client_send_noresp(client, "ALREADYEXISTS", error); + break; + case SIEVE_ERROR_NOT_POSSIBLE: + default: + client_send_no(client, error); + break; + } + + struct event_passthrough *e = + client_command_create_finish_event(cmd)-> + add_str("error", error); + + va_start(args, log_prefix); + event_log(e->event(), ¶ms, "%s: %s", + t_strdup_vprintf(log_prefix, args), error); + va_end(args); +} + +bool client_read_args(struct client_command_context *cmd, unsigned int count, + unsigned int flags, bool no_more, + const struct managesieve_arg **args_r) +{ + const struct managesieve_arg *dummy_args_r = NULL; + string_t *str; + int ret; + + if (args_r == NULL) + args_r = &dummy_args_r; + + i_assert(count <= INT_MAX); + + ret = managesieve_parser_read_args(cmd->client->parser, + (no_more ? 0 : count), + flags, args_r); + if (ret >= 0) { + if (count > 0 || no_more) { + if (ret < (int)count) { + client_send_command_error( + cmd, "Missing arguments."); + return FALSE; + } else if (no_more && ret > (int)count) { + client_send_command_error( + cmd, "Too many arguments."); + return FALSE; + } + } + + str = t_str_new(256); + managesieve_write_args(str, *args_r); + cmd->args = p_strdup(cmd->pool, str_c(str)); + + event_add_str(cmd->event, "cmd_args", cmd->args); + + /* all parameters read successfully */ + return TRUE; + } else if (ret == -2) { + /* need more data */ + if (cmd->client->input->closed) { + /* disconnected */ + cmd->param_error = TRUE; + } + return FALSE; + } else { + /* error */ + client_send_command_error(cmd, NULL); + return FALSE; + } +} + +bool client_read_string_args(struct client_command_context *cmd, + bool no_more, unsigned int count, ...) +{ + const struct managesieve_arg *msieve_args; + va_list va; + const char *str; + unsigned int i; + bool result = TRUE; + + if (!client_read_args(cmd, count, 0, no_more, &msieve_args)) + return FALSE; + + va_start(va, count); + for (i = 0; i < count; i++) { + const char **ret = va_arg(va, const char **); + + if (MANAGESIEVE_ARG_IS_EOL(&msieve_args[i])) { + client_send_command_error(cmd, "Missing arguments."); + result = FALSE; + break; + } + + if (!managesieve_arg_get_string(&msieve_args[i], &str)) { + client_send_command_error(cmd, "Invalid arguments."); + result = FALSE; + break; + } + + if (ret != NULL) + *ret = str; + } + va_end(va); + + return result; +} + +void _client_reset_command(struct client *client) +{ + pool_t pool; + size_t size; + + /* reset input idle time because command output might have taken a long + time and we don't want to disconnect client immediately then */ + client->last_input = ioloop_time; + timeout_reset(client->to_idle); + + client->command_pending = FALSE; + if (client->io == NULL && !client->disconnected) { + i_assert(i_stream_get_fd(client->input) >= 0); + client->io = io_add(i_stream_get_fd(client->input), + IO_READ, client_input, client); + } + o_stream_set_flush_callback(client->output, client_output, client); + + event_unref(&client->cmd.event); + + pool = client->cmd.pool; + i_zero(&client->cmd); + + p_clear(pool); + client->cmd.pool = pool; + client->cmd.client = client; + + client->cmd.event = event_create(client->event); + + managesieve_parser_reset(client->parser); + + /* if there's unread data in buffer, remember that there's input pending + and we should get around to calling client_input() soon. This is + mostly for APPEND/IDLE. */ + (void)i_stream_get_data(client->input, &size); + if (size > 0 && !client->destroyed) + client->input_pending = TRUE; +} + +/* Skip incoming data until newline is found, + returns TRUE if newline was found. */ +static bool client_skip_line(struct client *client) +{ + const unsigned char *data; + size_t i, data_size; + + data = i_stream_get_data(client->input, &data_size); + + for (i = 0; i < data_size; i++) { + if (data[i] == '\n') { + client->input_skip_line = FALSE; + i++; + break; + } + } + + i_stream_skip(client->input, i); + return !client->input_skip_line; +} + +static bool client_handle_input(struct client_command_context *cmd) +{ + struct client *client = cmd->client; + + if (cmd->func != NULL) { + bool finished; + + event_push_global(cmd->event); + finished = cmd->func(cmd); + event_pop_global(cmd->event); + + /* command is being executed - continue it */ + if (finished || cmd->param_error) { + /* command execution was finished */ + if (!cmd->param_error) + client->bad_counter = 0; + _client_reset_command(client); + return TRUE; + } + + /* unfinished */ + if (client->command_pending) + o_stream_set_flush_pending(client->output, TRUE); + return FALSE; + } + + if (client->input_skip_line) { + /* we're just waiting for new line.. */ + if (!client_skip_line(client)) + return FALSE; + + /* got the newline */ + _client_reset_command(client); + + /* pass through to parse next command */ + } + + if (cmd->name == NULL) { + cmd->name = managesieve_parser_read_word(client->parser); + if (cmd->name == NULL) + return FALSE; /* need more data */ + cmd->name = p_strdup(cmd->pool, cmd->name); + managesieve_refresh_proctitle(); + } + + if (cmd->name[0] == '\0') { + /* command not given - cmd_func is already NULL. */ + } else { + /* find the command function */ + struct command *command = command_find(cmd->name); + + if (command != NULL) + cmd->func = command->func; + } + + client->input_skip_line = TRUE; + if (cmd->func == NULL) { + /* unknown command */ + client_send_command_error(cmd, "Unknown command."); + _client_reset_command(client); + } else { + i_assert(!client->disconnected); + + event_add_str(cmd->event, "cmd_name", t_str_ucase(cmd->name)); + client_handle_input(cmd); + } + + return TRUE; +} + +void client_input(struct client *client) +{ + struct client_command_context *cmd = &client->cmd; + bool ret; + + if (client->command_pending) { + /* already processing one command. wait. */ + io_remove(&client->io); + return; + } + + client->input_pending = FALSE; + client->last_input = ioloop_time; + timeout_reset(client->to_idle); + + switch (i_stream_read(client->input)) { + case -1: + /* disconnected */ + client_destroy(client, NULL); + return; + case -2: + /* parameter word is longer than max. input buffer size. + this is most likely an error, so skip the new data + until newline is found. */ + client->input_skip_line = TRUE; + + client_send_command_error(cmd, "Too long argument."); + _client_reset_command(client); + break; + } + + client->handling_input = TRUE; + o_stream_cork(client->output); + do { + T_BEGIN { + ret = client_handle_input(cmd); + } T_END; + } while (ret && !client->disconnected); + o_stream_uncork(client->output); + client->handling_input = FALSE; + + if (client->command_pending) + client->input_pending = TRUE; + + if (client->output->closed) + client_destroy(client, NULL); +} + +int client_output(struct client *client) +{ + struct client_command_context *cmd = &client->cmd; + int ret; + bool finished; + + client->last_output = ioloop_time; + timeout_reset(client->to_idle); + if (client->to_idle_output != NULL) + timeout_reset(client->to_idle_output); + + if ((ret = o_stream_flush(client->output)) < 0) { + client_destroy(client, NULL); + return 1; + } + + if (!client->command_pending) + return 1; + + /* continue processing command */ + o_stream_cork(client->output); + client->output_pending = TRUE; + finished = cmd->func(cmd) || cmd->param_error; + + /* a bit kludgy check. normally we would want to get back to this + output handler, but IDLE is a special case which has command + pending but without necessarily anything to write. */ + if (!finished && client->output_pending) + o_stream_set_flush_pending(client->output, TRUE); + + o_stream_uncork(client->output); + + if (finished) { + /* command execution was finished */ + client->bad_counter = 0; + _client_reset_command(client); + + if (client->input_pending) + client_input(client); + } + return ret; +} + +void clients_destroy_all(void) +{ + while (managesieve_clients != NULL) { + mail_storage_service_io_activate_user(managesieve_clients->service_user); + client_send_bye(managesieve_clients, "Server shutting down."); + client_destroy(managesieve_clients, "Server shutting down."); + } +} diff --git a/pigeonhole/src/managesieve/managesieve-client.h b/pigeonhole/src/managesieve/managesieve-client.h new file mode 100644 index 0000000..6053eb6 --- /dev/null +++ b/pigeonhole/src/managesieve/managesieve-client.h @@ -0,0 +1,162 @@ +#ifndef MANAGESIEVE_CLIENT_H +#define MANAGESIEVE_CLIENT_H + +#include "managesieve-commands.h" + +struct client; +struct sieve_storage; +struct managesieve_parser; +struct managesieve_arg; + +struct client_command_context { + struct client *client; + struct event *event; + + pool_t pool; + /* Name of this command */ + const char *name; + /* Parameters for this command. These are generated from parsed + ManageSieve arguments, so they may not be exactly the same as how + client sent them. */ + const char *args; + + command_func_t *func; + void *context; + + bool param_error:1; +}; + +struct managesieve_module_register { + unsigned int id; +}; + +union managesieve_module_context { + struct managesieve_module_register *reg; +}; +extern struct managesieve_module_register managesieve_module_register; + +struct client { + struct client *prev, *next; + + struct event *event; + const char *session_id; + int fd_in, fd_out; + struct io *io; + struct istream *input; + struct ostream *output; + struct timeout *to_idle, *to_idle_output; + + pool_t pool; + struct mail_storage_service_user *service_user; + const struct managesieve_settings *set; + + struct mail_user *user; + + struct sieve_instance *svinst; + struct sieve_storage *storage; + + time_t last_input, last_output; + unsigned int bad_counter; + + struct managesieve_parser *parser; + struct client_command_context cmd; + + uoff_t put_bytes; + uoff_t get_bytes; + uoff_t check_bytes; + unsigned int put_count; + unsigned int get_count; + unsigned int check_count; + unsigned int deleted_count; + unsigned int renamed_count; + + bool disconnected:1; + bool destroyed:1; + bool command_pending:1; + bool input_pending:1; + bool output_pending:1; + bool handling_input:1; + bool anvil_sent:1; + bool input_skip_line:1; /* skip all the data until we've found a new + line */ +}; + +extern struct client *managesieve_clients; +extern unsigned int managesieve_client_count; + +/* Create new client with specified input/output handles. socket specifies + if the handle is a socket. */ +struct client * +client_create(int fd_in, int fd_out, const char *session_id, + struct event *event, struct mail_user *user, + struct mail_storage_service_user *service_user, + const struct managesieve_settings *set); +void client_create_finish(struct client *client); +void client_destroy(struct client *client, const char *reason); + +void client_dump_capability(struct client *client); + +/* Disconnect client connection */ +void client_disconnect(struct client *client, const char *reason); +void client_disconnect_with_error(struct client *client, const char *msg); + +/* Send a line of data to client. Returns 1 if ok, 0 if buffer is getting full, + -1 if error */ +int client_send_line(struct client *client, const char *data); + +void client_send_response(struct client *client, const char *oknobye, + const char *resp_code, const char *msg); + +#define client_send_ok(client, msg) \ + client_send_response(client, "OK", NULL, msg) +#define client_send_no(client, msg) \ + client_send_response(client, "NO", NULL, msg) +#define client_send_bye(client, msg) \ + client_send_response(client, "BYE", NULL, msg) + +#define client_send_okresp(client, resp_code, msg) \ + client_send_response(client, "OK", resp_code, msg) +#define client_send_noresp(client, resp_code, msg) \ + client_send_response(client, "NO", resp_code, msg) +#define client_send_byeresp(cmd, resp_code, msg) \ + client_send_response(client, "BYE", resp_code, msg) + +struct event_passthrough * +client_command_create_finish_event(struct client_command_context *cmd); + +/* Send BAD command error to client. msg can be NULL. */ +void client_send_command_error(struct client_command_context *cmd, + const char *msg); + +/* Send storage or sieve-related errors to the client. Returns command finish + event with the "error" field set accordingly. */ +void client_command_storage_error(struct client_command_context *cmd, + const char *source_filename, + unsigned int source_linenum, + const char *log_prefix, ...) + ATTR_FORMAT(4, 5); +#define client_command_storage_error(cmd, ...) \ + client_command_storage_error(cmd, __FILE__, __LINE__, __VA_ARGS__) + +/* Read a number of arguments. Returns TRUE if everything was read or + FALSE if either needs more data or error occurred. */ +bool client_read_args(struct client_command_context *cmd, unsigned int count, + unsigned int flags, bool no_more, + const struct managesieve_arg **args_r); +/* Reads a number of string arguments. ... is a list of pointers where to + store the arguments. */ +bool client_read_string_args(struct client_command_context *cmd, bool no_more, + unsigned int count, ...); + +static inline bool client_read_no_args(struct client_command_context *cmd) +{ + return client_read_args(cmd, 0, 0, TRUE, NULL); +} + +void _client_reset_command(struct client *client); +void client_input(struct client *client); +int client_output(struct client *client); + +void clients_destroy_all(void); + +#endif diff --git a/pigeonhole/src/managesieve/managesieve-commands.c b/pigeonhole/src/managesieve/managesieve-commands.c new file mode 100644 index 0000000..87e2048 --- /dev/null +++ b/pigeonhole/src/managesieve/managesieve-commands.c @@ -0,0 +1,110 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" + +#include "managesieve-common.h" +#include "managesieve-commands.h" + + +/* May want to combine this somewhere in a commands-common.c to avoid duplicate + code + */ + +static const struct command managesieve_base_commands[] = { + { "CAPABILITY", cmd_capability }, + { "LOGOUT", cmd_logout }, + { "PUTSCRIPT", cmd_putscript }, + { "CHECKSCRIPT", cmd_checkscript }, + { "GETSCRIPT", cmd_getscript }, + { "SETACTIVE", cmd_setactive }, + { "DELETESCRIPT", cmd_deletescript }, + { "LISTSCRIPTS", cmd_listscripts }, + { "HAVESPACE", cmd_havespace }, + { "RENAMESCRIPT", cmd_renamescript }, + { "NOOP", cmd_noop } +}; + +#define MANAGESIEVE_COMMANDS_COUNT N_ELEMENTS(managesieve_base_commands) + +static ARRAY(struct command) managesieve_commands; +static bool commands_unsorted; + +void command_register(const char *name, command_func_t *func) +{ + struct command cmd; + + i_zero(&cmd); + cmd.name = name; + cmd.func = func; + array_append(&managesieve_commands, &cmd, 1); + + commands_unsorted = TRUE; +} + +void command_unregister(const char *name) +{ + const struct command *cmd; + unsigned int i, count; + + cmd = array_get(&managesieve_commands, &count); + for (i = 0; i < count; i++) { + if (strcasecmp(cmd[i].name, name) == 0) { + array_delete(&managesieve_commands, i, 1); + return; + } + } + + i_error("Trying to unregister unknown command '%s'", name); +} + +void command_register_array(const struct command *cmdarr, unsigned int count) +{ + commands_unsorted = TRUE; + array_append(&managesieve_commands, cmdarr, count); +} + +void command_unregister_array(const struct command *cmdarr, unsigned int count) +{ + while (count > 0) { + command_unregister(cmdarr->name); + count--; cmdarr++; + } +} + +static int command_cmp(const struct command *c1, const struct command *c2) +{ + return strcasecmp(c1->name, c2->name); +} + +static int command_bsearch(const char *name, const struct command *cmd) +{ + return strcasecmp(name, cmd->name); +} + +struct command *command_find(const char *name) +{ + if (commands_unsorted) { + array_sort(&managesieve_commands, command_cmp); + commands_unsorted = FALSE; + } + + return array_bsearch(&managesieve_commands, name, command_bsearch); +} + +void commands_init(void) +{ + i_array_init(&managesieve_commands, 16); + commands_unsorted = FALSE; + + command_register_array(managesieve_base_commands, + MANAGESIEVE_COMMANDS_COUNT); +} + +void commands_deinit(void) +{ + command_unregister_array(managesieve_base_commands, + MANAGESIEVE_COMMANDS_COUNT); + array_free(&managesieve_commands); +} diff --git a/pigeonhole/src/managesieve/managesieve-commands.h b/pigeonhole/src/managesieve/managesieve-commands.h new file mode 100644 index 0000000..d922b74 --- /dev/null +++ b/pigeonhole/src/managesieve/managesieve-commands.h @@ -0,0 +1,46 @@ +#ifndef MANAGESIEVE_COMMANDS_H +#define MANAGESIEVE_COMMANDS_H + +struct client_command_context; + +#include "managesieve-parser.h" + +typedef bool command_func_t(struct client_command_context *cmd); + +struct command { + const char *name; + command_func_t *func; +}; + +/* Register command. Given name parameter must be permanently stored until + command is unregistered. */ +void command_register(const char *name, command_func_t *func); +void command_unregister(const char *name); + +/* Register array of commands. */ +void command_register_array(const struct command *cmdarr, unsigned int count); +void command_unregister_array(const struct command *cmdarr, unsigned int count); + +struct command *command_find(const char *name); + +void commands_init(void); +void commands_deinit(void); + +/* MANAGESIEVE commands: */ + +/* Non-Authenticated State */ +extern bool cmd_logout(struct client_command_context *cmd); +extern bool cmd_capability(struct client_command_context *cmd); +extern bool cmd_noop(struct client_command_context *cmd); + +/* Authenticated State */ +extern bool cmd_putscript(struct client_command_context *cmd); +extern bool cmd_checkscript(struct client_command_context *cmd); +extern bool cmd_getscript(struct client_command_context *cmd); +extern bool cmd_setactive(struct client_command_context *cmd); +extern bool cmd_deletescript(struct client_command_context *cmd); +extern bool cmd_listscripts(struct client_command_context *cmd); +extern bool cmd_havespace(struct client_command_context *cmd); +extern bool cmd_renamescript(struct client_command_context *cmd); + +#endif diff --git a/pigeonhole/src/managesieve/managesieve-common.h b/pigeonhole/src/managesieve/managesieve-common.h new file mode 100644 index 0000000..e1df589 --- /dev/null +++ b/pigeonhole/src/managesieve/managesieve-common.h @@ -0,0 +1,31 @@ +#ifndef MANAGESIEVE_COMMON_H +#define MANAGESIEVE_COMMON_H + +#include "pigeonhole-config.h" + +/* Disconnect client after idling this many milliseconds */ +#define CLIENT_IDLE_TIMEOUT_MSECS (60*30*1000) + +/* If we can't send anything to client for this long, disconnect the client */ +#define CLIENT_OUTPUT_TIMEOUT_MSECS (5*60*1000) + +/* Stop buffering more data into output stream after this many bytes */ +#define CLIENT_OUTPUT_OPTIMAL_SIZE 2048 + +/* Disconnect client when it sends too many bad commands in a row */ +#define CLIENT_MAX_BAD_COMMANDS 20 + +#define CRITICAL_MSG \ + "Internal error occurred. Refer to server log for more information." +#define CRITICAL_MSG_STAMP CRITICAL_MSG " [%Y-%m-%d %H:%M:%S]" + +#include "lib.h" +#include "managesieve-client.h" +#include "managesieve-settings.h" + +extern void (*hook_client_created)(struct client **client); +extern struct event_category event_category_managesieve; + +void managesieve_refresh_proctitle(void); + +#endif diff --git a/pigeonhole/src/managesieve/managesieve-quota.c b/pigeonhole/src/managesieve/managesieve-quota.c new file mode 100644 index 0000000..07b58ca --- /dev/null +++ b/pigeonhole/src/managesieve/managesieve-quota.c @@ -0,0 +1,98 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "strfuncs.h" + +#include "sieve.h" +#include "sieve-storage.h" + +#include "managesieve-client.h" +#include "managesieve-quota.h" + +uint64_t managesieve_quota_max_script_size(struct client *client) +{ + return sieve_storage_quota_max_script_size(client->storage); +} + +bool managesieve_quota_check_validsize(struct client_command_context *cmd, + size_t size) +{ + struct client *client = cmd->client; + uint64_t limit; + + if (!sieve_storage_quota_validsize(client->storage, size, &limit)) { + const char *error_msg; + + error_msg = t_strdup_printf( + "Script is too large (max %llu bytes).", + (unsigned long long int)limit); + + struct event_passthrough *e = + client_command_create_finish_event(cmd); + e_debug(e->event(), + "Script size check failed (size %zu bytes): %s", + size, error_msg); + + client_send_noresp(client, "QUOTA/MAXSIZE", error_msg); + return FALSE; + } + return TRUE; +} + +bool managesieve_quota_check_all(struct client_command_context *cmd, + const char *scriptname, size_t size) +{ + struct client *client = cmd->client; + enum sieve_storage_quota quota; + uint64_t limit; + const char *resp_code = NULL, *error_msg = NULL; + int ret; + + ret = sieve_storage_quota_havespace(client->storage, scriptname, + size, "a, &limit); + if (ret > 0) + return TRUE; + if (ret < 0) { + client_command_storage_error( + cmd, "Failed to check quota for script `%s' " + "(size %zu bytes)", scriptname, size); + return FALSE; + } + + switch (quota) { + case SIEVE_STORAGE_QUOTA_MAXSIZE: + resp_code = "QUOTA/MAXSIZE"; + error_msg = t_strdup_printf("Script is too large " + "(max %llu bytes).", + (unsigned long long int)limit); + break; + case SIEVE_STORAGE_QUOTA_MAXSCRIPTS: + resp_code = "QUOTA/MAXSCRIPTS"; + error_msg = t_strdup_printf("Script count quota exceeded " + "(max %llu scripts).", + (unsigned long long int)limit); + break; + case SIEVE_STORAGE_QUOTA_MAXSTORAGE: + resp_code = "QUOTA/MAXSTORAGE"; + error_msg = t_strdup_printf("Script storage quota exceeded " + "(max %llu bytes).", + (unsigned long long int)limit); + break; + default: + resp_code = "QUOTA"; + error_msg = "Quota exceeded."; + } + + struct event_passthrough *e = + client_command_create_finish_event(cmd)-> + add_str("error", error_msg); + e_debug(e->event(), + "Quota check failed for script `%s' (size %zu bytes): %s", + scriptname, size, error_msg); + + client_send_noresp(client, resp_code, error_msg); + + return FALSE; +} + diff --git a/pigeonhole/src/managesieve/managesieve-quota.h b/pigeonhole/src/managesieve/managesieve-quota.h new file mode 100644 index 0000000..f6b37bf --- /dev/null +++ b/pigeonhole/src/managesieve/managesieve-quota.h @@ -0,0 +1,11 @@ +#ifndef MANAGESIEVE_QUOTA_H +#define MANAGESIEVE_QUOTA_H + +uint64_t managesieve_quota_max_script_size(struct client *client); + +bool managesieve_quota_check_validsize(struct client_command_context *cmd, + size_t size); +bool managesieve_quota_check_all(struct client_command_context *cmd, + const char *scriptname, size_t size); + +#endif diff --git a/pigeonhole/src/managesieve/managesieve-settings.c b/pigeonhole/src/managesieve/managesieve-settings.c new file mode 100644 index 0000000..8e6e88b --- /dev/null +++ b/pigeonhole/src/managesieve/managesieve-settings.c @@ -0,0 +1,170 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "buffer.h" +#include "settings-parser.h" +#include "service-settings.h" +#include "mail-storage-settings.h" + +#include "pigeonhole-config.h" + +#include "managesieve-settings.h" + +#include <stddef.h> +#include <unistd.h> + +static bool managesieve_settings_verify(void *_set, pool_t pool, + const char **error_r); + +/* <settings checks> */ +static struct file_listener_settings managesieve_unix_listeners_array[] = { + { "login/sieve", 0666, "", "" } +}; +static struct file_listener_settings *managesieve_unix_listeners[] = { + &managesieve_unix_listeners_array[0] +}; +static buffer_t managesieve_unix_listeners_buf = { + { { managesieve_unix_listeners, sizeof(managesieve_unix_listeners) } } +}; +/* </settings checks> */ + +struct service_settings managesieve_settings_service_settings = { + .name = "managesieve", + .protocol = "sieve", + .type = "", + .executable = "managesieve", + .user = "", + .group = "", + .privileged_group = "", + .extra_groups = "", + .chroot = "", + + .drop_priv_before_exec = FALSE, + + .process_min_avail = 0, + .process_limit = 0, + .client_limit = 1, + .service_count = 1, + .idle_kill = 0, + .vsz_limit = (uoff_t)-1, + + .unix_listeners = { { &managesieve_unix_listeners_buf, + sizeof(managesieve_unix_listeners[0]) } }, + .fifo_listeners = ARRAY_INIT, + .inet_listeners = ARRAY_INIT +}; + +#undef DEF +#define DEF(type, name) \ + SETTING_DEFINE_STRUCT_##type(#name, name, struct managesieve_settings) + +static struct setting_define managesieve_setting_defines[] = { + DEF(BOOL, mail_debug), + DEF(BOOL, verbose_proctitle), + DEF(STR_VARS, rawlog_dir), + + DEF(SIZE, managesieve_max_line_length), + DEF(STR, managesieve_implementation_string), + DEF(STR, managesieve_client_workarounds), + DEF(STR, managesieve_logout_format), + DEF(UINT, managesieve_max_compile_errors), + + + SETTING_DEFINE_LIST_END +}; + +static struct managesieve_settings managesieve_default_settings = { + .mail_debug = FALSE, + .verbose_proctitle = FALSE, + .rawlog_dir = "", + + /* RFC-2683 recommends at least 8000 bytes. Some clients however don't + break large message sets to multiple commands, so we're pretty + liberal by default. */ + .managesieve_max_line_length = 65536, + .managesieve_implementation_string = DOVECOT_NAME " " PIGEONHOLE_NAME, + .managesieve_client_workarounds = "", + .managesieve_logout_format = "bytes=%i/%o", + .managesieve_max_compile_errors = 5 +}; + +static const struct setting_parser_info *managesieve_setting_dependencies[] = { + &mail_user_setting_parser_info, + NULL +}; + +const struct setting_parser_info managesieve_setting_parser_info = { + .module_name = "managesieve", + .defines = managesieve_setting_defines, + .defaults = &managesieve_default_settings, + + .type_offset = (size_t)-1, + .struct_size = sizeof(struct managesieve_settings), + + .parent_offset = (size_t)-1, + .parent = NULL, + + .check_func = managesieve_settings_verify, + .dependencies = managesieve_setting_dependencies +}; + +const struct setting_parser_info *managesieve_settings_set_roots[] = { + &managesieve_setting_parser_info, + NULL +}; + +/* <settings checks> */ +struct managesieve_client_workaround_list { + const char *name; + enum managesieve_client_workarounds num; +}; + +static const struct managesieve_client_workaround_list +managesieve_client_workaround_list[] = { + { NULL, 0 } +}; + +static int +managesieve_settings_parse_workarounds(struct managesieve_settings *set, + const char **error_r) +{ + enum managesieve_client_workarounds client_workarounds = 0; + const struct managesieve_client_workaround_list *list; + const char *const *str; + + str = t_strsplit_spaces(set->managesieve_client_workarounds, " ,"); + for (; *str != NULL; str++) { + list = managesieve_client_workaround_list; + for (; list->name != NULL; list++) { + if (strcasecmp(*str, list->name) == 0) { + client_workarounds |= list->num; + break; + } + } + if (list->name == NULL) { + *error_r = t_strdup_printf( + "managesieve_client_workarounds: " + "Unknown workaround: %s", *str); + return -1; + } + } + set->parsed_workarounds = client_workarounds; + return 0; +} + + +static bool +managesieve_settings_verify(void *_set, pool_t pool ATTR_UNUSED, + const char **error_r) +{ + struct managesieve_settings *set = _set; + + if (managesieve_settings_parse_workarounds(set, error_r) < 0) + return FALSE; + return TRUE; +} + +/* </settings checks> */ + +const char *managesieve_settings_version = DOVECOT_ABI_VERSION; diff --git a/pigeonhole/src/managesieve/managesieve-settings.h b/pigeonhole/src/managesieve/managesieve-settings.h new file mode 100644 index 0000000..a72338b --- /dev/null +++ b/pigeonhole/src/managesieve/managesieve-settings.h @@ -0,0 +1,29 @@ +#ifndef MANAGESIEVE_SETTINGS_H +#define MANAGESIEVE_SETTINGS_H + +struct mail_user_settings; + +/* <settings checks> */ +enum managesieve_client_workarounds { + WORKAROUND_NONE = 0x00 +}; +/* </settings checks> */ + +struct managesieve_settings { + bool mail_debug; + bool verbose_proctitle; + const char *rawlog_dir; + + /* managesieve: */ + uoff_t managesieve_max_line_length; + const char *managesieve_implementation_string; + const char *managesieve_client_workarounds; + const char *managesieve_logout_format; + unsigned int managesieve_max_compile_errors; + + enum managesieve_client_workarounds parsed_workarounds; +}; + +extern const struct setting_parser_info managesieve_setting_parser_info; + +#endif diff --git a/pigeonhole/src/plugins/Makefile.am b/pigeonhole/src/plugins/Makefile.am new file mode 100644 index 0000000..b6c7551 --- /dev/null +++ b/pigeonhole/src/plugins/Makefile.am @@ -0,0 +1,7 @@ +SUBDIRS = \ + doveadm-sieve \ + lda-sieve \ + sieve-extprograms \ + imapsieve \ + imap-filter-sieve \ + settings diff --git a/pigeonhole/src/plugins/Makefile.in b/pigeonhole/src/plugins/Makefile.in new file mode 100644 index 0000000..890ce14 --- /dev/null +++ b/pigeonhole/src/plugins/Makefile.in @@ -0,0 +1,699 @@ +# 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/plugins +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 $(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 = +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 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +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 +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +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@ +SUBDIRS = \ + doveadm-sieve \ + lda-sieve \ + sieve-extprograms \ + imapsieve \ + imap-filter-sieve \ + settings + +all: all-recursive + +.SUFFIXES: +$(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/plugins/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/plugins/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): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(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-recursive + +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-recursive + +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 + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +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-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-libtool cscopelist-am ctags \ + ctags-am distclean 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 \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean 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/plugins/doveadm-sieve/Makefile.am b/pigeonhole/src/plugins/doveadm-sieve/Makefile.am new file mode 100644 index 0000000..9f98dab --- /dev/null +++ b/pigeonhole/src/plugins/doveadm-sieve/Makefile.am @@ -0,0 +1,31 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib-sieve \ + $(LIBDOVECOT_INCLUDE) \ + $(LIBDOVECOT_STORAGE_INCLUDE) \ + $(LIBDOVECOT_DOVEADM_INCLUDE) + +doveadm_moduledir = $(dovecot_moduledir)/doveadm +lib10_doveadm_sieve_plugin_la_LDFLAGS = -module -avoid-version + +doveadm_module_LTLIBRARIES = lib10_doveadm_sieve_plugin.la + +lib10_doveadm_sieve_plugin_la_LIBADD = \ + $(top_builddir)/src/lib-sieve/libdovecot-sieve.la + +commands = \ + doveadm-sieve-cmd-list.c \ + doveadm-sieve-cmd-get.c \ + doveadm-sieve-cmd-put.c \ + doveadm-sieve-cmd-delete.c \ + doveadm-sieve-cmd-activate.c \ + doveadm-sieve-cmd-rename.c + +lib10_doveadm_sieve_plugin_la_SOURCES = \ + $(commands) \ + doveadm-sieve-cmd.c \ + doveadm-sieve-sync.c \ + doveadm-sieve-plugin.c + +noinst_HEADERS = \ + doveadm-sieve-cmd.h \ + doveadm-sieve-plugin.h diff --git a/pigeonhole/src/plugins/doveadm-sieve/Makefile.in b/pigeonhole/src/plugins/doveadm-sieve/Makefile.in new file mode 100644 index 0000000..be3b853 --- /dev/null +++ b/pigeonhole/src/plugins/doveadm-sieve/Makefile.in @@ -0,0 +1,796 @@ +# 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/plugins/doveadm-sieve +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 = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(doveadm_moduledir)" +LTLIBRARIES = $(doveadm_module_LTLIBRARIES) +lib10_doveadm_sieve_plugin_la_DEPENDENCIES = \ + $(top_builddir)/src/lib-sieve/libdovecot-sieve.la +am__objects_1 = doveadm-sieve-cmd-list.lo doveadm-sieve-cmd-get.lo \ + doveadm-sieve-cmd-put.lo doveadm-sieve-cmd-delete.lo \ + doveadm-sieve-cmd-activate.lo doveadm-sieve-cmd-rename.lo +am_lib10_doveadm_sieve_plugin_la_OBJECTS = $(am__objects_1) \ + doveadm-sieve-cmd.lo doveadm-sieve-sync.lo \ + doveadm-sieve-plugin.lo +lib10_doveadm_sieve_plugin_la_OBJECTS = \ + $(am_lib10_doveadm_sieve_plugin_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 = +lib10_doveadm_sieve_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) \ + $(lib10_doveadm_sieve_plugin_la_LDFLAGS) $(LDFLAGS) -o $@ +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)/doveadm-sieve-cmd-activate.Plo \ + ./$(DEPDIR)/doveadm-sieve-cmd-delete.Plo \ + ./$(DEPDIR)/doveadm-sieve-cmd-get.Plo \ + ./$(DEPDIR)/doveadm-sieve-cmd-list.Plo \ + ./$(DEPDIR)/doveadm-sieve-cmd-put.Plo \ + ./$(DEPDIR)/doveadm-sieve-cmd-rename.Plo \ + ./$(DEPDIR)/doveadm-sieve-cmd.Plo \ + ./$(DEPDIR)/doveadm-sieve-plugin.Plo \ + ./$(DEPDIR)/doveadm-sieve-sync.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 = $(lib10_doveadm_sieve_plugin_la_SOURCES) +DIST_SOURCES = $(lib10_doveadm_sieve_plugin_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@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib-sieve \ + $(LIBDOVECOT_INCLUDE) \ + $(LIBDOVECOT_STORAGE_INCLUDE) \ + $(LIBDOVECOT_DOVEADM_INCLUDE) + +doveadm_moduledir = $(dovecot_moduledir)/doveadm +lib10_doveadm_sieve_plugin_la_LDFLAGS = -module -avoid-version +doveadm_module_LTLIBRARIES = lib10_doveadm_sieve_plugin.la +lib10_doveadm_sieve_plugin_la_LIBADD = \ + $(top_builddir)/src/lib-sieve/libdovecot-sieve.la + +commands = \ + doveadm-sieve-cmd-list.c \ + doveadm-sieve-cmd-get.c \ + doveadm-sieve-cmd-put.c \ + doveadm-sieve-cmd-delete.c \ + doveadm-sieve-cmd-activate.c \ + doveadm-sieve-cmd-rename.c + +lib10_doveadm_sieve_plugin_la_SOURCES = \ + $(commands) \ + doveadm-sieve-cmd.c \ + doveadm-sieve-sync.c \ + doveadm-sieve-plugin.c + +noinst_HEADERS = \ + doveadm-sieve-cmd.h \ + doveadm-sieve-plugin.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/plugins/doveadm-sieve/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/plugins/doveadm-sieve/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): + +install-doveadm_moduleLTLIBRARIES: $(doveadm_module_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(doveadm_module_LTLIBRARIES)'; test -n "$(doveadm_moduledir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(doveadm_moduledir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(doveadm_moduledir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(doveadm_moduledir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(doveadm_moduledir)"; \ + } + +uninstall-doveadm_moduleLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(doveadm_module_LTLIBRARIES)'; test -n "$(doveadm_moduledir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(doveadm_moduledir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(doveadm_moduledir)/$$f"; \ + done + +clean-doveadm_moduleLTLIBRARIES: + -test -z "$(doveadm_module_LTLIBRARIES)" || rm -f $(doveadm_module_LTLIBRARIES) + @list='$(doveadm_module_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}; \ + } + +lib10_doveadm_sieve_plugin.la: $(lib10_doveadm_sieve_plugin_la_OBJECTS) $(lib10_doveadm_sieve_plugin_la_DEPENDENCIES) $(EXTRA_lib10_doveadm_sieve_plugin_la_DEPENDENCIES) + $(AM_V_CCLD)$(lib10_doveadm_sieve_plugin_la_LINK) -rpath $(doveadm_moduledir) $(lib10_doveadm_sieve_plugin_la_OBJECTS) $(lib10_doveadm_sieve_plugin_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-sieve-cmd-activate.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-sieve-cmd-delete.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-sieve-cmd-get.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-sieve-cmd-list.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-sieve-cmd-put.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-sieve-cmd-rename.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-sieve-cmd.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-sieve-plugin.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-sieve-sync.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: + for dir in "$(DESTDIR)$(doveadm_moduledir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-doveadm_moduleLTLIBRARIES clean-generic clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/doveadm-sieve-cmd-activate.Plo + -rm -f ./$(DEPDIR)/doveadm-sieve-cmd-delete.Plo + -rm -f ./$(DEPDIR)/doveadm-sieve-cmd-get.Plo + -rm -f ./$(DEPDIR)/doveadm-sieve-cmd-list.Plo + -rm -f ./$(DEPDIR)/doveadm-sieve-cmd-put.Plo + -rm -f ./$(DEPDIR)/doveadm-sieve-cmd-rename.Plo + -rm -f ./$(DEPDIR)/doveadm-sieve-cmd.Plo + -rm -f ./$(DEPDIR)/doveadm-sieve-plugin.Plo + -rm -f ./$(DEPDIR)/doveadm-sieve-sync.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-doveadm_moduleLTLIBRARIES + +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)/doveadm-sieve-cmd-activate.Plo + -rm -f ./$(DEPDIR)/doveadm-sieve-cmd-delete.Plo + -rm -f ./$(DEPDIR)/doveadm-sieve-cmd-get.Plo + -rm -f ./$(DEPDIR)/doveadm-sieve-cmd-list.Plo + -rm -f ./$(DEPDIR)/doveadm-sieve-cmd-put.Plo + -rm -f ./$(DEPDIR)/doveadm-sieve-cmd-rename.Plo + -rm -f ./$(DEPDIR)/doveadm-sieve-cmd.Plo + -rm -f ./$(DEPDIR)/doveadm-sieve-plugin.Plo + -rm -f ./$(DEPDIR)/doveadm-sieve-sync.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-doveadm_moduleLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-doveadm_moduleLTLIBRARIES clean-generic clean-libtool \ + 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-doveadm_moduleLTLIBRARIES \ + 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 uninstall-doveadm_moduleLTLIBRARIES + +.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/plugins/doveadm-sieve/doveadm-sieve-cmd-activate.c b/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-cmd-activate.c new file mode 100644 index 0000000..f392478 --- /dev/null +++ b/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-cmd-activate.c @@ -0,0 +1,145 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "doveadm-mail.h" + +#include "sieve.h" +#include "sieve-script.h" +#include "sieve-storage.h" + +#include "doveadm-sieve-cmd.h" + +struct doveadm_sieve_activate_cmd_context { + struct doveadm_sieve_cmd_context ctx; + + const char *scriptname; +}; + +static int +cmd_sieve_activate_run(struct doveadm_sieve_cmd_context *_ctx) +{ + struct doveadm_sieve_activate_cmd_context *ctx = + (struct doveadm_sieve_activate_cmd_context *)_ctx; + struct sieve_storage *storage = _ctx->storage; + struct sieve_script *script; + enum sieve_error error; + int ret = 0; + + script = sieve_storage_open_script + (storage, ctx->scriptname, NULL); + if ( script == NULL ) { + i_error("Failed to activate Sieve script: %s", + sieve_storage_get_last_error(storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + return -1; + } + + if ( sieve_script_is_active(script) <= 0 ) { + /* Script is first being activated; compile it again without the UPLOAD + * flag. + */ + struct sieve_error_handler *ehandler; + enum sieve_compile_flags cpflags = + SIEVE_COMPILE_FLAG_NOGLOBAL | SIEVE_COMPILE_FLAG_ACTIVATED; + struct sieve_binary *sbin; + enum sieve_error error; + + /* Compile */ + ehandler = sieve_master_ehandler_create(ctx->ctx.svinst, 0); + if ( (sbin=sieve_compile_script + (script, ehandler, cpflags, &error)) == NULL ) { + doveadm_sieve_cmd_failed_error(_ctx, error); + ret = -1; + } else { + sieve_close(&sbin); + } + sieve_error_handler_unref(&ehandler); + } + + /* Activate only when script is valid (or already active) */ + if ( ret == 0 ) { + /* Refresh activation no matter what; this can also resolve some erroneous + * situations. + */ + ret = sieve_script_activate(script, (time_t)-1); + if ( ret < 0 ) { + i_error("Failed to activate Sieve script: %s", + sieve_storage_get_last_error(storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + ret = -1; + } + } + + sieve_script_unref(&script); + return ret; +} + +static int cmd_sieve_deactivate_run +(struct doveadm_sieve_cmd_context *_ctx) +{ + struct sieve_storage *storage = _ctx->storage; + enum sieve_error error; + + if (sieve_storage_deactivate(storage, (time_t)-1) < 0) { + i_error("Failed to deactivate Sieve script: %s", + sieve_storage_get_last_error(storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + return -1; + } + return 0; +} + +static void cmd_sieve_activate_init +(struct doveadm_mail_cmd_context *_ctx, + const char *const args[]) +{ + struct doveadm_sieve_activate_cmd_context *ctx = + (struct doveadm_sieve_activate_cmd_context *)_ctx; + + if (str_array_length(args) != 1) + doveadm_mail_help_name("sieve activate"); + doveadm_sieve_cmd_scriptnames_check(args); + + ctx->scriptname = p_strdup(ctx->ctx.ctx.pool, args[0]); +} + +static struct doveadm_mail_cmd_context * +cmd_sieve_activate_alloc(void) +{ + struct doveadm_sieve_activate_cmd_context *ctx; + + ctx = doveadm_sieve_cmd_alloc(struct doveadm_sieve_activate_cmd_context); + ctx->ctx.ctx.v.init = cmd_sieve_activate_init; + ctx->ctx.v.run = cmd_sieve_activate_run; + return &ctx->ctx.ctx; +} + +static struct doveadm_mail_cmd_context * +cmd_sieve_deactivate_alloc(void) +{ + struct doveadm_sieve_cmd_context *ctx; + + ctx = doveadm_sieve_cmd_alloc(struct doveadm_sieve_cmd_context); + ctx->v.run = cmd_sieve_deactivate_run; + return &ctx->ctx; +} + +struct doveadm_cmd_ver2 doveadm_sieve_cmd_activate = { + .name = "sieve activate", + .mail_cmd = cmd_sieve_activate_alloc, + .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX"<scriptname>", +DOVEADM_CMD_PARAMS_START +DOVEADM_CMD_MAIL_COMMON +DOVEADM_CMD_PARAM('\0',"scriptname",CMD_PARAM_STR,CMD_PARAM_FLAG_POSITIONAL) +DOVEADM_CMD_PARAMS_END +}; + +struct doveadm_cmd_ver2 doveadm_sieve_cmd_deactivate = { + .name = "sieve deactivate", + .mail_cmd = cmd_sieve_deactivate_alloc, + .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX, +DOVEADM_CMD_PARAMS_START +DOVEADM_CMD_MAIL_COMMON +DOVEADM_CMD_PARAMS_END +}; diff --git a/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-cmd-delete.c b/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-cmd-delete.c new file mode 100644 index 0000000..8517abd --- /dev/null +++ b/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-cmd-delete.c @@ -0,0 +1,116 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" +#include "doveadm-mail.h" + +#include "sieve.h" +#include "sieve-script.h" +#include "sieve-storage.h" + +#include "doveadm-sieve-cmd.h" + +struct doveadm_sieve_delete_cmd_context { + struct doveadm_sieve_cmd_context ctx; + + ARRAY_TYPE(const_string) scriptnames; + bool ignore_active:1; +}; + +static int +cmd_sieve_delete_run(struct doveadm_sieve_cmd_context *_ctx) +{ + struct doveadm_sieve_delete_cmd_context *ctx = + (struct doveadm_sieve_delete_cmd_context *)_ctx; + struct sieve_storage *storage = _ctx->storage; + const ARRAY_TYPE(const_string) *scriptnames = &ctx->scriptnames; + const char *scriptname; + struct sieve_script *script; + enum sieve_error error; + int ret = 0; + + array_foreach_elem(scriptnames, scriptname) { + int sret = 0; + + script = sieve_storage_open_script + (storage, scriptname, NULL); + if (script == NULL) { + sret = -1; + } else { + if (sieve_script_delete(script, ctx->ignore_active) < 0) { + (void)sieve_storage_get_last_error(storage, &error); + sret = -1; + } + sieve_script_unref(&script); + } + + if (sret < 0) { + i_error("Failed to delete Sieve script: %s", + sieve_storage_get_last_error(storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + ret = -1; + } + } + return ret; +} + +static void cmd_sieve_delete_init +(struct doveadm_mail_cmd_context *_ctx, + const char *const args[]) +{ + struct doveadm_sieve_delete_cmd_context *ctx = + (struct doveadm_sieve_delete_cmd_context *)_ctx; + const char *name; + unsigned int i; + + if (args[0] == NULL) + doveadm_mail_help_name("sieve delete"); + doveadm_sieve_cmd_scriptnames_check(args); + + for (i = 0; args[i] != NULL; i++) { + name = p_strdup(ctx->ctx.ctx.pool, args[i]); + array_append(&ctx->scriptnames, &name, 1); + } +} + +static bool +cmd_sieve_delete_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) +{ + struct doveadm_sieve_delete_cmd_context *ctx = + (struct doveadm_sieve_delete_cmd_context *)_ctx; + + switch ( c ) { + case 'a': + ctx->ignore_active = TRUE; + break; + default: + return FALSE; + } + return TRUE; +} + +static struct doveadm_mail_cmd_context * +cmd_sieve_delete_alloc(void) +{ + struct doveadm_sieve_delete_cmd_context *ctx; + + ctx = doveadm_sieve_cmd_alloc(struct doveadm_sieve_delete_cmd_context); + ctx->ctx.ctx.getopt_args = "a"; + ctx->ctx.ctx.v.parse_arg = cmd_sieve_delete_parse_arg; + ctx->ctx.ctx.v.init = cmd_sieve_delete_init; + ctx->ctx.v.run = cmd_sieve_delete_run; + p_array_init(&ctx->scriptnames, ctx->ctx.ctx.pool, 16); + return &ctx->ctx.ctx; +} + +struct doveadm_cmd_ver2 doveadm_sieve_cmd_delete = { + .name = "sieve delete", + .mail_cmd = cmd_sieve_delete_alloc, + .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX"[-a] <scriptname> [...]", +DOVEADM_CMD_PARAMS_START +DOVEADM_CMD_MAIL_COMMON +DOVEADM_CMD_PARAM('a',"ignore-active",CMD_PARAM_BOOL,0) +DOVEADM_CMD_PARAM('\0',"scriptname",CMD_PARAM_ARRAY,CMD_PARAM_FLAG_POSITIONAL) +DOVEADM_CMD_PARAMS_END +}; diff --git a/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-cmd-get.c b/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-cmd-get.c new file mode 100644 index 0000000..e1bf7e3 --- /dev/null +++ b/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-cmd-get.c @@ -0,0 +1,83 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "doveadm-print.h" +#include "doveadm-mail.h" + +#include "sieve.h" +#include "sieve-script.h" +#include "sieve-storage.h" + +#include "doveadm-sieve-cmd.h" + +struct doveadm_sieve_get_cmd_context { + struct doveadm_sieve_cmd_context ctx; + + const char *scriptname; +}; + +static int +cmd_sieve_get_run(struct doveadm_sieve_cmd_context *_ctx) +{ + struct doveadm_sieve_get_cmd_context *ctx = + (struct doveadm_sieve_get_cmd_context *)_ctx; + struct sieve_script *script; + struct istream *input; + enum sieve_error error; + int ret; + + script = sieve_storage_open_script + (_ctx->storage, ctx->scriptname, &error); + if ( script == NULL || sieve_script_get_stream + (script, &input, &error) < 0 ) { + i_error("Failed to open Sieve script: %s", + sieve_storage_get_last_error(_ctx->storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + if (script != NULL) + sieve_script_unref(&script); + return -1; + } + + ret = doveadm_print_istream(input); + sieve_script_unref(&script); + return ret; +} + +static void cmd_sieve_get_init +(struct doveadm_mail_cmd_context *_ctx, + const char *const args[]) +{ + struct doveadm_sieve_get_cmd_context *ctx = + (struct doveadm_sieve_get_cmd_context *)_ctx; + + if ( str_array_length(args) != 1 ) + doveadm_mail_help_name("sieve get"); + doveadm_sieve_cmd_scriptnames_check(args); + + ctx->scriptname = p_strdup(ctx->ctx.ctx.pool, args[0]); + + doveadm_print_header_simple("sieve script"); +} + +static struct doveadm_mail_cmd_context * +cmd_sieve_get_alloc(void) +{ + struct doveadm_sieve_get_cmd_context *ctx; + + ctx = doveadm_sieve_cmd_alloc(struct doveadm_sieve_get_cmd_context); + ctx->ctx.ctx.v.init = cmd_sieve_get_init; + ctx->ctx.v.run = cmd_sieve_get_run; + doveadm_print_init("pager"); + return &ctx->ctx.ctx; +} + +struct doveadm_cmd_ver2 doveadm_sieve_cmd_get = { + .name = "sieve get", + .mail_cmd = cmd_sieve_get_alloc, + .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX"<scriptname>", +DOVEADM_CMD_PARAMS_START +DOVEADM_CMD_MAIL_COMMON +DOVEADM_CMD_PARAM('\0',"scriptname",CMD_PARAM_STR,CMD_PARAM_FLAG_POSITIONAL) +DOVEADM_CMD_PARAMS_END +}; diff --git a/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-cmd-list.c b/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-cmd-list.c new file mode 100644 index 0000000..0dbf379 --- /dev/null +++ b/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-cmd-list.c @@ -0,0 +1,79 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "doveadm-print.h" +#include "doveadm-mail.h" + +#include "sieve.h" +#include "sieve-storage.h" + +#include "doveadm-sieve-cmd.h" + +static int +cmd_sieve_list_run(struct doveadm_sieve_cmd_context *_ctx) +{ + struct sieve_storage *storage = _ctx->storage; + struct sieve_storage_list_context *lctx; + enum sieve_error error; + const char *scriptname; + bool active; + + if ( (lctx = sieve_storage_list_init(storage)) + == NULL ) { + i_error("Listing Sieve scripts failed: %s", + sieve_storage_get_last_error(storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + return -1; + } + + while ( (scriptname=sieve_storage_list_next(lctx, &active)) + != NULL ) { + doveadm_print(scriptname); + if ( active ) + doveadm_print("ACTIVE"); + else + doveadm_print(""); + } + + if ( sieve_storage_list_deinit(&lctx) < 0 ) { + i_error("Listing Sieve scripts failed: %s", + sieve_storage_get_last_error(storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + return -1; + } + return 0; +} + +static void cmd_sieve_list_init +(struct doveadm_mail_cmd_context *_ctx ATTR_UNUSED, + const char *const args[] ATTR_UNUSED) +{ + doveadm_print_header("script", "script", + DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); + doveadm_print_header("active", "active", + DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); +} + +static struct doveadm_mail_cmd_context * +cmd_sieve_list_alloc(void) +{ + struct doveadm_sieve_cmd_context *ctx; + + ctx = doveadm_sieve_cmd_alloc(struct doveadm_sieve_cmd_context); + ctx->ctx.v.init = cmd_sieve_list_init; + ctx->ctx.getopt_args = "s"; + ctx->v.run = cmd_sieve_list_run; + doveadm_print_init(DOVEADM_PRINT_TYPE_FLOW); + return &ctx->ctx; +} + +struct doveadm_cmd_ver2 doveadm_sieve_cmd_list = { + .name = "sieve list", + .mail_cmd = cmd_sieve_list_alloc, + .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX, +DOVEADM_CMD_PARAMS_START +DOVEADM_CMD_MAIL_COMMON +DOVEADM_CMD_PARAMS_END +}; + diff --git a/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-cmd-put.c b/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-cmd-put.c new file mode 100644 index 0000000..ef9472d --- /dev/null +++ b/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-cmd-put.c @@ -0,0 +1,189 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "istream.h" +#include "doveadm-mail.h" + +#include "sieve.h" +#include "sieve-script.h" +#include "sieve-storage.h" + +#include "doveadm-sieve-cmd.h" + +struct doveadm_sieve_put_cmd_context { + struct doveadm_sieve_cmd_context ctx; + + const char *scriptname; + + bool activate:1; +}; + +static int cmd_sieve_put_run +(struct doveadm_sieve_cmd_context *_ctx) +{ + struct doveadm_sieve_put_cmd_context *ctx = + (struct doveadm_sieve_put_cmd_context *)_ctx; + struct sieve_storage_save_context *save_ctx; + struct sieve_storage *storage = _ctx->storage; + struct istream *input = _ctx->ctx.cmd_input; + enum sieve_error error; + ssize_t ret; + bool save_failed = FALSE; + + save_ctx = sieve_storage_save_init + (storage, ctx->scriptname, input); + if ( save_ctx == NULL ) { + i_error("Saving failed: %s", + sieve_storage_get_last_error(storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + return -1; + } + + while ( (ret = i_stream_read(input)) > 0 || ret == -2 ) { + if ( sieve_storage_save_continue(save_ctx) < 0 ) { + save_failed = TRUE; + ret = -1; + break; + } + } + i_assert(ret == -1); + + if ( input->stream_errno != 0 ) { + i_error("read(script input) failed: %s", i_stream_get_error(input)); + doveadm_sieve_cmd_failed_error + (&ctx->ctx, SIEVE_ERROR_TEMP_FAILURE); + } else if ( save_failed ) { + i_error("Saving failed: %s", + sieve_storage_get_last_error(storage, NULL)); + doveadm_sieve_cmd_failed_storage(&ctx->ctx, storage); + } else if ( sieve_storage_save_finish(save_ctx) < 0 ) { + i_error("Saving failed: %s", + sieve_storage_get_last_error(storage, NULL)); + doveadm_sieve_cmd_failed_storage(&ctx->ctx, storage); + } else { + ret = 0; + } + + /* Verify that script compiles */ + if ( ret == 0 ) { + struct sieve_error_handler *ehandler; + enum sieve_compile_flags cpflags = + SIEVE_COMPILE_FLAG_NOGLOBAL | SIEVE_COMPILE_FLAG_UPLOADED; + struct sieve_script *script; + struct sieve_binary *sbin; + + /* Obtain script object for uploaded script */ + script = sieve_storage_save_get_tempscript(save_ctx); + + /* Check result */ + if ( script == NULL ) { + i_error("Saving failed: %s", + sieve_storage_get_last_error(storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + ret = -1; + + } else { + /* Mark this as an activation when we are replacing the active script */ + if ( ctx->activate || sieve_storage_save_will_activate(save_ctx) ) + cpflags |= SIEVE_COMPILE_FLAG_ACTIVATED; + + /* Compile */ + ehandler = sieve_master_ehandler_create(ctx->ctx.svinst, 0); + if ( (sbin=sieve_compile_script + (script, ehandler, cpflags, &error)) == NULL ) { + doveadm_sieve_cmd_failed_error(_ctx, error); + ret = -1; + } else { + sieve_close(&sbin); + + /* Script is valid; commit it to storage */ + ret = sieve_storage_save_commit(&save_ctx); + if (ret < 0) { + i_error("Saving failed: %s", + sieve_storage_get_last_error(storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + ret = -1; + } + } + sieve_error_handler_unref(&ehandler); + } + } + + if ( save_ctx != NULL ) + sieve_storage_save_cancel(&save_ctx); + + if ( ctx->activate && ret == 0 ) { + struct sieve_script *script = sieve_storage_open_script + (storage, ctx->scriptname, NULL); + if ( script == NULL || + sieve_script_activate(script, (time_t)-1) < 0) { + i_error("Failed to activate Sieve script: %s", + sieve_storage_get_last_error(storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + ret = -1; + } + if (script != NULL) + sieve_script_unref(&script); + } + + i_assert(input->eof); + return ret < 0 ? -1 : 0; +} + +static void cmd_sieve_put_init +(struct doveadm_mail_cmd_context *_ctx, + const char *const args[]) +{ + struct doveadm_sieve_put_cmd_context *ctx = + (struct doveadm_sieve_put_cmd_context *)_ctx; + + if ( str_array_length(args) != 1 ) + doveadm_mail_help_name("sieve put"); + doveadm_sieve_cmd_scriptnames_check(args); + + ctx->scriptname = p_strdup(ctx->ctx.ctx.pool, args[0]); + + doveadm_mail_get_input(_ctx); +} + +static bool +cmd_sieve_put_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) +{ + struct doveadm_sieve_put_cmd_context *ctx = + (struct doveadm_sieve_put_cmd_context *)_ctx; + + switch ( c ) { + case 'a': + ctx->activate = TRUE; + break; + default: + return FALSE; + } + return TRUE; +} + +static struct doveadm_mail_cmd_context * +cmd_sieve_put_alloc(void) +{ + struct doveadm_sieve_put_cmd_context *ctx; + + ctx = doveadm_sieve_cmd_alloc(struct doveadm_sieve_put_cmd_context); + ctx->ctx.ctx.getopt_args = "a"; + ctx->ctx.ctx.v.parse_arg = cmd_sieve_put_parse_arg; + ctx->ctx.ctx.v.init = cmd_sieve_put_init; + ctx->ctx.v.run = cmd_sieve_put_run; + return &ctx->ctx.ctx; +} + +struct doveadm_cmd_ver2 doveadm_sieve_cmd_put = { + .name = "sieve put", + .mail_cmd = cmd_sieve_put_alloc, + .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX"[-a] <scriptname>", +DOVEADM_CMD_PARAMS_START +DOVEADM_CMD_MAIL_COMMON +DOVEADM_CMD_PARAM('a',"activate",CMD_PARAM_BOOL,0) +DOVEADM_CMD_PARAM('\0',"scriptname",CMD_PARAM_STR,CMD_PARAM_FLAG_POSITIONAL) +DOVEADM_CMD_PARAM('\0',"file",CMD_PARAM_ISTREAM,CMD_PARAM_FLAG_POSITIONAL) +DOVEADM_CMD_PARAMS_END +}; diff --git a/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-cmd-rename.c b/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-cmd-rename.c new file mode 100644 index 0000000..3cc53c7 --- /dev/null +++ b/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-cmd-rename.c @@ -0,0 +1,83 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "doveadm-mail.h" + +#include "sieve.h" +#include "sieve-script.h" +#include "sieve-storage.h" + +#include "doveadm-sieve-cmd.h" + +struct doveadm_sieve_rename_cmd_context { + struct doveadm_sieve_cmd_context ctx; + + const char *oldname, *newname; +}; + +static int +cmd_sieve_rename_run(struct doveadm_sieve_cmd_context *_ctx) +{ + struct doveadm_sieve_rename_cmd_context *ctx = + (struct doveadm_sieve_rename_cmd_context *)_ctx; + struct sieve_storage *storage = _ctx->storage; + struct sieve_script *script; + enum sieve_error error; + int ret = 0; + + script = sieve_storage_open_script + (storage, ctx->oldname, NULL); + if ( script == NULL ) { + i_error("Failed to rename Sieve script: %s", + sieve_storage_get_last_error(storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + ret = -1; + } else if ( sieve_script_rename(script, ctx->newname) < 0 ) { + i_error("Failed to rename Sieve script: %s", + sieve_storage_get_last_error(storage, &error)); + doveadm_sieve_cmd_failed_error(_ctx, error); + ret = -1; + } + + if ( script != NULL ) + sieve_script_unref(&script); + return ret; +} + +static void cmd_sieve_rename_init +(struct doveadm_mail_cmd_context *_ctx, + const char *const args[]) +{ + struct doveadm_sieve_rename_cmd_context *ctx = + (struct doveadm_sieve_rename_cmd_context *)_ctx; + + if ( str_array_length(args) != 2 ) + doveadm_mail_help_name("sieve rename"); + doveadm_sieve_cmd_scriptnames_check(args); + + ctx->oldname = p_strdup(ctx->ctx.ctx.pool, args[0]); + ctx->newname = p_strdup(ctx->ctx.ctx.pool, args[1]); +} + +static struct doveadm_mail_cmd_context * +cmd_sieve_rename_alloc(void) +{ + struct doveadm_sieve_rename_cmd_context *ctx; + + ctx = doveadm_sieve_cmd_alloc(struct doveadm_sieve_rename_cmd_context); + ctx->ctx.ctx.v.init = cmd_sieve_rename_init; + ctx->ctx.v.run = cmd_sieve_rename_run; + return &ctx->ctx.ctx; +} + +struct doveadm_cmd_ver2 doveadm_sieve_cmd_rename = { + .name = "sieve rename", + .mail_cmd = cmd_sieve_rename_alloc, + .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX"<oldname> <newname>", +DOVEADM_CMD_PARAMS_START +DOVEADM_CMD_MAIL_COMMON +DOVEADM_CMD_PARAM('\0',"oldname",CMD_PARAM_STR,CMD_PARAM_FLAG_POSITIONAL) +DOVEADM_CMD_PARAM('\0',"newname",CMD_PARAM_STR,CMD_PARAM_FLAG_POSITIONAL) +DOVEADM_CMD_PARAMS_END +}; diff --git a/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-cmd.c b/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-cmd.c new file mode 100644 index 0000000..eb9318c --- /dev/null +++ b/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-cmd.c @@ -0,0 +1,179 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "unichar.h" +#include "mail-storage.h" +#include "doveadm-mail.h" + +#include "sieve.h" +#include "sieve-script.h" +#include "sieve-storage.h" + +#include "doveadm-sieve-cmd.h" + +void doveadm_sieve_cmd_failed_error +(struct doveadm_sieve_cmd_context *ctx, enum sieve_error error) +{ + struct doveadm_mail_cmd_context *mctx = &ctx->ctx; + int exit_code = 0; + + switch ( error ) { + case SIEVE_ERROR_NONE: + i_unreached(); + return; + case SIEVE_ERROR_TEMP_FAILURE: + exit_code = EX_TEMPFAIL; + break; + case SIEVE_ERROR_NOT_POSSIBLE: + case SIEVE_ERROR_EXISTS: + case SIEVE_ERROR_NOT_VALID: + case SIEVE_ERROR_ACTIVE: + exit_code = DOVEADM_EX_NOTPOSSIBLE; + break; + case SIEVE_ERROR_BAD_PARAMS: + exit_code = EX_USAGE; + break; + case SIEVE_ERROR_NO_PERMISSION: + exit_code = EX_NOPERM; + break; + case SIEVE_ERROR_NO_QUOTA: + exit_code = EX_CANTCREAT; + break; + case SIEVE_ERROR_NOT_FOUND: + exit_code = DOVEADM_EX_NOTFOUND; + break; + default: + i_unreached(); + } + /* tempfail overrides all other exit codes, otherwise use whatever + error happened first */ + if ( mctx->exit_code == 0 || exit_code == EX_TEMPFAIL ) + mctx->exit_code = exit_code; +} + +void doveadm_sieve_cmd_failed_storage +(struct doveadm_sieve_cmd_context *ctx, struct sieve_storage *storage) +{ + enum sieve_error error; + + (void)sieve_storage_get_last_error(storage, &error); + doveadm_sieve_cmd_failed_error(ctx, error); +} + +static const char *doveadm_sieve_cmd_get_setting +(void *context, const char *identifier) +{ + struct doveadm_sieve_cmd_context *ctx = + (struct doveadm_sieve_cmd_context *) context; + + return mail_user_plugin_getenv(ctx->ctx.cur_mail_user, identifier); +} + +static const struct sieve_callbacks sieve_callbacks = { + NULL, + doveadm_sieve_cmd_get_setting +}; + +static bool doveadm_sieve_cmd_parse_arg +(struct doveadm_mail_cmd_context *_ctx ATTR_UNUSED, + int c ATTR_UNUSED) +{ + return FALSE; +} + +void doveadm_sieve_cmd_scriptnames_check(const char *const args[]) +{ + unsigned int i; + + for (i = 0; args[i] != NULL; i++) { + if (!uni_utf8_str_is_valid(args[i])) { + i_fatal_status(EX_DATAERR, + "Sieve script name not valid UTF-8: %s", args[i]); + } + if ( !sieve_script_name_is_valid(args[i]) ) { + i_fatal_status(EX_DATAERR, + "Sieve script name not valid: %s", args[i]); + } + } +} + +static int +doveadm_sieve_cmd_run +(struct doveadm_mail_cmd_context *_ctx, + struct mail_user *user) +{ + struct doveadm_sieve_cmd_context *ctx = + (struct doveadm_sieve_cmd_context *)_ctx; + struct sieve_environment svenv; + enum sieve_error error; + int ret; + + memset((void*)&svenv, 0, sizeof(svenv)); + svenv.username = user->username; + (void)mail_user_get_home(user, &svenv.home_dir); + svenv.base_dir = user->set->base_dir; + svenv.flags = SIEVE_FLAG_HOME_RELATIVE; + + ctx->svinst = sieve_init + (&svenv, &sieve_callbacks, (void *)ctx, user->mail_debug); + + ctx->storage = sieve_storage_create_main + (ctx->svinst, user, SIEVE_STORAGE_FLAG_READWRITE, &error); + if ( ctx->storage == NULL ) { + switch ( error ) { + case SIEVE_ERROR_NOT_POSSIBLE: + error = SIEVE_ERROR_NOT_FOUND; + i_error("Failed to open Sieve storage: " + "Sieve is disabled for this user"); + break; + case SIEVE_ERROR_NOT_FOUND: + i_error("Failed to open Sieve storage: " + "User cannot manage personal Sieve scripts."); + break; + default: + i_error("Failed to open Sieve storage."); + } + doveadm_sieve_cmd_failed_error(ctx, error); + ret = -1; + + } else { + i_assert( ctx->v.run != NULL ); + ret = ctx->v.run(ctx); + sieve_storage_unref(&ctx->storage); + } + + sieve_deinit(&ctx->svinst); + return ret; +} + +struct doveadm_sieve_cmd_context * +doveadm_sieve_cmd_alloc_size(size_t size) +{ + struct doveadm_sieve_cmd_context *ctx; + + ctx = (struct doveadm_sieve_cmd_context *) + doveadm_mail_cmd_alloc_size(size); + ctx->ctx.getopt_args = "s"; + ctx->ctx.v.parse_arg = doveadm_sieve_cmd_parse_arg; + ctx->ctx.v.run = doveadm_sieve_cmd_run; + return ctx; +} + +static struct doveadm_cmd_ver2 *doveadm_sieve_commands[] = { + &doveadm_sieve_cmd_list, + &doveadm_sieve_cmd_get, + &doveadm_sieve_cmd_put, + &doveadm_sieve_cmd_delete, + &doveadm_sieve_cmd_activate, + &doveadm_sieve_cmd_deactivate, + &doveadm_sieve_cmd_rename +}; + +void doveadm_sieve_cmds_init(void) +{ + unsigned int i; + + for (i = 0; i < N_ELEMENTS(doveadm_sieve_commands); i++) + doveadm_cmd_register_ver2(doveadm_sieve_commands[i]); +} diff --git a/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-cmd.h b/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-cmd.h new file mode 100644 index 0000000..1296701 --- /dev/null +++ b/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-cmd.h @@ -0,0 +1,43 @@ +#ifndef DOVEADM_SIEVE_CMD_H +#define DOVEADM_SIEVE_CMD_H + +struct doveadm_sieve_cmd_context; + +struct doveadm_sieve_cmd_vfuncs { + /* This is the main function which performs all the work for the + command. This is called once per each user. */ + int (*run)(struct doveadm_sieve_cmd_context *ctx); +}; + +struct doveadm_sieve_cmd_context { + struct doveadm_mail_cmd_context ctx; + + struct sieve_instance *svinst; + struct sieve_storage *storage; + + struct doveadm_sieve_cmd_vfuncs v; +}; + +void doveadm_sieve_cmd_failed_error +(struct doveadm_sieve_cmd_context *ctx, enum sieve_error error); +void doveadm_sieve_cmd_failed_storage +(struct doveadm_sieve_cmd_context *ctx, struct sieve_storage *storage); + +#define doveadm_sieve_cmd_alloc(type) \ + (type *)doveadm_sieve_cmd_alloc_size(sizeof(type)) +struct doveadm_sieve_cmd_context * +doveadm_sieve_cmd_alloc_size(size_t size); + +void doveadm_sieve_cmd_scriptnames_check(const char *const args[]); + +extern struct doveadm_cmd_ver2 doveadm_sieve_cmd_list; +extern struct doveadm_cmd_ver2 doveadm_sieve_cmd_get; +extern struct doveadm_cmd_ver2 doveadm_sieve_cmd_put; +extern struct doveadm_cmd_ver2 doveadm_sieve_cmd_delete; +extern struct doveadm_cmd_ver2 doveadm_sieve_cmd_activate; +extern struct doveadm_cmd_ver2 doveadm_sieve_cmd_deactivate; +extern struct doveadm_cmd_ver2 doveadm_sieve_cmd_rename; + +void doveadm_sieve_cmds_init(void); + +#endif diff --git a/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-plugin.c b/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-plugin.c new file mode 100644 index 0000000..0478b55 --- /dev/null +++ b/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-plugin.c @@ -0,0 +1,24 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "doveadm-mail.h" + +#include "sieve.h" + +#include "doveadm-sieve-cmd.h" +#include "doveadm-sieve-plugin.h" + +const char *doveadm_sieve_plugin_version = DOVECOT_ABI_VERSION; + +void doveadm_sieve_plugin_init(struct module *module) +{ + doveadm_sieve_sync_init(module); + doveadm_sieve_cmds_init(); +} + +void doveadm_sieve_plugin_deinit(void) +{ + /* the hooks array is freed already */ + /*mail_storage_hooks_remove(&doveadm_sieve_mail_storage_hooks);*/ +} diff --git a/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-plugin.h b/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-plugin.h new file mode 100644 index 0000000..6e9446d --- /dev/null +++ b/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-plugin.h @@ -0,0 +1,17 @@ +#ifndef DOVEADM_SIEVE_PLUGIN_H +#define DOVEADM_SIEVE_PLUGIN_H + +/* + * Plugin interface + */ + +void doveadm_sieve_plugin_init(struct module *module); +void doveadm_sieve_plugin_deinit(void); + +/* + * Replication + */ + +void doveadm_sieve_sync_init(struct module *module); + +#endif diff --git a/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-sync.c b/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-sync.c new file mode 100644 index 0000000..c85a3fc --- /dev/null +++ b/pigeonhole/src/plugins/doveadm-sieve/doveadm-sieve-sync.c @@ -0,0 +1,746 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "ioloop.h" +#include "time-util.h" +#include "istream.h" +#include "istream-concat.h" +#include "mail-storage-private.h" + +#include "sieve.h" +#include "sieve-script.h" +#include "sieve-storage.h" + +#include "doveadm-sieve-plugin.h" + +#define SIEVE_MAIL_CONTEXT(obj) \ + MODULE_CONTEXT_REQUIRE(obj, sieve_storage_module) +#define SIEVE_USER_CONTEXT(obj) \ + MODULE_CONTEXT_REQUIRE(obj, sieve_user_module) + +struct sieve_mail_user { + union mail_user_module_context module_ctx; + + struct sieve_instance *svinst; + struct sieve_storage *sieve_storage; +}; + +struct sieve_mailbox_attribute_iter { + struct mailbox_attribute_iter iter; + struct mailbox_attribute_iter *super; + + struct sieve_storage_list_context *sieve_list; + string_t *name; + + bool failed; + bool have_active; +}; + +static MODULE_CONTEXT_DEFINE_INIT(sieve_storage_module, + &mail_storage_module_register); +static MODULE_CONTEXT_DEFINE_INIT(sieve_user_module, + &mail_user_module_register); + +static const char * +mail_sieve_get_setting(void *context, const char *identifier) +{ + struct mail_user *mail_user = context; + + return mail_user_plugin_getenv(mail_user, identifier); +} + +static const struct sieve_callbacks mail_sieve_callbacks = { + NULL, + mail_sieve_get_setting +}; + +static void mail_sieve_user_deinit(struct mail_user *user) +{ + struct sieve_mail_user *suser = SIEVE_USER_CONTEXT(user); + + if ( suser->svinst != NULL ) { + if (suser->sieve_storage != NULL) + sieve_storage_unref(&suser->sieve_storage); + sieve_deinit(&suser->svinst); + } + + suser->module_ctx.super.deinit(user); +} + +static int +mail_sieve_user_init +(struct mail_user *user, struct sieve_storage **svstorage_r) +{ + struct sieve_mail_user *suser = SIEVE_USER_CONTEXT(user); + enum sieve_storage_flags storage_flags = + SIEVE_STORAGE_FLAG_READWRITE | + SIEVE_STORAGE_FLAG_SYNCHRONIZING; + struct sieve_environment svenv; + + if ( suser->svinst != NULL ) { + *svstorage_r = suser->sieve_storage; + return suser->sieve_storage != NULL ? 1 : 0; + } + + /* Delayed initialization of sieve storage until it's actually needed */ + i_zero(&svenv); + svenv.username = user->username; + (void)mail_user_get_home(user, &svenv.home_dir); + svenv.base_dir = user->set->base_dir; + svenv.flags = SIEVE_FLAG_HOME_RELATIVE; + + suser->svinst = sieve_init(&svenv, &mail_sieve_callbacks, + user, user->mail_debug); + suser->sieve_storage = sieve_storage_create_main + (suser->svinst, user, storage_flags, NULL); + + *svstorage_r = suser->sieve_storage; + return suser->sieve_storage != NULL ? 1 : 0; +} + +static int sieve_attribute_unset_script(struct mail_storage *storage, + struct sieve_storage *svstorage, + const char *scriptname) +{ + struct sieve_script *script; + const char *errstr; + enum sieve_error error; + int ret = 0; + + script = sieve_storage_open_script(svstorage, scriptname, NULL); + if (script == NULL) { + ret = -1; + } else { + ret = sieve_script_delete(script, TRUE); + sieve_script_unref(&script); + } + + if (ret < 0) { + errstr = sieve_storage_get_last_error(svstorage, &error); + if (error == SIEVE_ERROR_NOT_FOUND) { + /* already deleted, ignore */ + return 0; + } + mail_storage_set_critical(storage, + "Failed to delete Sieve script '%s': %s", scriptname, + errstr); + return -1; + } + return 0; +} + +static int +sieve_attribute_set_active(struct mail_storage *storage, + struct sieve_storage *svstorage, + const struct mail_attribute_value *value) +{ + const char *scriptname; + struct sieve_script *script; + time_t last_change = + (value->last_change == 0 ? ioloop_time : value->last_change); + int ret; + + if (mailbox_attribute_value_to_string(storage, value, &scriptname) < 0) + return -1; + + if (scriptname == NULL) { + /* don't affect non-link active script */ + if ((ret=sieve_storage_is_singular(svstorage)) != 0) { + if (ret < 0) { + mail_storage_set_internal_error(storage); + return -1; + } + return 0; + } + + /* deactivate current script */ + if (sieve_storage_deactivate(svstorage, last_change) < 0) { + mail_storage_set_critical(storage, + "Failed to deactivate Sieve: %s", + sieve_storage_get_last_error(svstorage, NULL)); + return -1; + } + return 0; + } + i_assert(scriptname[0] == MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_LINK); + scriptname++; + + /* activate specified script */ + script = sieve_storage_open_script(svstorage, scriptname, NULL); + ret = script == NULL ? -1 : + sieve_script_activate(script, last_change); + if (ret < 0) { + mail_storage_set_critical(storage, + "Failed to activate Sieve script '%s': %s", scriptname, + sieve_storage_get_last_error(svstorage, NULL)); + } + if (script != NULL) + sieve_script_unref(&script); + sieve_storage_set_modified(svstorage, last_change); + return ret; +} + +static int +sieve_attribute_unset_active_script(struct mail_storage *storage, + struct sieve_storage *svstorage, time_t last_change) +{ + int ret; + + if ((ret=sieve_storage_is_singular(svstorage)) != 0) { + if (ret < 0) + mail_storage_set_internal_error(storage); + return ret; + } + + if (sieve_storage_deactivate(svstorage, last_change) < 0) { + mail_storage_set_critical(storage, + "Failed to deactivate sieve: %s", + sieve_storage_get_last_error(svstorage, NULL)); + return -1; + } + return 0; +} + +static int +sieve_attribute_set_active_script(struct mail_storage *storage, + struct sieve_storage *svstorage, + const struct mail_attribute_value *value) +{ + struct istream *input; + time_t last_change = + (value->last_change == 0 ? ioloop_time : value->last_change); + + if (value->value != NULL) { + input = i_stream_create_from_data(value->value, strlen(value->value)); + } else if (value->value_stream != NULL) { + input = value->value_stream; + i_stream_ref(input); + } else { + return sieve_attribute_unset_active_script + (storage, svstorage, last_change); + } + /* skip over the 'S' type */ + i_stream_skip(input, 1); + + if (sieve_storage_save_as_active + (svstorage, input, last_change) < 0) { + mail_storage_set_critical(storage, + "Failed to save active sieve script: %s", + sieve_storage_get_last_error(svstorage, NULL)); + i_stream_unref(&input); + return -1; + } + + sieve_storage_set_modified(svstorage, last_change); + i_stream_unref(&input); + return 0; +} + +static int +sieve_attribute_set_default(struct mail_storage *storage, + struct sieve_storage *svstorage, + const struct mail_attribute_value *value) +{ + const unsigned char *data; + size_t size; + ssize_t ret; + char type; + + if (value->value != NULL) { + type = value->value[0]; + } else if (value->value_stream != NULL) { + ret = i_stream_read_more(value->value_stream, &data, &size); + if (ret == -1) { + mail_storage_set_critical(storage, "read(%s) failed: %m", + i_stream_get_name(value->value_stream)); + return -1; + } + i_assert(ret > 0); + type = data[0]; + } else { + type = MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_SCRIPT; + } + if (type == MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_LINK) + return sieve_attribute_set_active(storage, svstorage, value); + if (type == MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_SCRIPT) + return sieve_attribute_set_active_script(storage, svstorage, value); + mail_storage_set_error(storage, MAIL_ERROR_PARAMS, + "Invalid value for default sieve attribute"); + return -1; +} + +static int +sieve_attribute_set_sieve(struct mail_storage *storage, + const char *key, + const struct mail_attribute_value *value) +{ + struct sieve_storage *svstorage; + struct sieve_storage_save_context *save_ctx; + struct istream *input; + const char *scriptname; + int ret; + + if ((ret = mail_sieve_user_init(storage->user, &svstorage)) <= 0) { + if (ret == 0) { + mail_storage_set_error(storage, MAIL_ERROR_NOTFOUND, + "Sieve not enabled for user"); + } + return -1; + } + + if (strcmp(key, MAILBOX_ATTRIBUTE_SIEVE_DEFAULT) == 0) + return sieve_attribute_set_default(storage, svstorage, value); + if (!str_begins(key, MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES)) { + mail_storage_set_error(storage, MAIL_ERROR_NOTFOUND, + "Nonexistent sieve attribute"); + return -1; + } + scriptname = key + strlen(MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES); + + if (value->value != NULL) { + input = i_stream_create_from_data(value->value, + strlen(value->value)); + save_ctx = sieve_storage_save_init(svstorage, scriptname, input); + } else if (value->value_stream != NULL) { + input = value->value_stream; + i_stream_ref(input); + save_ctx = sieve_storage_save_init(svstorage, scriptname, input); + } else { + return sieve_attribute_unset_script(storage, svstorage, scriptname); + } + + if (save_ctx == NULL) { + /* save initialization failed */ + mail_storage_set_critical(storage, + "Failed to save sieve script '%s': %s", scriptname, + sieve_storage_get_last_error(svstorage, NULL)); + i_stream_unref(&input); + return -1; + } + + if (value->last_change != 0) + sieve_storage_save_set_mtime(save_ctx, value->last_change); + + ret = 0; + while (input->stream_errno == 0 && + !i_stream_read_eof(input)) { + if (sieve_storage_save_continue(save_ctx) < 0) { + mail_storage_set_critical(storage, + "Failed to save sieve script '%s': %s", scriptname, + sieve_storage_get_last_error(svstorage, NULL)); + ret = -1; + break; + } + } + if (input->stream_errno != 0) { + errno = input->stream_errno; + mail_storage_set_critical(storage, + "Saving sieve script: read(%s) failed: %m", + i_stream_get_name(input)); + ret = -1; + } + i_assert(input->eof || ret < 0); + if (ret == 0 && sieve_storage_save_finish(save_ctx) < 0) { + mail_storage_set_critical(storage, + "Failed to save sieve script '%s': %s", scriptname, + sieve_storage_get_last_error(svstorage, NULL)); + ret = -1; + } + if (ret < 0) + sieve_storage_save_cancel(&save_ctx); + else if (sieve_storage_save_commit(&save_ctx) < 0) { + mail_storage_set_critical(storage, + "Failed to save sieve script '%s': %s", scriptname, + sieve_storage_get_last_error(svstorage, NULL)); + ret = -1; + } + i_stream_unref(&input); + return ret; +} + +static int +sieve_attribute_set(struct mailbox_transaction_context *t, + enum mail_attribute_type type, const char *key, + const struct mail_attribute_value *value) +{ + struct mail_user *user = t->box->storage->user; + union mailbox_module_context *sbox = SIEVE_MAIL_CONTEXT(t->box); + + if (t->box->storage->user->dsyncing && + type == MAIL_ATTRIBUTE_TYPE_PRIVATE && + str_begins(key, MAILBOX_ATTRIBUTE_PREFIX_SIEVE)) { + time_t ts = + (value->last_change != 0 ? value->last_change : ioloop_time); + + if (sieve_attribute_set_sieve(t->box->storage, key, value) < 0) + return -1; + + if (user->mail_debug) { + const char *change; + if (value->last_change != 0) { + change = t_strflocaltime + ("(last change: %Y-%m-%d %H:%M:%S)", value->last_change); + } else { + change = t_strflocaltime + ("(time: %Y-%m-%d %H:%M:%S)", ioloop_time); + } + i_debug("doveadm-sieve: Assigned value for key `%s' %s", + key, change); + } + + /* FIXME: set value len to sieve script size / active name + length */ + if (value->value != NULL || value->value_stream != NULL) + mail_index_attribute_set(t->itrans, TRUE, key, ts, 0); + else + mail_index_attribute_unset(t->itrans, TRUE, key, ts); + return 0; + } + return sbox->super.attribute_set(t, type, key, value); +} + +static int +sieve_attribute_retrieve_script(struct mail_storage *storage, + struct sieve_storage *svstorage, struct sieve_script *script, + bool add_type_prefix, + struct mail_attribute_value *value_r, const char **errorstr_r) +{ + static char type = MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_SCRIPT; + struct istream *input, *inputs[3]; + const struct stat *st; + enum sieve_error error; + + if (script == NULL) + *errorstr_r = sieve_storage_get_last_error(svstorage, &error); + else if (sieve_script_get_stream(script, &input, &error) < 0) + sieve_script_unref(&script); + + if (script == NULL) { + if (error == SIEVE_ERROR_NOT_FOUND) { + /* already deleted, but return the last_change */ + (void)sieve_storage_get_last_change(svstorage, + &value_r->last_change); + return 0; + } + *errorstr_r = sieve_storage_get_last_error(svstorage, &error); + return -1; + } + if (i_stream_stat(input, FALSE, &st) < 0) { + mail_storage_set_critical(storage, + "stat(%s) failed: %m", i_stream_get_name(input)); + } else { + value_r->last_change = st->st_mtime; + } + if (!add_type_prefix) { + i_stream_ref(input); + value_r->value_stream = input; + } else { + inputs[0] = i_stream_create_from_data(&type, 1); + inputs[1] = input; + inputs[2] = NULL; + value_r->value_stream = i_stream_create_concat(inputs); + i_stream_unref(&inputs[0]); + } + sieve_script_unref(&script); + return 1; +} + +static int +sieve_attribute_get_active_script(struct mail_storage *storage, + struct sieve_storage *svstorage, + struct mail_attribute_value *value_r) +{ + struct sieve_script *script; + const char *errstr; + int ret; + + if ((ret=sieve_storage_is_singular(svstorage)) <= 0) { + if (ret == 0 && sieve_storage_active_script_get_last_change + (svstorage, &value_r->last_change) < 0) { + ret = -1; + } + if (ret < 0) + mail_storage_set_internal_error(storage); + return ret; + } + + if ((script=sieve_storage_active_script_open + (svstorage, NULL)) == NULL) + return 0; + + if ((ret=sieve_attribute_retrieve_script + (storage, svstorage, script, TRUE, value_r, &errstr)) < 0) { + mail_storage_set_critical(storage, + "Failed to access active sieve script: %s", errstr); + } + return ret; +} + +static int +sieve_attribute_get_default(struct mail_storage *storage, + struct sieve_storage *svstorage, + struct mail_attribute_value *value_r) +{ + const char *scriptname; + int ret; + + ret = sieve_storage_active_script_get_name(svstorage, &scriptname); + if (ret == 0) + return sieve_attribute_get_active_script(storage, svstorage, value_r); + + if (ret > 0) { + value_r->value = t_strdup_printf("%c%s", + MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_LINK, scriptname); + if (sieve_storage_active_script_get_last_change + (svstorage, &value_r->last_change) < 0) + ret = -1; + } + if (ret < 0) + mail_storage_set_internal_error(storage); + return ret; +} + +static int +sieve_attribute_get_sieve(struct mail_storage *storage, const char *key, + struct mail_attribute_value *value_r) +{ + struct sieve_storage *svstorage; + struct sieve_script *script; + const char *scriptname, *errstr; + int ret; + + if ((ret = mail_sieve_user_init(storage->user, &svstorage)) <= 0) + return ret; + + if (strcmp(key, MAILBOX_ATTRIBUTE_SIEVE_DEFAULT) == 0) + return sieve_attribute_get_default(storage, svstorage, value_r); + if (!str_begins(key, MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES)) + return 0; + if ((value_r->flags & MAIL_ATTRIBUTE_VALUE_FLAG_INT_STREAMS) == 0) { + mail_storage_set_error(storage, MAIL_ERROR_PARAMS, + "Sieve attributes are available only as streams"); + return -1; + } + scriptname = key + strlen(MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES); + script = sieve_storage_open_script(svstorage, scriptname, NULL); + if ((ret=sieve_attribute_retrieve_script + (storage, svstorage, script, FALSE, value_r, &errstr)) < 0) { + mail_storage_set_critical(storage, + "Failed to access sieve script '%s': %s", + scriptname, errstr); + } + return ret; +} + +static int +sieve_attribute_get(struct mailbox *box, + enum mail_attribute_type type, const char *key, + struct mail_attribute_value *value_r) +{ + union mailbox_module_context *sbox = SIEVE_MAIL_CONTEXT(box); + struct mail_user *user = box->storage->user; + int ret; + + if (box->storage->user->dsyncing && + type == MAIL_ATTRIBUTE_TYPE_PRIVATE && + str_begins(key, MAILBOX_ATTRIBUTE_PREFIX_SIEVE)) { + + ret = sieve_attribute_get_sieve(box->storage, key, value_r); + if (ret >= 0 && user->mail_debug) { + struct tm *tm = localtime(&value_r->last_change); + char str[256]; + const char *timestamp = ""; + + if (strftime(str, sizeof(str), + " (last change: %Y-%m-%d %H:%M:%S)", tm) > 0) + timestamp = str; + + if (ret > 0) { + i_debug("doveadm-sieve: Retrieved value for key `%s'%s", + key, timestamp); + } else { + i_debug("doveadm-sieve: Value missing for key `%s'%s", + key, timestamp); + } + } + return ret; + } + return sbox->super.attribute_get(box, type, key, value_r); +} + +static int +sieve_attribute_iter_script_init(struct sieve_mailbox_attribute_iter *siter) +{ + struct mail_user *user = siter->iter.box->storage->user; + struct sieve_storage *svstorage; + int ret; + + if (user->mail_debug) + i_debug("doveadm-sieve: Iterating Sieve mailbox attributes"); + + if ((ret = mail_sieve_user_init(user, &svstorage)) <= 0) + return ret; + + siter->sieve_list = sieve_storage_list_init(svstorage); + if (siter->sieve_list == NULL) { + mail_storage_set_critical(siter->iter.box->storage, + "Failed to iterate sieve scripts: %s", + sieve_storage_get_last_error(svstorage, NULL)); + return -1; + } + siter->name = str_new(default_pool, 128); + str_append(siter->name, MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES); + return 0; +} + +static struct mailbox_attribute_iter * +sieve_attribute_iter_init(struct mailbox *box, enum mail_attribute_type type, + const char *prefix) +{ + union mailbox_module_context *sbox = SIEVE_MAIL_CONTEXT(box); + struct sieve_mailbox_attribute_iter *siter; + + siter = i_new(struct sieve_mailbox_attribute_iter, 1); + siter->iter.box = box; + siter->super = sbox->super.attribute_iter_init(box, type, prefix); + + if (box->storage->user->dsyncing && + type == MAIL_ATTRIBUTE_TYPE_PRIVATE) { + if (sieve_attribute_iter_script_init(siter) < 0) + siter->failed = TRUE; + } + return &siter->iter; +} + +static const char * +sieve_attribute_iter_next_script(struct sieve_mailbox_attribute_iter *siter) +{ + struct mail_user *user = siter->iter.box->storage->user; + struct sieve_mail_user *suser = SIEVE_USER_CONTEXT(user); + struct sieve_storage *svstorage = suser->sieve_storage; + const char *scriptname; + bool active; + int ret; + + if (siter->sieve_list == NULL) + return NULL; + + /* Iterate through all scripts in sieve_dir */ + while ((scriptname = sieve_storage_list_next(siter->sieve_list, &active)) + != NULL) { + if (active) + siter->have_active = TRUE; + str_truncate(siter->name, strlen(MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES)); + str_append(siter->name, scriptname); + return str_c(siter->name); + } + if (sieve_storage_list_deinit(&siter->sieve_list) < 0) { + mail_storage_set_critical(siter->iter.box->storage, + "Failed to iterate sieve scripts: %s", + sieve_storage_get_last_error(svstorage, NULL)); + siter->failed = TRUE; + return NULL; + } + + /* Check whether active script is a proper symlink or a regular file */ + if ((ret=sieve_storage_is_singular(svstorage)) < 0) { + mail_storage_set_critical(siter->iter.box->storage, + "Failed to iterate sieve scripts: %s", + sieve_storage_get_last_error(svstorage, NULL)); + return NULL; + } + + /* Regular file */ + if (ret > 0) + return MAILBOX_ATTRIBUTE_SIEVE_DEFAULT; + + /* Symlink or none active */ + return siter->have_active ? MAILBOX_ATTRIBUTE_SIEVE_DEFAULT : NULL; +} + +static const char * +sieve_attribute_iter_next(struct mailbox_attribute_iter *iter) +{ + struct sieve_mailbox_attribute_iter *siter = + (struct sieve_mailbox_attribute_iter *)iter; + union mailbox_module_context *sbox = SIEVE_MAIL_CONTEXT(iter->box); + struct mail_user *user = iter->box->storage->user; + const char *key; + + if (siter->sieve_list != NULL) { + if ((key = sieve_attribute_iter_next_script(siter)) != NULL) { + if (user->mail_debug) { + i_debug("doveadm-sieve: Iterating Sieve mailbox attribute: %s", key); + } + return key; + } + } + return sbox->super.attribute_iter_next(siter->super); +} + +static int +sieve_attribute_iter_deinit(struct mailbox_attribute_iter *iter) +{ + struct sieve_mailbox_attribute_iter *siter = + (struct sieve_mailbox_attribute_iter *)iter; + union mailbox_module_context *sbox = SIEVE_MAIL_CONTEXT(iter->box); + int ret = siter->failed ? -1 : 0; + + if (siter->super != NULL) { + if (sbox->super.attribute_iter_deinit(siter->super) < 0) + ret = -1; + } + if (siter->sieve_list != NULL) + (void)sieve_storage_list_deinit(&siter->sieve_list); + if (siter->name != NULL) + str_free(&siter->name); + i_free(siter); + return ret; +} + +static void +sieve_mail_user_created(struct mail_user *user) +{ + struct sieve_mail_user *suser; + struct mail_user_vfuncs *v = user->vlast; + + suser = p_new(user->pool, struct sieve_mail_user, 1); + suser->module_ctx.super = *v; + user->vlast = &suser->module_ctx.super; + v->deinit = mail_sieve_user_deinit; + MODULE_CONTEXT_SET(user, sieve_user_module, suser); +} + +static void +sieve_mailbox_allocated(struct mailbox *box) +{ + struct mailbox_vfuncs *v = box->vlast; + union mailbox_module_context *sbox; + + /* attribute syncing is done via INBOX */ + if (!box->inbox_user) + return; + + sbox = p_new(box->pool, union mailbox_module_context, 1); + sbox->super = *v; + box->vlast = &sbox->super; + v->attribute_set = sieve_attribute_set; + v->attribute_get = sieve_attribute_get; + v->attribute_iter_init = sieve_attribute_iter_init; + v->attribute_iter_next = sieve_attribute_iter_next; + v->attribute_iter_deinit = sieve_attribute_iter_deinit; + MODULE_CONTEXT_SET_SELF(box, sieve_storage_module, sbox); +} + +static struct mail_storage_hooks doveadm_sieve_mail_storage_hooks = { + .mail_user_created = sieve_mail_user_created, + .mailbox_allocated = sieve_mailbox_allocated +}; + +void doveadm_sieve_sync_init(struct module *module) +{ + mail_storage_hooks_add_forced + (module, &doveadm_sieve_mail_storage_hooks); +} diff --git a/pigeonhole/src/plugins/imap-filter-sieve/Makefile.am b/pigeonhole/src/plugins/imap-filter-sieve/Makefile.am new file mode 100644 index 0000000..915e128 --- /dev/null +++ b/pigeonhole/src/plugins/imap-filter-sieve/Makefile.am @@ -0,0 +1,26 @@ +imap_moduledir = $(dovecot_moduledir) + +imap_module_LTLIBRARIES = lib95_imap_filter_sieve_plugin.la + +lib95_imap_filter_sieve_plugin_la_LDFLAGS = -module -avoid-version + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib-sieve \ + $(LIBDOVECOT_IMAP_INCLUDE) \ + $(LIBDOVECOT_LDA_INCLUDE) \ + $(LIBDOVECOT_INCLUDE) \ + -DPKG_RUNDIR=\""$(rundir)"\" + +lib95_imap_filter_sieve_plugin_la_SOURCES = \ + cmd-filter.c \ + cmd-filter-sieve.c \ + imap-filter.c \ + imap-filter-sieve.c \ + imap-filter-sieve-plugin.c +lib95_imap_filter_sieve_plugin_la_LIBADD = \ + $(top_builddir)/src/lib-sieve/libdovecot-sieve.la + +noinst_HEADERS = \ + imap-filter.h \ + imap-filter-sieve.h \ + imap-filter-sieve-plugin.h diff --git a/pigeonhole/src/plugins/imap-filter-sieve/Makefile.in b/pigeonhole/src/plugins/imap-filter-sieve/Makefile.in new file mode 100644 index 0000000..b74a15b --- /dev/null +++ b/pigeonhole/src/plugins/imap-filter-sieve/Makefile.in @@ -0,0 +1,771 @@ +# 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/plugins/imap-filter-sieve +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 = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(imap_moduledir)" +LTLIBRARIES = $(imap_module_LTLIBRARIES) +lib95_imap_filter_sieve_plugin_la_DEPENDENCIES = \ + $(top_builddir)/src/lib-sieve/libdovecot-sieve.la +am_lib95_imap_filter_sieve_plugin_la_OBJECTS = cmd-filter.lo \ + cmd-filter-sieve.lo imap-filter.lo imap-filter-sieve.lo \ + imap-filter-sieve-plugin.lo +lib95_imap_filter_sieve_plugin_la_OBJECTS = \ + $(am_lib95_imap_filter_sieve_plugin_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 = +lib95_imap_filter_sieve_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) \ + --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \ + $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(lib95_imap_filter_sieve_plugin_la_LDFLAGS) $(LDFLAGS) -o $@ +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)/cmd-filter-sieve.Plo \ + ./$(DEPDIR)/cmd-filter.Plo \ + ./$(DEPDIR)/imap-filter-sieve-plugin.Plo \ + ./$(DEPDIR)/imap-filter-sieve.Plo ./$(DEPDIR)/imap-filter.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 = $(lib95_imap_filter_sieve_plugin_la_SOURCES) +DIST_SOURCES = $(lib95_imap_filter_sieve_plugin_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@ +imap_moduledir = $(dovecot_moduledir) +imap_module_LTLIBRARIES = lib95_imap_filter_sieve_plugin.la +lib95_imap_filter_sieve_plugin_la_LDFLAGS = -module -avoid-version +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib-sieve \ + $(LIBDOVECOT_IMAP_INCLUDE) \ + $(LIBDOVECOT_LDA_INCLUDE) \ + $(LIBDOVECOT_INCLUDE) \ + -DPKG_RUNDIR=\""$(rundir)"\" + +lib95_imap_filter_sieve_plugin_la_SOURCES = \ + cmd-filter.c \ + cmd-filter-sieve.c \ + imap-filter.c \ + imap-filter-sieve.c \ + imap-filter-sieve-plugin.c + +lib95_imap_filter_sieve_plugin_la_LIBADD = \ + $(top_builddir)/src/lib-sieve/libdovecot-sieve.la + +noinst_HEADERS = \ + imap-filter.h \ + imap-filter-sieve.h \ + imap-filter-sieve-plugin.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/plugins/imap-filter-sieve/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/plugins/imap-filter-sieve/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): + +install-imap_moduleLTLIBRARIES: $(imap_module_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(imap_module_LTLIBRARIES)'; test -n "$(imap_moduledir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(imap_moduledir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(imap_moduledir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(imap_moduledir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(imap_moduledir)"; \ + } + +uninstall-imap_moduleLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(imap_module_LTLIBRARIES)'; test -n "$(imap_moduledir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(imap_moduledir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(imap_moduledir)/$$f"; \ + done + +clean-imap_moduleLTLIBRARIES: + -test -z "$(imap_module_LTLIBRARIES)" || rm -f $(imap_module_LTLIBRARIES) + @list='$(imap_module_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}; \ + } + +lib95_imap_filter_sieve_plugin.la: $(lib95_imap_filter_sieve_plugin_la_OBJECTS) $(lib95_imap_filter_sieve_plugin_la_DEPENDENCIES) $(EXTRA_lib95_imap_filter_sieve_plugin_la_DEPENDENCIES) + $(AM_V_CCLD)$(lib95_imap_filter_sieve_plugin_la_LINK) -rpath $(imap_moduledir) $(lib95_imap_filter_sieve_plugin_la_OBJECTS) $(lib95_imap_filter_sieve_plugin_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-filter-sieve.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-filter.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-filter-sieve-plugin.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-filter-sieve.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-filter.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: + for dir in "$(DESTDIR)$(imap_moduledir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-imap_moduleLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/cmd-filter-sieve.Plo + -rm -f ./$(DEPDIR)/cmd-filter.Plo + -rm -f ./$(DEPDIR)/imap-filter-sieve-plugin.Plo + -rm -f ./$(DEPDIR)/imap-filter-sieve.Plo + -rm -f ./$(DEPDIR)/imap-filter.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-imap_moduleLTLIBRARIES + +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)/cmd-filter-sieve.Plo + -rm -f ./$(DEPDIR)/cmd-filter.Plo + -rm -f ./$(DEPDIR)/imap-filter-sieve-plugin.Plo + -rm -f ./$(DEPDIR)/imap-filter-sieve.Plo + -rm -f ./$(DEPDIR)/imap-filter.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-imap_moduleLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-imap_moduleLTLIBRARIES clean-libtool \ + 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-imap_moduleLTLIBRARIES 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 uninstall-imap_moduleLTLIBRARIES + +.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/plugins/imap-filter-sieve/cmd-filter-sieve.c b/pigeonhole/src/plugins/imap-filter-sieve/cmd-filter-sieve.c new file mode 100644 index 0000000..6b96bd3 --- /dev/null +++ b/pigeonhole/src/plugins/imap-filter-sieve/cmd-filter-sieve.c @@ -0,0 +1,403 @@ +/* Copyright (c) 2017-2018 Pigeonhole authors, see the included COPYING file */ + +#include "imap-common.h" +#include "str.h" +#include "istream.h" +#include "istream-seekable.h" +#include "ostream.h" +#include "imap-commands.h" + +#include "imap-filter.h" +#include "imap-filter-sieve.h" + +#define FILTER_MAX_INMEM_SIZE (1024*128) + +static int cmd_filter_sieve_compile_script(struct imap_filter_context *ctx) +{ + struct client_command_context *cmd = ctx->cmd; + struct imap_filter_sieve_context *sctx = ctx->sieve; + struct client *client = cmd->client; + string_t *errors = NULL; + bool have_warnings = FALSE; + int ret = 0; + + ret = imap_filter_sieve_compile(sctx, &errors, &have_warnings); + if (ret >= 0 && !have_warnings) + return 0; + + o_stream_nsend_str(client->output, + t_strdup_printf("* FILTER (TAG %s) " + "%s {%zu}\r\n", + cmd->tag, (ret < 0 ? "ERRORS" : "WARNINGS"), + str_len(errors))); + o_stream_nsend(client->output, + str_data(errors), str_len(errors)); + o_stream_nsend_str(client->output, "\r\n"); + + if (ret < 0) { + ctx->compile_failure = TRUE; + ctx->failed = TRUE; + return -1; + } + return 0; +} + +static bool cmd_filter_sieve_delivery(struct client_command_context *cmd) +{ + struct imap_filter_context *ctx = cmd->context; + struct client *client = cmd->client; + struct imap_filter_sieve_context *sctx = ctx->sieve; + enum mail_error error; + const char *error_string; + int ret; + + if (cmd->cancel) { + imap_filter_deinit(ctx); + return TRUE; + } + + i_assert(sctx->filter_type == IMAP_FILTER_SIEVE_TYPE_DELIVERY); + ret = imap_filter_sieve_open_personal(sctx, NULL, + &error, &error_string); + if (ret < 0) { + client_send_tagline( + cmd, imap_get_error_string(cmd, error_string, error)); + imap_filter_deinit(ctx); + return TRUE; + } + if (cmd_filter_sieve_compile_script(ctx) < 0) { + client_send_tagline(cmd, "NO Failed to compile Sieve script"); + client->input_skip_line = TRUE; + imap_filter_deinit(ctx); + return TRUE; + } + + imap_parser_reset(ctx->parser); + cmd->func = imap_filter_search; + return imap_filter_search(cmd); +} + +static int +cmd_filter_sieve_script_parse_name_arg(struct imap_filter_context *ctx) +{ + struct client_command_context *cmd = ctx->cmd; + const struct imap_arg *args; + const char *error; + enum imap_parser_error parse_error; + int ret; + + ret = imap_parser_read_args(ctx->parser, 1, 0, &args); + if (ret < 0) { + if (ret == -2) + return 0; + error = imap_parser_get_error(ctx->parser, &parse_error); + switch (parse_error) { + case IMAP_PARSE_ERROR_NONE: + i_unreached(); + case IMAP_PARSE_ERROR_LITERAL_TOO_BIG: + client_disconnect_with_error(ctx->cmd->client, error); + break; + default: + client_send_command_error(ctx->cmd, error); + break; + } + return -1; + } + + switch (args[0].type) { + case IMAP_ARG_EOL: + client_send_command_error(ctx->cmd, "Script name missing"); + return -1; + case IMAP_ARG_NIL: + case IMAP_ARG_LIST: + client_send_command_error( + ctx->cmd, "Script name must be an atom or a string"); + return -1; + case IMAP_ARG_ATOM: + case IMAP_ARG_STRING: + /* We have the value already */ + if (ctx->failed) + return 1; + ctx->script_name = p_strdup(cmd->pool, + imap_arg_as_astring(&args[0])); + break; + case IMAP_ARG_LITERAL: + case IMAP_ARG_LITERAL_SIZE: + case IMAP_ARG_LITERAL_SIZE_NONSYNC: + i_unreached(); + } + return 1; +} + +static bool +cmd_filter_sieve_script_parse_name(struct client_command_context *cmd) +{ + struct imap_filter_context *ctx = cmd->context; + struct client *client = cmd->client; + struct imap_filter_sieve_context *sctx = ctx->sieve; + enum mail_error error; + const char *error_string; + int ret; + + if (cmd->cancel) { + imap_filter_deinit(ctx); + return TRUE; + } + + if ((ret = cmd_filter_sieve_script_parse_name_arg(ctx)) == 0) + return FALSE; + if (ret < 0) { + /* Already sent the error to client */ + imap_filter_deinit(ctx); + return TRUE; + } + + switch (sctx->filter_type) { + case IMAP_FILTER_SIEVE_TYPE_PERSONAL: + ret = imap_filter_sieve_open_personal(sctx, ctx->script_name, + &error, &error_string); + break; + case IMAP_FILTER_SIEVE_TYPE_GLOBAL: + ret = imap_filter_sieve_open_global(sctx, ctx->script_name, + &error, &error_string); + break; + case IMAP_FILTER_SIEVE_TYPE_DELIVERY: + case IMAP_FILTER_SIEVE_TYPE_SCRIPT: + i_unreached(); + } + if (ret < 0) { + client_send_tagline( + cmd, imap_get_error_string(cmd, error_string, error)); + imap_filter_deinit(ctx); + return TRUE; + } + if (cmd_filter_sieve_compile_script(ctx) < 0) { + client_send_tagline(cmd, "NO Failed to compile Sieve script"); + client->input_skip_line = TRUE; + imap_filter_deinit(ctx); + return TRUE; + } + + imap_parser_reset(ctx->parser); + cmd->func = imap_filter_search; + return imap_filter_search(cmd); +} + +static void +cmd_filter_sieve_compile_input(struct imap_filter_context *ctx, + struct istream *input) +{ + struct imap_filter_sieve_context *sctx = ctx->sieve; + + imap_filter_sieve_open_input(sctx, input); + (void)cmd_filter_sieve_compile_script(ctx); +} + +static int cmd_filter_sieve_script_read_stream(struct imap_filter_context *ctx) +{ + struct istream *input = ctx->script_input; + const unsigned char *data; + size_t size; + int ret; + + while ((ret = i_stream_read_more(input, &data, &size)) > 0) + i_stream_skip(input, size); + if (ret == 0) + return 0; + + if (input->v_offset != ctx->script_len) { + /* Client disconnected */ + i_assert(input->eof); + return -1; + } + /* Finished reading the value */ + i_stream_seek(input, 0); + + if (ctx->failed) { + i_stream_unref(&ctx->script_input); + return 1; + } + + cmd_filter_sieve_compile_input(ctx, ctx->script_input); + i_stream_unref(&ctx->script_input); + return 1; +} + +static int +cmd_filter_sieve_script_parse_value_arg(struct imap_filter_context *ctx) +{ + const struct imap_arg *args; + const char *value, *error; + enum imap_parser_error parse_error; + struct istream *input, *inputs[2]; + string_t *path; + int ret; + + ret = imap_parser_read_args(ctx->parser, 1, + IMAP_PARSE_FLAG_LITERAL_SIZE | + IMAP_PARSE_FLAG_LITERAL8, &args); + if (ret < 0) { + if (ret == -2) + return 0; + error = imap_parser_get_error(ctx->parser, &parse_error); + switch (parse_error) { + case IMAP_PARSE_ERROR_NONE: + i_unreached(); + case IMAP_PARSE_ERROR_LITERAL_TOO_BIG: + client_disconnect_with_error(ctx->cmd->client, error); + break; + default: + client_send_command_error(ctx->cmd, error); + break; + } + return -1; + } + + switch (args[0].type) { + case IMAP_ARG_EOL: + client_send_command_error(ctx->cmd, "Script value missing"); + return -1; + case IMAP_ARG_NIL: + case IMAP_ARG_ATOM: + case IMAP_ARG_LIST: + client_send_command_error(ctx->cmd, + "Script value must be a string"); + return -1; + case IMAP_ARG_STRING: + /* We have the value already */ + if (ctx->failed) + return 1; + value = imap_arg_as_astring(&args[0]); + input = i_stream_create_from_data(value, strlen(value)); + cmd_filter_sieve_compile_input(ctx, input); + i_stream_unref(&input); + return 1; + case IMAP_ARG_LITERAL_SIZE: + o_stream_nsend(ctx->cmd->client->output, "+ OK\r\n", 6); + o_stream_uncork(ctx->cmd->client->output); + o_stream_cork(ctx->cmd->client->output); + /* Fall through */ + case IMAP_ARG_LITERAL_SIZE_NONSYNC: + ctx->script_len = imap_arg_as_literal_size(&args[0]); + + inputs[0] = i_stream_create_limit(ctx->cmd->client->input, + ctx->script_len); + inputs[1] = NULL; + + path = t_str_new(128); + mail_user_set_get_temp_prefix(path, + ctx->cmd->client->user->set); + ctx->script_input = i_stream_create_seekable_path( + inputs, FILTER_MAX_INMEM_SIZE, str_c(path)); + i_stream_set_name(ctx->script_input, + i_stream_get_name(inputs[0])); + i_stream_unref(&inputs[0]); + break; + case IMAP_ARG_LITERAL: + i_unreached(); + } + return cmd_filter_sieve_script_read_stream(ctx); +} + +static bool +cmd_filter_sieve_script_parse_value(struct client_command_context *cmd) +{ + struct imap_filter_context *ctx = cmd->context; + struct client *client = cmd->client; + int ret; + + if (cmd->cancel) { + imap_filter_deinit(ctx); + return TRUE; + } + + if (ctx->script_input != NULL) { + if ((ret = cmd_filter_sieve_script_read_stream(ctx)) == 0) + return FALSE; + } else { + if ((ret = cmd_filter_sieve_script_parse_value_arg(ctx)) == 0) + return FALSE; + } + + if (ret < 0) { + /* Already sent the error to client */ ; + imap_filter_deinit(ctx); + return TRUE; + } else if (ctx->compile_failure) { + client_send_tagline(cmd, "NO Failed to compile Sieve script"); + client->input_skip_line = TRUE; + imap_filter_deinit(ctx); + return TRUE; + } + + imap_parser_reset(ctx->parser); + cmd->func = imap_filter_search; + return imap_filter_search(cmd); +} + +bool cmd_filter_sieve(struct client_command_context *cmd) +{ + struct imap_filter_context *ctx = cmd->context; + struct client *client = cmd->client; + enum imap_filter_sieve_type type; + const struct imap_arg *args; + const char *sieve_type; + + if (!client_read_args(cmd, 2, 0, &args)) + return FALSE; + args++; + + /* sieve-type */ + if (IMAP_ARG_IS_EOL(args)) { + client_send_command_error( + cmd, "Missing SIEVE filter sub-type."); + return TRUE; + } + if (!imap_arg_get_atom(args, &sieve_type)) { + client_send_command_error( + cmd, "SIEVE filter sub-type is not an atom."); + return TRUE; + } + if (strcasecmp(sieve_type, "DELIVERY") == 0) { + type = IMAP_FILTER_SIEVE_TYPE_DELIVERY; + } else if (strcasecmp(sieve_type, "PERSONAL") == 0) { + type = IMAP_FILTER_SIEVE_TYPE_PERSONAL; + } else if (strcasecmp(sieve_type, "GLOBAL") == 0) { + type = IMAP_FILTER_SIEVE_TYPE_GLOBAL; + } else if (strcasecmp(sieve_type, "SCRIPT") == 0) { + type = IMAP_FILTER_SIEVE_TYPE_SCRIPT; + } else { + client_send_command_error(cmd, t_strdup_printf( + "Unknown SIEVE filter sub-type `%s'", + sieve_type)); + return TRUE; + } + + ctx->sieve = imap_filter_sieve_context_create(ctx, type); + + /* We support large scripts, so read the values from client + asynchronously the same way as APPEND does. */ + client->input_lock = cmd; + ctx->parser = imap_parser_create(client->input, client->output, + client->set->imap_max_line_length); + if (client->set->imap_literal_minus) + imap_parser_enable_literal_minus(ctx->parser); + o_stream_unset_flush_callback(client->output); + + switch (type) { + case IMAP_FILTER_SIEVE_TYPE_DELIVERY: + cmd->func = cmd_filter_sieve_delivery; + break; + case IMAP_FILTER_SIEVE_TYPE_PERSONAL: + cmd->func = cmd_filter_sieve_script_parse_name; + break; + case IMAP_FILTER_SIEVE_TYPE_GLOBAL: + cmd->func = cmd_filter_sieve_script_parse_name; + break; + case IMAP_FILTER_SIEVE_TYPE_SCRIPT: + cmd->func = cmd_filter_sieve_script_parse_value; + break; + } + cmd->context = ctx; + return cmd->func(cmd); +} diff --git a/pigeonhole/src/plugins/imap-filter-sieve/cmd-filter.c b/pigeonhole/src/plugins/imap-filter-sieve/cmd-filter.c new file mode 100644 index 0000000..2458047 --- /dev/null +++ b/pigeonhole/src/plugins/imap-filter-sieve/cmd-filter.c @@ -0,0 +1,57 @@ +/* Copyright (c) 2017-2018 Pigeonhole authors, see the included COPYING file */ + +#include "imap-common.h" + +#include "imap-filter.h" +#include "imap-filter-sieve.h" + +static bool +cmd_filter_parse_spec(struct imap_filter_context *ctx, + const struct imap_arg **_args) +{ + const struct imap_arg *args = *_args; + struct client_command_context *cmd = ctx->cmd; + const char *filter_type; + + /* filter-type */ + if (IMAP_ARG_IS_EOL(args)) { + client_send_command_error(cmd, + "Missing filter type."); + return TRUE; + } + if (!imap_arg_get_atom(args, &filter_type)) { + client_send_command_error(cmd, + "Filter type is not an atom."); + return TRUE; + } + if (strcasecmp(filter_type, "SIEVE") != 0) { + client_send_command_error(cmd, t_strdup_printf( + "Unknown filter type `%s'", filter_type)); + return TRUE; + } + + cmd->func = cmd_filter_sieve; + cmd->context = ctx; + return cmd_filter_sieve(cmd); +} + +bool cmd_filter(struct client_command_context *cmd) +{ + struct imap_filter_context *ctx; + const struct imap_arg *args; + + if (!client_read_args(cmd, 1, 0, &args)) + return FALSE; + + if (!client_verify_open_mailbox(cmd)) + return TRUE; + + ctx = p_new(cmd->pool, struct imap_filter_context, 1); + ctx->cmd = cmd; + + if (!cmd_filter_parse_spec(ctx, &args)) + return FALSE; + + imap_filter_context_free(ctx); + return TRUE; +} diff --git a/pigeonhole/src/plugins/imap-filter-sieve/imap-filter-sieve-plugin.c b/pigeonhole/src/plugins/imap-filter-sieve/imap-filter-sieve-plugin.c new file mode 100644 index 0000000..3021dbe --- /dev/null +++ b/pigeonhole/src/plugins/imap-filter-sieve/imap-filter-sieve-plugin.c @@ -0,0 +1,56 @@ +/* Copyright (c) 2017-2018 Pigeonhole authors, see the included COPYING file */ + +#include "imap-common.h" +#include "str.h" + +#include "imap-filter-sieve.h" +#include "imap-filter-sieve-plugin.h" + +static struct module *imap_filter_sieve_module; +static imap_client_created_func_t *next_hook_client_created; + +/* + * Client + */ + +static void imap_filter_sieve_plugin_client_created(struct client **clientp) +{ + struct client *client = *clientp; + struct mail_user *user = client->user; + + if (mail_user_is_plugin_loaded(user, imap_filter_sieve_module)) { + client_add_capability(client, "FILTER=SIEVE"); + + imap_filter_sieve_client_created(client); + } + + if (next_hook_client_created != NULL) + next_hook_client_created(clientp); +} + +/* + * Plugin + */ + +const char *imap_filter_sieve_plugin_version = DOVECOT_ABI_VERSION; +const char imap_filter_sieve_plugin_binary_dependency[] = "imap"; + +void imap_filter_sieve_plugin_init(struct module *module) +{ + command_register("FILTER", cmd_filter, COMMAND_FLAG_USES_SEQS); + command_register("UID FILTER", cmd_filter, COMMAND_FLAG_BREAKS_SEQS); + + imap_filter_sieve_module = module; + next_hook_client_created = imap_client_created_hook_set( + imap_filter_sieve_plugin_client_created); + imap_filter_sieve_init(module); +} + +void imap_filter_sieve_plugin_deinit(void) +{ + command_unregister("FILTER"); + command_unregister("UID FILTER"); + + imap_filter_sieve_deinit(); + imap_client_created_hook_set(next_hook_client_created); +} diff --git a/pigeonhole/src/plugins/imap-filter-sieve/imap-filter-sieve-plugin.h b/pigeonhole/src/plugins/imap-filter-sieve/imap-filter-sieve-plugin.h new file mode 100644 index 0000000..59b4b11 --- /dev/null +++ b/pigeonhole/src/plugins/imap-filter-sieve/imap-filter-sieve-plugin.h @@ -0,0 +1,16 @@ +/* Copyright (c) 2017-2018 Pigeonhole authors, see the included COPYING file + */ + +#ifndef IMAP_FILTER_SIEVE_PLUGIN_H +#define IMAP_FILTER_SIEVE_PLUGIN_H + +struct module; + +extern const char imap_filter_sieve_plugin_binary_dependency[]; + +bool cmd_filter(struct client_command_context *cmd); + +void imap_filter_sieve_plugin_init(struct module *module); +void imap_filter_sieve_plugin_deinit(void); + +#endif diff --git a/pigeonhole/src/plugins/imap-filter-sieve/imap-filter-sieve.c b/pigeonhole/src/plugins/imap-filter-sieve/imap-filter-sieve.c new file mode 100644 index 0000000..62519f4 --- /dev/null +++ b/pigeonhole/src/plugins/imap-filter-sieve/imap-filter-sieve.c @@ -0,0 +1,1188 @@ +/* Copyright (c) 2017-2018 Pigeonhole authors, see the included COPYING file */ + +#include "imap-common.h" +#include "str.h" +#include "ioloop.h" +#include "time-util.h" +#include "module-context.h" +#include "message-address.h" +#include "mail-user.h" +#include "mail-duplicate.h" +#include "mail-storage-private.h" +#include "iostream-ssl.h" +#include "smtp-submit.h" +#include "sieve.h" +#include "sieve-storage.h" +#include "sieve-script.h" + +#include "imap-filter-sieve.h" + +#define DUPLICATE_DB_NAME "lda-dupes" + +#define IMAP_FILTER_SIEVE_USER_CONTEXT(obj) \ + MODULE_CONTEXT(obj, imap_filter_sieve_user_module) +#define IMAP_FILTER_SIEVE_USER_CONTEXT_REQUIRE(obj) \ + MODULE_CONTEXT_REQUIRE(obj, imap_filter_sieve_user_module) + +struct imap_filter_sieve_script { + struct sieve_script *script; + struct sieve_binary *binary; + + /* Compile failed once with this error; + don't try again for this transaction */ + enum sieve_error compile_error; + + /* Binary corrupt after recompile; don't recompile again */ + bool binary_corrupt:1; + /* Resource usage exceeded */ + bool rusage_exceeded:1; +}; + +struct imap_filter_sieve_user { + union mail_user_module_context module_ctx; + struct client *client; + + struct sieve_instance *svinst; + struct sieve_storage *storage; + struct sieve_storage *global_storage; + + struct mail_duplicate_db *dup_db; + + struct sieve_error_handler *master_ehandler; +}; + +static MODULE_CONTEXT_DEFINE_INIT(imap_filter_sieve_user_module, + &mail_user_module_register); + +/* + * + */ + +static const char * +imap_filter_sieve_get_setting(void *context, const char *identifier) +{ + struct imap_filter_sieve_user *ifsuser = context; + struct mail_user *user = ifsuser->client->user; + + return mail_user_plugin_getenv(user, identifier); +} + +static const struct sieve_callbacks imap_filter_sieve_callbacks = { + NULL, + imap_filter_sieve_get_setting +}; + +static struct sieve_instance * +imap_filter_sieve_get_svinst(struct imap_filter_sieve_context *sctx) +{ + struct mail_user *user = sctx->user; + struct imap_filter_sieve_user *ifsuser = + IMAP_FILTER_SIEVE_USER_CONTEXT_REQUIRE(user); + struct sieve_environment svenv; + const struct mail_storage_settings *mail_set; + bool debug = user->mail_debug; + + if (ifsuser->svinst != NULL) + return ifsuser->svinst; + + mail_set = mail_user_set_get_storage_set(user); + + ifsuser->dup_db = mail_duplicate_db_init(user, DUPLICATE_DB_NAME); + + i_zero(&svenv); + svenv.username = user->username; + (void)mail_user_get_home(user, &svenv.home_dir); + svenv.hostname = mail_set->hostname; + svenv.base_dir = user->set->base_dir; + svenv.event_parent = ifsuser->client->event; + svenv.flags = SIEVE_FLAG_HOME_RELATIVE; + svenv.location = SIEVE_ENV_LOCATION_MS; + svenv.delivery_phase = SIEVE_DELIVERY_PHASE_POST; + + ifsuser->svinst = sieve_init(&svenv, &imap_filter_sieve_callbacks, + ifsuser, debug); + + ifsuser->master_ehandler = + sieve_master_ehandler_create(ifsuser->svinst, 0); + sieve_error_handler_accept_infolog(ifsuser->master_ehandler, TRUE); + sieve_error_handler_accept_debuglog(ifsuser->master_ehandler, debug); + + return ifsuser->svinst; +} + +static void +imap_filter_sieve_init_trace_log(struct imap_filter_sieve_context *sctx, + struct sieve_trace_config *trace_config_r, + struct sieve_trace_log **trace_log_r) +{ + struct sieve_instance *svinst = imap_filter_sieve_get_svinst(sctx); + struct client_command_context *cmd = sctx->filter_context->cmd; + struct mail_user *user = sctx->user; + + if (sctx->trace_log_initialized) { + *trace_config_r = sctx->trace_config; + *trace_log_r = sctx->trace_log; + return; + } + sctx->trace_log_initialized = TRUE; + + if (sieve_trace_config_get(svinst, &sctx->trace_config) < 0 || + sieve_trace_log_open(svinst, &sctx->trace_log) < 0) { + i_zero(&sctx->trace_config); + sctx->trace_log = NULL; + + i_zero(trace_config_r); + *trace_log_r = NULL; + return; + } + + /* Write header for trace file */ + sieve_trace_log_printf(sctx->trace_log, + "Sieve trace log for IMAP FILTER=SIEVE:\n" + "\n" + " Username: %s\n", user->username); + if (user->session_id != NULL) { + sieve_trace_log_printf(sctx->trace_log, + " Session ID: %s\n", user->session_id); + } + sieve_trace_log_printf(sctx->trace_log, + " Mailbox: %s\n" + " Command: %s %s %s\n\n", + mailbox_get_vname(sctx->filter_context->box), + cmd->tag, cmd->name, + cmd->human_args != NULL ? cmd->human_args : ""); + + *trace_config_r = sctx->trace_config; + *trace_log_r = sctx->trace_log; +} + +static int +imap_filter_sieve_get_personal_storage(struct imap_filter_sieve_context *sctx, + struct sieve_storage **storage_r, + enum mail_error *error_code_r, + const char **error_r) +{ + struct mail_user *user = sctx->user; + struct imap_filter_sieve_user *ifsuser = + IMAP_FILTER_SIEVE_USER_CONTEXT_REQUIRE(user); + enum sieve_storage_flags storage_flags = 0; + struct sieve_instance *svinst; + enum sieve_error error; + + *error_code_r = MAIL_ERROR_NONE; + *error_r = NULL; + + if (ifsuser->storage != NULL) { + *storage_r = ifsuser->storage; + return 0; + } + + // FIXME: limit interval between retries + + svinst = imap_filter_sieve_get_svinst(sctx); + ifsuser->storage = sieve_storage_create_main(svinst, user, + storage_flags, &error); + if (ifsuser->storage != NULL) { + *storage_r = ifsuser->storage; + return 0; + } + + switch (error) { + case SIEVE_ERROR_NOT_POSSIBLE: + *error_r = "Sieve processing is disabled for this user"; + *error_code_r = MAIL_ERROR_NOTPOSSIBLE; + break; + case SIEVE_ERROR_NOT_FOUND: + *error_r = "Sieve script storage not accessible"; + *error_code_r = MAIL_ERROR_NOTFOUND; + break; + default: + *error_r = t_strflocaltime(MAIL_ERRSTR_CRITICAL_MSG_STAMP, + ioloop_time); + *error_code_r = MAIL_ERROR_TEMP; + break; + } + + return -1; +} + +static int +imap_filter_sieve_get_global_storage(struct imap_filter_sieve_context *sctx, + struct sieve_storage **storage_r, + enum mail_error *error_code_r, + const char **error_r) +{ + struct mail_user *user = sctx->user; + struct imap_filter_sieve_user *ifsuser = + IMAP_FILTER_SIEVE_USER_CONTEXT_REQUIRE(user); + struct sieve_instance *svinst; + const char *location; + enum sieve_error error; + + *error_code_r = MAIL_ERROR_NONE; + *error_r = NULL; + + if (ifsuser->global_storage != NULL) { + *storage_r = ifsuser->global_storage; + return 0; + } + + svinst = imap_filter_sieve_get_svinst(sctx); + + location = mail_user_plugin_getenv(user, "sieve_global"); + if (location == NULL) { + e_info(sieve_get_event(svinst), + "include: sieve_global is unconfigured; " + "include of `:global' script is therefore not possible"); + *error_code_r = MAIL_ERROR_NOTFOUND; + *error_r = "No global Sieve scripts available"; + return -1; + } + ifsuser->global_storage = + sieve_storage_create(svinst, location, 0, &error); + if (ifsuser->global_storage != NULL) { + *storage_r = ifsuser->global_storage; + return 0; + } + + switch (error) { + case SIEVE_ERROR_NOT_POSSIBLE: + case SIEVE_ERROR_NOT_FOUND: + *error_r = "No global Sieve scripts available"; + *error_code_r = MAIL_ERROR_NOTFOUND; + break; + default: + *error_r = t_strflocaltime(MAIL_ERRSTR_CRITICAL_MSG_STAMP, + ioloop_time); + *error_code_r = MAIL_ERROR_TEMP; + break; + } + + return -1; +} + +/* + * + */ + +struct imap_filter_sieve_context * +imap_filter_sieve_context_create(struct imap_filter_context *ctx, + enum imap_filter_sieve_type type) +{ + struct client_command_context *cmd = ctx->cmd; + struct imap_filter_sieve_context *sctx; + + sctx = p_new(cmd->pool, struct imap_filter_sieve_context, 1); + sctx->pool = cmd->pool; + sctx->filter_context = ctx; + sctx->filter_type = type; + sctx->user = ctx->cmd->client->user; + + return sctx; +} + +void imap_filter_sieve_context_free(struct imap_filter_sieve_context **_sctx) +{ + struct imap_filter_sieve_context *sctx = *_sctx; + struct imap_filter_sieve_script *scripts; + unsigned int i; + + *_sctx = NULL; + + if (sctx == NULL) + return; + + scripts = sctx->scripts; + for (i = 0; i < sctx->scripts_count; i++) { + if (scripts[i].binary != NULL) + sieve_close(&scripts[i].binary); + if (scripts[i].script != NULL) + sieve_script_unref(&scripts[i].script); + } + + if (sctx->trace_log != NULL) + sieve_trace_log_free(&sctx->trace_log); + + str_free(&sctx->errors); +} + +/* + * Error handling + */ + +static struct sieve_error_handler * +imap_filter_sieve_create_error_handler(struct imap_filter_sieve_context *sctx) +{ + struct sieve_instance *svinst = imap_filter_sieve_get_svinst(sctx); + + /* Prepare error handler */ + if (sctx->errors == NULL) + sctx->errors = str_new(default_pool, 1024); + else + str_truncate(sctx->errors, 0); + return sieve_strbuf_ehandler_create(svinst, sctx->errors, TRUE, + 10 /* client->set->_max_compile_errors */); +} + +/* + * + */ + +static struct sieve_binary * +imap_sieve_filter_open_script(struct imap_filter_sieve_context *sctx, + struct sieve_script *script, + enum sieve_compile_flags cpflags, + struct sieve_error_handler *user_ehandler, + bool recompile, enum sieve_error *error_r) +{ + struct mail_user *user = sctx->user; + struct imap_filter_sieve_user *ifsuser = + IMAP_FILTER_SIEVE_USER_CONTEXT_REQUIRE(user); + struct sieve_instance *svinst = imap_filter_sieve_get_svinst(sctx); + struct sieve_error_handler *ehandler; + struct sieve_binary *sbin; + const char *compile_name = "compile"; + + if (recompile) { + /* Warn */ + e_warning(sieve_get_event(svinst), + "Encountered corrupt binary: re-compiling script %s", + sieve_script_location(script)); + compile_name = "re-compile"; + } else { + e_debug(sieve_get_event(svinst), "Loading script %s", + sieve_script_location(script)); + } + + if (script == sctx->user_script) + ehandler = user_ehandler; + else + ehandler = ifsuser->master_ehandler; + sieve_error_handler_reset(ehandler); + + /* Load or compile the sieve script */ + if (recompile) { + sbin = sieve_compile_script(script, ehandler, cpflags, error_r); + } else { + sbin = sieve_open_script(script, ehandler, cpflags, error_r); + } + + /* Handle error */ + if (sbin == NULL) { + switch (*error_r) { + /* Script not found */ + case SIEVE_ERROR_NOT_FOUND: + e_debug(sieve_get_event(svinst), + "Script `%s' is missing for %s", + sieve_script_location(script), compile_name); + break; + /* Temporary failure */ + case SIEVE_ERROR_TEMP_FAILURE: + e_error(sieve_get_event(svinst), + "Failed to open script `%s' for %s " + "(temporary failure)", + sieve_script_location(script), compile_name); + break; + /* Compile failed */ + case SIEVE_ERROR_NOT_VALID: + if (script == sctx->user_script) + break; + e_error(sieve_get_event(svinst), + "Failed to %s script `%s'", + compile_name, sieve_script_location(script)); + break; + /* Cumulative resource limit exceeded */ + case SIEVE_ERROR_RESOURCE_LIMIT: + e_error(sieve_get_event(svinst), + "Failed to open script `%s' for %s " + "(cumulative resource limit exceeded)", + sieve_script_location(script), compile_name); + break; + /* Something else */ + default: + e_error(sieve_get_event(svinst), + "Failed to open script `%s' for %s", + sieve_script_location(script), compile_name); + break; + } + + return NULL; + } + + if (!recompile) + (void)sieve_save(sbin, FALSE, NULL); + return sbin; +} + +int imap_filter_sieve_compile(struct imap_filter_sieve_context *sctx, + string_t **errors_r, bool *have_warnings_r) +{ + struct imap_filter_sieve_script *scripts = sctx->scripts; + unsigned int count = sctx->scripts_count, i; + struct sieve_error_handler *ehandler; + enum sieve_error error; + int ret = 0; + + *errors_r = NULL; + *have_warnings_r = FALSE; + + /* Prepare error handler */ + ehandler = imap_filter_sieve_create_error_handler(sctx); + + for (i = 0; i < count; i++) { + struct sieve_script *script = scripts[i].script; + + i_assert(script != NULL); + + scripts[i].binary = + imap_sieve_filter_open_script(sctx, script, 0, ehandler, + FALSE, &error); + if (scripts[i].binary == NULL) { + if (error != SIEVE_ERROR_NOT_VALID) { + const char *errormsg = + sieve_script_get_last_error( + script, &error); + if (error != SIEVE_ERROR_NONE) { + str_truncate(sctx->errors, 0); + str_append(sctx->errors, errormsg); + } + } + ret = -1; + break; + } + } + + if (ret < 0 && str_len(sctx->errors) == 0) { + /* Failed, but no user error was logged: log a generic internal + error instead. */ + sieve_internal_error(ehandler, NULL, NULL); + } + + *have_warnings_r = (sieve_get_warnings(ehandler) > 0); + *errors_r = sctx->errors; + + sieve_error_handler_unref(&ehandler); + return ret; +} + +void imap_filter_sieve_open_input(struct imap_filter_sieve_context *sctx, + struct istream *input) +{ + struct sieve_instance *svinst; + struct sieve_script *script; + + svinst = imap_filter_sieve_get_svinst(sctx); + script = sieve_data_script_create_from_input(svinst, "script", input); + + sctx->user_script = script; + sctx->scripts = p_new(sctx->pool, struct imap_filter_sieve_script, 1); + sctx->scripts_count = 1; + sctx->scripts[0].script = script; +} + +int imap_filter_sieve_open_personal(struct imap_filter_sieve_context *sctx, + const char *name, + enum mail_error *error_code_r, + const char **error_r) +{ + struct sieve_storage *storage; + struct sieve_script *script; + enum sieve_error error; + + if (imap_filter_sieve_get_personal_storage(sctx, &storage, + error_code_r, error_r) < 0) + return -1; + + if (name == NULL) + script = sieve_storage_active_script_open(storage, NULL); + else + script = sieve_storage_open_script(storage, name, NULL); + if (script == NULL) { + *error_r = sieve_storage_get_last_error(storage, &error); + + switch (error) { + case SIEVE_ERROR_NOT_FOUND: + *error_code_r = MAIL_ERROR_NOTFOUND; + break; + case SIEVE_ERROR_NOT_POSSIBLE: + *error_code_r = MAIL_ERROR_NOTPOSSIBLE; + break; + default: + *error_code_r = MAIL_ERROR_TEMP; + } + return -1; + } + + sctx->user_script = script; + sctx->scripts = p_new(sctx->pool, struct imap_filter_sieve_script, 1); + sctx->scripts_count = 1; + sctx->scripts[0].script = script; + return 0; +} + +int imap_filter_sieve_open_global(struct imap_filter_sieve_context *sctx, + const char *name, + enum mail_error *error_code_r, + const char **error_r) +{ + struct sieve_storage *storage; + struct sieve_script *script; + enum sieve_error error; + + if (imap_filter_sieve_get_global_storage(sctx, &storage, + error_code_r, error_r) < 0) + return -1; + + script = sieve_storage_open_script(storage, name, NULL); + if (script == NULL) { + *error_r = sieve_storage_get_last_error(storage, &error); + + switch (error) { + case SIEVE_ERROR_NOT_FOUND: + *error_code_r = MAIL_ERROR_NOTFOUND; + break; + case SIEVE_ERROR_NOT_POSSIBLE: + *error_code_r = MAIL_ERROR_NOTPOSSIBLE; + break; + default: + *error_code_r = MAIL_ERROR_TEMP; + } + return -1; + } + + sctx->user_script = script; + sctx->scripts = p_new(sctx->pool, struct imap_filter_sieve_script, 1); + sctx->scripts_count = 1; + sctx->scripts[0].script = script; + return 0; +} + +/* + * Mail transmission + */ + +static void * +imap_filter_sieve_smtp_start(const struct sieve_script_env *senv, + const struct smtp_address *mail_from) +{ + struct imap_filter_sieve_context *sctx = senv->script_context; + struct mail_user *user = sctx->user; + struct imap_filter_sieve_user *ifsuser = + IMAP_FILTER_SIEVE_USER_CONTEXT_REQUIRE(user); + const struct smtp_submit_settings *smtp_set = ifsuser->client->smtp_set; + struct ssl_iostream_settings ssl_set; + struct smtp_submit_input submit_input; + + i_zero(&ssl_set); + mail_user_init_ssl_client_settings(user, &ssl_set); + + i_zero(&submit_input); + submit_input.ssl = &ssl_set; + + return (void *)smtp_submit_init_simple(&submit_input, smtp_set, + mail_from); +} + +static void +imap_filter_sieve_smtp_add_rcpt(const struct sieve_script_env *senv ATTR_UNUSED, + void *handle, + const struct smtp_address *rcpt_to) +{ + struct smtp_submit *smtp_submit = handle; + + smtp_submit_add_rcpt(smtp_submit, rcpt_to); +} + +static struct ostream * +imap_filter_sieve_smtp_send(const struct sieve_script_env *senv ATTR_UNUSED, + void *handle) +{ + struct smtp_submit *smtp_submit = handle; + + return smtp_submit_send(smtp_submit); +} + +static void +imap_filter_sieve_smtp_abort(const struct sieve_script_env *senv ATTR_UNUSED, + void *handle) +{ + struct smtp_submit *smtp_submit = handle; + + smtp_submit_deinit(&smtp_submit); +} + +static int +imap_filter_sieve_smtp_finish(const struct sieve_script_env *senv ATTR_UNUSED, + void *handle, const char **error_r) +{ + struct smtp_submit *smtp_submit = handle; + int ret; + + ret = smtp_submit_run(smtp_submit, error_r); + smtp_submit_deinit(&smtp_submit); + return ret; +} + +/* + * Duplicate checking + */ + +static void * +imap_filter_sieve_duplicate_transaction_begin( + const struct sieve_script_env *senv) +{ + struct imap_filter_sieve_context *sctx = senv->script_context; + struct imap_filter_sieve_user *ifsuser = + IMAP_FILTER_SIEVE_USER_CONTEXT_REQUIRE(sctx->user); + + return mail_duplicate_transaction_begin(ifsuser->dup_db); +} + +static void imap_filter_sieve_duplicate_transaction_commit(void **_dup_trans) +{ + struct mail_duplicate_transaction *dup_trans = *_dup_trans; + + *_dup_trans = NULL; + + mail_duplicate_transaction_commit(&dup_trans); +} + +static void imap_filter_sieve_duplicate_transaction_rollback(void **_dup_trans) +{ + struct mail_duplicate_transaction *dup_trans = *_dup_trans; + + *_dup_trans = NULL; + + mail_duplicate_transaction_rollback(&dup_trans); +} + +static enum sieve_duplicate_check_result +imap_filter_sieve_duplicate_check(void *_dup_trans, + const struct sieve_script_env *senv, + const void *id, size_t id_size) +{ + struct mail_duplicate_transaction *dup_trans = _dup_trans; + + switch (mail_duplicate_check(dup_trans, id, id_size, + senv->user->username)) { + case MAIL_DUPLICATE_CHECK_RESULT_EXISTS: + return SIEVE_DUPLICATE_CHECK_RESULT_EXISTS; + case MAIL_DUPLICATE_CHECK_RESULT_NOT_FOUND: + return SIEVE_DUPLICATE_CHECK_RESULT_NOT_FOUND; + case MAIL_DUPLICATE_CHECK_RESULT_DEADLOCK: + case MAIL_DUPLICATE_CHECK_RESULT_LOCK_TIMEOUT: + return SIEVE_DUPLICATE_CHECK_RESULT_TEMP_FAILURE; + case MAIL_DUPLICATE_CHECK_RESULT_IO_ERROR: + case MAIL_DUPLICATE_CHECK_RESULT_TOO_MANY_LOCKS: + break; + } + return SIEVE_DUPLICATE_CHECK_RESULT_FAILURE; +} + +static void +imap_filter_sieve_duplicate_mark(void *_dup_trans, + const struct sieve_script_env *senv, + const void *id, size_t id_size, time_t time) +{ + struct mail_duplicate_transaction *dup_trans = _dup_trans; + + mail_duplicate_mark(dup_trans, id, id_size, senv->user->username, time); +} + +/* + * Result logging + */ + +static const char * +imap_filter_sieve_result_amend_log_message(const struct sieve_script_env *senv, + enum log_type log_type ATTR_UNUSED, + const char *message) +{ + struct imap_filter_sieve_context *sctx = senv->script_context; + string_t *str; + + if (sctx->mail == NULL) + return message; + + str = t_str_new(256); + str_printfa(str, "uid=%u: ", sctx->mail->uid); + str_append(str, message); + return str_c(str); +} + +/* + * + */ + +static int +imap_sieve_filter_handle_exec_status(struct imap_filter_sieve_context *sctx, + struct sieve_script *script, int status, + struct sieve_exec_status *estatus, + bool *fatal_r) +{ + struct imap_filter_sieve_user *ifsuser = + IMAP_FILTER_SIEVE_USER_CONTEXT_REQUIRE(sctx->user); + struct sieve_instance *svinst = ifsuser->svinst; + enum log_type log_level, user_log_level; + enum mail_error mail_error = MAIL_ERROR_NONE; + int ret = -1; + + *fatal_r = FALSE; + + log_level = user_log_level = LOG_TYPE_ERROR; + + if (estatus->last_storage != NULL && estatus->store_failed) { + (void)mail_storage_get_last_error(estatus->last_storage, + &mail_error); + + /* Don't bother administrator too much with benign errors */ + if (mail_error == MAIL_ERROR_NOQUOTA) { + log_level = LOG_TYPE_INFO; + user_log_level = LOG_TYPE_INFO; + } + } + + switch (status) { + case SIEVE_EXEC_FAILURE: + e_log(sieve_get_event(svinst), user_log_level, + "Execution of script %s failed", + sieve_script_location(script)); + ret = -1; + break; + case SIEVE_EXEC_TEMP_FAILURE: + e_log(sieve_get_event(svinst), log_level, + "Execution of script %s was aborted " + "due to temporary failure", + sieve_script_location(script)); + *fatal_r = TRUE; + ret = -1; + break; + case SIEVE_EXEC_BIN_CORRUPT: + e_error(sieve_get_event(svinst), + "!!BUG!!: Binary compiled from %s is still corrupt; " + "bailing out and reverting to default action", + sieve_script_location(script)); + *fatal_r = TRUE; + ret = -1; + break; + case SIEVE_EXEC_RESOURCE_LIMIT: + e_error(sieve_get_event(svinst), + "Execution of script %s was aborted " + "due to excessive resource usage", + sieve_script_location(script)); + *fatal_r = TRUE; + ret = -1; + break; + case SIEVE_EXEC_KEEP_FAILED: + e_log(sieve_get_event(svinst), log_level, + "Execution of script %s failed " + "with unsuccessful implicit keep", + sieve_script_location(script)); + ret = -1; + break; + case SIEVE_EXEC_OK: + ret = (estatus->keep_original ? 0 : 1); + break; + } + + return ret; +} + +static int +imap_sieve_filter_run_scripts(struct imap_filter_sieve_context *sctx, + struct sieve_error_handler *user_ehandler, + const struct sieve_message_data *msgdata, + const struct sieve_script_env *scriptenv, + bool *fatal_r) +{ + struct mail_user *user = sctx->user; + struct imap_filter_sieve_user *ifsuser = + IMAP_FILTER_SIEVE_USER_CONTEXT_REQUIRE(user); + struct sieve_instance *svinst = ifsuser->svinst; + struct imap_filter_sieve_script *scripts = sctx->scripts; + unsigned int count = sctx->scripts_count; + struct sieve_resource_usage *rusage = + &scriptenv->exec_status->resource_usage; + struct sieve_multiscript *mscript; + struct sieve_error_handler *ehandler; + struct sieve_script *last_script = NULL; + bool user_script = FALSE, more = TRUE, rusage_exceeded = FALSE; + enum sieve_compile_flags cpflags; + enum sieve_execute_flags exflags; + enum sieve_error compile_error = SIEVE_ERROR_NONE; + unsigned int i; + int ret; + + *fatal_r = FALSE; + + /* Start execution */ + mscript = sieve_multiscript_start_execute(svinst, msgdata, scriptenv); + + /* Execute scripts */ + for (i = 0; i < count && more; i++) { + struct sieve_script *script = scripts[i].script; + struct sieve_binary *sbin = scripts[i].binary; + int mstatus; + + if (sbin == NULL) { + e_debug(sieve_get_event(svinst), + "Skipping script from `%s'", + sieve_script_location(script)); + continue; + } + + cpflags = 0; + exflags = SIEVE_EXECUTE_FLAG_SKIP_RESPONSES; + + user_script = (script == sctx->user_script); + last_script = script; + + if (scripts[i].rusage_exceeded) { + rusage_exceeded = TRUE; + break; + } + + sieve_resource_usage_init(rusage); + if (user_script) { + cpflags |= SIEVE_COMPILE_FLAG_NOGLOBAL; + exflags |= SIEVE_EXECUTE_FLAG_NOGLOBAL; + ehandler = user_ehandler; + } else { + ehandler = ifsuser->master_ehandler; + } + + /* Execute */ + e_debug(sieve_get_event(svinst), + "Executing script from `%s'", + sieve_get_source(sbin)); + more = sieve_multiscript_run(mscript, + sbin, ehandler, ehandler, exflags); + + mstatus = sieve_multiscript_status(mscript); + if (!more && mstatus == SIEVE_EXEC_BIN_CORRUPT && + !scripts[i].binary_corrupt && sieve_is_loaded(sbin)) { + /* Close corrupt script */ + sieve_close(&sbin); + + /* Recompile */ + scripts[i].binary = sbin = + imap_sieve_filter_open_script( + sctx, script, cpflags, user_ehandler, + FALSE, &compile_error); + if (sbin == NULL) { + scripts[i].compile_error = compile_error; + break; + } + + /* Execute again */ + more = sieve_multiscript_run(mscript, sbin, + ehandler, ehandler, + exflags); + + /* Save new version */ + + mstatus = sieve_multiscript_status(mscript); + if (mstatus == SIEVE_EXEC_BIN_CORRUPT) + scripts[i].binary_corrupt = TRUE; + else if (more) + (void)sieve_save(sbin, FALSE, NULL); + } + + if (user_script && !sieve_record_resource_usage(sbin, rusage)) { + rusage_exceeded = ((i + 1) < count && more); + scripts[i].rusage_exceeded = TRUE; + break; + } + } + + /* Finish execution */ + exflags = SIEVE_EXECUTE_FLAG_SKIP_RESPONSES; + ehandler = (user_ehandler != NULL ? + user_ehandler : ifsuser->master_ehandler); + if (compile_error == SIEVE_ERROR_TEMP_FAILURE) { + ret = sieve_multiscript_finish(&mscript, ehandler, exflags, + SIEVE_EXEC_TEMP_FAILURE); + } else if (rusage_exceeded) { + i_assert(last_script != NULL); + (void)sieve_multiscript_finish(&mscript, ehandler, exflags, + SIEVE_EXEC_TEMP_FAILURE); + sieve_error(ehandler, sieve_script_name(last_script), + "cumulative resource usage limit exceeded"); + ret = SIEVE_EXEC_RESOURCE_LIMIT; + } else { + ret = sieve_multiscript_finish(&mscript, ehandler, exflags, + SIEVE_EXEC_OK); + } + + /* Don't log additional messages about compile failure */ + if (compile_error != SIEVE_ERROR_NONE && + ret == SIEVE_EXEC_FAILURE) { + e_info(sieve_get_event(svinst), + "Aborted script execution sequence " + "with successful implicit keep"); + return 0; + } + + if (last_script == NULL && ret == SIEVE_EXEC_OK) + return 0; + i_assert(last_script != NULL); /* at least one script is executed */ + return imap_sieve_filter_handle_exec_status(sctx, last_script, ret, + scriptenv->exec_status, + fatal_r); +} + +static int +parse_address(const char *address, const struct smtp_address **addr_r) +{ + struct message_address *msg_addr; + struct smtp_address *smtp_addr; + + if (message_address_parse_path(pool_datastack_create(), + (const unsigned char *)address, + strlen(address), &msg_addr) < 0) { + *addr_r = NULL; + return -1; + } + if (smtp_address_create_from_msg_temp(msg_addr, &smtp_addr) < 0) { + *addr_r = NULL; + return -1; + } + + *addr_r = smtp_addr; + return 1; +} + +int imap_sieve_filter_run_init(struct imap_filter_sieve_context *sctx) +{ + struct sieve_instance *svinst = imap_filter_sieve_get_svinst(sctx); + struct sieve_script_env *scriptenv = &sctx->scriptenv; + struct mail_user *user = sctx->user; + const char *error; + + if (sieve_script_env_init(scriptenv, user, &error) < 0) { + e_error(sieve_get_event(svinst), + "Failed to initialize script execution: %s", + error); + return -1; + } + + scriptenv->smtp_start = imap_filter_sieve_smtp_start; + scriptenv->smtp_add_rcpt = imap_filter_sieve_smtp_add_rcpt; + scriptenv->smtp_send = imap_filter_sieve_smtp_send; + scriptenv->smtp_abort = imap_filter_sieve_smtp_abort; + scriptenv->smtp_finish = imap_filter_sieve_smtp_finish; + scriptenv->duplicate_transaction_begin = + imap_filter_sieve_duplicate_transaction_begin; + scriptenv->duplicate_transaction_commit = + imap_filter_sieve_duplicate_transaction_commit; + scriptenv->duplicate_transaction_rollback = + imap_filter_sieve_duplicate_transaction_rollback; + scriptenv->duplicate_mark = imap_filter_sieve_duplicate_mark; + scriptenv->duplicate_check = imap_filter_sieve_duplicate_check; + scriptenv->script_context = sctx; + return 0; +} + +static void +imap_sieve_filter_get_msgdata(struct imap_filter_sieve_context *sctx, + struct mail *mail, + struct sieve_message_data *msgdata_r) +{ + struct sieve_instance *svinst = imap_filter_sieve_get_svinst(sctx); + struct mail_user *user = sctx->user; + const char *address, *error; + const struct smtp_address *mail_from, *rcpt_to; + struct smtp_address *user_addr; + int ret; + + mail_from = NULL; + if ((ret = mail_get_special(mail, MAIL_FETCH_FROM_ENVELOPE, + &address)) > 0 && + (ret = parse_address(address, &mail_from)) < 0) { + e_warning(sieve_get_event(svinst), + "Failed to parse message FROM_ENVELOPE"); + } + if (ret <= 0 && + mail_get_first_header(mail, "Return-Path", + &address) > 0 && + parse_address(address, &mail_from) < 0) { + e_info(sieve_get_event(svinst), + "Failed to parse Return-Path header"); + } + + rcpt_to = NULL; + if (mail_get_first_header(mail, "Delivered-To", + &address) > 0 && + parse_address(address, &rcpt_to) < 0) { + e_info(sieve_get_event(svinst), + "Failed to parse Delivered-To header"); + } + if (rcpt_to == NULL) { + if (svinst->user_email != NULL) + rcpt_to = svinst->user_email; + else if (smtp_address_parse_username(sctx->pool, user->username, + &user_addr, &error) < 0) { + e_warning(sieve_get_event(svinst), + "Cannot obtain SMTP address from username `%s': %s", + user->username, error); + } else { + if (user_addr->domain == NULL) + user_addr->domain = svinst->domainname; + rcpt_to = user_addr; + } + } + + // FIXME: maybe parse top Received header. + + i_zero(msgdata_r); + msgdata_r->mail = mail; + msgdata_r->envelope.mail_from = mail_from; + msgdata_r->envelope.rcpt_to = rcpt_to; + msgdata_r->auth_user = user->username; + (void)mail_get_message_id(mail, &msgdata_r->id); +} + +int imap_sieve_filter_run_mail(struct imap_filter_sieve_context *sctx, + struct mail *mail, string_t **errors_r, + bool *have_warnings_r, bool *have_changes_r, + bool *fatal_r) +{ + struct sieve_error_handler *user_ehandler; + struct sieve_message_data msgdata; + struct sieve_script_env *scriptenv = &sctx->scriptenv; + struct sieve_exec_status estatus; + struct sieve_trace_config trace_config; + struct sieve_trace_log *trace_log; + int ret; + + *errors_r = NULL; + *have_warnings_r = FALSE; + *have_changes_r = FALSE; + i_zero(&estatus); + + sctx->mail = mail; + + /* Prepare error handler */ + user_ehandler = imap_filter_sieve_create_error_handler(sctx); + + /* Initialize trace logging */ + imap_filter_sieve_init_trace_log(sctx, &trace_config, &trace_log); + + T_BEGIN { + if (trace_log != NULL) { + /* Write trace header for message */ + sieve_trace_log_printf(trace_log, + "Filtering message:\n" + "\n" + " UID: %u\n", mail->uid); + } + + /* Collect necessary message data */ + + imap_sieve_filter_get_msgdata(sctx, mail, &msgdata); + + /* Complete script execution environment */ + + scriptenv->default_mailbox = mailbox_get_vname(mail->box); + scriptenv->result_amend_log_message = + imap_filter_sieve_result_amend_log_message; + scriptenv->trace_log = trace_log; + scriptenv->trace_config = trace_config; + scriptenv->script_context = sctx; + + scriptenv->exec_status = &estatus; + + /* Execute script(s) */ + + ret = imap_sieve_filter_run_scripts(sctx, user_ehandler, + &msgdata, scriptenv, + fatal_r); + } T_END; + + if (ret < 0 && str_len(sctx->errors) == 0) { + /* Failed, but no user error was logged: log a generic internal + error instead. */ + sieve_internal_error(user_ehandler, NULL, NULL); + } + + *have_warnings_r = (sieve_get_warnings(user_ehandler) > 0); + *have_changes_r = estatus.significant_action_executed; + *errors_r = sctx->errors; + + sieve_error_handler_unref(&user_ehandler); + + sctx->mail = NULL; + + return ret; +} + +/* + * User + */ + +static void imap_filter_sieve_user_deinit(struct mail_user *user) +{ + struct imap_filter_sieve_user *ifsuser = + IMAP_FILTER_SIEVE_USER_CONTEXT_REQUIRE(user); + + sieve_error_handler_unref(&ifsuser->master_ehandler); + + if (ifsuser->storage != NULL) + sieve_storage_unref(&ifsuser->storage); + if (ifsuser->global_storage != NULL) + sieve_storage_unref(&ifsuser->global_storage); + if (ifsuser->svinst != NULL) + sieve_deinit(&ifsuser->svinst); + if (ifsuser->dup_db != NULL) + mail_duplicate_db_deinit(&ifsuser->dup_db); + + ifsuser->module_ctx.super.deinit(user); +} + +static void imap_filter_sieve_user_created(struct mail_user *user) +{ + struct imap_filter_sieve_user *ifsuser; + struct mail_user_vfuncs *v = user->vlast; + + ifsuser = p_new(user->pool, struct imap_filter_sieve_user, 1); + ifsuser->module_ctx.super = *v; + user->vlast = &ifsuser->module_ctx.super; + v->deinit = imap_filter_sieve_user_deinit; + MODULE_CONTEXT_SET(user, imap_filter_sieve_user_module, ifsuser); +} + +/* + * Hooks + */ + +static struct mail_storage_hooks imap_filter_sieve_mail_storage_hooks = { + .mail_user_created = imap_filter_sieve_user_created, +}; + +/* + * Client + */ + +void imap_filter_sieve_client_created(struct client *client) +{ + struct imap_filter_sieve_user *ifsuser = + IMAP_FILTER_SIEVE_USER_CONTEXT_REQUIRE(client->user); + + ifsuser->client = client; +} + +/* + * + */ + +void imap_filter_sieve_init(struct module *module) +{ + mail_storage_hooks_add(module, &imap_filter_sieve_mail_storage_hooks); +} + +void imap_filter_sieve_deinit(void) +{ + mail_storage_hooks_remove(&imap_filter_sieve_mail_storage_hooks); +} + + diff --git a/pigeonhole/src/plugins/imap-filter-sieve/imap-filter-sieve.h b/pigeonhole/src/plugins/imap-filter-sieve/imap-filter-sieve.h new file mode 100644 index 0000000..c06cbdb --- /dev/null +++ b/pigeonhole/src/plugins/imap-filter-sieve/imap-filter-sieve.h @@ -0,0 +1,97 @@ +#ifndef IMAP_FILTER_SIEVE_H +#define IMAP_FILTER_SIEVE_H + +#include "sieve.h" +#include "imap-filter.h" + +struct imap_filter_sieve_script; +struct imap_filter_sieve_context; + +enum imap_filter_sieve_type { + IMAP_FILTER_SIEVE_TYPE_DELIVERY, + IMAP_FILTER_SIEVE_TYPE_PERSONAL, + IMAP_FILTER_SIEVE_TYPE_GLOBAL, + IMAP_FILTER_SIEVE_TYPE_SCRIPT, +}; + +struct imap_filter_sieve_context { + pool_t pool; + + struct imap_filter_context *filter_context; + enum imap_filter_sieve_type filter_type; + + struct mail_user *user; + + struct sieve_script *user_script; + struct imap_filter_sieve_script *scripts; + unsigned int scripts_count; + + struct mail *mail; + + struct sieve_script_env scriptenv; + struct sieve_trace_config trace_config; + struct sieve_trace_log *trace_log; + + string_t *errors; + + bool warnings:1; + bool trace_log_initialized:1; +}; + +/* + * FILTER Command + */ + +bool cmd_filter_sieve(struct client_command_context *cmd); + +/* + * Context + */ + +struct imap_filter_sieve_context * +imap_filter_sieve_context_create(struct imap_filter_context *ctx, + enum imap_filter_sieve_type type); +void imap_filter_sieve_context_free(struct imap_filter_sieve_context **_sctx); + +/* + * Compile + */ + +int imap_filter_sieve_compile(struct imap_filter_sieve_context *sctx, + string_t **errors_r, bool *have_warnings_r); + +/* + * Open + */ + +void imap_filter_sieve_open_input(struct imap_filter_sieve_context *sctx, + struct istream *input); +int imap_filter_sieve_open_personal(struct imap_filter_sieve_context *sctx, + const char *name, + enum mail_error *error_code_r, + const char **error_r) ATTR_NULL(2); +int imap_filter_sieve_open_global(struct imap_filter_sieve_context *sctx, + const char *name, + enum mail_error *error_code_r, + const char **error_r); + +/* + * Run + */ + +int imap_sieve_filter_run_init(struct imap_filter_sieve_context *sctx); +int imap_sieve_filter_run_mail(struct imap_filter_sieve_context *sctx, + struct mail *mail, string_t **errors_r, + bool *have_warnings_r, bool *have_changes_r, + bool *fatal_r); + +/* + * + */ + +void imap_filter_sieve_client_created(struct client *client); + +void imap_filter_sieve_init(struct module *module); +void imap_filter_sieve_deinit(void); + +#endif diff --git a/pigeonhole/src/plugins/imap-filter-sieve/imap-filter.c b/pigeonhole/src/plugins/imap-filter-sieve/imap-filter.c new file mode 100644 index 0000000..fd7d758 --- /dev/null +++ b/pigeonhole/src/plugins/imap-filter-sieve/imap-filter.c @@ -0,0 +1,273 @@ +/* Copyright (c) 2017-2018 Pigeonhole authors, see the included COPYING file */ + +#include "imap-common.h" +#include "str.h" +#include "ostream.h" +#include "time-util.h" +#include "imap-resp-code.h" +#include "imap-search-args.h" + +#include "imap-filter.h" +#include "imap-filter-sieve.h" + +static void imap_filter_args_check(struct imap_filter_context *ctx, + const struct mail_search_arg *sargs) +{ + for (; sargs != NULL; sargs = sargs->next) { + switch (sargs->type) { + case SEARCH_SEQSET: + ctx->have_seqsets = TRUE; + break; + case SEARCH_MODSEQ: + ctx->have_modseqs = TRUE; + break; + case SEARCH_OR: + case SEARCH_SUB: + imap_filter_args_check(ctx, sargs->value.subargs); + break; + default: + break; + } + } +} + +static bool +imap_filter_mail(struct client_command_context *cmd, struct mail *mail) +{ + struct imap_filter_context *ctx = cmd->context; + struct client *client = cmd->client; + string_t *errors = NULL; + bool have_warnings = FALSE, have_changes = FALSE, fatal = FALSE; + string_t *reply = t_str_new(128); + int ret; + + ret = imap_sieve_filter_run_mail(ctx->sieve, mail, &errors, + &have_warnings, &have_changes, &fatal); + + str_printfa(reply, "* %u FILTERED (TAG %s) UID %u ", + mail->seq, cmd->tag, mail->uid); + if (ret < 0 || have_warnings) { + str_printfa(reply, "%s {%zu}\r\n", + (ret < 0 ? "ERRORS" : "WARNINGS"), + str_len(errors)); + str_append_str(reply, errors); + str_append(reply, "\r\n"); + } else if (have_changes || ret > 0) { + str_append(reply, "OK\r\n"); + } else { + str_truncate(reply, 0); + } + if (str_len(reply) > 0) + o_stream_nsend(client->output, str_data(reply), str_len(reply)); + + /* Handle the result */ + if (ret < 0) { + /* Sieve error; keep */ + } else { + if (ret > 0) { + /* Discard */ + mail_update_flags(mail, MODIFY_ADD, MAIL_DELETED); + } + } + + return !fatal; +} + +static bool imap_filter_more(struct client_command_context *cmd) +{ + struct imap_filter_context *ctx = cmd->context; + struct mail *mail; + enum mailbox_sync_flags sync_flags; + const char *ok_reply; + bool tryagain, lost_data; + + if (cmd->cancel) { + (void)imap_filter_deinit(ctx); + return TRUE; + } + + while (mailbox_search_next_nonblock(ctx->search_ctx, + &mail, &tryagain)) { + bool ret; + T_BEGIN { + ret = imap_filter_mail(cmd, mail); + } T_END; + if (!ret) + break; + } + if (tryagain) + return FALSE; + + lost_data = mailbox_search_seen_lost_data(ctx->search_ctx); + if (imap_filter_deinit(ctx) < 0) { + client_send_box_error(cmd, cmd->client->mailbox); + return TRUE; + } + + sync_flags = MAILBOX_SYNC_FLAG_FAST; + if (!cmd->uid || ctx->have_seqsets) + sync_flags |= MAILBOX_SYNC_FLAG_NO_EXPUNGES; + ok_reply = t_strdup_printf("OK %sFilter completed", + lost_data ? "["IMAP_RESP_CODE_EXPUNGEISSUED"] " : ""); + return cmd_sync(cmd, sync_flags, 0, ok_reply); +} + +static void imap_filter_more_callback(struct client_command_context *cmd) +{ + struct client *client = cmd->client; + bool finished; + + o_stream_cork(client->output); + finished = command_exec(cmd); + o_stream_uncork(client->output); + + if (!finished) + (void)client_handle_unfinished_cmd(cmd); + else + client_command_free(&cmd); + cmd_sync_delayed(client); + + if (client->disconnected) + client_destroy(client, NULL); + else + client_continue_pending_input(client); +} + +static bool +imap_filter_start(struct imap_filter_context *ctx, + struct mail_search_args *sargs) +{ + struct client_command_context *cmd = ctx->cmd; + + imap_filter_args_check(ctx, sargs->args); + + if (ctx->have_modseqs) + (void)client_enable(cmd->client, MAILBOX_FEATURE_CONDSTORE); + + ctx->box = cmd->client->mailbox; + ctx->trans = mailbox_transaction_begin(ctx->box, 0, + imap_client_command_get_reason(cmd)); + ctx->sargs = sargs; + ctx->search_ctx = mailbox_search_init(ctx->trans, sargs, NULL, 0, NULL); + + if (imap_sieve_filter_run_init(ctx->sieve) < 0) { + const char *error = t_strflocaltime( + MAIL_ERRSTR_CRITICAL_MSG_STAMP, ioloop_time); + + o_stream_nsend_str(cmd->client->output, + t_strdup_printf("* FILTER (TAG %s) " + "ERRORS {%zu}\r\n%s\r\n", + cmd->tag, strlen(error), error)); + client_send_tagline(cmd, + "NO Failed to initialize script execution"); + (void)imap_filter_deinit(ctx); + return TRUE; + } + + cmd->func = imap_filter_more; + cmd->context = ctx; + + if (imap_filter_more(cmd)) + return TRUE; + + /* we may have moved onto syncing by now */ + if (cmd->func == imap_filter_more) { + ctx->to = timeout_add(0, imap_filter_more_callback, cmd); + cmd->state = CLIENT_COMMAND_STATE_WAIT_EXTERNAL; + } + return FALSE; +} + +static bool +imap_filter_parse_search(struct imap_filter_context *ctx, + const struct imap_arg *args) +{ + struct client_command_context *cmd = ctx->cmd; + struct mail_search_args *sargs; + const char *charset; + int ret; + + if (imap_arg_atom_equals(args, "CHARSET")) { + /* CHARSET specified */ + if (!imap_arg_get_astring(&args[1], &charset)) { + client_send_command_error(cmd, + "Invalid charset argument."); + imap_filter_context_free(ctx); + return TRUE; + } + args += 2; + } else { + charset = "UTF-8"; + } + + ret = imap_search_args_build(cmd, args, charset, &sargs); + if (ret <= 0) { + imap_filter_context_free(ctx); + return ret < 0; + } + + return imap_filter_start(ctx, sargs); +} + +bool imap_filter_search(struct client_command_context *cmd) +{ + struct imap_filter_context *ctx = cmd->context; + const struct imap_arg *args; + const char *error; + enum imap_parser_error parse_error; + int ret; + + ret = imap_parser_read_args(ctx->parser, 0, 0, &args); + if (ret < 0) { + if (ret == -2) + return FALSE; + error = imap_parser_get_error(ctx->parser, &parse_error); + switch (parse_error) { + case IMAP_PARSE_ERROR_NONE: + i_unreached(); + case IMAP_PARSE_ERROR_LITERAL_TOO_BIG: + client_disconnect_with_error(ctx->cmd->client, error); + break; + default: + client_send_command_error(ctx->cmd, error); + break; + } + return TRUE; + } + return imap_filter_parse_search(ctx, args); +} + +int imap_filter_deinit(struct imap_filter_context *ctx) +{ + int ret = 0; + + o_stream_set_flush_callback(ctx->cmd->client->output, + client_output, ctx->cmd->client); + ctx->cmd->client->input_lock = NULL; + imap_parser_unref(&ctx->parser); + + if (ctx->search_ctx != NULL && + mailbox_search_deinit(&ctx->search_ctx) < 0) + ret = -1; + + if (ctx->trans != NULL) + (void)mailbox_transaction_commit(&ctx->trans); + + timeout_remove(&ctx->to); + if (ctx->sargs != NULL) { + mail_search_args_deinit(ctx->sargs); + mail_search_args_unref(&ctx->sargs); + } + imap_filter_context_free(ctx); + + ctx->cmd->context = NULL; + return ret; +} + +void imap_filter_context_free(struct imap_filter_context *ctx) +{ + imap_filter_sieve_context_free(&ctx->sieve); +} + + + diff --git a/pigeonhole/src/plugins/imap-filter-sieve/imap-filter.h b/pigeonhole/src/plugins/imap-filter-sieve/imap-filter.h new file mode 100644 index 0000000..d504704 --- /dev/null +++ b/pigeonhole/src/plugins/imap-filter-sieve/imap-filter.h @@ -0,0 +1,43 @@ +#ifndef IMAP_FILTER_H +#define IMAP_FILTER_H + +struct mail_duplicate_db; + +struct sieve_script; +struct sieve_storage; +struct sieve_binary; + +struct imap_filter_context { + struct client_command_context *cmd; + struct mailbox *box; + struct mailbox_transaction_context *trans; + struct mail_search_context *search_ctx; + + struct imap_parser *parser; + + struct imap_filter_sieve_context *sieve; + const char *script_name; + uoff_t script_len; + struct istream *script_input; + + struct mail_search_args *sargs; + + struct timeout *to; + + bool failed:1; + bool compile_failure:1; + bool have_seqsets:1; + bool have_modseqs:1; +}; + +bool imap_filter_search(struct client_command_context *cmd); + +int imap_filter_deinit(struct imap_filter_context *ctx); + +void imap_filter_context_free(struct imap_filter_context *ctx); + +/* Commands */ + +bool cmd_filter(struct client_command_context *cmd); + +#endif diff --git a/pigeonhole/src/plugins/imapsieve/Makefile.am b/pigeonhole/src/plugins/imapsieve/Makefile.am new file mode 100644 index 0000000..1fea23e --- /dev/null +++ b/pigeonhole/src/plugins/imapsieve/Makefile.am @@ -0,0 +1,40 @@ +imap_moduledir = $(dovecot_moduledir) +sieve_plugindir = $(dovecot_moduledir)/sieve + +imap_module_LTLIBRARIES = lib95_imap_sieve_plugin.la +sieve_plugin_LTLIBRARIES = lib90_sieve_imapsieve_plugin.la + +lib95_imap_sieve_plugin_la_LDFLAGS = -module -avoid-version +lib90_sieve_imapsieve_plugin_la_LDFLAGS = -module -avoid-version + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib-sieve \ + -I$(top_srcdir)/src/lib-sieve/util \ + -I$(top_srcdir)/src/lib-sieve/plugins/environment \ + $(LIBDOVECOT_IMAP_INCLUDE) \ + $(LIBDOVECOT_LDA_INCLUDE) \ + $(LIBDOVECOT_INCLUDE) \ + -DPKG_RUNDIR=\""$(rundir)"\" + +lib95_imap_sieve_plugin_la_SOURCES = \ + ext-imapsieve.c \ + ext-imapsieve-environment.c \ + imap-sieve.c \ + imap-sieve-storage.c \ + imap-sieve-plugin.c +lib95_imap_sieve_plugin_la_LIBADD = \ + $(top_builddir)/src/lib-sieve/libdovecot-sieve.la + +lib90_sieve_imapsieve_plugin_la_SOURCES = \ + ext-imapsieve.c \ + sieve-imapsieve-plugin.c +lib90_sieve_imapsieve_plugin_la_CPPFLAGS = \ + ${AM_CPPFLAGS} \ + -D__IMAPSIEVE_DUMMY + +noinst_HEADERS = \ + ext-imapsieve-common.h \ + imap-sieve.h \ + imap-sieve-storage.h \ + imap-sieve-plugin.h \ + sieve-imapsieve-plugin.h diff --git a/pigeonhole/src/plugins/imapsieve/Makefile.in b/pigeonhole/src/plugins/imapsieve/Makefile.in new file mode 100644 index 0000000..648975b --- /dev/null +++ b/pigeonhole/src/plugins/imapsieve/Makefile.in @@ -0,0 +1,864 @@ +# 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/plugins/imapsieve +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 = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(imap_moduledir)" \ + "$(DESTDIR)$(sieve_plugindir)" +LTLIBRARIES = $(imap_module_LTLIBRARIES) $(sieve_plugin_LTLIBRARIES) +lib90_sieve_imapsieve_plugin_la_LIBADD = +am_lib90_sieve_imapsieve_plugin_la_OBJECTS = \ + lib90_sieve_imapsieve_plugin_la-ext-imapsieve.lo \ + lib90_sieve_imapsieve_plugin_la-sieve-imapsieve-plugin.lo +lib90_sieve_imapsieve_plugin_la_OBJECTS = \ + $(am_lib90_sieve_imapsieve_plugin_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 = +lib90_sieve_imapsieve_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) \ + $(lib90_sieve_imapsieve_plugin_la_LDFLAGS) $(LDFLAGS) -o $@ +lib95_imap_sieve_plugin_la_DEPENDENCIES = \ + $(top_builddir)/src/lib-sieve/libdovecot-sieve.la +am_lib95_imap_sieve_plugin_la_OBJECTS = ext-imapsieve.lo \ + ext-imapsieve-environment.lo imap-sieve.lo \ + imap-sieve-storage.lo imap-sieve-plugin.lo +lib95_imap_sieve_plugin_la_OBJECTS = \ + $(am_lib95_imap_sieve_plugin_la_OBJECTS) +lib95_imap_sieve_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(lib95_imap_sieve_plugin_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +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)/ext-imapsieve-environment.Plo \ + ./$(DEPDIR)/ext-imapsieve.Plo \ + ./$(DEPDIR)/imap-sieve-plugin.Plo \ + ./$(DEPDIR)/imap-sieve-storage.Plo ./$(DEPDIR)/imap-sieve.Plo \ + ./$(DEPDIR)/lib90_sieve_imapsieve_plugin_la-ext-imapsieve.Plo \ + ./$(DEPDIR)/lib90_sieve_imapsieve_plugin_la-sieve-imapsieve-plugin.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 = $(lib90_sieve_imapsieve_plugin_la_SOURCES) \ + $(lib95_imap_sieve_plugin_la_SOURCES) +DIST_SOURCES = $(lib90_sieve_imapsieve_plugin_la_SOURCES) \ + $(lib95_imap_sieve_plugin_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@ +imap_moduledir = $(dovecot_moduledir) +sieve_plugindir = $(dovecot_moduledir)/sieve +imap_module_LTLIBRARIES = lib95_imap_sieve_plugin.la +sieve_plugin_LTLIBRARIES = lib90_sieve_imapsieve_plugin.la +lib95_imap_sieve_plugin_la_LDFLAGS = -module -avoid-version +lib90_sieve_imapsieve_plugin_la_LDFLAGS = -module -avoid-version +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib-sieve \ + -I$(top_srcdir)/src/lib-sieve/util \ + -I$(top_srcdir)/src/lib-sieve/plugins/environment \ + $(LIBDOVECOT_IMAP_INCLUDE) \ + $(LIBDOVECOT_LDA_INCLUDE) \ + $(LIBDOVECOT_INCLUDE) \ + -DPKG_RUNDIR=\""$(rundir)"\" + +lib95_imap_sieve_plugin_la_SOURCES = \ + ext-imapsieve.c \ + ext-imapsieve-environment.c \ + imap-sieve.c \ + imap-sieve-storage.c \ + imap-sieve-plugin.c + +lib95_imap_sieve_plugin_la_LIBADD = \ + $(top_builddir)/src/lib-sieve/libdovecot-sieve.la + +lib90_sieve_imapsieve_plugin_la_SOURCES = \ + ext-imapsieve.c \ + sieve-imapsieve-plugin.c + +lib90_sieve_imapsieve_plugin_la_CPPFLAGS = \ + ${AM_CPPFLAGS} \ + -D__IMAPSIEVE_DUMMY + +noinst_HEADERS = \ + ext-imapsieve-common.h \ + imap-sieve.h \ + imap-sieve-storage.h \ + imap-sieve-plugin.h \ + sieve-imapsieve-plugin.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/plugins/imapsieve/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/plugins/imapsieve/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): + +install-imap_moduleLTLIBRARIES: $(imap_module_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(imap_module_LTLIBRARIES)'; test -n "$(imap_moduledir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(imap_moduledir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(imap_moduledir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(imap_moduledir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(imap_moduledir)"; \ + } + +uninstall-imap_moduleLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(imap_module_LTLIBRARIES)'; test -n "$(imap_moduledir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(imap_moduledir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(imap_moduledir)/$$f"; \ + done + +clean-imap_moduleLTLIBRARIES: + -test -z "$(imap_module_LTLIBRARIES)" || rm -f $(imap_module_LTLIBRARIES) + @list='$(imap_module_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}; \ + } + +install-sieve_pluginLTLIBRARIES: $(sieve_plugin_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(sieve_plugin_LTLIBRARIES)'; test -n "$(sieve_plugindir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(sieve_plugindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sieve_plugindir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(sieve_plugindir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(sieve_plugindir)"; \ + } + +uninstall-sieve_pluginLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(sieve_plugin_LTLIBRARIES)'; test -n "$(sieve_plugindir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(sieve_plugindir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(sieve_plugindir)/$$f"; \ + done + +clean-sieve_pluginLTLIBRARIES: + -test -z "$(sieve_plugin_LTLIBRARIES)" || rm -f $(sieve_plugin_LTLIBRARIES) + @list='$(sieve_plugin_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}; \ + } + +lib90_sieve_imapsieve_plugin.la: $(lib90_sieve_imapsieve_plugin_la_OBJECTS) $(lib90_sieve_imapsieve_plugin_la_DEPENDENCIES) $(EXTRA_lib90_sieve_imapsieve_plugin_la_DEPENDENCIES) + $(AM_V_CCLD)$(lib90_sieve_imapsieve_plugin_la_LINK) -rpath $(sieve_plugindir) $(lib90_sieve_imapsieve_plugin_la_OBJECTS) $(lib90_sieve_imapsieve_plugin_la_LIBADD) $(LIBS) + +lib95_imap_sieve_plugin.la: $(lib95_imap_sieve_plugin_la_OBJECTS) $(lib95_imap_sieve_plugin_la_DEPENDENCIES) $(EXTRA_lib95_imap_sieve_plugin_la_DEPENDENCIES) + $(AM_V_CCLD)$(lib95_imap_sieve_plugin_la_LINK) -rpath $(imap_moduledir) $(lib95_imap_sieve_plugin_la_OBJECTS) $(lib95_imap_sieve_plugin_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-imapsieve-environment.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-imapsieve.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-sieve-plugin.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-sieve-storage.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-sieve.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lib90_sieve_imapsieve_plugin_la-ext-imapsieve.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lib90_sieve_imapsieve_plugin_la-sieve-imapsieve-plugin.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 $@ $< + +lib90_sieve_imapsieve_plugin_la-ext-imapsieve.lo: ext-imapsieve.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lib90_sieve_imapsieve_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lib90_sieve_imapsieve_plugin_la-ext-imapsieve.lo -MD -MP -MF $(DEPDIR)/lib90_sieve_imapsieve_plugin_la-ext-imapsieve.Tpo -c -o lib90_sieve_imapsieve_plugin_la-ext-imapsieve.lo `test -f 'ext-imapsieve.c' || echo '$(srcdir)/'`ext-imapsieve.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lib90_sieve_imapsieve_plugin_la-ext-imapsieve.Tpo $(DEPDIR)/lib90_sieve_imapsieve_plugin_la-ext-imapsieve.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ext-imapsieve.c' object='lib90_sieve_imapsieve_plugin_la-ext-imapsieve.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lib90_sieve_imapsieve_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lib90_sieve_imapsieve_plugin_la-ext-imapsieve.lo `test -f 'ext-imapsieve.c' || echo '$(srcdir)/'`ext-imapsieve.c + +lib90_sieve_imapsieve_plugin_la-sieve-imapsieve-plugin.lo: sieve-imapsieve-plugin.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lib90_sieve_imapsieve_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lib90_sieve_imapsieve_plugin_la-sieve-imapsieve-plugin.lo -MD -MP -MF $(DEPDIR)/lib90_sieve_imapsieve_plugin_la-sieve-imapsieve-plugin.Tpo -c -o lib90_sieve_imapsieve_plugin_la-sieve-imapsieve-plugin.lo `test -f 'sieve-imapsieve-plugin.c' || echo '$(srcdir)/'`sieve-imapsieve-plugin.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lib90_sieve_imapsieve_plugin_la-sieve-imapsieve-plugin.Tpo $(DEPDIR)/lib90_sieve_imapsieve_plugin_la-sieve-imapsieve-plugin.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sieve-imapsieve-plugin.c' object='lib90_sieve_imapsieve_plugin_la-sieve-imapsieve-plugin.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lib90_sieve_imapsieve_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lib90_sieve_imapsieve_plugin_la-sieve-imapsieve-plugin.lo `test -f 'sieve-imapsieve-plugin.c' || echo '$(srcdir)/'`sieve-imapsieve-plugin.c + +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: + for dir in "$(DESTDIR)$(imap_moduledir)" "$(DESTDIR)$(sieve_plugindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-imap_moduleLTLIBRARIES clean-libtool \ + clean-sieve_pluginLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/ext-imapsieve-environment.Plo + -rm -f ./$(DEPDIR)/ext-imapsieve.Plo + -rm -f ./$(DEPDIR)/imap-sieve-plugin.Plo + -rm -f ./$(DEPDIR)/imap-sieve-storage.Plo + -rm -f ./$(DEPDIR)/imap-sieve.Plo + -rm -f ./$(DEPDIR)/lib90_sieve_imapsieve_plugin_la-ext-imapsieve.Plo + -rm -f ./$(DEPDIR)/lib90_sieve_imapsieve_plugin_la-sieve-imapsieve-plugin.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-imap_moduleLTLIBRARIES \ + install-sieve_pluginLTLIBRARIES + +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)/ext-imapsieve-environment.Plo + -rm -f ./$(DEPDIR)/ext-imapsieve.Plo + -rm -f ./$(DEPDIR)/imap-sieve-plugin.Plo + -rm -f ./$(DEPDIR)/imap-sieve-storage.Plo + -rm -f ./$(DEPDIR)/imap-sieve.Plo + -rm -f ./$(DEPDIR)/lib90_sieve_imapsieve_plugin_la-ext-imapsieve.Plo + -rm -f ./$(DEPDIR)/lib90_sieve_imapsieve_plugin_la-sieve-imapsieve-plugin.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-imap_moduleLTLIBRARIES \ + uninstall-sieve_pluginLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-imap_moduleLTLIBRARIES clean-libtool \ + clean-sieve_pluginLTLIBRARIES 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-imap_moduleLTLIBRARIES install-info install-info-am \ + install-man install-pdf install-pdf-am install-ps \ + install-ps-am install-sieve_pluginLTLIBRARIES install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am \ + uninstall-imap_moduleLTLIBRARIES \ + uninstall-sieve_pluginLTLIBRARIES + +.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/plugins/imapsieve/ext-imapsieve-common.h b/pigeonhole/src/plugins/imapsieve/ext-imapsieve-common.h new file mode 100644 index 0000000..d3e69cd --- /dev/null +++ b/pigeonhole/src/plugins/imapsieve/ext-imapsieve-common.h @@ -0,0 +1,29 @@ +#ifndef EXT_IMAPSIEVE_COMMON_H +#define EXT_IMAPSIEVE_COMMON_H + +#include "sieve-extensions.h" + +#include "imap-sieve.h" + +/* + * Extensions + */ + +extern const struct sieve_extension_def imapsieve_extension; +extern const struct sieve_extension_def imapsieve_extension_dummy; + +extern const struct sieve_extension_def vnd_imapsieve_extension; +extern const struct sieve_extension_def vnd_imapsieve_extension_dummy; + +/* + * Environment items + */ + +void ext_imapsieve_environment_items_register + (const struct sieve_extension *ext, + const struct sieve_runtime_env *renv); +void ext_imapsieve_environment_vendor_items_register + (const struct sieve_extension *ext, + const struct sieve_runtime_env *renv); + +#endif diff --git a/pigeonhole/src/plugins/imapsieve/ext-imapsieve-environment.c b/pigeonhole/src/plugins/imapsieve/ext-imapsieve-environment.c new file mode 100644 index 0000000..2b61cd4 --- /dev/null +++ b/pigeonhole/src/plugins/imapsieve/ext-imapsieve-environment.c @@ -0,0 +1,184 @@ +/* Copyright (c) 2016-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" +#include "mail-storage.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-runtime.h" + +#include "sieve-ext-environment.h" + +#include "ext-imapsieve-common.h" + +/* + * Environment items + */ + +/* imap.user */ + +static const char * +envit_imap_user_get_value(const struct sieve_runtime_env *renv, + const char *name ATTR_UNUSED) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + + return eenv->svinst->username; +} + +const struct sieve_environment_item imap_user_env_item = { + .name = "imap.user", + .get_value = envit_imap_user_get_value +}; + +/* imap.email */ + +static const char * +envit_imap_email_get_value(const struct sieve_runtime_env *renv, + const char *name ATTR_UNUSED) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + const struct smtp_address *user_email = + sieve_get_user_email(eenv->svinst); + + if (user_email == NULL) + return NULL; + return smtp_address_encode(user_email); +} + +const struct sieve_environment_item imap_email_env_item = { + .name = "imap.email", + .get_value = envit_imap_email_get_value +}; + +/* imap.cause */ + +static const char * +envit_imap_cause_get_value(const struct sieve_runtime_env *renv, + const char *name ATTR_UNUSED) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + const struct sieve_script_env *senv = eenv->scriptenv; + struct imap_sieve_context *isctx = + (struct imap_sieve_context *)senv->script_context; + + return isctx->event.cause; +} + +const struct sieve_environment_item imap_cause_env_item = { + .name = "imap.cause", + .get_value = envit_imap_cause_get_value +}; + +/* imap.mailbox */ + +static const char * +envit_imap_mailbox_get_value(const struct sieve_runtime_env *renv, + const char *name ATTR_UNUSED) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + const struct sieve_message_data *msgdata = eenv->msgdata; + + return mailbox_get_vname(msgdata->mail->box); +} + +const struct sieve_environment_item imap_mailbox_env_item = { + .name = "imap.mailbox", + .get_value = envit_imap_mailbox_get_value +}; + + +/* imap.changedflags */ + +static const char * +envit_imap_changedflags_get_value(const struct sieve_runtime_env *renv, + const char *name ATTR_UNUSED) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + const struct sieve_script_env *senv = eenv->scriptenv; + struct imap_sieve_context *isctx = + (struct imap_sieve_context *)senv->script_context; + + return isctx->event.changed_flags; +} + +const struct sieve_environment_item imap_changedflags_env_item = { + .name = "imap.changedflags", + .get_value = envit_imap_changedflags_get_value +}; + +/* vnd.dovecot.mailbox-from */ + +static const char * +envit_vnd_mailbox_from_get_value(const struct sieve_runtime_env *renv, + const char *name ATTR_UNUSED) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + const struct sieve_script_env *senv = eenv->scriptenv; + struct imap_sieve_context *isctx = + (struct imap_sieve_context *)senv->script_context; + + return mailbox_get_vname(isctx->event.src_mailbox); +} + +const struct sieve_environment_item vnd_mailbox_from_env_item = { + .name = "vnd.dovecot.mailbox-from", + .get_value = envit_vnd_mailbox_from_get_value +}; + +/* vnd.dovecot.mailbox-to */ + +static const char * +envit_vnd_mailbox_to_get_value(const struct sieve_runtime_env *renv, + const char *name ATTR_UNUSED) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + const struct sieve_script_env *senv = eenv->scriptenv; + struct imap_sieve_context *isctx = + (struct imap_sieve_context *)senv->script_context; + + return mailbox_get_vname(isctx->event.dest_mailbox); +} + +const struct sieve_environment_item vnd_mailbox_to_env_item = { + .name = "vnd.dovecot.mailbox-to", + .get_value = envit_vnd_mailbox_to_get_value +}; + +/* + * Register + */ + +void ext_imapsieve_environment_items_register( + const struct sieve_extension *ext, const struct sieve_runtime_env *renv) +{ + const struct sieve_extension *env_ext = + (const struct sieve_extension *)ext->context; + + sieve_environment_item_register(env_ext, renv->interp, + &imap_user_env_item); + sieve_environment_item_register(env_ext, renv->interp, + &imap_email_env_item); + sieve_environment_item_register(env_ext, renv->interp, + &imap_cause_env_item); + sieve_environment_item_register(env_ext, renv->interp, + &imap_mailbox_env_item); + sieve_environment_item_register(env_ext, renv->interp, + &imap_changedflags_env_item); +} + +void ext_imapsieve_environment_vendor_items_register +(const struct sieve_extension *ext, const struct sieve_runtime_env *renv) +{ + const struct sieve_extension *env_ext = + (const struct sieve_extension *)ext->context; + + sieve_environment_item_register(env_ext, renv->interp, + &vnd_mailbox_from_env_item); + sieve_environment_item_register(env_ext, renv->interp, + &vnd_mailbox_to_env_item); +} diff --git a/pigeonhole/src/plugins/imapsieve/ext-imapsieve.c b/pigeonhole/src/plugins/imapsieve/ext-imapsieve.c new file mode 100644 index 0000000..64dfc07 --- /dev/null +++ b/pigeonhole/src/plugins/imapsieve/ext-imapsieve.c @@ -0,0 +1,168 @@ +/* Copyright (c) 2016-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension imapsieve + * ------------------- + * + * Authors: Stephan Bosch + * Specification: RFC 6785 + * Implementation: full + * Status: experimental + * + */ + +#include "lib.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-binary.h" + +#include "sieve-validator.h" +#include "sieve-interpreter.h" + +#include "sieve-ext-environment.h" + +#include "ext-imapsieve-common.h" + +/* + * Extension + */ + +static bool ext_imapsieve_load + (const struct sieve_extension *ext, void **context); +static bool ext_vnd_imapsieve_load + (const struct sieve_extension *ext, void **context); +static bool ext_vnd_imapsieve_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); + +static bool ext_imapsieve_interpreter_load + (const struct sieve_extension *ext, + const struct sieve_runtime_env *renv, + sieve_size_t *address ATTR_UNUSED); + +#ifdef __IMAPSIEVE_DUMMY +const struct sieve_extension_def imapsieve_extension_dummy = { +#else +const struct sieve_extension_def imapsieve_extension = { +#endif + .name = "imapsieve", + .load = ext_imapsieve_load, + .interpreter_load = ext_imapsieve_interpreter_load +}; + +#ifdef __IMAPSIEVE_DUMMY +const struct sieve_extension_def vnd_imapsieve_extension_dummy = { +#else +const struct sieve_extension_def vnd_imapsieve_extension = { +#endif + .name = "vnd.dovecot.imapsieve", + .load = ext_vnd_imapsieve_load, + .interpreter_load = ext_imapsieve_interpreter_load, + .validator_load = ext_vnd_imapsieve_validator_load +}; + +/* + * Context + */ + +static bool ext_imapsieve_load +(const struct sieve_extension *ext, void **context) +{ + *context = (void*) + sieve_ext_environment_require_extension(ext->svinst); + return TRUE; +} + +static bool ext_vnd_imapsieve_load +(const struct sieve_extension *ext, void **context) +{ + *context = (void*)sieve_extension_require +#ifdef __IMAPSIEVE_DUMMY + (ext->svinst, &imapsieve_extension_dummy, TRUE); +#else + (ext->svinst, &imapsieve_extension, TRUE); +#endif + return TRUE; +} + +/* + * Validator + */ + +static bool ext_vnd_imapsieve_validator_load +(const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_validator *valdtr) +{ + const struct sieve_extension *ims_ext; + + /* Load environment extension implicitly */ + + ims_ext = sieve_validator_extension_load_implicit +#ifdef __IMAPSIEVE_DUMMY + (valdtr, imapsieve_extension_dummy.name); +#else + (valdtr, imapsieve_extension.name); +#endif + if ( ims_ext == NULL ) + return FALSE; + + return TRUE; +} + +/* + * Interpreter + */ + +static int ext_imapsieve_interpreter_run + (const struct sieve_extension *this_ext, + const struct sieve_runtime_env *renv, + void *context, bool deferred); + +const struct sieve_interpreter_extension +imapsieve_interpreter_extension = { +#ifdef __IMAPSIEVE_DUMMY + .ext_def = &imapsieve_extension_dummy, +#else + .ext_def = &imapsieve_extension, +#endif + .run = ext_imapsieve_interpreter_run +}; + +static bool ext_imapsieve_interpreter_load +(const struct sieve_extension *ext ATTR_UNUSED, + const struct sieve_runtime_env *renv, + sieve_size_t *address ATTR_UNUSED) +{ + sieve_interpreter_extension_register(renv->interp, + ext, &imapsieve_interpreter_extension, NULL); + return TRUE; +} + +#ifdef __IMAPSIEVE_DUMMY +static int ext_imapsieve_interpreter_run +(const struct sieve_extension *ext ATTR_UNUSED, + const struct sieve_runtime_env *renv, + void *context ATTR_UNUSED, bool deferred) +{ + if ( !deferred ) { + sieve_runtime_error(renv, NULL, + "the imapsieve extension cannot be used outside IMAP"); + } + return SIEVE_EXEC_FAILURE; +} +#else +static int ext_imapsieve_interpreter_run +(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv, + void *context ATTR_UNUSED, bool deferred ATTR_UNUSED) +{ + if (ext->def == &vnd_imapsieve_extension) { + const struct sieve_extension *ims_ext = + (const struct sieve_extension *)ext->context; + ext_imapsieve_environment_vendor_items_register(ims_ext, renv); + } else { + ext_imapsieve_environment_items_register(ext, renv); + } + return SIEVE_EXEC_OK; +} +#endif diff --git a/pigeonhole/src/plugins/imapsieve/imap-sieve-plugin.c b/pigeonhole/src/plugins/imapsieve/imap-sieve-plugin.c new file mode 100644 index 0000000..0c9b52e --- /dev/null +++ b/pigeonhole/src/plugins/imapsieve/imap-sieve-plugin.c @@ -0,0 +1,60 @@ +/* Copyright (c) 2016-2018 Pigeonhole authors, see the included COPYING file */ + +#include "imap-common.h" +#include "str.h" + +#include "imap-sieve.h" +#include "imap-sieve-storage.h" + +#include "imap-sieve-plugin.h" + +static struct module *imap_sieve_module; +static imap_client_created_func_t *next_hook_client_created; + +/* + * Client + */ + +static void imap_sieve_client_created(struct client **clientp) +{ + struct client *client = *clientp; + struct mail_user *user = client->user; + const char *url = NULL; + + if (mail_user_is_plugin_loaded(user, imap_sieve_module)) { + url = mail_user_plugin_getenv(user, "imapsieve_url"); + // FIXME: parse the URL and report error if it is bad + if (url != NULL && strncasecmp(url, "sieve:", 6) == 0) { + client_add_capability(client, t_strconcat( + "IMAPSIEVE=", url, NULL)); + } else { + url = NULL; + } + + imap_sieve_storage_client_created(client, (url != NULL)); + } + + if (next_hook_client_created != NULL) + next_hook_client_created(clientp); +} + +/* + * Plugin + */ + +const char *imap_sieve_plugin_version = DOVECOT_ABI_VERSION; +const char imap_sieve_plugin_binary_dependency[] = "imap"; + +void imap_sieve_plugin_init(struct module *module) +{ + imap_sieve_module = module; + next_hook_client_created = + imap_client_created_hook_set(imap_sieve_client_created); + imap_sieve_storage_init(module); +} + +void imap_sieve_plugin_deinit(void) +{ + imap_sieve_storage_deinit(); + imap_client_created_hook_set(next_hook_client_created); +} diff --git a/pigeonhole/src/plugins/imapsieve/imap-sieve-plugin.h b/pigeonhole/src/plugins/imapsieve/imap-sieve-plugin.h new file mode 100644 index 0000000..0271710 --- /dev/null +++ b/pigeonhole/src/plugins/imapsieve/imap-sieve-plugin.h @@ -0,0 +1,11 @@ +#ifndef IMAP_SIEVE_PLUGIN_H +#define IMAP_SIEVE_PLUGIN_H + +struct module; + +extern const char imap_sieve_plugin_binary_dependency[]; + +void imap_sieve_plugin_init(struct module *module); +void imap_sieve_plugin_deinit(void); + +#endif diff --git a/pigeonhole/src/plugins/imapsieve/imap-sieve-storage.c b/pigeonhole/src/plugins/imapsieve/imap-sieve-storage.c new file mode 100644 index 0000000..f8e5efe --- /dev/null +++ b/pigeonhole/src/plugins/imapsieve/imap-sieve-storage.c @@ -0,0 +1,1273 @@ +/* Copyright (c) 2016-2018 Pigeonhole authors, see the included COPYING file */ + +#include "imap-common.h" +#include "array.h" +#include "hash.h" +#include "str.h" +#include "istream.h" +#include "ostream.h" +#include "module-context.h" +#include "mail-user.h" +#include "mail-storage-private.h" +#include "mailbox-attribute.h" +#include "mailbox-list-private.h" +#include "imap-match.h" +#include "imap-util.h" + +#include "imap-sieve.h" +#include "imap-sieve-storage.h" + +#define MAILBOX_ATTRIBUTE_IMAPSIEVE_SCRIPT "imapsieve/script" +#define MAIL_SERVER_ATTRIBUTE_IMAPSIEVE_SCRIPT "imapsieve/script" + +#define IMAP_SIEVE_USER_CONTEXT(obj) \ + MODULE_CONTEXT(obj, imap_sieve_user_module) +#define IMAP_SIEVE_USER_CONTEXT_REQUIRE(obj) \ + MODULE_CONTEXT_REQUIRE(obj, imap_sieve_user_module) +#define IMAP_SIEVE_CONTEXT(obj) \ + MODULE_CONTEXT(obj, imap_sieve_storage_module) +#define IMAP_SIEVE_CONTEXT_REQUIRE(obj) \ + MODULE_CONTEXT_REQUIRE(obj, imap_sieve_storage_module) +#define IMAP_SIEVE_MAIL_CONTEXT(obj) \ + MODULE_CONTEXT_REQUIRE(obj, imap_sieve_mail_module) + +struct imap_sieve_mailbox_rule; +struct imap_sieve_user; +struct imap_sieve_mailbox_event; +struct imap_sieve_mailbox_transaction; +struct imap_sieve_mail; + +enum imap_sieve_command { + IMAP_SIEVE_CMD_NONE = 0, + IMAP_SIEVE_CMD_APPEND, + IMAP_SIEVE_CMD_COPY, + IMAP_SIEVE_CMD_MOVE, + IMAP_SIEVE_CMD_STORE, + IMAP_SIEVE_CMD_OTHER +}; + +ARRAY_DEFINE_TYPE(imap_sieve_mailbox_rule, + struct imap_sieve_mailbox_rule *); +ARRAY_DEFINE_TYPE(imap_sieve_mailbox_event, + struct imap_sieve_mailbox_event); + +HASH_TABLE_DEFINE_TYPE(imap_sieve_mailbox_rule, + struct imap_sieve_mailbox_rule *, + struct imap_sieve_mailbox_rule *); + +struct imap_sieve_mailbox_rule { + unsigned int index; + const char *mailbox; + const char *from; + const char *const *causes; + const char *before, *after; + const char *copy_source_after; +}; + +struct imap_sieve_user { + union mail_user_module_context module_ctx; + struct client *client; + struct imap_sieve *isieve; + + enum imap_sieve_command cur_cmd; + + HASH_TABLE_TYPE(imap_sieve_mailbox_rule) mbox_rules; + ARRAY_TYPE(imap_sieve_mailbox_rule) mbox_patterns; + + bool sieve_active:1; + bool user_script:1; + bool expunge_discarded:1; +}; + +struct imap_sieve_mailbox_event { + uint32_t dest_mail_uid, src_mail_uid; + unsigned int save_seq; + + const char *changed_flags; +}; + +struct imap_sieve_mailbox_transaction { + pool_t pool; + + union mailbox_transaction_module_context module_ctx; + + struct mailbox *src_box; + struct mailbox_transaction_context *src_mail_trans; + + ARRAY_TYPE(imap_sieve_mailbox_event) events; +}; + +struct imap_sieve_mail { + union mail_module_context module_ctx; + + string_t *flags; +}; + +static MODULE_CONTEXT_DEFINE_INIT(imap_sieve_user_module, + &mail_user_module_register); +static MODULE_CONTEXT_DEFINE_INIT(imap_sieve_storage_module, + &mail_storage_module_register); +static MODULE_CONTEXT_DEFINE_INIT(imap_sieve_mail_module, + &mail_module_register); + +static void +imap_sieve_mailbox_rules_get(struct mail_user *user, + struct mailbox *dst_box, struct mailbox *src_box, + const char *cause, + ARRAY_TYPE(imap_sieve_mailbox_rule) *rules); + +/* + * Logging + */ + +static inline void +imap_sieve_debug(struct mail_user *user, + const char *format, ...) ATTR_FORMAT(2, 3); +static inline void +imap_sieve_debug(struct mail_user *user, + const char *format, ...) +{ + va_list args; + + if (user->mail_debug) { + va_start(args, format); + i_debug("imapsieve: %s", + t_strdup_vprintf(format, args)); + va_end(args); + } +} + +static inline void +imap_sieve_warning(struct mail_user *user ATTR_UNUSED, + const char *format, ...) ATTR_FORMAT(2, 3); +static inline void +imap_sieve_warning(struct mail_user *user ATTR_UNUSED, + const char *format, ...) +{ + va_list args; + + va_start(args, format); + i_warning("imapsieve: %s", + t_strdup_vprintf(format, args)); + va_end(args); +} + +static inline void +imap_sieve_mailbox_debug(struct mailbox *box, + const char *format, ...) ATTR_FORMAT(2, 3); +static inline void +imap_sieve_mailbox_debug(struct mailbox *box, + const char *format, ...) +{ + va_list args; + + if (box->storage->user->mail_debug) { + va_start(args, format); + i_debug("imapsieve: mailbox %s: %s", + mailbox_get_vname(box), + t_strdup_vprintf(format, args)); + va_end(args); + } +} + +static inline void +imap_sieve_mailbox_warning(struct mailbox *box, + const char *format, ...) ATTR_FORMAT(2, 3); +static inline void +imap_sieve_mailbox_warning(struct mailbox *box, + const char *format, ...) +{ + va_list args; + + va_start(args, format); + i_warning("imapsieve: mailbox %s: %s", + mailbox_get_vname(box), + t_strdup_vprintf(format, args)); + va_end(args); +} + +static inline void +imap_sieve_mailbox_error(struct mailbox *box, + const char *format, ...) ATTR_FORMAT(2, 3); +static inline void +imap_sieve_mailbox_error(struct mailbox *box, + const char *format, ...) +{ + va_list args; + + va_start(args, format); + i_error("imapsieve: mailbox %s: %s", + mailbox_get_vname(box), + t_strdup_vprintf(format, args)); + va_end(args); +} + +/* + * Events + */ + +static int imap_sieve_mailbox_get_script_real +(struct mailbox *box, + const char **script_name_r) +{ + struct mail_user *user = box->storage->user; + struct mail_attribute_value value; + int ret; + + *script_name_r = NULL; + + /* get the name of the Sieve script from mailbox METADATA */ + if ((ret=mailbox_attribute_get(box, MAIL_ATTRIBUTE_TYPE_SHARED, + MAILBOX_ATTRIBUTE_IMAPSIEVE_SCRIPT, &value)) < 0) { + imap_sieve_mailbox_error(box, + "Failed to read /shared/" + MAILBOX_ATTRIBUTE_IMAPSIEVE_SCRIPT" " + "mailbox attribute: %s", + mailbox_get_last_internal_error(box, NULL)); + return -1; + } + + if (ret > 0) { + imap_sieve_mailbox_debug(box, + "Mailbox attribute /shared/" + MAILBOX_ATTRIBUTE_IMAPSIEVE_SCRIPT" " + "points to Sieve script `%s'", value.value); + + /* if not found, get the name of the Sieve script from + server METADATA */ + } else { + struct mail_namespace *ns; + struct mailbox *inbox; + + imap_sieve_mailbox_debug(box, + "Mailbox attribute /shared/" + MAILBOX_ATTRIBUTE_IMAPSIEVE_SCRIPT" " + "not found"); + + ns = mail_namespace_find_inbox(user->namespaces); + inbox = mailbox_alloc(ns->list, "INBOX", + MAILBOX_FLAG_READONLY); + ret = mailbox_attribute_get(inbox, + MAIL_ATTRIBUTE_TYPE_SHARED, + MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER + MAILBOX_ATTRIBUTE_IMAPSIEVE_SCRIPT, &value); + + if (ret <= 0) { + if (ret < 0) { + imap_sieve_mailbox_error(box, + "Failed to read /shared/" + MAIL_SERVER_ATTRIBUTE_IMAPSIEVE_SCRIPT" " + "server attribute: %s", + mailbox_get_last_internal_error(inbox, NULL)); + } else if (ret == 0) { + imap_sieve_mailbox_debug(box, + "Server attribute /shared/" + MAIL_SERVER_ATTRIBUTE_IMAPSIEVE_SCRIPT" " + "not found"); + } + mailbox_free(&inbox); + return ret; + } + mailbox_free(&inbox); + + imap_sieve_mailbox_debug(box, + "Server attribute /shared/" + MAIL_SERVER_ATTRIBUTE_IMAPSIEVE_SCRIPT" " + "points to Sieve script `%s'", value.value); + } + + *script_name_r = value.value; + return 1; +} + +static int imap_sieve_mailbox_get_script +(struct mailbox *box, const char **script_name_r) +{ + int ret; + + ret = imap_sieve_mailbox_get_script_real + (box, script_name_r); + return ret; +} + +static struct imap_sieve_mailbox_event * +imap_sieve_create_mailbox_event +(struct mailbox_transaction_context *t, struct mail *dest_mail) +{ + struct imap_sieve_mailbox_transaction *ismt = IMAP_SIEVE_CONTEXT_REQUIRE(t); + struct imap_sieve_mailbox_event *event; + + if (!array_is_created(&ismt->events)) + i_array_init(&ismt->events, 64); + + event = array_append_space(&ismt->events); + event->save_seq = t->save_count; + event->dest_mail_uid = dest_mail->uid; + return event; +} + +static void imap_sieve_add_mailbox_event +(struct mailbox_transaction_context *t, + struct mail *dest_mail, struct mailbox *src_box, + const char *changed_flags) +{ + struct imap_sieve_mailbox_transaction *ismt = IMAP_SIEVE_CONTEXT_REQUIRE(t); + struct imap_sieve_mailbox_event *event; + + i_assert(ismt->src_box == NULL || ismt->src_box == src_box); + ismt->src_box = src_box; + + event = imap_sieve_create_mailbox_event(t, dest_mail); + event->changed_flags = p_strdup(ismt->pool, changed_flags); +} + +static void imap_sieve_add_mailbox_copy_event +(struct mailbox_transaction_context *t, + struct mail *dest_mail, struct mail *src_mail) +{ + struct imap_sieve_mailbox_transaction *ismt = IMAP_SIEVE_CONTEXT_REQUIRE(t); + struct imap_sieve_mailbox_event *event; + + i_assert(ismt->src_box == NULL || ismt->src_box == src_mail->box); + i_assert(ismt->src_mail_trans == NULL || + ismt->src_mail_trans == src_mail->transaction); + + ismt->src_box = src_mail->box; + ismt->src_mail_trans = src_mail->transaction; + + event = imap_sieve_create_mailbox_event(t, dest_mail); + event->src_mail_uid = src_mail->uid; +} + +/* + * Mail + */ + +static void +imap_sieve_mail_update_flags(struct mail *_mail, + enum modify_type modify_type, enum mail_flags flags) +{ + struct mail_private *mail = (struct mail_private *)_mail; + struct imap_sieve_mail *ismail = IMAP_SIEVE_MAIL_CONTEXT(mail); + enum mail_flags old_flags, new_flags, changed_flags; + + old_flags = mail_get_flags(_mail); + ismail->module_ctx.super.update_flags(_mail, modify_type, flags); + new_flags = mail_get_flags(_mail); + + changed_flags = old_flags ^ new_flags; + if (changed_flags == 0) + return; + + if (ismail->flags == NULL) + ismail->flags = str_new(default_pool, 64); + imap_write_flags(ismail->flags, changed_flags, NULL); +} + +static void +imap_sieve_mail_update_keywords(struct mail *_mail, + enum modify_type modify_type, struct mail_keywords *keywords) +{ + struct mail_private *mail = (struct mail_private *)_mail; + struct imap_sieve_mail *ismail = IMAP_SIEVE_MAIL_CONTEXT(mail); + const char *const *old_keywords, *const *new_keywords; + unsigned int i, j; + + old_keywords = mail_get_keywords(_mail); + ismail->module_ctx.super.update_keywords(_mail, modify_type, keywords); + new_keywords = mail_get_keywords(_mail); + + if (ismail->flags == NULL) + ismail->flags = str_new(default_pool, 64); + + /* Removed flags */ + for (i = 0; old_keywords[i] != NULL; i++) { + for (j = 0; new_keywords[j] != NULL; j++) { + if (strcmp(old_keywords[i], new_keywords[j]) == 0) + break; + } + if (new_keywords[j] == NULL) { + if (str_len(ismail->flags) > 0) + str_append_c(ismail->flags, ' '); + str_append(ismail->flags, old_keywords[i]); + } + } + + /* Added flags */ + for (i = 0; new_keywords[i] != NULL; i++) { + for (j = 0; old_keywords[j] != NULL; j++) { + if (strcmp(new_keywords[i], old_keywords[j]) == 0) + break; + } + if (old_keywords[j] == NULL) { + if (str_len(ismail->flags) > 0) + str_append_c(ismail->flags, ' '); + str_append(ismail->flags, new_keywords[i]); + } + } +} + +static void imap_sieve_mail_close(struct mail *_mail) +{ + struct mail_private *mail = (struct mail_private *)_mail; + struct mailbox_transaction_context *t = _mail->transaction; + struct imap_sieve_mail *ismail = IMAP_SIEVE_MAIL_CONTEXT(mail); + + if (ismail->flags != NULL && str_len(ismail->flags) > 0) { + if (!_mail->expunged) { + imap_sieve_mailbox_debug(_mail->box, + "FLAG event (changed flags: %s)", + str_c(ismail->flags)); + + imap_sieve_add_mailbox_event(t, + _mail, _mail->box, str_c(ismail->flags)); + } + str_truncate(ismail->flags, 0); + } + + ismail->module_ctx.super.close(_mail); +} + +static void imap_sieve_mail_free(struct mail *_mail) +{ + struct mail_private *mail = (struct mail_private *)_mail; + struct imap_sieve_mail *ismail = IMAP_SIEVE_MAIL_CONTEXT(mail); + string_t *flags = ismail->flags; + + ismail->module_ctx.super.free(_mail); + + if (flags != NULL) + str_free(&flags); +} + +static void imap_sieve_mail_allocated(struct mail *_mail) +{ + struct mail_private *mail = (struct mail_private *)_mail; + struct imap_sieve_mailbox_transaction *ismt = + IMAP_SIEVE_CONTEXT(_mail->transaction); + struct mail_user *user = _mail->box->storage->user; + struct imap_sieve_user *isuser = + IMAP_SIEVE_USER_CONTEXT_REQUIRE(user); + struct mail_vfuncs *v = mail->vlast; + struct imap_sieve_mail *ismail; + + if (ismt == NULL || isuser->sieve_active) + return; + + ismail = p_new(mail->pool, struct imap_sieve_mail, 1); + ismail->module_ctx.super = *v; + mail->vlast = &ismail->module_ctx.super; + + v->close = imap_sieve_mail_close; + v->free = imap_sieve_mail_free; + v->update_flags = imap_sieve_mail_update_flags; + v->update_keywords = imap_sieve_mail_update_keywords; + MODULE_CONTEXT_SET(mail, imap_sieve_mail_module, ismail); +} + +/* + * Save/copy + */ + +static int +imap_sieve_mailbox_copy(struct mail_save_context *ctx, struct mail *mail) +{ + struct mailbox_transaction_context *t = ctx->transaction; + struct mail_storage *storage = t->box->storage; + struct mail_user *user = storage->user; + struct imap_sieve_user *isuser = + IMAP_SIEVE_USER_CONTEXT_REQUIRE(user); + union mailbox_module_context *lbox = + IMAP_SIEVE_CONTEXT_REQUIRE(t->box); + struct imap_sieve_mailbox_transaction *ismt = + IMAP_SIEVE_CONTEXT(t); + + if (lbox->super.copy(ctx, mail) < 0) + return -1; + + if (ismt != NULL && !isuser->sieve_active && + !ctx->dest_mail->expunged && + (isuser->cur_cmd == IMAP_SIEVE_CMD_COPY || + isuser->cur_cmd == IMAP_SIEVE_CMD_MOVE)) { + imap_sieve_mailbox_debug(t->box, "%s event", + (isuser->cur_cmd == IMAP_SIEVE_CMD_COPY ? + "COPY" : "MOVE")); + imap_sieve_add_mailbox_copy_event(t, ctx->dest_mail, + ctx->copy_src_mail); + } + + return 0; +} + +static int +imap_sieve_mailbox_save_finish(struct mail_save_context *ctx) +{ + struct mailbox_transaction_context *t = ctx->transaction; + struct mailbox *box = t->box; + struct imap_sieve_mailbox_transaction *ismt = IMAP_SIEVE_CONTEXT(t); + union mailbox_module_context *lbox = IMAP_SIEVE_CONTEXT_REQUIRE(box); + struct mail_user *user = box->storage->user; + struct imap_sieve_user *isuser = IMAP_SIEVE_USER_CONTEXT_REQUIRE(user); + struct mail *dest_mail = ctx->copying_via_save ? NULL : ctx->dest_mail; + + if (lbox->super.save_finish(ctx) < 0) + return -1; + + if (ismt != NULL && !isuser->sieve_active && + dest_mail != NULL && !dest_mail->expunged && + isuser->cur_cmd == IMAP_SIEVE_CMD_APPEND) { + + imap_sieve_mailbox_debug(t->box, "APPEND event"); + imap_sieve_add_mailbox_event(t, dest_mail, box, NULL); + } + return 0; +} + +/* + * Mailbox + */ + +static struct mailbox_transaction_context * +imap_sieve_mailbox_transaction_begin(struct mailbox *box, + enum mailbox_transaction_flags flags, + const char *reason) +{ + union mailbox_module_context *lbox = IMAP_SIEVE_CONTEXT_REQUIRE(box); + struct mail_user *user = box->storage->user; + struct imap_sieve_user *isuser = IMAP_SIEVE_USER_CONTEXT(user); + struct mailbox_transaction_context *t; + struct imap_sieve_mailbox_transaction *ismt; + pool_t pool; + + /* commence parent transaction */ + t = lbox->super.transaction_begin(box, flags, reason); + + if (isuser == NULL || isuser->sieve_active || + isuser->cur_cmd == IMAP_SIEVE_CMD_NONE) + return t; + + i_assert(isuser->client != NULL); + + pool = pool_alloconly_create("imap_sieve_mailbox_transaction", 1024); + ismt = p_new(pool, struct imap_sieve_mailbox_transaction, 1); + ismt->pool = pool; + MODULE_CONTEXT_SET(t, imap_sieve_storage_module, ismt); + + return t; +} + +static void +imap_sieve_mailbox_transaction_free +(struct imap_sieve_mailbox_transaction *ismt) +{ + if (array_is_created(&ismt->events)) + array_free(&ismt->events); + pool_unref(&ismt->pool); +} + +static void +imap_sieve_mailbox_run_copy_source( + struct imap_sieve_mailbox_transaction *ismt, + struct imap_sieve_run *isrun, + const struct imap_sieve_mailbox_event *mevent, + struct mail **src_mail, bool *fatal_r) +{ + struct mailbox *src_box = ismt->src_box; + int ret; + + *fatal_r = FALSE; + + if (isrun == NULL) + return; + + i_assert(ismt->src_mail_trans->box == src_box); + i_assert(mevent->src_mail_uid > 0); + + if (*src_mail == NULL) + *src_mail = mail_alloc(ismt->src_mail_trans, 0, NULL); + + /* Select source message */ + if (!mail_set_uid(*src_mail, mevent->src_mail_uid)) { + imap_sieve_mailbox_warning(src_box, + "Failed to find source message for Sieve event " + "(UID=%llu)", (unsigned long long)mevent->src_mail_uid); + return; + } + + imap_sieve_mailbox_debug(src_box, + "Running copy_source_after scripts."); + + /* Run scripts for source mail */ + ret = imap_sieve_run_mail(isrun, *src_mail, NULL, fatal_r); + if (ret > 0) { + /* Discard */ + mail_update_flags(*src_mail, MODIFY_ADD, MAIL_DELETED); + } +} + +static int +imap_sieve_mailbox_transaction_run( + struct imap_sieve_mailbox_transaction *ismt, + struct mailbox *dest_box, + struct mail_transaction_commit_changes *changes) +{ + static const char *wanted_headers[] = { + "From", "To", "Message-ID", "Subject", "Return-Path", + NULL + }; + struct mailbox *src_box = ismt->src_box; + struct mail_user *user = dest_box->storage->user; + struct imap_sieve_user *isuser = IMAP_SIEVE_USER_CONTEXT_REQUIRE(user); + const struct imap_sieve_mailbox_event *mevent; + struct mailbox_header_lookup_ctx *headers_ctx; + struct mailbox_transaction_context *st; + struct mailbox *sbox; + struct imap_sieve_run *isrun, *isrun_src; + struct seq_range_iter siter; + const char *cause, *script_name = NULL; + bool can_discard; + struct mail *mail, *src_mail = NULL; + int ret; + + if (ismt == NULL || !array_is_created(&ismt->events)) { + /* Nothing to do */ + return 0; + } + + i_assert(isuser->client != NULL); + + /* Get user script for this mailbox */ + if (isuser->user_script && imap_sieve_mailbox_get_script + (dest_box, &script_name) < 0) { + return 0; // FIXME: some errors may warrant -1 + } + + /* Make sure IMAPSIEVE is initialized for this user */ + if (isuser->isieve == NULL) + isuser->isieve = imap_sieve_init(isuser->client); + + can_discard = FALSE; + switch (isuser->cur_cmd) { + case IMAP_SIEVE_CMD_APPEND: + cause = "APPEND"; + can_discard = TRUE; + break; + case IMAP_SIEVE_CMD_COPY: + case IMAP_SIEVE_CMD_MOVE: + cause = "COPY"; + can_discard = TRUE; + break; + case IMAP_SIEVE_CMD_STORE: + case IMAP_SIEVE_CMD_OTHER: + cause = "FLAG"; + break; + default: + i_unreached(); + } + + /* Initialize execution */ + T_BEGIN { + ARRAY_TYPE(imap_sieve_mailbox_rule) mbrules; + ARRAY_TYPE(const_string) scripts_before, scripts_after; + ARRAY_TYPE(const_string) scripts_copy_source; + struct imap_sieve_mailbox_rule *rule; + + /* Find matching rules */ + t_array_init(&mbrules, 16); + imap_sieve_mailbox_rules_get + (user, dest_box, src_box, cause, &mbrules); + + /* Apply all matched rules */ + t_array_init(&scripts_before, 8); + t_array_init(&scripts_after, 8); + t_array_init(&scripts_copy_source, 4); + array_foreach_elem(&mbrules, rule) { + if (rule->before != NULL) + array_append(&scripts_before, &rule->before, 1); + if (rule->after != NULL) + array_append(&scripts_after, &rule->after, 1); + if (rule->copy_source_after != NULL) + array_append(&scripts_copy_source, &rule->copy_source_after, 1); + } + (void)array_append_space(&scripts_before); + (void)array_append_space(&scripts_after); + + /* Initialize */ + ret = imap_sieve_run_init + (isuser->isieve, dest_box, src_box, cause, script_name, + array_idx(&scripts_before, 0), + array_idx(&scripts_after, 0), &isrun); + + /* Initialize source script execution */ + isrun_src = NULL; + if (ret > 0 && ismt->src_mail_trans != NULL && + isuser->cur_cmd == IMAP_SIEVE_CMD_COPY && + array_count(&scripts_copy_source) > 0) { + const char *no_scripts = NULL; + + (void)array_append_space(&scripts_copy_source); + if (imap_sieve_run_init(isuser->isieve, + dest_box, src_box, cause, NULL, + &no_scripts, array_idx(&scripts_copy_source, 0), + &isrun_src) <= 0) + isrun_src = NULL; + } + } T_END; + + if (ret <= 0) { + // FIXME: temp fail should be handled properly + return 0; + } + + /* Get synchronized view on the destination mailbox */ + sbox = mailbox_alloc(dest_box->list, dest_box->vname, 0); + if (mailbox_sync(sbox, 0) < 0) { + mailbox_free(&sbox); + imap_sieve_run_deinit(&isrun); + if (isrun_src != NULL) + imap_sieve_run_deinit(&isrun_src); + return -1; + } + + /* Create transaction for event messages */ + st = mailbox_transaction_begin(sbox, 0, __func__); + headers_ctx = mailbox_header_lookup_init(sbox, wanted_headers); + mail = mail_alloc(st, 0, headers_ctx); + mailbox_header_lookup_unref(&headers_ctx); + + /* Iterate through all events */ + seq_range_array_iter_init(&siter, &changes->saved_uids); + array_foreach(&ismt->events, mevent) { + uint32_t uid; + bool fatal; + + /* Determine UID for saved message */ + if (mevent->dest_mail_uid > 0) + uid = mevent->dest_mail_uid; + else if (!seq_range_array_iter_nth(&siter, mevent->save_seq, + &uid)) { + /* already gone for some reason */ + imap_sieve_mailbox_debug( + sbox, "Message for Sieve event gone"); + continue; + } + + /* Select event message */ + i_assert(uid > 0); + if (!mail_set_uid(mail, uid) || mail->expunged) { + /* already gone for some reason */ + imap_sieve_mailbox_debug(sbox, + "Message for Sieve event gone (UID=%llu)", + (unsigned long long)uid); + continue; + } + + /* Run scripts for this mail */ + ret = imap_sieve_run_mail(isrun, mail, mevent->changed_flags, + &fatal); + if (fatal) + break; + + /* Handle the result */ + if (ret < 0) { + /* Sieve error; keep */ + } else { + if (ret <= 0 || !can_discard) { + /* Keep */ + } else if (!isuser->expunge_discarded) { + /* Mark as \Deleted */ + mail_update_flags(mail, + MODIFY_ADD, MAIL_DELETED); + } else { + /* Expunge */ + mail_expunge(mail); + } + + imap_sieve_mailbox_run_copy_source( + ismt, isrun_src, mevent, &src_mail, &fatal); + if (fatal) + break; + } + } + + /* Cleanup */ + mail_free(&mail); + ret = mailbox_transaction_commit(&st); + if (src_mail != NULL) + mail_free(&src_mail); + imap_sieve_run_deinit(&isrun); + if (isrun_src != NULL) + imap_sieve_run_deinit(&isrun_src); + mailbox_free(&sbox); + return ret; +} + +static int +imap_sieve_mailbox_transaction_commit( + struct mailbox_transaction_context *t, + struct mail_transaction_commit_changes *changes_r) +{ + struct mailbox *box = t->box; + struct mail_user *user = box->storage->user; + struct imap_sieve_mailbox_transaction *ismt = IMAP_SIEVE_CONTEXT(t); + union mailbox_module_context *lbox = IMAP_SIEVE_CONTEXT_REQUIRE(t->box); + struct imap_sieve_user *isuser = IMAP_SIEVE_USER_CONTEXT_REQUIRE(user); + int ret = 0; + + if ((lbox->super.transaction_commit(t, changes_r)) < 0) + ret = -1; + else if (ismt != NULL) { + isuser->sieve_active = TRUE; + if (imap_sieve_mailbox_transaction_run + (ismt, box, changes_r) < 0) + ret = -1; + isuser->sieve_active = FALSE; + } + + if (ismt != NULL) + imap_sieve_mailbox_transaction_free(ismt); + return ret; +} + +static void +imap_sieve_mailbox_transaction_rollback( + struct mailbox_transaction_context *t) +{ + struct imap_sieve_mailbox_transaction *ismt = IMAP_SIEVE_CONTEXT(t); + union mailbox_module_context *lbox = IMAP_SIEVE_CONTEXT_REQUIRE(t->box); + + lbox->super.transaction_rollback(t); + + if (ismt != NULL) + imap_sieve_mailbox_transaction_free(ismt); +} + +static void imap_sieve_mailbox_allocated(struct mailbox *box) +{ + struct mail_user *user = box->storage->user; + struct imap_sieve_user *isuser = IMAP_SIEVE_USER_CONTEXT_REQUIRE(user); + struct mailbox_vfuncs *v = box->vlast; + union mailbox_module_context *lbox; + + if (isuser->client == NULL || isuser->sieve_active || + (box->flags & MAILBOX_FLAG_READONLY) != 0) + return; + + lbox = p_new(box->pool, union mailbox_module_context, 1); + lbox->super = *v; + box->vlast = &lbox->super; + + v->copy = imap_sieve_mailbox_copy; + v->save_finish = imap_sieve_mailbox_save_finish; + v->transaction_begin = imap_sieve_mailbox_transaction_begin; + v->transaction_commit = imap_sieve_mailbox_transaction_commit; + v->transaction_rollback = imap_sieve_mailbox_transaction_rollback; + MODULE_CONTEXT_SET_SELF(box, imap_sieve_storage_module, lbox); +} + +/* + * Mailbox rules + */ + +static unsigned int imap_sieve_mailbox_rule_hash +(const struct imap_sieve_mailbox_rule *rule) +{ + unsigned int hash = str_hash(rule->mailbox); + + if (rule->from != NULL) + hash += str_hash(rule->from); + return hash; +} + +static int imap_sieve_mailbox_rule_cmp +(const struct imap_sieve_mailbox_rule *rule1, + const struct imap_sieve_mailbox_rule *rule2) +{ + int ret; + + if ((ret=strcmp(rule1->mailbox, rule2->mailbox)) != 0) + return ret; + return null_strcmp(rule1->from, rule2->from); +} + +static bool rule_pattern_has_wildcards(const char *pattern) +{ + for (; *pattern != '\0'; pattern++) { + if (*pattern == '%' || *pattern == '*') + return TRUE; + } + return FALSE; +} + +static void +imap_sieve_mailbox_rules_init(struct mail_user *user) +{ + struct imap_sieve_user *isuser = IMAP_SIEVE_USER_CONTEXT_REQUIRE(user); + string_t *identifier; + unsigned int i = 0; + size_t prefix_len; + + if (hash_table_is_created(isuser->mbox_rules)) + return; + + hash_table_create(&isuser->mbox_rules, default_pool, 0, + imap_sieve_mailbox_rule_hash, imap_sieve_mailbox_rule_cmp); + i_array_init(&isuser->mbox_patterns, 8); + + identifier = t_str_new(256); + str_append(identifier, "imapsieve_mailbox"); + prefix_len = str_len(identifier); + + for (i = 1; ; i++) { + struct imap_sieve_mailbox_rule *mbrule; + const char *setval; + size_t id_len; + + str_truncate(identifier, prefix_len); + str_printfa(identifier, "%u", i); + id_len = str_len(identifier); + + str_append(identifier, "_name"); + setval = mail_user_plugin_getenv + (user, str_c(identifier)); + if (setval == NULL || *setval == '\0') + break; + setval = t_str_trim(setval, "\t "); + if (strcasecmp(setval, "INBOX") == 0) + setval = t_str_ucase(setval); + + mbrule = p_new(user->pool, + struct imap_sieve_mailbox_rule, 1); + mbrule->index = i; + mbrule->mailbox = p_strdup(user->pool, setval); + + str_truncate(identifier, id_len); + str_append(identifier, "_from"); + setval = mail_user_plugin_getenv(user, str_c(identifier)); + if (setval != NULL && *setval != '\0') { + setval = t_str_trim(setval, "\t "); + if (strcasecmp(setval, "INBOX") == 0) + setval = t_str_ucase(setval); + mbrule->from = p_strdup(user->pool, setval); + if (strcmp(mbrule->from, "*") == 0) + mbrule->from = NULL; + } + + if ((strcmp(mbrule->mailbox, "*") == 0 || + !rule_pattern_has_wildcards(mbrule->mailbox)) && + (mbrule->from == NULL || + !rule_pattern_has_wildcards(mbrule->from)) && + hash_table_lookup(isuser->mbox_rules, mbrule) != NULL) { + imap_sieve_warning(user, + "Duplicate static mailbox rule [%u] for mailbox `%s' " + "(skipped)", i, mbrule->mailbox); + continue; + } + + str_truncate(identifier, id_len); + str_append(identifier, "_causes"); + setval = mail_user_plugin_getenv(user, str_c(identifier)); + if (setval != NULL && *setval != '\0') { + const char *const *cause; + + mbrule->causes = (const char *const *) + p_strsplit_spaces(user->pool, setval, " \t,"); + + for (cause = mbrule->causes; *cause != NULL; cause++) { + if (!imap_sieve_event_cause_valid(*cause)) + break; + } + if (*cause != NULL) { + imap_sieve_warning(user, + "Static mailbox rule [%u] has invalid event cause `%s' " + "(skipped)", i, *cause); + continue; + } + } + + str_truncate(identifier, id_len); + str_append(identifier, "_before"); + setval = mail_user_plugin_getenv(user, str_c(identifier)); + mbrule->before = p_strdup_empty(user->pool, setval); + + str_truncate(identifier, id_len); + str_append(identifier, "_after"); + setval = mail_user_plugin_getenv(user, str_c(identifier)); + mbrule->after = p_strdup_empty(user->pool, setval); + + str_truncate(identifier, id_len); + str_append(identifier, "_copy_source_after"); + setval = mail_user_plugin_getenv(user, str_c(identifier)); + mbrule->copy_source_after = p_strdup_empty(user->pool, setval); + + if (user->mail_debug) { + imap_sieve_debug(user, "Static mailbox rule [%u]: " + "mailbox=`%s' from=`%s' causes=(%s) => " + "before=%s after=%s%s", + mbrule->index, mbrule->mailbox, + (mbrule->from == NULL ? "*" : mbrule->from), + t_strarray_join(mbrule->causes, " "), + (mbrule->before == NULL ? "(none)" : + t_strconcat("`", mbrule->before, "'", NULL)), + (mbrule->after == NULL ? "(none)" : + t_strconcat("`", mbrule->after, "'", NULL)), + (mbrule->copy_source_after == NULL ? "": + t_strconcat(" copy_source_after=`", + mbrule->copy_source_after, "'", NULL))); + } + + if ((strcmp(mbrule->mailbox, "*") == 0 || + !rule_pattern_has_wildcards(mbrule->mailbox)) && + (mbrule->from == NULL || + !rule_pattern_has_wildcards(mbrule->from))) { + hash_table_insert(isuser->mbox_rules, mbrule, mbrule); + } else { + array_append(&isuser->mbox_patterns, &mbrule, 1); + } + } + + if (i == 0) + imap_sieve_debug(user, "No static mailbox rules"); +} + +static bool +imap_sieve_mailbox_rule_match_cause +(struct imap_sieve_mailbox_rule *rule, const char *cause) +{ + const char *const *cp; + + if (rule->causes == NULL || *rule->causes == NULL) + return TRUE; + + for (cp = rule->causes; *cp != NULL; cp++) { + if (strcasecmp(cause, *cp) == 0) + return TRUE; + } + return FALSE; +} + +static void +imap_sieve_mailbox_rules_match_patterns(struct mail_user *user, + struct mailbox *dst_box, struct mailbox *src_box, + const char *cause, + ARRAY_TYPE(imap_sieve_mailbox_rule) *rules) +{ + struct imap_sieve_user *isuser = IMAP_SIEVE_USER_CONTEXT_REQUIRE(user); + struct imap_sieve_mailbox_rule *rule; + struct mail_namespace *dst_ns, *src_ns; + + if (array_count(&isuser->mbox_patterns) == 0) + return; + + dst_ns = mailbox_get_namespace(dst_box); + src_ns = (src_box == NULL ? NULL : + mailbox_get_namespace(src_box)); + + array_foreach_elem(&isuser->mbox_patterns, rule) { + struct imap_match_glob *glob; + + if (src_ns == NULL && rule->from != NULL) + continue; + if (!imap_sieve_mailbox_rule_match_cause(rule, cause)) + continue; + + if (strcmp(rule->mailbox, "*") != 0) { + glob = imap_match_init(pool_datastack_create(), + rule->mailbox, TRUE, mail_namespace_get_sep(dst_ns)); + if (imap_match(glob, mailbox_get_vname(dst_box)) + != IMAP_MATCH_YES) + continue; + } + if (rule->from != NULL) { + glob = imap_match_init(pool_datastack_create(), + rule->from, TRUE, mail_namespace_get_sep(src_ns)); + if (imap_match(glob, mailbox_get_vname(src_box)) + != IMAP_MATCH_YES) + continue; + } + + imap_sieve_debug(user, + "Matched static mailbox rule [%u]", + rule->index); + array_append(rules, &rule, 1); + } +} + +static void +imap_sieve_mailbox_rules_match(struct mail_user *user, + const char *dst_box, const char *src_box, + const char *cause, + ARRAY_TYPE(imap_sieve_mailbox_rule) *rules) +{ + struct imap_sieve_user *isuser = IMAP_SIEVE_USER_CONTEXT_REQUIRE(user); + struct imap_sieve_mailbox_rule lookup_rule; + struct imap_sieve_mailbox_rule *rule; + + i_zero(&lookup_rule); + lookup_rule.mailbox = dst_box; + lookup_rule.from = src_box; + rule = hash_table_lookup(isuser->mbox_rules, &lookup_rule); + + if (rule != NULL && + imap_sieve_mailbox_rule_match_cause(rule, cause)) { + struct imap_sieve_mailbox_rule *const *rule_idx; + unsigned int insert_idx = array_count(rules); + + /* Insert sorted by rule index */ + array_foreach(rules, rule_idx) { + if (rule->index < (*rule_idx)->index) { + insert_idx = array_foreach_idx(rules, rule_idx); + break; + } + } + array_insert(rules, insert_idx, &rule, 1); + + imap_sieve_debug(user, + "Matched static mailbox rule [%u]", + rule->index); + } +} + +static void +imap_sieve_mailbox_rules_get(struct mail_user *user, + struct mailbox *dst_box, struct mailbox *src_box, + const char *cause, + ARRAY_TYPE(imap_sieve_mailbox_rule) *rules) +{ + const char *dst_name, *src_name; + + imap_sieve_mailbox_rules_init(user); + + imap_sieve_mailbox_rules_match_patterns + (user, dst_box, src_box, cause, rules); + + dst_name = mailbox_get_vname(dst_box); + src_name = (src_box == NULL ? NULL : + mailbox_get_vname(src_box)); + + imap_sieve_mailbox_rules_match + (user, dst_name, src_name, cause, rules); + imap_sieve_mailbox_rules_match + (user, "*", src_name, cause, rules); + if (src_name != NULL) { + imap_sieve_mailbox_rules_match + (user, dst_name, NULL, cause, rules); + imap_sieve_mailbox_rules_match + (user, "*", NULL, cause, rules); + } +} + +/* + * User + */ + +static void imap_sieve_user_deinit(struct mail_user *user) +{ + struct imap_sieve_user *isuser = IMAP_SIEVE_USER_CONTEXT_REQUIRE(user); + + if (isuser->isieve != NULL) + imap_sieve_deinit(&isuser->isieve); + + hash_table_destroy(&isuser->mbox_rules); + if (array_is_created(&isuser->mbox_patterns)) + array_free(&isuser->mbox_patterns); + + isuser->module_ctx.super.deinit(user); +} + +static void imap_sieve_user_created(struct mail_user *user) +{ + struct imap_sieve_user *isuser; + struct mail_user_vfuncs *v = user->vlast; + + isuser = p_new(user->pool, struct imap_sieve_user, 1); + isuser->module_ctx.super = *v; + user->vlast = &isuser->module_ctx.super; + v->deinit = imap_sieve_user_deinit; + MODULE_CONTEXT_SET(user, imap_sieve_user_module, isuser); +} + +/* + * Hooks + */ + +static struct mail_storage_hooks imap_sieve_mail_storage_hooks = { + .mail_user_created = imap_sieve_user_created, + .mailbox_allocated = imap_sieve_mailbox_allocated, + .mail_allocated = imap_sieve_mail_allocated +}; + +/* + * Commands + */ + +static void imap_sieve_command_pre(struct client_command_context *cmd) +{ + struct client *client = cmd->client; + struct mail_user *user = client->user; + struct imap_sieve_user *isuser = IMAP_SIEVE_USER_CONTEXT(user); + + if (isuser == NULL) + return; + + if (strcasecmp(cmd->name, "APPEND") == 0) { + isuser->cur_cmd = IMAP_SIEVE_CMD_APPEND; + } else if (strcasecmp(cmd->name, "COPY") == 0 || + strcasecmp(cmd->name, "UID COPY") == 0) { + isuser->cur_cmd = IMAP_SIEVE_CMD_COPY; + } else if (strcasecmp(cmd->name, "MOVE") == 0 || + strcasecmp(cmd->name, "UID MOVE") == 0) { + isuser->cur_cmd = IMAP_SIEVE_CMD_MOVE; + } else if (strcasecmp(cmd->name, "STORE") == 0 || + strcasecmp(cmd->name, "UID STORE") == 0) { + isuser->cur_cmd = IMAP_SIEVE_CMD_STORE; + } else { + isuser->cur_cmd = IMAP_SIEVE_CMD_OTHER; + } +} + +static void imap_sieve_command_post(struct client_command_context *cmd) +{ + struct client *client = cmd->client; + struct mail_user *user = client->user; + struct imap_sieve_user *isuser = IMAP_SIEVE_USER_CONTEXT(user); + + if (isuser == NULL) + return; + isuser->cur_cmd = IMAP_SIEVE_CMD_NONE; +} + +/* + * Client + */ + +void imap_sieve_storage_client_created(struct client *client, + bool user_script) +{ + struct mail_user *user = client->user; + struct imap_sieve_user *isuser = IMAP_SIEVE_USER_CONTEXT_REQUIRE(user); + const char *set; + + isuser->client = client; + isuser->user_script = user_script; + + set = mail_user_plugin_getenv(user, "imapsieve_expunge_discarded"); + isuser->expunge_discarded = + (set != NULL && strcasecmp(set, "yes") == 0); +} + +/* + * + */ + +void imap_sieve_storage_init(struct module *module) +{ + command_hook_register(imap_sieve_command_pre, imap_sieve_command_post); + mail_storage_hooks_add(module, &imap_sieve_mail_storage_hooks); +} + +void imap_sieve_storage_deinit(void) +{ + mail_storage_hooks_remove(&imap_sieve_mail_storage_hooks); + command_hook_unregister(imap_sieve_command_pre, imap_sieve_command_post); +} diff --git a/pigeonhole/src/plugins/imapsieve/imap-sieve-storage.h b/pigeonhole/src/plugins/imapsieve/imap-sieve-storage.h new file mode 100644 index 0000000..f96860f --- /dev/null +++ b/pigeonhole/src/plugins/imapsieve/imap-sieve-storage.h @@ -0,0 +1,10 @@ +#ifndef IMAP_SIEVE_STORAGE_H +#define IMAP_SIEVE_STORAGE_H + +void imap_sieve_storage_init(struct module *module); +void imap_sieve_storage_deinit(void); + +void imap_sieve_storage_client_created(struct client *client, + bool user_script); + +#endif diff --git a/pigeonhole/src/plugins/imapsieve/imap-sieve.c b/pigeonhole/src/plugins/imapsieve/imap-sieve.c new file mode 100644 index 0000000..e6cc24a --- /dev/null +++ b/pigeonhole/src/plugins/imapsieve/imap-sieve.c @@ -0,0 +1,940 @@ +/* Copyright (c) 2016-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "home-expand.h" +#include "smtp-address.h" +#include "smtp-submit.h" +#include "mail-storage.h" +#include "mail-user.h" +#include "mail-duplicate.h" +#include "iostream-ssl.h" +#include "imap-client.h" +#include "imap-settings.h" + +#include "sieve.h" +#include "sieve-script.h" +#include "sieve-storage.h" + +#include "ext-imapsieve-common.h" + +#include "imap-sieve.h" + +/* + * Configuration + */ + +#define DUPLICATE_DB_NAME "lda-dupes" +#define IMAP_SIEVE_MAX_USER_ERRORS 30 + +/* + * IMAP Sieve + */ + +struct imap_sieve { + pool_t pool; + struct client *client; + const char *home_dir; + + struct sieve_instance *svinst; + struct sieve_storage *storage; + + const struct sieve_extension *ext_imapsieve; + const struct sieve_extension *ext_vnd_imapsieve; + + struct mail_duplicate_db *dup_db; + + struct sieve_error_handler *master_ehandler; +}; + +static const char * +mail_sieve_get_setting(void *context, const char *identifier) +{ + struct imap_sieve *isieve = context; + struct mail_user *user = isieve->client->user; + + return mail_user_plugin_getenv(user, identifier); +} + +static const struct sieve_callbacks mail_sieve_callbacks = { + NULL, + mail_sieve_get_setting +}; + +struct imap_sieve *imap_sieve_init(struct client *client) +{ + struct sieve_environment svenv; + struct imap_sieve *isieve; + struct mail_user *user = client->user; + const struct mail_storage_settings *mail_set = + mail_user_set_get_storage_set(user); + bool debug = user->mail_debug; + pool_t pool; + + pool = pool_alloconly_create("imap_sieve", 256); + isieve = p_new(pool, struct imap_sieve, 1); + isieve->pool = pool; + isieve->client = client; + + isieve->dup_db = mail_duplicate_db_init(user, DUPLICATE_DB_NAME); + + i_zero(&svenv); + svenv.username = user->username; + (void)mail_user_get_home(user, &svenv.home_dir); + svenv.hostname = mail_set->hostname; + svenv.base_dir = user->set->base_dir; + svenv.event_parent = client->event; + svenv.flags = SIEVE_FLAG_HOME_RELATIVE; + svenv.location = SIEVE_ENV_LOCATION_MS; + svenv.delivery_phase = SIEVE_DELIVERY_PHASE_POST; + + isieve->home_dir = p_strdup(pool, svenv.home_dir); + + isieve->svinst = sieve_init(&svenv, &mail_sieve_callbacks, isieve, + debug); + + isieve->ext_imapsieve = sieve_extension_replace( + isieve->svinst, &imapsieve_extension, TRUE); + isieve->ext_vnd_imapsieve = sieve_extension_replace( + isieve->svinst, &vnd_imapsieve_extension, TRUE); + + isieve->master_ehandler = + sieve_master_ehandler_create(isieve->svinst, 0); + sieve_error_handler_accept_infolog(isieve->master_ehandler, TRUE); + sieve_error_handler_accept_debuglog(isieve->master_ehandler, debug); + + return isieve; +} + +void imap_sieve_deinit(struct imap_sieve **_isieve) +{ + struct imap_sieve *isieve = *_isieve; + + *_isieve = NULL; + + sieve_error_handler_unref(&isieve->master_ehandler); + + if (isieve->storage != NULL) + sieve_storage_unref(&isieve->storage); + sieve_extension_unregister(isieve->ext_imapsieve); + sieve_extension_unregister(isieve->ext_vnd_imapsieve); + sieve_deinit(&isieve->svinst); + + mail_duplicate_db_deinit(&isieve->dup_db); + + pool_unref(&isieve->pool); +} + +static int +imap_sieve_get_storage(struct imap_sieve *isieve, + struct sieve_storage **storage_r) +{ + enum sieve_storage_flags storage_flags = 0; + struct mail_user *user = isieve->client->user; + enum sieve_error error; + + if (isieve->storage != NULL) { + *storage_r = isieve->storage; + return 1; + } + + // FIXME: limit interval between retries + + isieve->storage = sieve_storage_create_main(isieve->svinst, user, + storage_flags, &error); + if (isieve->storage == NULL) { + if (error == SIEVE_ERROR_TEMP_FAILURE) + return -1; + return 0; + } + *storage_r = isieve->storage; + return 1; +} + +/* + * Mail transmission + */ + +static void * +imap_sieve_smtp_start(const struct sieve_script_env *senv, + const struct smtp_address *mail_from) +{ + struct imap_sieve_context *isctx = senv->script_context; + struct imap_sieve *isieve = isctx->isieve; + struct mail_user *user = isieve->client->user; + const struct smtp_submit_settings *smtp_set = isieve->client->smtp_set; + struct ssl_iostream_settings ssl_set; + struct smtp_submit_input submit_input; + + i_zero(&ssl_set); + mail_user_init_ssl_client_settings(user, &ssl_set); + + i_zero(&submit_input); + submit_input.ssl = &ssl_set; + + return smtp_submit_init_simple(&submit_input, smtp_set, mail_from); +} + +static void +imap_sieve_smtp_add_rcpt(const struct sieve_script_env *senv ATTR_UNUSED, + void *handle, const struct smtp_address *rcpt_to) +{ + struct smtp_submit *smtp_submit = handle; + + smtp_submit_add_rcpt(smtp_submit, rcpt_to); +} + +static struct ostream * +imap_sieve_smtp_send(const struct sieve_script_env *senv ATTR_UNUSED, + void *handle) +{ + struct smtp_submit *smtp_submit = handle; + + return smtp_submit_send(smtp_submit); +} + +static void +imap_sieve_smtp_abort(const struct sieve_script_env *senv ATTR_UNUSED, + void *handle) +{ + struct smtp_submit *smtp_submit = handle; + + smtp_submit_deinit(&smtp_submit); +} + +static int +imap_sieve_smtp_finish(const struct sieve_script_env *senv ATTR_UNUSED, + void *handle, const char **error_r) +{ + struct smtp_submit *smtp_submit = handle; + int ret; + + ret = smtp_submit_run(smtp_submit, error_r); + smtp_submit_deinit(&smtp_submit); + return ret; +} + +/* + * Duplicate checking + */ + +static void * +imap_sieve_duplicate_transaction_begin(const struct sieve_script_env *senv) +{ + struct imap_sieve_context *isctx = senv->script_context; + + return mail_duplicate_transaction_begin(isctx->isieve->dup_db); +} + +static void imap_sieve_duplicate_transaction_commit(void **_dup_trans) +{ + struct mail_duplicate_transaction *dup_trans = *_dup_trans; + + *_dup_trans = NULL; + mail_duplicate_transaction_commit(&dup_trans); +} + +static void imap_sieve_duplicate_transaction_rollback(void **_dup_trans) +{ + struct mail_duplicate_transaction *dup_trans = *_dup_trans; + + *_dup_trans = NULL; + mail_duplicate_transaction_rollback(&dup_trans); +} + +static enum sieve_duplicate_check_result +imap_sieve_duplicate_check(void *_dup_trans, + const struct sieve_script_env *senv, + const void *id, size_t id_size) +{ + struct mail_duplicate_transaction *dup_trans = _dup_trans; + + switch (mail_duplicate_check(dup_trans, id, id_size, + senv->user->username)) { + case MAIL_DUPLICATE_CHECK_RESULT_EXISTS: + return SIEVE_DUPLICATE_CHECK_RESULT_EXISTS; + case MAIL_DUPLICATE_CHECK_RESULT_NOT_FOUND: + return SIEVE_DUPLICATE_CHECK_RESULT_NOT_FOUND; + case MAIL_DUPLICATE_CHECK_RESULT_DEADLOCK: + case MAIL_DUPLICATE_CHECK_RESULT_LOCK_TIMEOUT: + return SIEVE_DUPLICATE_CHECK_RESULT_TEMP_FAILURE; + case MAIL_DUPLICATE_CHECK_RESULT_IO_ERROR: + case MAIL_DUPLICATE_CHECK_RESULT_TOO_MANY_LOCKS: + break; + } + return SIEVE_DUPLICATE_CHECK_RESULT_FAILURE; +} + +static void +imap_sieve_duplicate_mark(void *_dup_trans, const struct sieve_script_env *senv, + const void *id, size_t id_size, time_t time) +{ + struct mail_duplicate_transaction *dup_trans = _dup_trans; + + mail_duplicate_mark(dup_trans, id, id_size, senv->user->username, time); +} + +/* + * Result logging + */ + +static const char * +imap_sieve_result_amend_log_message(const struct sieve_script_env *senv, + enum log_type log_type ATTR_UNUSED, + const char *message) +{ + struct imap_sieve_context *isctx = senv->script_context; + string_t *str; + + if (isctx->mail == NULL) + return message; + + str = t_str_new(256); + str_printfa(str, "uid=%u: ", isctx->mail->uid); + str_append(str, message); + return str_c(str); +} + +/* + * IMAP Sieve run + */ + +struct imap_sieve_run_script { + struct sieve_script *script; + struct sieve_binary *binary; + + /* Compile failed once with this error; + don't try again for this transaction */ + enum sieve_error compile_error; + + /* Binary corrupt after recompile; don't recompile again */ + bool binary_corrupt:1; + /* Resource usage exceeded */ + bool rusage_exceeded:1; +}; + +struct imap_sieve_run { + pool_t pool; + struct imap_sieve *isieve; + struct mailbox *dest_mailbox, *src_mailbox; + char *cause; + + struct sieve_error_handler *user_ehandler; + char *userlog; + + struct sieve_trace_config trace_config; + struct sieve_trace_log *trace_log; + + struct sieve_script *user_script; + struct imap_sieve_run_script *scripts; + unsigned int scripts_count; + + bool trace_log_initialized:1; +}; + +static void +imap_sieve_run_init_user_log(struct imap_sieve_run *isrun) +{ + struct imap_sieve *isieve = isrun->isieve; + struct sieve_instance *svinst = isieve->svinst; + const char *log_path; + + log_path = sieve_user_get_log_path(svinst, isrun->user_script); + if (log_path != NULL) { + isrun->userlog = p_strdup(isrun->pool, log_path); + isrun->user_ehandler = sieve_logfile_ehandler_create( + svinst, log_path, IMAP_SIEVE_MAX_USER_ERRORS); + } +} + +static void +imap_sieve_run_init_trace_log(struct imap_sieve_run *isrun, + struct sieve_trace_config *trace_config_r, + struct sieve_trace_log **trace_log_r) +{ + struct imap_sieve *isieve = isrun->isieve; + struct sieve_instance *svinst = isieve->svinst; + struct mail_user *user = isieve->client->user; + + if (isrun->trace_log_initialized) { + *trace_config_r = isrun->trace_config; + *trace_log_r = isrun->trace_log; + return; + } + isrun->trace_log_initialized = TRUE; + + if (sieve_trace_config_get(svinst, &isrun->trace_config) < 0 || + sieve_trace_log_open(svinst, &isrun->trace_log) < 0) { + i_zero(&isrun->trace_config); + isrun->trace_log = NULL; + + i_zero(trace_config_r); + *trace_log_r = NULL; + return; + } + + /* Write header for trace file */ + sieve_trace_log_printf(isrun->trace_log, + "Sieve trace log for IMAPSIEVE:\n" + "\n" + " Username: %s\n", user->username); + if (user->session_id != NULL) { + sieve_trace_log_printf(isrun->trace_log, + " Session ID: %s\n", user->session_id); + } + if (isrun->src_mailbox != NULL) { + sieve_trace_log_printf(isrun->trace_log, + " Source mailbox: %s\n", + mailbox_get_vname(isrun->src_mailbox)); + } + + sieve_trace_log_printf(isrun->trace_log, + " Destination mailbox: %s\n" + " Cause: %s\n\n", + mailbox_get_vname(isrun->dest_mailbox), + isrun->cause); + + *trace_config_r = isrun->trace_config; + *trace_log_r = isrun->trace_log; +} + +int imap_sieve_run_init(struct imap_sieve *isieve, + struct mailbox *dest_mailbox, + struct mailbox *src_mailbox, + const char *cause, const char *script_name, + const char *const *scripts_before, + const char *const *scripts_after, + struct imap_sieve_run **isrun_r) +{ + struct sieve_instance *svinst = isieve->svinst; + struct imap_sieve_run *isrun; + struct sieve_storage *storage; + struct imap_sieve_run_script *scripts; + struct sieve_script *user_script; + const char *const *sp; + enum sieve_error error; + pool_t pool; + unsigned int max_len, count; + int ret; + + /* Determine how many scripts we may run for this event */ + max_len = 0; + if (scripts_before != NULL) + max_len += str_array_length(scripts_before); + if (script_name != NULL) + max_len++; + if (scripts_after != NULL) + max_len += str_array_length(scripts_after); + if (max_len == 0) + return 0; + + /* Get storage for user script */ + storage = NULL; + if (script_name != NULL && *script_name != '\0' && + (ret = imap_sieve_get_storage(isieve, &storage)) < 0) + return ret; + + /* Open all scripts */ + count = 0; + pool = pool_alloconly_create("imap_sieve_run", 256); + scripts = p_new(pool, struct imap_sieve_run_script, max_len); + + /* Admin scripts before user script */ + if (scripts_before != NULL) { + for (sp = scripts_before; *sp != NULL; sp++) { + i_assert(count < max_len); + scripts[count].script = + sieve_script_create_open(svinst, *sp, NULL, + &error); + if (scripts[count].script != NULL) + count++; + else if (error == SIEVE_ERROR_TEMP_FAILURE) + return -1; + } + } + + /* The user script */ + user_script = NULL; + if (storage != NULL) { + i_assert(count < max_len); + scripts[count].script = + sieve_storage_open_script(storage, script_name, &error); + if (scripts[count].script != NULL) { + user_script = scripts[count].script; + count++; + } else if (error == SIEVE_ERROR_TEMP_FAILURE) { + return -1; + } + } + + /* Admin scripts after user script */ + if (scripts_after != NULL) { + for (sp = scripts_after; *sp != NULL; sp++) { + i_assert(count < max_len); + scripts[count].script = + sieve_script_create_open(svinst, *sp, NULL, + &error); + if (scripts[count].script != NULL) + count++; + else if (error == SIEVE_ERROR_TEMP_FAILURE) + return -1; + } + } + + if (count == 0) { + /* None of the scripts could be opened */ + pool_unref(&pool); + return 0; + } + + /* Initialize */ + isrun = p_new(pool, struct imap_sieve_run, 1); + isrun->pool = pool; + isrun->isieve = isieve; + isrun->dest_mailbox = dest_mailbox; + isrun->src_mailbox = src_mailbox; + isrun->cause = p_strdup(pool, cause); + isrun->user_script = user_script; + isrun->scripts = scripts; + isrun->scripts_count = count; + + imap_sieve_run_init_user_log(isrun); + + *isrun_r = isrun; + return 1; +} + +void imap_sieve_run_deinit(struct imap_sieve_run **_isrun) +{ + struct imap_sieve_run *isrun = *_isrun; + unsigned int i; + + *_isrun = NULL; + + for (i = 0; i < isrun->scripts_count; i++) { + if (isrun->scripts[i].binary != NULL) + sieve_close(&isrun->scripts[i].binary); + if (isrun->scripts[i].script != NULL) + sieve_script_unref(&isrun->scripts[i].script); + } + if (isrun->user_ehandler != NULL) + sieve_error_handler_unref(&isrun->user_ehandler); + if (isrun->trace_log != NULL) + sieve_trace_log_free(&isrun->trace_log); + + pool_unref(&isrun->pool); +} + +static struct sieve_binary * +imap_sieve_run_open_script(struct imap_sieve_run *isrun, + struct sieve_script *script, + enum sieve_compile_flags cpflags, + bool recompile, enum sieve_error *error_r) +{ + struct imap_sieve *isieve = isrun->isieve; + struct sieve_instance *svinst = isieve->svinst; + struct sieve_error_handler *ehandler; + struct sieve_binary *sbin; + const char *compile_name = "compile"; + + if (recompile) { + /* Warn */ + e_warning(sieve_get_event(svinst), + "Encountered corrupt binary: re-compiling script %s", + sieve_script_location(script)); + compile_name = "re-compile"; + } else { + e_debug(sieve_get_event(svinst), + "Loading script %s", sieve_script_location(script)); + } + + if (script == isrun->user_script) + ehandler = isrun->user_ehandler; + else + ehandler = isieve->master_ehandler; + sieve_error_handler_reset(ehandler); + + /* Load or compile the sieve script */ + if (recompile) { + sbin = sieve_compile_script(script, ehandler, cpflags, error_r); + } else { + sbin = sieve_open_script(script, ehandler, cpflags, error_r); + } + + /* Handle error */ + if (sbin == NULL) { + switch (*error_r) { + /* Script not found */ + case SIEVE_ERROR_NOT_FOUND: + e_debug(sieve_get_event(svinst), + "Script `%s' is missing for %s", + sieve_script_location(script), compile_name); + break; + /* Temporary failure */ + case SIEVE_ERROR_TEMP_FAILURE: + e_error(sieve_get_event(svinst), + "Failed to open script `%s' for %s " + "(temporary failure)", + sieve_script_location(script), compile_name); + break; + /* Compile failed */ + case SIEVE_ERROR_NOT_VALID: + if (script == isrun->user_script && + isrun->userlog != NULL ) { + e_info(sieve_get_event(svinst), + "Failed to %s script `%s' " + "(view user logfile `%s' for more information)", + compile_name, sieve_script_location(script), + isrun->userlog); + break; + } + e_error(sieve_get_event(svinst), + "Failed to %s script `%s'", + compile_name, sieve_script_location(script)); + break; + /* Cumulative resource limit exceeded */ + case SIEVE_ERROR_RESOURCE_LIMIT: + e_error(sieve_get_event(svinst), + "Failed to open script `%s' for %s " + "(cumulative resource limit exceeded)", + sieve_script_location(script), compile_name); + break; + /* Something else */ + default: + e_error(sieve_get_event(svinst), + "Failed to open script `%s' for %s", + sieve_script_location(script), compile_name); + break; + } + + return NULL; + } + + if (!recompile) + (void)sieve_save(sbin, FALSE, NULL); + return sbin; +} + +static int +imap_sieve_handle_exec_status(struct imap_sieve_run *isrun, + struct sieve_script *script, int status, + struct sieve_exec_status *estatus, bool *fatal_r) + ATTR_NULL(2) +{ + struct imap_sieve *isieve = isrun->isieve; + struct sieve_instance *svinst = isieve->svinst; + const char *userlog_notice = ""; + enum log_type log_level, user_log_level; + enum mail_error mail_error = MAIL_ERROR_NONE; + int ret = -1; + + *fatal_r = FALSE; + + log_level = user_log_level = LOG_TYPE_ERROR; + + if (estatus->last_storage != NULL && estatus->store_failed) { + mail_storage_get_last_error(estatus->last_storage, &mail_error); + + /* Don't bother administrator too much with benign errors */ + if (mail_error == MAIL_ERROR_NOQUOTA) { + log_level = LOG_TYPE_INFO; + user_log_level = LOG_TYPE_INFO; + } + } + + if (script == isrun->user_script && isrun->userlog != NULL) { + userlog_notice = t_strdup_printf( + " (user logfile %s may reveal additional details)", + isrun->userlog); + user_log_level = LOG_TYPE_INFO; + } + + switch (status) { + case SIEVE_EXEC_FAILURE: + e_log(sieve_get_event(svinst), user_log_level, + "Execution of script %s failed%s", + sieve_script_location(script), userlog_notice); + ret = 0; + break; + case SIEVE_EXEC_TEMP_FAILURE: + e_log(sieve_get_event(svinst), log_level, + "Execution of script %s was aborted " + "due to temporary failure%s", + sieve_script_location(script), userlog_notice); + *fatal_r = TRUE; + ret = -1; + break; + case SIEVE_EXEC_BIN_CORRUPT: + e_error(sieve_get_event(svinst), + "!!BUG!!: Binary compiled from %s is still corrupt; " + "bailing out and reverting to default action", + sieve_script_location(script)); + *fatal_r = TRUE; + ret = -1; + break; + case SIEVE_EXEC_RESOURCE_LIMIT: + e_error(sieve_get_event(svinst), + "Execution of script %s was aborted " + "due to excessive resource usage", + sieve_script_location(script)); + *fatal_r = TRUE; + ret = -1; + break; + case SIEVE_EXEC_KEEP_FAILED: + e_log(sieve_get_event(svinst), log_level, + "Execution of script %s failed " + "with unsuccessful implicit keep%s", + sieve_script_location(script), userlog_notice); + ret = 0; + break; + case SIEVE_EXEC_OK: + ret = (estatus->keep_original ? 0 : 1); + break; + } + + return ret; +} + +static int +imap_sieve_run_scripts(struct imap_sieve_run *isrun, + const struct sieve_message_data *msgdata, + const struct sieve_script_env *scriptenv, bool *fatal_r) +{ + struct imap_sieve *isieve = isrun->isieve; + struct sieve_instance *svinst = isieve->svinst; + struct imap_sieve_run_script *scripts = isrun->scripts; + unsigned int count = isrun->scripts_count; + struct sieve_resource_usage *rusage = + &scriptenv->exec_status->resource_usage; + struct sieve_multiscript *mscript; + struct sieve_error_handler *ehandler; + struct sieve_script *last_script = NULL; + bool user_script = FALSE, more = TRUE, rusage_exceeded = FALSE; + enum sieve_compile_flags cpflags; + enum sieve_execute_flags exflags; + enum sieve_error compile_error = SIEVE_ERROR_NONE; + unsigned int i; + int ret; + + *fatal_r = FALSE; + + /* Start execution */ + mscript = sieve_multiscript_start_execute(svinst, msgdata, scriptenv); + + /* Execute scripts */ + for (i = 0; i < count && more; i++) { + struct sieve_script *script = scripts[i].script; + struct sieve_binary *sbin = scripts[i].binary; + int mstatus; + + cpflags = 0; + exflags = SIEVE_EXECUTE_FLAG_NO_ENVELOPE | + SIEVE_EXECUTE_FLAG_SKIP_RESPONSES; + + user_script = (script == isrun->user_script); + last_script = script; + + if (scripts[i].rusage_exceeded) { + rusage_exceeded = TRUE; + break; + } + + sieve_resource_usage_init(rusage); + if (user_script) { + cpflags |= SIEVE_COMPILE_FLAG_NOGLOBAL; + exflags |= SIEVE_EXECUTE_FLAG_NOGLOBAL; + ehandler = isrun->user_ehandler; + } else { + cpflags |= SIEVE_COMPILE_FLAG_NO_ENVELOPE; + ehandler = isieve->master_ehandler; + } + + /* Open */ + if (sbin == NULL) { + e_debug(sieve_get_event(svinst), + "Opening script %d of %d from `%s'", + i+1, count, sieve_script_location(script)); + + /* Already known to fail */ + if (scripts[i].compile_error != SIEVE_ERROR_NONE) { + compile_error = scripts[i].compile_error; + break; + } + + /* Try to open/compile binary */ + scripts[i].binary = sbin = imap_sieve_run_open_script( + isrun, script, cpflags, FALSE, &compile_error); + if (sbin == NULL) { + scripts[i].compile_error = compile_error; + break; + } + } + + /* Execute */ + e_debug(sieve_get_event(svinst), + "Executing script from `%s'", + sieve_get_source(sbin)); + more = sieve_multiscript_run(mscript, sbin, ehandler, ehandler, + exflags); + + mstatus = sieve_multiscript_status(mscript); + if (!more && mstatus == SIEVE_EXEC_BIN_CORRUPT && + !scripts[i].binary_corrupt && sieve_is_loaded(sbin)) { + /* Close corrupt script */ + sieve_close(&sbin); + + /* Recompile */ + scripts[i].binary = sbin = + imap_sieve_run_open_script( + isrun, script, cpflags, FALSE, + &compile_error); + if (sbin == NULL) { + scripts[i].compile_error = compile_error; + break; + } + + /* Execute again */ + more = sieve_multiscript_run(mscript, sbin, + ehandler, ehandler, + exflags); + + /* Save new version */ + + mstatus = sieve_multiscript_status(mscript); + if (mstatus == SIEVE_EXEC_BIN_CORRUPT) + scripts[i].binary_corrupt = TRUE; + else if (more) + (void)sieve_save(sbin, FALSE, NULL); + } + + if (user_script && !sieve_record_resource_usage(sbin, rusage)) { + rusage_exceeded = ((i + 1) < count && more); + scripts[i].rusage_exceeded = TRUE; + break; + } + } + + /* Finish execution */ + exflags = SIEVE_EXECUTE_FLAG_NO_ENVELOPE | + SIEVE_EXECUTE_FLAG_SKIP_RESPONSES; + ehandler = (isrun->user_ehandler != NULL ? + isrun->user_ehandler : isieve->master_ehandler); + if (compile_error == SIEVE_ERROR_TEMP_FAILURE) { + ret = sieve_multiscript_finish(&mscript, ehandler, exflags, + SIEVE_EXEC_TEMP_FAILURE); + } else if (rusage_exceeded) { + i_assert(last_script != NULL); + (void)sieve_multiscript_finish(&mscript, ehandler, exflags, + SIEVE_EXEC_TEMP_FAILURE); + sieve_error(ehandler, sieve_script_name(last_script), + "cumulative resource usage limit exceeded"); + ret = SIEVE_EXEC_RESOURCE_LIMIT; + } else { + ret = sieve_multiscript_finish(&mscript, ehandler, exflags, + SIEVE_EXEC_OK); + } + + /* Don't log additional messages about compile failure */ + if (compile_error != SIEVE_ERROR_NONE && ret == SIEVE_EXEC_FAILURE) { + e_info(sieve_get_event(svinst), + "Aborted script execution sequence " + "with successful implicit keep"); + return 1; + } + + if (last_script == NULL && ret == SIEVE_EXEC_OK) + return 0; + return imap_sieve_handle_exec_status(isrun, last_script, ret, + scriptenv->exec_status, fatal_r); +} + +int imap_sieve_run_mail(struct imap_sieve_run *isrun, struct mail *mail, + const char *changed_flags, bool *fatal_r) +{ + struct imap_sieve *isieve = isrun->isieve; + struct sieve_instance *svinst = isieve->svinst; + struct mail_user *user = isieve->client->user; + struct sieve_message_data msgdata; + struct sieve_script_env scriptenv; + struct sieve_exec_status estatus; + struct imap_sieve_context context; + struct sieve_trace_config trace_config; + struct sieve_trace_log *trace_log; + const char *error; + int ret; + + *fatal_r = FALSE; + + i_zero(&context); + context.event.dest_mailbox = isrun->dest_mailbox; + context.event.src_mailbox = isrun->src_mailbox; + context.event.cause = isrun->cause; + context.event.changed_flags = changed_flags; + context.mail = mail; + context.isieve = isieve; + + /* Initialize trace logging */ + imap_sieve_run_init_trace_log(isrun, &trace_config, &trace_log); + + T_BEGIN { + if (trace_log != NULL) { + /* Write trace header for message */ + sieve_trace_log_printf(trace_log, + "Filtering message:\n" + "\n" + " UID: %u\n", mail->uid); + if (changed_flags != NULL && *changed_flags != '\0') { + sieve_trace_log_printf(trace_log, + " Changed flags: %s\n", changed_flags); + } + } + + /* Collect necessary message data */ + + i_zero(&msgdata); + msgdata.mail = mail; + msgdata.auth_user = user->username; + (void)mail_get_message_id(msgdata.mail, &msgdata.id); + + /* Compose script execution environment */ + + if (sieve_script_env_init(&scriptenv, user, &error) < 0) { + e_error(sieve_get_event(svinst), + "Failed to initialize script execution: %s", + error); + ret = -1; + } else { + scriptenv.default_mailbox = + mailbox_get_vname(mail->box); + scriptenv.smtp_start = imap_sieve_smtp_start; + scriptenv.smtp_add_rcpt = imap_sieve_smtp_add_rcpt; + scriptenv.smtp_send = imap_sieve_smtp_send; + scriptenv.smtp_abort = imap_sieve_smtp_abort; + scriptenv.smtp_finish = imap_sieve_smtp_finish; + scriptenv.duplicate_transaction_begin = + imap_sieve_duplicate_transaction_begin; + scriptenv.duplicate_transaction_commit = + imap_sieve_duplicate_transaction_commit; + scriptenv.duplicate_transaction_rollback = + imap_sieve_duplicate_transaction_rollback; + scriptenv.duplicate_mark = imap_sieve_duplicate_mark; + scriptenv.duplicate_check = imap_sieve_duplicate_check; + scriptenv.result_amend_log_message = + imap_sieve_result_amend_log_message; + scriptenv.trace_log = trace_log; + scriptenv.trace_config = trace_config; + scriptenv.script_context = &context; + + i_zero(&estatus); + scriptenv.exec_status = &estatus; + + /* Execute script(s) */ + + ret = imap_sieve_run_scripts(isrun, &msgdata, + &scriptenv, fatal_r); + } + } T_END; + + return ret; +} diff --git a/pigeonhole/src/plugins/imapsieve/imap-sieve.h b/pigeonhole/src/plugins/imapsieve/imap-sieve.h new file mode 100644 index 0000000..12d0ac7 --- /dev/null +++ b/pigeonhole/src/plugins/imapsieve/imap-sieve.h @@ -0,0 +1,59 @@ +#ifndef IMAP_SIEVE_H +#define IMAP_SIEVE_H + +struct client; + +/* + * IMAP event + */ + +struct imap_sieve_event { + struct mailbox *dest_mailbox, *src_mailbox; + const char *cause; + const char *changed_flags; +}; + +struct imap_sieve_context { + struct imap_sieve_event event; + struct mail *mail; + + struct imap_sieve *isieve; +}; + +static inline bool +imap_sieve_event_cause_valid(const char *cause) +{ + return (strcasecmp(cause, "APPEND") == 0 || + strcasecmp(cause, "COPY") == 0 || + strcasecmp(cause, "FLAG") == 0); +} + +/* + * IMAP Sieve + */ + +struct imap_sieve; + +struct imap_sieve *imap_sieve_init(struct client *client); +void imap_sieve_deinit(struct imap_sieve **_isieve); + +/* + * IMAP Sieve run + */ + +struct imap_sieve_run; + +int imap_sieve_run_init(struct imap_sieve *isieve, struct mailbox *dest_mailbox, + struct mailbox *src_mailbox, const char *cause, + const char *script_name, + const char *const *scripts_before, + const char *const *scripts_after, + struct imap_sieve_run **isrun_r) + ATTR_NULL(4, 5, 6); + +int imap_sieve_run_mail(struct imap_sieve_run *isrun, struct mail *mail, + const char *changed_flags, bool *fatal_r); + +void imap_sieve_run_deinit(struct imap_sieve_run **_isrun); + +#endif diff --git a/pigeonhole/src/plugins/imapsieve/sieve-imapsieve-plugin.c b/pigeonhole/src/plugins/imapsieve/sieve-imapsieve-plugin.c new file mode 100644 index 0000000..12611b3 --- /dev/null +++ b/pigeonhole/src/plugins/imapsieve/sieve-imapsieve-plugin.c @@ -0,0 +1,62 @@ +/* Copyright (c) 2016-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve.h" +#include "sieve-error.h" +#include "sieve-extensions.h" + +#include "ext-imapsieve-common.h" + +#include "sieve-imapsieve-plugin.h" + +/* + * Sieve plugin interface + */ + +struct _plugin_context { + const struct sieve_extension *ext_imapsieve; + const struct sieve_extension *ext_vnd_imapsieve; +}; + +const char *sieve_imapsieve_plugin_version = PIGEONHOLE_ABI_VERSION; + +void sieve_imapsieve_plugin_load +(struct sieve_instance *svinst, void **context) +{ + struct _plugin_context *pctx = i_new(struct _plugin_context, 1); + + pctx->ext_imapsieve = sieve_extension_register + (svinst, &imapsieve_extension_dummy, TRUE); + pctx->ext_vnd_imapsieve = sieve_extension_register + (svinst, &vnd_imapsieve_extension_dummy, TRUE); + + e_debug(sieve_get_event(svinst), + "Sieve imapsieve plugin for %s version %s loaded", + PIGEONHOLE_NAME, PIGEONHOLE_VERSION_FULL); + + *context = (void *)pctx; +} + +void sieve_imapsieve_plugin_unload +(struct sieve_instance *svinst ATTR_UNUSED, void *context) +{ + struct _plugin_context *pctx = (struct _plugin_context *)context; + + sieve_extension_unregister(pctx->ext_imapsieve); + sieve_extension_unregister(pctx->ext_vnd_imapsieve); + + i_free(pctx);} + +/* + * Module interface + */ + +void sieve_imapsieve_plugin_init(void) +{ + /* Nothing */ +} + +void sieve_imapsieve_plugin_deinit(void) +{ + /* Nothing */ +} diff --git a/pigeonhole/src/plugins/imapsieve/sieve-imapsieve-plugin.h b/pigeonhole/src/plugins/imapsieve/sieve-imapsieve-plugin.h new file mode 100644 index 0000000..fcf9777 --- /dev/null +++ b/pigeonhole/src/plugins/imapsieve/sieve-imapsieve-plugin.h @@ -0,0 +1,20 @@ +#ifndef SIEVE_IMAPSIEVE_PLUGIN_H +#define SIEVE_IMAPSIEVE_PLUGIN_H + +/* + * Plugin interface + */ + +void sieve_imapsieve_plugin_load + (struct sieve_instance *svinst, void **context); +void sieve_imapsieve_plugin_unload + (struct sieve_instance *svinst, void *context); + +/* + * Module interface + */ + +void sieve_imapsieve_plugin_init(void); +void sieve_imapsieve_plugin_deinit(void); + +#endif diff --git a/pigeonhole/src/plugins/lda-sieve/Makefile.am b/pigeonhole/src/plugins/lda-sieve/Makefile.am new file mode 100644 index 0000000..7c2797a --- /dev/null +++ b/pigeonhole/src/plugins/lda-sieve/Makefile.am @@ -0,0 +1,19 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib-sieve \ + $(LIBDOVECOT_INCLUDE) \ + $(LIBDOVECOT_DICT_INCLUDE) \ + $(LIBDOVECOT_SMTP_INCLUDE) \ + $(LIBDOVECOT_LDA_INCLUDE) + +lib90_sieve_plugin_la_LDFLAGS = -module -avoid-version + +dovecot_module_LTLIBRARIES = lib90_sieve_plugin.la + +lib90_sieve_plugin_la_LIBADD = \ + $(top_builddir)/src/lib-sieve/libdovecot-sieve.la + +lib90_sieve_plugin_la_SOURCES = \ + lda-sieve-plugin.c + +noinst_HEADERS = \ + lda-sieve-plugin.h diff --git a/pigeonhole/src/plugins/lda-sieve/Makefile.in b/pigeonhole/src/plugins/lda-sieve/Makefile.in new file mode 100644 index 0000000..6f0fba0 --- /dev/null +++ b/pigeonhole/src/plugins/lda-sieve/Makefile.in @@ -0,0 +1,746 @@ +# 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/plugins/lda-sieve +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 = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(dovecot_moduledir)" +LTLIBRARIES = $(dovecot_module_LTLIBRARIES) +lib90_sieve_plugin_la_DEPENDENCIES = \ + $(top_builddir)/src/lib-sieve/libdovecot-sieve.la +am_lib90_sieve_plugin_la_OBJECTS = lda-sieve-plugin.lo +lib90_sieve_plugin_la_OBJECTS = $(am_lib90_sieve_plugin_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 = +lib90_sieve_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(lib90_sieve_plugin_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +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)/lda-sieve-plugin.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 = $(lib90_sieve_plugin_la_SOURCES) +DIST_SOURCES = $(lib90_sieve_plugin_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@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib-sieve \ + $(LIBDOVECOT_INCLUDE) \ + $(LIBDOVECOT_DICT_INCLUDE) \ + $(LIBDOVECOT_SMTP_INCLUDE) \ + $(LIBDOVECOT_LDA_INCLUDE) + +lib90_sieve_plugin_la_LDFLAGS = -module -avoid-version +dovecot_module_LTLIBRARIES = lib90_sieve_plugin.la +lib90_sieve_plugin_la_LIBADD = \ + $(top_builddir)/src/lib-sieve/libdovecot-sieve.la + +lib90_sieve_plugin_la_SOURCES = \ + lda-sieve-plugin.c + +noinst_HEADERS = \ + lda-sieve-plugin.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/plugins/lda-sieve/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/plugins/lda-sieve/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): + +install-dovecot_moduleLTLIBRARIES: $(dovecot_module_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(dovecot_module_LTLIBRARIES)'; test -n "$(dovecot_moduledir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(dovecot_moduledir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(dovecot_moduledir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(dovecot_moduledir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(dovecot_moduledir)"; \ + } + +uninstall-dovecot_moduleLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(dovecot_module_LTLIBRARIES)'; test -n "$(dovecot_moduledir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(dovecot_moduledir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(dovecot_moduledir)/$$f"; \ + done + +clean-dovecot_moduleLTLIBRARIES: + -test -z "$(dovecot_module_LTLIBRARIES)" || rm -f $(dovecot_module_LTLIBRARIES) + @list='$(dovecot_module_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}; \ + } + +lib90_sieve_plugin.la: $(lib90_sieve_plugin_la_OBJECTS) $(lib90_sieve_plugin_la_DEPENDENCIES) $(EXTRA_lib90_sieve_plugin_la_DEPENDENCIES) + $(AM_V_CCLD)$(lib90_sieve_plugin_la_LINK) -rpath $(dovecot_moduledir) $(lib90_sieve_plugin_la_OBJECTS) $(lib90_sieve_plugin_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lda-sieve-plugin.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: + for dir in "$(DESTDIR)$(dovecot_moduledir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-dovecot_moduleLTLIBRARIES clean-generic clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/lda-sieve-plugin.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-dovecot_moduleLTLIBRARIES + +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)/lda-sieve-plugin.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-dovecot_moduleLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-dovecot_moduleLTLIBRARIES clean-generic clean-libtool \ + 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-dovecot_moduleLTLIBRARIES \ + 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 uninstall-dovecot_moduleLTLIBRARIES + +.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/plugins/lda-sieve/lda-sieve-plugin.c b/pigeonhole/src/plugins/lda-sieve/lda-sieve-plugin.c new file mode 100644 index 0000000..4e79c1f --- /dev/null +++ b/pigeonhole/src/plugins/lda-sieve/lda-sieve-plugin.c @@ -0,0 +1,1128 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "array.h" +#include "home-expand.h" +#include "var-expand.h" +#include "eacces-error.h" +#include "smtp-address.h" +#include "smtp-submit.h" +#include "mail-storage.h" +#include "mail-deliver.h" +#include "mail-user.h" +#include "mail-duplicate.h" +#include "smtp-submit.h" +#include "mail-send.h" +#include "iostream-ssl.h" +#include "lda-settings.h" + +#include "sieve.h" +#include "sieve-script.h" +#include "sieve-storage.h" + +#include "lda-sieve-plugin.h" + +#include <sys/stat.h> +#include <dirent.h> + +/* + * Configuration + */ + +#define LDA_SIEVE_DEFAULT_LOCATION "~/.dovecot.sieve" + +#define LDA_SIEVE_MAX_USER_ERRORS 30 + +/* + * Global variables + */ + +static deliver_mail_func_t *next_deliver_mail; + +/* + * Settings handling + */ + +static const char * +lda_sieve_get_setting(void *context, const char *identifier) +{ + struct mail_deliver_context *mdctx = + (struct mail_deliver_context *)context; + const char *value = NULL; + + if (mdctx == NULL) + return NULL; + + if (mdctx->rcpt_user == NULL || + (value = mail_user_plugin_getenv( + mdctx->rcpt_user, identifier)) == NULL) { + if (strcmp(identifier, "recipient_delimiter") == 0) + value = mdctx->set->recipient_delimiter; + } + + return value; +} + +static const struct sieve_callbacks lda_sieve_callbacks = { + NULL, + lda_sieve_get_setting +}; + +/* + * Mail transmission + */ + +static void * +lda_sieve_smtp_start(const struct sieve_script_env *senv, + const struct smtp_address *mail_from) +{ + struct mail_deliver_context *dctx = + (struct mail_deliver_context *)senv->script_context; + struct mail_user *user = dctx->rcpt_user; + struct ssl_iostream_settings ssl_set; + struct smtp_submit_input submit_input; + + i_zero(&ssl_set); + mail_user_init_ssl_client_settings(user, &ssl_set); + + i_zero(&submit_input); + submit_input.ssl = &ssl_set; + + return (void *)smtp_submit_init_simple(&submit_input, dctx->smtp_set, + mail_from); +} + +static void +lda_sieve_smtp_add_rcpt(const struct sieve_script_env *senv ATTR_UNUSED, + void *handle, const struct smtp_address *rcpt_to) +{ + struct smtp_submit *smtp_submit = (struct smtp_submit *) handle; + + smtp_submit_add_rcpt(smtp_submit, rcpt_to); +} + +static struct ostream * +lda_sieve_smtp_send(const struct sieve_script_env *senv ATTR_UNUSED, + void *handle) +{ + struct smtp_submit *smtp_submit = (struct smtp_submit *) handle; + + return smtp_submit_send(smtp_submit); +} + +static void +lda_sieve_smtp_abort(const struct sieve_script_env *senv ATTR_UNUSED, + void *handle) +{ + struct smtp_submit *smtp_submit = (struct smtp_submit *) handle; + + smtp_submit_deinit(&smtp_submit); +} + +static int +lda_sieve_smtp_finish(const struct sieve_script_env *senv ATTR_UNUSED, + void *handle, const char **error_r) +{ + struct smtp_submit *smtp_submit = (struct smtp_submit *) handle; + int ret; + + ret = smtp_submit_run(smtp_submit, error_r); + smtp_submit_deinit(&smtp_submit); + return ret; +} + +static int +lda_sieve_reject_mail(const struct sieve_script_env *senv, + const struct smtp_address *recipient, + const char *reason) +{ + struct mail_deliver_context *dctx = + (struct mail_deliver_context *)senv->script_context; + + return mail_send_rejection(dctx, recipient, reason); +} + +/* + * Duplicate checking + */ + +static void * +lda_sieve_duplicate_transaction_begin(const struct sieve_script_env *senv) +{ + struct mail_deliver_context *dctx = + (struct mail_deliver_context *)senv->script_context; + + return mail_duplicate_transaction_begin(dctx->dup_db); +} + +static void lda_sieve_duplicate_transaction_commit(void **_dup_trans) +{ + struct mail_duplicate_transaction *dup_trans = *_dup_trans; + + *_dup_trans = NULL; + mail_duplicate_transaction_commit(&dup_trans); +} + +static void lda_sieve_duplicate_transaction_rollback(void **_dup_trans) +{ + struct mail_duplicate_transaction *dup_trans = *_dup_trans; + + *_dup_trans = NULL; + mail_duplicate_transaction_rollback(&dup_trans); +} + +static enum sieve_duplicate_check_result +lda_sieve_duplicate_check(void *_dup_trans, const struct sieve_script_env *senv, + const void *id, size_t id_size) +{ + struct mail_duplicate_transaction *dup_trans = _dup_trans; + + switch (mail_duplicate_check(dup_trans, id, id_size, + senv->user->username)) { + case MAIL_DUPLICATE_CHECK_RESULT_EXISTS: + return SIEVE_DUPLICATE_CHECK_RESULT_EXISTS; + case MAIL_DUPLICATE_CHECK_RESULT_NOT_FOUND: + return SIEVE_DUPLICATE_CHECK_RESULT_NOT_FOUND; + case MAIL_DUPLICATE_CHECK_RESULT_DEADLOCK: + case MAIL_DUPLICATE_CHECK_RESULT_LOCK_TIMEOUT: + return SIEVE_DUPLICATE_CHECK_RESULT_TEMP_FAILURE; + case MAIL_DUPLICATE_CHECK_RESULT_IO_ERROR: + case MAIL_DUPLICATE_CHECK_RESULT_TOO_MANY_LOCKS: + break; + } + return SIEVE_DUPLICATE_CHECK_RESULT_FAILURE; +} + +static void +lda_sieve_duplicate_mark(void *_dup_trans, const struct sieve_script_env *senv, + const void *id, size_t id_size, time_t time) +{ + struct mail_duplicate_transaction *dup_trans = _dup_trans; + + mail_duplicate_mark(dup_trans, id, id_size, senv->user->username, time); +} + +/* + * Result logging + */ + +static const char * +lda_sieve_result_amend_log_message(const struct sieve_script_env *senv, + enum log_type log_type ATTR_UNUSED, + const char *message) +{ + struct mail_deliver_context *mdctx = senv->script_context; + const struct var_expand_table *table; + string_t *str; + const char *error; + + table = mail_deliver_ctx_get_log_var_expand_table(mdctx, message); + + str = t_str_new(256); + if (var_expand(str, mdctx->set->deliver_log_format, + table, &error) <= 0) { + e_error(mdctx->event, + "Failed to expand deliver_log_format=%s: %s", + mdctx->set->deliver_log_format, error); + } + return str_c(str); +} + +/* + * Plugin implementation + */ + +struct lda_sieve_run_context { + struct sieve_instance *svinst; + + struct mail_deliver_context *mdctx; + const char *home_dir; + + struct sieve_script **scripts; + unsigned int script_count; + + struct sieve_script *user_script; + struct sieve_script *main_script; + struct sieve_script *discard_script; + + const struct sieve_message_data *msgdata; + const struct sieve_script_env *scriptenv; + + struct sieve_error_handler *user_ehandler; + struct sieve_error_handler *master_ehandler; + struct sieve_error_handler *action_ehandler; + const char *userlog; +}; + +static int +lda_sieve_get_personal_storage(struct sieve_instance *svinst, + struct mail_user *user, + struct sieve_storage **storage_r, + enum sieve_error *error_r) +{ + *storage_r = sieve_storage_create_main(svinst, user, 0, error_r); + if (*storage_r == NULL) { + switch (*error_r) { + case SIEVE_ERROR_NOT_POSSIBLE: + case SIEVE_ERROR_NOT_FOUND: + break; + case SIEVE_ERROR_TEMP_FAILURE: + e_error(sieve_get_event(svinst), + "Failed to access user's personal storage " + "(temporary failure)"); + return -1; + default: + e_error(sieve_get_event(svinst), + "Failed to access user's personal storage"); + break; + } + return 0; + } + return 1; +} + +static int +lda_sieve_multiscript_get_scripts(struct sieve_instance *svinst, + const char *label, const char *location, + ARRAY_TYPE(sieve_script) *scripts, + enum sieve_error *error_r) +{ + struct sieve_script_sequence *seq; + struct sieve_script *script; + bool finished = FALSE; + int ret = 1; + + seq = sieve_script_sequence_create(svinst, location, error_r); + if (seq == NULL) + return (*error_r == SIEVE_ERROR_NOT_FOUND ? 0 : -1); + + while (ret > 0 && !finished) { + script = sieve_script_sequence_next(seq, error_r); + if (script == NULL) { + switch (*error_r) { + case SIEVE_ERROR_NONE: + finished = TRUE; + break; + case SIEVE_ERROR_TEMP_FAILURE: + e_error(sieve_get_event(svinst), + "Failed to access %s script from `%s' " + "(temporary failure)", + label, location); + ret = -1; + default: + break; + } + continue; + } + + array_append(scripts, &script, 1); + } + + sieve_script_sequence_free(&seq); + return ret; +} + +static void +lda_sieve_binary_save(struct lda_sieve_run_context *srctx, + struct sieve_binary *sbin, struct sieve_script *script) +{ + enum sieve_error error; + + /* Save binary when compiled */ + if (sieve_save(sbin, FALSE, &error) < 0 && + error == SIEVE_ERROR_NO_PERMISSION && + script != srctx->user_script) { + /* Cannot save binary for global script */ + e_error(sieve_get_event(srctx->svinst), + "The LDA Sieve plugin does not have permission " + "to save global Sieve script binaries; " + "global Sieve scripts like `%s' need to be " + "pre-compiled using the sievec tool", + sieve_script_location(script)); + } +} + +static struct +sieve_binary *lda_sieve_open(struct lda_sieve_run_context *srctx, + struct sieve_script *script, + enum sieve_compile_flags cpflags, bool recompile, + enum sieve_error *error_r) +{ + struct sieve_instance *svinst = srctx->svinst; + struct sieve_error_handler *ehandler; + struct sieve_binary *sbin; + const char *compile_name = "compile"; + + if (recompile) { + /* Warn */ + e_warning(sieve_get_event(svinst), + "Encountered corrupt binary: re-compiling script %s", + sieve_script_location(script)); + compile_name = "re-compile"; + } else { + e_debug(sieve_get_event(svinst), + "Loading script %s", sieve_script_location(script)); + } + + if (script == srctx->user_script) + ehandler = srctx->user_ehandler; + else + ehandler = srctx->master_ehandler; + + sieve_error_handler_reset(ehandler); + + if (recompile) + sbin = sieve_compile_script(script, ehandler, cpflags, error_r); + else + sbin = sieve_open_script(script, ehandler, cpflags, error_r); + + /* Load or compile the sieve script */ + if (sbin == NULL) { + switch (*error_r) { + /* Script not found */ + case SIEVE_ERROR_NOT_FOUND: + e_debug(sieve_get_event(svinst), + "Script `%s' is missing for %s", + sieve_script_location(script), + compile_name); + break; + /* Temporary failure */ + case SIEVE_ERROR_TEMP_FAILURE: + e_error(sieve_get_event(svinst), + "Failed to open script `%s' for %s " + "(temporary failure)", + sieve_script_location(script), compile_name); + break; + /* Compile failed */ + case SIEVE_ERROR_NOT_VALID: + if (script == srctx->user_script && + srctx->userlog != NULL ) { + e_info(sieve_get_event(svinst), + "Failed to %s script `%s' " + "(view user logfile `%s' for more information)", + compile_name, + sieve_script_location(script), + srctx->userlog); + break; + } + e_error(sieve_get_event(svinst), + "Failed to %s script `%s'", + compile_name, sieve_script_location(script)); + break; + /* Cumulative resource limit exceeded */ + case SIEVE_ERROR_RESOURCE_LIMIT: + e_error(sieve_get_event(svinst), + "Failed to open script `%s' for %s " + "(cumulative resource limit exceeded)", + sieve_script_location(script), compile_name); + break; + /* Something else */ + default: + e_error(sieve_get_event(svinst), + "Failed to open script `%s' for %s", + sieve_script_location(script), compile_name); + break; + } + + return NULL; + } + + if (!recompile) + lda_sieve_binary_save(srctx, sbin, script); + return sbin; +} + +static int +lda_sieve_handle_exec_status(struct lda_sieve_run_context *srctx, + struct sieve_script *script, int status) +{ + struct sieve_instance *svinst = srctx->svinst; + struct mail_deliver_context *mdctx = srctx->mdctx; + struct sieve_exec_status *estatus = srctx->scriptenv->exec_status; + const char *userlog_notice = ""; + enum log_type log_level, user_log_level; + enum mail_error mail_error = MAIL_ERROR_NONE; + int ret; + + log_level = user_log_level = LOG_TYPE_ERROR; + + if (estatus != NULL && estatus->last_storage != NULL && + estatus->store_failed) { + mail_storage_get_last_error(estatus->last_storage, &mail_error); + + /* Don't bother administrator too much with benign errors */ + if (mail_error == MAIL_ERROR_NOQUOTA) { + log_level = LOG_TYPE_INFO; + user_log_level = LOG_TYPE_INFO; + } + } + + if (script == srctx->user_script && srctx->userlog != NULL) { + userlog_notice = t_strdup_printf( + " (user logfile %s may reveal additional details)", + srctx->userlog); + user_log_level = LOG_TYPE_INFO; + } + + switch (status) { + case SIEVE_EXEC_FAILURE: + e_log(sieve_get_event(svinst), user_log_level, + "Execution of script %s failed, " + "but implicit keep was successful%s", + sieve_script_location(script), userlog_notice); + ret = 1; + break; + case SIEVE_EXEC_TEMP_FAILURE: + e_log(sieve_get_event(svinst), log_level, + "Execution of script %s was aborted due to temporary failure%s", + sieve_script_location(script), userlog_notice); + if (mail_error != MAIL_ERROR_TEMP && + mdctx->tempfail_error == NULL) { + mdctx->tempfail_error = + "Execution of Sieve filters was aborted due to temporary failure"; + } + ret = -1; + break; + case SIEVE_EXEC_BIN_CORRUPT: + e_error(sieve_get_event(svinst), + "!!BUG!!: Binary compiled from %s is still corrupt; " + "bailing out and reverting to default delivery", + sieve_script_location(script)); + ret = -1; + break; + case SIEVE_EXEC_RESOURCE_LIMIT: + e_error(sieve_get_event(svinst), + "Execution of script %s was aborted " + "due to excessive resource usage", + sieve_script_location(script)); + ret = -1; + break; + case SIEVE_EXEC_KEEP_FAILED: + e_log(sieve_get_event(svinst), log_level, + "Execution of script %s failed with unsuccessful implicit keep%s", + sieve_script_location(script), userlog_notice); + ret = -1; + break; + default: + ret = status > 0 ? 1 : -1; + break; + } + + return ret; +} + +static int +lda_sieve_execute_script(struct lda_sieve_run_context *srctx, + struct sieve_multiscript *mscript, + struct sieve_script *script, + unsigned int index, bool discard_script, + enum sieve_error *error_r) +{ + struct sieve_instance *svinst = srctx->svinst; + struct sieve_error_handler *exec_ehandler; + struct sieve_binary *sbin = NULL; + enum sieve_compile_flags cpflags = 0; + enum sieve_execute_flags exflags = SIEVE_EXECUTE_FLAG_LOG_RESULT; + struct sieve_resource_usage *rusage = + &srctx->scriptenv->exec_status->resource_usage; + bool user_script; + int mstatus, ret; + + *error_r = SIEVE_ERROR_NONE; + + user_script = (script == srctx->user_script); + + sieve_resource_usage_init(rusage); + if (user_script) { + cpflags |= SIEVE_COMPILE_FLAG_NOGLOBAL; + exflags |= SIEVE_EXECUTE_FLAG_NOGLOBAL; + exec_ehandler = srctx->user_ehandler; + } else { + exec_ehandler = srctx->master_ehandler; + } + + /* Open */ + + if (!discard_script) { + e_debug(sieve_get_event(svinst), + "Opening script %d of %d from `%s'", + index, srctx->script_count, + sieve_script_location(script)); + } else { + e_debug(sieve_get_event(svinst), + "Opening discard script from `%s'", + sieve_script_location(script)); + } + + sbin = lda_sieve_open(srctx, script, cpflags, FALSE, error_r); + if (sbin == NULL) + return 0; + + /* Execute */ + + e_debug(sieve_get_event(svinst), + "Executing script from `%s'", + sieve_get_source(sbin)); + + if (!discard_script) { + ret = (sieve_multiscript_run(mscript, sbin, exec_ehandler, + exec_ehandler, exflags) ? 1 : 0); + } else { + sieve_multiscript_run_discard(mscript, sbin, exec_ehandler, + exec_ehandler, exflags); + ret = 0; + } + + mstatus = sieve_multiscript_status(mscript); + if (ret == 0 && mstatus == SIEVE_EXEC_BIN_CORRUPT && + sieve_is_loaded(sbin)) { + /* Close corrupt script */ + + sieve_close(&sbin); + + /* Recompile */ + + sbin = lda_sieve_open(srctx, script, cpflags, TRUE, + error_r); + if (sbin == NULL) + return 0; + + /* Execute again */ + + if (!discard_script) { + ret = (sieve_multiscript_run( + mscript, sbin, exec_ehandler, + exec_ehandler, exflags) ? 1 : 0); + } else { + sieve_multiscript_run_discard( + mscript, sbin, exec_ehandler, + exec_ehandler, exflags); + } + + /* Save new version */ + + mstatus = sieve_multiscript_status(mscript); + if (mstatus != SIEVE_EXEC_BIN_CORRUPT) + lda_sieve_binary_save(srctx, sbin, script); + } + if (ret == 0 && mstatus == SIEVE_EXEC_RESOURCE_LIMIT) + ret = -1; + + if (user_script) + (void)sieve_record_resource_usage(sbin, rusage); + + sieve_close(&sbin); + + return ret; +} + +static int lda_sieve_execute_scripts(struct lda_sieve_run_context *srctx) +{ + struct sieve_instance *svinst = srctx->svinst; + struct sieve_multiscript *mscript; + struct sieve_error_handler *exec_ehandler; + struct sieve_script *script, *last_script = NULL; + enum sieve_execute_flags exflags = SIEVE_EXECUTE_FLAG_LOG_RESULT; + bool discard_script; + enum sieve_error error; + unsigned int i; + int ret; + + i_assert(srctx->script_count > 0); + + /* Start execution */ + + mscript = sieve_multiscript_start_execute(svinst, srctx->msgdata, + srctx->scriptenv); + + /* Execute scripts */ + + i = 0; + discard_script = FALSE; + error = SIEVE_ERROR_NONE; + for (;;) { + if (!discard_script) { + /* normal script sequence */ + i_assert(i < srctx->script_count); + script = srctx->scripts[i]; + i++; + } else { + /* discard script */ + script = srctx->discard_script; + } + + i_assert(script != NULL); + last_script = script; + + ret = lda_sieve_execute_script(srctx, mscript, script, i, + discard_script, &error); + if (ret < 0) + break; + if (error == SIEVE_ERROR_NOT_FOUND) { + /* skip scripts which finally turn out not to exist */ + ret = 1; + } + + if (discard_script) { + /* Executed discard script, which is always final */ + break; + } else if (ret > 0) { + /* The "keep" action is applied; execute next script */ + i_assert(i <= srctx->script_count); + if (i == srctx->script_count) { + /* End of normal script sequence */ + break; + } + } else if (error != SIEVE_ERROR_NONE) { + break; + } else if (sieve_multiscript_will_discard(mscript) && + srctx->discard_script != NULL) { + /* Mail is set to be discarded, but we have a discard script. */ + discard_script = TRUE; + } else { + break; + } + } + + /* Finish execution */ + exec_ehandler = (srctx->user_ehandler != NULL ? + srctx->user_ehandler : srctx->master_ehandler); + ret = sieve_multiscript_finish(&mscript, exec_ehandler, exflags, + (error == SIEVE_ERROR_TEMP_FAILURE ? + SIEVE_EXEC_TEMP_FAILURE : + SIEVE_EXEC_OK)); + + /* Don't log additional messages about compile failure */ + if (error != SIEVE_ERROR_NONE && ret == SIEVE_EXEC_FAILURE) { + e_info(sieve_get_event(svinst), + "Aborted script execution sequence with successful implicit keep"); + return 1; + } + + return lda_sieve_handle_exec_status(srctx, last_script, ret); +} + +static int lda_sieve_find_scripts(struct lda_sieve_run_context *srctx) +{ + struct mail_deliver_context *mdctx = srctx->mdctx; + struct sieve_instance *svinst = srctx->svinst; + struct sieve_storage *main_storage; + const char *sieve_before, *sieve_after, *sieve_discard; + const char *setting_name; + enum sieve_error error; + ARRAY_TYPE(sieve_script) script_sequence; + struct sieve_script *const *scripts; + unsigned int after_index, count, i; + int ret = 1; + + /* Find the personal script to execute */ + + ret = lda_sieve_get_personal_storage(svinst, mdctx->rcpt_user, + &main_storage, &error); + if (ret == 0 && error == SIEVE_ERROR_NOT_POSSIBLE) + return 0; + if (ret > 0) { + srctx->main_script = + sieve_storage_active_script_open(main_storage, &error); + + if (srctx->main_script == NULL) { + switch (error) { + case SIEVE_ERROR_NOT_FOUND: + e_debug(sieve_get_event(svinst), + "User has no active script in storage `%s'", + sieve_storage_location(main_storage)); + break; + case SIEVE_ERROR_TEMP_FAILURE: + e_error(sieve_get_event(svinst), + "Failed to access active Sieve script in user storage `%s' " + "(temporary failure)", + sieve_storage_location(main_storage)); + ret = -1; + break; + default: + e_error(sieve_get_event(svinst), + "Failed to access active Sieve script in user storage `%s'", + sieve_storage_location(main_storage)); + break; + } + } else if (!sieve_script_is_default(srctx->main_script)) { + srctx->user_script = srctx->main_script; + } + sieve_storage_unref(&main_storage); + } + + if (ret >= 0 && srctx->main_script == NULL) { + e_debug(sieve_get_event(svinst), + "User has no personal script"); + } + + /* Compose script array */ + + t_array_init(&script_sequence, 16); + + /* before */ + if (ret >= 0) { + i = 2; + setting_name = "sieve_before"; + sieve_before = mail_user_plugin_getenv( + mdctx->rcpt_user, setting_name); + while (ret >= 0 && + sieve_before != NULL && *sieve_before != '\0') { + ret = lda_sieve_multiscript_get_scripts( + svinst, setting_name, sieve_before, + &script_sequence, &error); + if (ret < 0 && error == SIEVE_ERROR_TEMP_FAILURE) { + ret = -1; + break; + } else if (ret == 0) { + e_debug(sieve_get_event(svinst), + "Location for %s not found: %s", + setting_name, sieve_before); + } + ret = 0; + setting_name = t_strdup_printf("sieve_before%u", i++); + sieve_before = mail_user_plugin_getenv( + mdctx->rcpt_user, setting_name); + } + + if (ret >= 0) { + scripts = array_get(&script_sequence, &count); + for (i = 0; i < count; i ++) { + e_debug(sieve_get_event(svinst), + "Executed before user's personal Sieve script(%d): %s", + i+1, sieve_script_location(scripts[i])); + } + } + } + + /* main */ + if (srctx->main_script != NULL) { + array_append(&script_sequence, &srctx->main_script, 1); + + if (ret >= 0) { + e_debug(sieve_get_event(svinst), + "Using the following location for user's Sieve script: %s", + sieve_script_location(srctx->main_script)); + } + } + + after_index = array_count(&script_sequence); + + /* after */ + if (ret >= 0) { + i = 2; + setting_name = "sieve_after"; + sieve_after = mail_user_plugin_getenv(mdctx->rcpt_user, setting_name); + while (sieve_after != NULL && *sieve_after != '\0') { + ret = lda_sieve_multiscript_get_scripts( + svinst, setting_name, sieve_after, + &script_sequence, &error); + if (ret < 0 && error == SIEVE_ERROR_TEMP_FAILURE) { + ret = -1; + break; + } else if (ret == 0) { + e_debug(sieve_get_event(svinst), + "Location for %s not found: %s", + setting_name, sieve_after); + } + ret = 0; + setting_name = t_strdup_printf("sieve_after%u", i++); + sieve_after = mail_user_plugin_getenv( + mdctx->rcpt_user, setting_name); + } + + if (ret >= 0) { + scripts = array_get(&script_sequence, &count); + for ( i = after_index; i < count; i ++ ) { + e_debug(sieve_get_event(svinst), + "executed after user's Sieve script(%d): %s", + i+1, sieve_script_location(scripts[i])); + } + } + } + + /* discard */ + sieve_discard = mail_user_plugin_getenv( + mdctx->rcpt_user, "sieve_discard"); + if (sieve_discard != NULL && *sieve_discard != '\0') { + srctx->discard_script = sieve_script_create_open( + svinst, sieve_discard, NULL, &error); + if (srctx->discard_script == NULL) { + switch (error) { + case SIEVE_ERROR_NOT_FOUND: + e_debug(sieve_get_event(svinst), + "Location for sieve_discard not found: %s", + sieve_discard); + break; + case SIEVE_ERROR_TEMP_FAILURE: + ret = -1; + break; + default: + break; + } + } + } + + if (ret < 0) { + mdctx->tempfail_error = + "Temporarily unable to access necessary Sieve scripts"; + } + srctx->scripts = + array_get_modifiable(&script_sequence, &srctx->script_count); + return ret; +} + +static void +lda_sieve_free_scripts(struct lda_sieve_run_context *srctx) +{ + unsigned int i; + + for (i = 0; i < srctx->script_count; i++) + sieve_script_unref(&srctx->scripts[i]); + if (srctx->discard_script != NULL) + sieve_script_unref(&srctx->discard_script); +} + +static void +lda_sieve_init_trace_log(struct lda_sieve_run_context *srctx, + const struct smtp_address *mail_from, + struct sieve_trace_config *trace_config_r, + struct sieve_trace_log **trace_log_r) +{ + struct mail_deliver_context *mdctx = srctx->mdctx; + struct sieve_instance *svinst = srctx->svinst; + struct sieve_trace_log *trace_log = NULL; + + if (sieve_trace_config_get(svinst, trace_config_r) < 0 || + sieve_trace_log_open(svinst, &trace_log) < 0) { + i_zero(trace_config_r); + *trace_log_r = NULL; + return; + } + + /* Write header for trace file */ + sieve_trace_log_printf(trace_log, + "Sieve trace log for message delivery:\n" + "\n" + " Username: %s\n", mdctx->rcpt_user->username); + if (mdctx->rcpt_user->session_id != NULL) { + sieve_trace_log_printf(trace_log, + " Session ID: %s\n", + mdctx->rcpt_user->session_id); + } + sieve_trace_log_printf(trace_log, + " Sender: %s\n" + " Final recipient: %s\n" + " Default mailbox: %s\n\n", + smtp_address_encode_path(mail_from), + smtp_address_encode_path(mdctx->rcpt_to), + (mdctx->rcpt_default_mailbox != NULL ? + mdctx->rcpt_default_mailbox : "INBOX")); + + *trace_log_r = trace_log; +} + +static int +lda_sieve_execute(struct lda_sieve_run_context *srctx, + struct mail_storage **storage_r) +{ + struct mail_deliver_context *mdctx = srctx->mdctx; + struct sieve_instance *svinst = srctx->svinst; + struct sieve_message_data msgdata; + struct sieve_script_env scriptenv; + struct sieve_exec_status estatus; + struct sieve_trace_config trace_config; + const struct smtp_address *mail_from; + struct sieve_trace_log *trace_log; + const char *error; + int ret; + + /* Check whether there are any scripts to execute at all */ + + if (srctx->script_count == 0) { + e_debug(sieve_get_event(svinst), + "No scripts to execute: " + "reverting to default delivery."); + + /* No error, but no delivery by this plugin either. A return + value of <= 0 for a deliver plugin is is considered a + failure. In deliver itself, saved_mail and tried_default_save + remain unset, meaning that deliver will then attempt the + default delivery. We return 0 to signify the lack of a real + error. + */ + return 0; + } + + /* Initialize user error handler */ + + if (srctx->user_script != NULL) { + const char *log_path = + sieve_user_get_log_path(svinst, srctx->user_script); + + if (log_path != NULL) { + srctx->userlog = log_path; + srctx->user_ehandler = sieve_logfile_ehandler_create( + svinst, srctx->userlog, + LDA_SIEVE_MAX_USER_ERRORS); + } + } + + /* Determine return address */ + + mail_from = mail_deliver_get_return_address(mdctx); + + /* Initialize trace logging */ + + lda_sieve_init_trace_log(srctx, mail_from, &trace_config, &trace_log); + + /* Collect necessary message data */ + + i_zero(&msgdata); + + msgdata.mail = mdctx->src_mail; + msgdata.auth_user = mdctx->rcpt_user->username; + msgdata.envelope.mail_from = mail_from; + msgdata.envelope.mail_params = &mdctx->mail_params; + msgdata.envelope.rcpt_to = mdctx->rcpt_to; + msgdata.envelope.rcpt_params = &mdctx->rcpt_params; + (void)mail_get_message_id(msgdata.mail, &msgdata.id); + + srctx->msgdata = &msgdata; + + /* Compose script execution environment */ + + if (sieve_script_env_init(&scriptenv, mdctx->rcpt_user, &error) < 0) { + e_error(sieve_get_event(svinst), + "Failed to initialize script execution: %s", error); + if (trace_log != NULL) + sieve_trace_log_free(&trace_log); + return -1; + } + + scriptenv.default_mailbox = mdctx->rcpt_default_mailbox; + scriptenv.mailbox_autocreate = mdctx->set->lda_mailbox_autocreate; + scriptenv.mailbox_autosubscribe = mdctx->set->lda_mailbox_autosubscribe; + scriptenv.smtp_start = lda_sieve_smtp_start; + scriptenv.smtp_add_rcpt = lda_sieve_smtp_add_rcpt; + scriptenv.smtp_send = lda_sieve_smtp_send; + scriptenv.smtp_abort = lda_sieve_smtp_abort; + scriptenv.smtp_finish = lda_sieve_smtp_finish; + scriptenv.duplicate_transaction_begin = + lda_sieve_duplicate_transaction_begin; + scriptenv.duplicate_transaction_commit = + lda_sieve_duplicate_transaction_commit; + scriptenv.duplicate_transaction_rollback = + lda_sieve_duplicate_transaction_rollback; + scriptenv.duplicate_mark = lda_sieve_duplicate_mark; + scriptenv.duplicate_check = lda_sieve_duplicate_check; + scriptenv.reject_mail = lda_sieve_reject_mail; + scriptenv.result_amend_log_message = lda_sieve_result_amend_log_message; + scriptenv.script_context = (void *) mdctx; + scriptenv.trace_log = trace_log; + scriptenv.trace_config = trace_config; + + i_zero(&estatus); + scriptenv.exec_status = &estatus; + + srctx->scriptenv = &scriptenv; + + /* Execute script(s) */ + + ret = lda_sieve_execute_scripts(srctx); + + /* Record status */ + + mdctx->tried_default_save = estatus.tried_default_save; + *storage_r = estatus.last_storage; + + if (trace_log != NULL) + sieve_trace_log_free(&trace_log); + + return ret; +} + +static int +lda_sieve_deliver_mail(struct mail_deliver_context *mdctx, + struct mail_storage **storage_r) +{ + struct lda_sieve_run_context srctx; + const struct mail_storage_settings *mail_set = + mail_user_set_get_storage_set(mdctx->rcpt_user); + bool debug = mdctx->rcpt_user->mail_debug; + struct sieve_environment svenv; + int ret = 0; + + /* Initialize run context */ + + i_zero(&srctx); + srctx.mdctx = mdctx; + (void)mail_user_get_home(mdctx->rcpt_user, &srctx.home_dir); + + /* Initialize Sieve engine */ + + memset((void*)&svenv, 0, sizeof(svenv)); + svenv.username = mdctx->rcpt_user->username; + svenv.home_dir = srctx.home_dir; + svenv.hostname = mail_set->hostname; + svenv.base_dir = mdctx->rcpt_user->set->base_dir; + svenv.temp_dir = mdctx->rcpt_user->set->mail_temp_dir; + svenv.event_parent = mdctx->event; + svenv.flags = SIEVE_FLAG_HOME_RELATIVE; + svenv.location = SIEVE_ENV_LOCATION_MDA; + svenv.delivery_phase = SIEVE_DELIVERY_PHASE_DURING; + + srctx.svinst = sieve_init(&svenv, &lda_sieve_callbacks, mdctx, debug); + + /* Initialize master error handler */ + + srctx.master_ehandler = sieve_master_ehandler_create(srctx.svinst, 0); + + sieve_error_handler_accept_infolog(srctx.master_ehandler, TRUE); + sieve_error_handler_accept_debuglog(srctx.master_ehandler, debug); + + *storage_r = NULL; + + /* Find Sieve scripts and run them */ + + T_BEGIN { + if (lda_sieve_find_scripts(&srctx) < 0) + ret = -1; + else if (srctx.scripts == NULL) + ret = 0; + else + ret = lda_sieve_execute(&srctx, storage_r); + + lda_sieve_free_scripts(&srctx); + } T_END; + + /* Clean up */ + + if (srctx.user_ehandler != NULL) + sieve_error_handler_unref(&srctx.user_ehandler); + sieve_error_handler_unref(&srctx.master_ehandler); + sieve_deinit(&srctx.svinst); + + return ret; +} + +/* + * Plugin interface + */ + +const char *sieve_plugin_version = DOVECOT_ABI_VERSION; +const char sieve_plugin_binary_dependency[] = "lda lmtp"; + +void sieve_plugin_init(void) +{ + /* Hook into the delivery process */ + next_deliver_mail = mail_deliver_hook_set(lda_sieve_deliver_mail); +} + +void sieve_plugin_deinit(void) +{ + /* Remove hook */ + mail_deliver_hook_set(next_deliver_mail); +} diff --git a/pigeonhole/src/plugins/lda-sieve/lda-sieve-plugin.h b/pigeonhole/src/plugins/lda-sieve/lda-sieve-plugin.h new file mode 100644 index 0000000..2357097 --- /dev/null +++ b/pigeonhole/src/plugins/lda-sieve/lda-sieve-plugin.h @@ -0,0 +1,11 @@ +#ifndef LDA_SIEVE_PLUGIN_H +#define LDA_SIEVE_PLUGIN_H + +/* + * Plugin interface + */ + +void sieve_plugin_init(void); +void sieve_plugin_deinit(void); + +#endif diff --git a/pigeonhole/src/plugins/settings/Makefile.am b/pigeonhole/src/plugins/settings/Makefile.am new file mode 100644 index 0000000..49537e1 --- /dev/null +++ b/pigeonhole/src/plugins/settings/Makefile.am @@ -0,0 +1,12 @@ +settingsdir = $(dovecot_moduledir)/settings + +AM_CPPFLAGS = \ + $(LIBDOVECOT_INCLUDE) + +libpigeonhole_settings_la_LDFLAGS = -module -avoid-version + +settings_LTLIBRARIES = \ + libpigeonhole_settings.la + +libpigeonhole_settings_la_SOURCES = \ + pigeonhole-settings.c diff --git a/pigeonhole/src/plugins/settings/Makefile.in b/pigeonhole/src/plugins/settings/Makefile.in new file mode 100644 index 0000000..ee34c03 --- /dev/null +++ b/pigeonhole/src/plugins/settings/Makefile.in @@ -0,0 +1,737 @@ +# 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/plugins/settings +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 $(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 = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(settingsdir)" +LTLIBRARIES = $(settings_LTLIBRARIES) +libpigeonhole_settings_la_LIBADD = +am_libpigeonhole_settings_la_OBJECTS = pigeonhole-settings.lo +libpigeonhole_settings_la_OBJECTS = \ + $(am_libpigeonhole_settings_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 = +libpigeonhole_settings_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(libpigeonhole_settings_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +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)/pigeonhole-settings.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 = $(libpigeonhole_settings_la_SOURCES) +DIST_SOURCES = $(libpigeonhole_settings_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__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@ +settingsdir = $(dovecot_moduledir)/settings +AM_CPPFLAGS = \ + $(LIBDOVECOT_INCLUDE) + +libpigeonhole_settings_la_LDFLAGS = -module -avoid-version +settings_LTLIBRARIES = \ + libpigeonhole_settings.la + +libpigeonhole_settings_la_SOURCES = \ + pigeonhole-settings.c + +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/plugins/settings/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/plugins/settings/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): + +install-settingsLTLIBRARIES: $(settings_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(settings_LTLIBRARIES)'; test -n "$(settingsdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(settingsdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(settingsdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(settingsdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(settingsdir)"; \ + } + +uninstall-settingsLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(settings_LTLIBRARIES)'; test -n "$(settingsdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(settingsdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(settingsdir)/$$f"; \ + done + +clean-settingsLTLIBRARIES: + -test -z "$(settings_LTLIBRARIES)" || rm -f $(settings_LTLIBRARIES) + @list='$(settings_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}; \ + } + +libpigeonhole_settings.la: $(libpigeonhole_settings_la_OBJECTS) $(libpigeonhole_settings_la_DEPENDENCIES) $(EXTRA_libpigeonhole_settings_la_DEPENDENCIES) + $(AM_V_CCLD)$(libpigeonhole_settings_la_LINK) -rpath $(settingsdir) $(libpigeonhole_settings_la_OBJECTS) $(libpigeonhole_settings_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pigeonhole-settings.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) +installdirs: + for dir in "$(DESTDIR)$(settingsdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-settingsLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/pigeonhole-settings.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-settingsLTLIBRARIES + +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)/pigeonhole-settings.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-settingsLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-settingsLTLIBRARIES \ + 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-settingsLTLIBRARIES install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am \ + uninstall-settingsLTLIBRARIES + +.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/plugins/settings/pigeonhole-settings.c b/pigeonhole/src/plugins/settings/pigeonhole-settings.c new file mode 100644 index 0000000..21f8e85 --- /dev/null +++ b/pigeonhole/src/plugins/settings/pigeonhole-settings.c @@ -0,0 +1,13 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "pigeonhole-config.h" +#include "pigeonhole-version.h" + +/* This is currently just a dummy plugin that adds a Pigeonhole + * version banner the doveconf output. + */ + +const char *pigeonhole_settings_version = DOVECOT_ABI_VERSION; +const char *pigeonhole_settings_doveconf_banner = "Pigeonhole version "PIGEONHOLE_VERSION_FULL; diff --git a/pigeonhole/src/plugins/sieve-extprograms/Makefile.am b/pigeonhole/src/plugins/sieve-extprograms/Makefile.am new file mode 100644 index 0000000..ab7844f --- /dev/null +++ b/pigeonhole/src/plugins/sieve-extprograms/Makefile.am @@ -0,0 +1,34 @@ +sieve_plugindir = $(dovecot_moduledir)/sieve + +sieve_plugin_LTLIBRARIES = lib90_sieve_extprograms_plugin.la + +lib90_sieve_extprograms_plugin_la_LDFLAGS = -module -avoid-version + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib-sieve \ + -I$(top_srcdir)/src/lib-sieve/util \ + -I$(top_srcdir)/src/lib-sieve/plugins/copy \ + -I$(top_srcdir)/src/lib-sieve/plugins/variables \ + $(LIBDOVECOT_INCLUDE) \ + -DPKG_RUNDIR=\""$(rundir)"\" + +commands = \ + cmd-pipe.c \ + cmd-filter.c \ + cmd-execute.c + +extensions = \ + ext-pipe.c \ + ext-filter.c \ + ext-execute.c + +lib90_sieve_extprograms_plugin_la_SOURCES = \ + $(commands) \ + $(extensions) \ + sieve-extprograms-common.c \ + sieve-extprograms-plugin.c + +noinst_HEADERS = \ + sieve-extprograms-common.h \ + sieve-extprograms-plugin.h + diff --git a/pigeonhole/src/plugins/sieve-extprograms/Makefile.in b/pigeonhole/src/plugins/sieve-extprograms/Makefile.in new file mode 100644 index 0000000..e35da7b --- /dev/null +++ b/pigeonhole/src/plugins/sieve-extprograms/Makefile.in @@ -0,0 +1,790 @@ +# 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/plugins/sieve-extprograms +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 = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(sieve_plugindir)" +LTLIBRARIES = $(sieve_plugin_LTLIBRARIES) +lib90_sieve_extprograms_plugin_la_LIBADD = +am__objects_1 = cmd-pipe.lo cmd-filter.lo cmd-execute.lo +am__objects_2 = ext-pipe.lo ext-filter.lo ext-execute.lo +am_lib90_sieve_extprograms_plugin_la_OBJECTS = $(am__objects_1) \ + $(am__objects_2) sieve-extprograms-common.lo \ + sieve-extprograms-plugin.lo +lib90_sieve_extprograms_plugin_la_OBJECTS = \ + $(am_lib90_sieve_extprograms_plugin_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 = +lib90_sieve_extprograms_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) \ + --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \ + $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(lib90_sieve_extprograms_plugin_la_LDFLAGS) $(LDFLAGS) -o $@ +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)/cmd-execute.Plo \ + ./$(DEPDIR)/cmd-filter.Plo ./$(DEPDIR)/cmd-pipe.Plo \ + ./$(DEPDIR)/ext-execute.Plo ./$(DEPDIR)/ext-filter.Plo \ + ./$(DEPDIR)/ext-pipe.Plo \ + ./$(DEPDIR)/sieve-extprograms-common.Plo \ + ./$(DEPDIR)/sieve-extprograms-plugin.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 = $(lib90_sieve_extprograms_plugin_la_SOURCES) +DIST_SOURCES = $(lib90_sieve_extprograms_plugin_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@ +sieve_plugindir = $(dovecot_moduledir)/sieve +sieve_plugin_LTLIBRARIES = lib90_sieve_extprograms_plugin.la +lib90_sieve_extprograms_plugin_la_LDFLAGS = -module -avoid-version +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib-sieve \ + -I$(top_srcdir)/src/lib-sieve/util \ + -I$(top_srcdir)/src/lib-sieve/plugins/copy \ + -I$(top_srcdir)/src/lib-sieve/plugins/variables \ + $(LIBDOVECOT_INCLUDE) \ + -DPKG_RUNDIR=\""$(rundir)"\" + +commands = \ + cmd-pipe.c \ + cmd-filter.c \ + cmd-execute.c + +extensions = \ + ext-pipe.c \ + ext-filter.c \ + ext-execute.c + +lib90_sieve_extprograms_plugin_la_SOURCES = \ + $(commands) \ + $(extensions) \ + sieve-extprograms-common.c \ + sieve-extprograms-plugin.c + +noinst_HEADERS = \ + sieve-extprograms-common.h \ + sieve-extprograms-plugin.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/plugins/sieve-extprograms/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/plugins/sieve-extprograms/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): + +install-sieve_pluginLTLIBRARIES: $(sieve_plugin_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(sieve_plugin_LTLIBRARIES)'; test -n "$(sieve_plugindir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(sieve_plugindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sieve_plugindir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(sieve_plugindir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(sieve_plugindir)"; \ + } + +uninstall-sieve_pluginLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(sieve_plugin_LTLIBRARIES)'; test -n "$(sieve_plugindir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(sieve_plugindir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(sieve_plugindir)/$$f"; \ + done + +clean-sieve_pluginLTLIBRARIES: + -test -z "$(sieve_plugin_LTLIBRARIES)" || rm -f $(sieve_plugin_LTLIBRARIES) + @list='$(sieve_plugin_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}; \ + } + +lib90_sieve_extprograms_plugin.la: $(lib90_sieve_extprograms_plugin_la_OBJECTS) $(lib90_sieve_extprograms_plugin_la_DEPENDENCIES) $(EXTRA_lib90_sieve_extprograms_plugin_la_DEPENDENCIES) + $(AM_V_CCLD)$(lib90_sieve_extprograms_plugin_la_LINK) -rpath $(sieve_plugindir) $(lib90_sieve_extprograms_plugin_la_OBJECTS) $(lib90_sieve_extprograms_plugin_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-execute.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-filter.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-pipe.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-execute.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-filter.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-pipe.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-extprograms-common.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-extprograms-plugin.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: + for dir in "$(DESTDIR)$(sieve_plugindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-sieve_pluginLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/cmd-execute.Plo + -rm -f ./$(DEPDIR)/cmd-filter.Plo + -rm -f ./$(DEPDIR)/cmd-pipe.Plo + -rm -f ./$(DEPDIR)/ext-execute.Plo + -rm -f ./$(DEPDIR)/ext-filter.Plo + -rm -f ./$(DEPDIR)/ext-pipe.Plo + -rm -f ./$(DEPDIR)/sieve-extprograms-common.Plo + -rm -f ./$(DEPDIR)/sieve-extprograms-plugin.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-sieve_pluginLTLIBRARIES + +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)/cmd-execute.Plo + -rm -f ./$(DEPDIR)/cmd-filter.Plo + -rm -f ./$(DEPDIR)/cmd-pipe.Plo + -rm -f ./$(DEPDIR)/ext-execute.Plo + -rm -f ./$(DEPDIR)/ext-filter.Plo + -rm -f ./$(DEPDIR)/ext-pipe.Plo + -rm -f ./$(DEPDIR)/sieve-extprograms-common.Plo + -rm -f ./$(DEPDIR)/sieve-extprograms-plugin.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-sieve_pluginLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-sieve_pluginLTLIBRARIES \ + 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-sieve_pluginLTLIBRARIES install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am \ + uninstall-sieve_pluginLTLIBRARIES + +.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/plugins/sieve-extprograms/cmd-execute.c b/pigeonhole/src/plugins/sieve-extprograms/cmd-execute.c new file mode 100644 index 0000000..79f6663 --- /dev/null +++ b/pigeonhole/src/plugins/sieve-extprograms/cmd-execute.c @@ -0,0 +1,488 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" +#include "buffer.h" +#include "str.h" +#include "str-sanitize.h" +#include "istream.h" +#include "ostream.h" + +#include "sieve-common.h" +#include "sieve-stringlist.h" +#include "sieve-binary.h" +#include "sieve-code.h" +#include "sieve-message.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-actions.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-result.h" + +#include "sieve-ext-variables.h" + +#include "sieve-extprograms-common.h" + +/* Execute command + * + * Syntax: + * "execute" [":input" <input-data: string> / ":pipe"] + * [":output" <varname: string>] + * <program-name: string> [<arguments: string-list>] + * + */ + +static bool +cmd_execute_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool +cmd_execute_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +const struct sieve_command_def sieve_cmd_execute = { + .identifier = "execute", + .type = SCT_HYBRID, + .positional_args = -1, /* We check positional arguments ourselves */ + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = cmd_execute_registered, + .validate = sieve_extprogram_command_validate, + .generate = cmd_execute_generate, +}; + +/* + * Tagged arguments + */ + +static bool +cmd_execute_validate_input_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd); +static bool +cmd_execute_generate_input_tag(const struct sieve_codegen_env *cgenv, + struct sieve_ast_argument *arg, + struct sieve_command *cmd); + +static bool +cmd_execute_validate_output_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd); + +static const struct sieve_argument_def execute_input_tag = { + .identifier = "input", + .validate = cmd_execute_validate_input_tag, + .generate = cmd_execute_generate_input_tag +}; + +static const struct sieve_argument_def execute_pipe_tag = { + .identifier = "pipe", + .validate = cmd_execute_validate_input_tag, + .generate = cmd_execute_generate_input_tag +}; + +static const struct sieve_argument_def execute_output_tag = { + .identifier = "output", + .validate = cmd_execute_validate_output_tag, +}; + + +/* + * Execute operation + */ + +static bool +cmd_execute_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +cmd_execute_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def sieve_opr_execute = { + .mnemonic = "EXECUTE", + .ext_def = &sieve_ext_vnd_execute, + .dump = cmd_execute_operation_dump, + .execute = cmd_execute_operation_execute +}; + +/* Codes for optional operands */ + +enum cmd_execute_optional { + OPT_END, + OPT_INPUT, + OPT_OUTPUT +}; + +/* + * Tag validation + */ + +static bool +cmd_execute_validate_input_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + + if ((bool)cmd->data) { + sieve_argument_validate_error( + valdtr, *arg, + "multiple :input or :pipe arguments specified for the %s %s", + sieve_command_identifier(cmd), + sieve_command_type_name(cmd)); + return FALSE; + } + + cmd->data = (void *)TRUE; + + /* Skip tag */ + *arg = sieve_ast_argument_next(*arg); + + if (sieve_argument_is(tag, execute_input_tag)) { + /* Check syntax: + * :input <input-data: string> + */ + if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, + 0, SAAT_STRING, FALSE)) + return FALSE; + + /* Assign tag parameters */ + tag->parameters = *arg; + *arg = sieve_ast_arguments_detach(*arg,1); + } + + return TRUE; +} + +static bool +cmd_execute_validate_output_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + struct sieve_extprograms_config *ext_config = + (struct sieve_extprograms_config *)cmd->ext->context; + + if (ext_config == NULL || ext_config->var_ext == NULL || + !sieve_ext_variables_is_active(ext_config->var_ext, valdtr)) { + sieve_argument_validate_error( + valdtr,*arg, + "the %s %s only allows for the specification of an " + ":output argument when the variables extension is active", + sieve_command_identifier(cmd), + sieve_command_type_name(cmd)); + return FALSE; + } + + /* Detach the tag itself */ + *arg = sieve_ast_arguments_detach(*arg, 1); + + if (!sieve_variable_argument_activate(ext_config->var_ext, + ext_config->var_ext, valdtr, + cmd, *arg, TRUE)) + return FALSE; + + (*arg)->argument->id_code = tag->argument->id_code; + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + + return TRUE; +} + +/* + * Command registration + */ + +static bool +cmd_execute_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &execute_input_tag, OPT_INPUT); + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &execute_pipe_tag, OPT_INPUT); + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &execute_output_tag, OPT_OUTPUT); + return TRUE; +} + +/* + * Code generation + */ + +static bool +cmd_execute_generate_input_tag(const struct sieve_codegen_env *cgenv, + struct sieve_ast_argument *arg, + struct sieve_command *cmd) +{ + if (arg->parameters == NULL) { + sieve_opr_omitted_emit(cgenv->sblock); + return TRUE; + } + + return sieve_generate_argument_parameters(cgenv, cmd, arg); +} + +static bool +cmd_execute_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd) +{ + sieve_operation_emit(cgenv->sblock, cmd->ext, &sieve_opr_execute); + + /* Emit is_test flag */ + sieve_binary_emit_byte(cgenv->sblock, + (uint8_t)(cmd->ast_node->type == SAT_TEST ? + 1 : 0)); + + /* Generate arguments */ + if (!sieve_generate_arguments(cgenv, cmd, NULL)) + return FALSE; + + /* Emit a placeholder when the <arguments> argument is missing */ + if (sieve_ast_argument_next(cmd->first_positional) == NULL) + sieve_opr_omitted_emit(cgenv->sblock); + + return TRUE; +} + +/* + * Code dump + */ + +static bool +cmd_execute_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + int opt_code = 0; + unsigned int is_test = 0; + + /* Read is_test flag */ + if (!sieve_binary_read_byte(denv->sblock, address, &is_test)) + return FALSE; + + sieve_code_dumpf(denv, "EXECUTE (%s)", + (is_test > 0 ? "test" : "command")); + sieve_code_descend(denv); + + /* Dump optional operands */ + for (;;) { + int opt; + bool opok = TRUE; + + if ((opt = sieve_action_opr_optional_dump(denv, address, + &opt_code)) < 0) + return FALSE; + + if (opt == 0) + break; + + switch (opt_code) { + case OPT_INPUT: + opok = sieve_opr_string_dump_ex(denv, address, + "input", "PIPE"); + break; + case OPT_OUTPUT: + opok = sieve_opr_string_dump(denv, address, "output"); + break; + default: + return FALSE; + } + + if (!opok) + return FALSE; + } + + if (!sieve_opr_string_dump(denv, address, "program-name")) + return FALSE; + + return sieve_opr_stringlist_dump_ex(denv, address, "arguments", ""); +} + +/* + * Code execution + */ + +static int +cmd_execute_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + const struct sieve_extension *this_ext = renv->oprtn->ext; + struct sieve_side_effects_list *slist = NULL; + int opt_code = 0; + unsigned int is_test = 0; + struct sieve_stringlist *args_list = NULL; + string_t *pname = NULL, *input = NULL; + struct sieve_variable_storage *var_storage = NULL; + unsigned int var_index; + bool have_input = FALSE; + const char *program_name = NULL; + const char *const *args = NULL; + enum sieve_error error = SIEVE_ERROR_NONE; + buffer_t *outbuf = NULL; + struct sieve_extprogram *sprog = NULL; + int ret; + + /* + * Read operands + */ + + /* The is_test flag */ + if (!sieve_binary_read_byte(renv->sblock, address, &is_test)) { + sieve_runtime_trace_error(renv, "invalid is_test flag"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + /* Optional operands */ + + for (;;) { + int opt; + + if ((opt = sieve_action_opr_optional_read( + renv, address, &opt_code, &ret, &slist)) < 0) + return ret; + + if (opt == 0) + break; + + switch (opt_code) { + case OPT_INPUT: + ret = sieve_opr_string_read_ex(renv, address, "input", + TRUE, &input, NULL); + have_input = TRUE; + break; + case OPT_OUTPUT: + ret = sieve_variable_operand_read( + renv, address, "output", &var_storage, + &var_index); + break; + default: + sieve_runtime_trace_error( + renv, "unknown optional operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if (ret <= 0) + return ret; + } + + /* Fixed operands */ + + if ((ret = sieve_extprogram_command_read_operands( + renv, address, &pname, &args_list)) <= 0) + return ret; + + program_name = str_c(pname); + if (args_list != NULL && + sieve_stringlist_read_all(args_list, pool_datastack_create(), + &args) < 0) { + sieve_runtime_trace_error(renv, "failed to read args operand"); + return args_list->exec_status; + } + + /* + * Perform operation + */ + + /* Trace */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "execute action"); + sieve_runtime_trace_descend(renv); + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, + "execute program `%s'", + str_sanitize(program_name, 128)); + + sprog = sieve_extprogram_create(this_ext, eenv->scriptenv, + eenv->msgdata, "execute", + program_name, args, &error); + if (sprog != NULL) { + if (var_storage != NULL) { + // FIXME: limit output size + struct ostream *outdata; + + outbuf = buffer_create_dynamic(default_pool, 1024); + outdata = o_stream_create_buffer(outbuf); + sieve_extprogram_set_output(sprog, outdata); + o_stream_unref(&outdata); + } + + if (input == NULL && have_input) { + struct mail *mail = sieve_message_get_mail(renv->msgctx); + + if (sieve_extprogram_set_input_mail(sprog, mail) < 0) { + sieve_extprogram_destroy(&sprog); + if (outbuf != NULL) + buffer_free(&outbuf); + return sieve_runtime_mail_error( + renv, mail, "execute action: " + "failed to read input message"); + } + ret = 1; + } else if (input != NULL) { + struct istream *indata = + i_stream_create_from_data(str_data(input), + str_len(input)); + sieve_extprogram_set_input(sprog, indata); + i_stream_unref(&indata); + ret = 1; + } + + if (ret >= 0) + ret = sieve_extprogram_run(sprog); + sieve_extprogram_destroy(&sprog); + } else { + ret = -1; + } + + if (ret > 0) { + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, + "executed program successfully"); + + if (var_storage != NULL) { + string_t *var; + + if (sieve_variable_get_modifiable(var_storage, + var_index, &var)) { + str_truncate(var, 0); + str_append_str(var, outbuf); + + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, + "assigned output variable"); + } // FIXME: handle failure + } + + } else if (ret < 0) { + if (error == SIEVE_ERROR_NOT_FOUND) { + sieve_runtime_error( + renv, NULL, + "execute action: program `%s' not found", + str_sanitize(program_name, 80)); + } else { + sieve_extprogram_exec_error( + renv->ehandler, + sieve_runtime_get_full_command_location(renv), + "execute action: failed to execute to program `%s'", + str_sanitize(program_name, 80)); + } + } else { + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, + "execute action: " + "program indicated false result"); + } + + if (outbuf != NULL) + buffer_free(&outbuf); + + if (is_test > 0) { + sieve_interpreter_set_test_result(renv->interp, (ret > 0)); + return SIEVE_EXEC_OK; + } + return (ret >= 0 ? SIEVE_EXEC_OK : SIEVE_EXEC_FAILURE); +} diff --git a/pigeonhole/src/plugins/sieve-extprograms/cmd-filter.c b/pigeonhole/src/plugins/sieve-extprograms/cmd-filter.c new file mode 100644 index 0000000..7118ff1 --- /dev/null +++ b/pigeonhole/src/plugins/sieve-extprograms/cmd-filter.c @@ -0,0 +1,254 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" +#include "buffer.h" +#include "str.h" +#include "str-sanitize.h" +#include "istream.h" +#include "ostream.h" +#include "safe-mkstemp.h" +#include "mail-user.h" + +#include "sieve-common.h" +#include "sieve-stringlist.h" +#include "sieve-binary.h" +#include "sieve-code.h" +#include "sieve-message.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-actions.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-result.h" + +#include "sieve-ext-variables.h" + +#include "sieve-extprograms-common.h" + +/* Filter command + * + * Syntax: + * "filter" <program-name: string> [<arguments: string-list>] + * + */ + +static bool +cmd_filter_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +const struct sieve_command_def sieve_cmd_filter = { + .identifier = "filter", + .type = SCT_HYBRID, + .positional_args = -1, /* We check positional arguments ourselves */ + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = sieve_extprogram_command_validate, + .generate = cmd_filter_generate +}; + +/* + * Filter operation + */ + +static bool +cmd_filter_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +cmd_filter_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def sieve_opr_filter = { + .mnemonic = "FILTER", + .ext_def = &sieve_ext_vnd_filter, + .dump = cmd_filter_operation_dump, + .execute = cmd_filter_operation_execute +}; + +/* + * Code generation + */ + +static bool +cmd_filter_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd) +{ + sieve_operation_emit(cgenv->sblock, cmd->ext, &sieve_opr_filter); + + /* Emit is_test flag */ + sieve_binary_emit_byte(cgenv->sblock, + (uint8_t)(cmd->ast_node->type == SAT_TEST ? + 1 : 0)); + + /* Generate arguments */ + if (!sieve_generate_arguments(cgenv, cmd, NULL)) + return FALSE; + + /* Emit a placeholder when the <arguments> argument is missing */ + if (sieve_ast_argument_next(cmd->first_positional) == NULL) + sieve_opr_omitted_emit(cgenv->sblock); + return TRUE; +} + +/* + * Code dump + */ + +static bool +cmd_filter_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + unsigned int is_test = 0; + + /* Read is_test flag */ + if (!sieve_binary_read_byte(denv->sblock, address, &is_test)) + return FALSE; + + sieve_code_dumpf(denv, "FILTER (%s)", + (is_test > 0 ? "test" : "command")); + sieve_code_descend(denv); + + /* Dump optional operands */ + if (sieve_action_opr_optional_dump(denv, address, NULL) != 0) + return FALSE; + + if (!sieve_opr_string_dump(denv, address, "program-name")) + return FALSE; + + return sieve_opr_stringlist_dump_ex(denv, address, "arguments", ""); +} + +/* + * Code execution + */ + +static int +cmd_filter_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + const struct sieve_extension *this_ext = renv->oprtn->ext; + unsigned int is_test = 0; + struct sieve_stringlist *args_list = NULL; + enum sieve_error error = SIEVE_ERROR_NONE; + string_t *pname = NULL; + const char *program_name = NULL; + const char *const *args = NULL; + struct istream *newmsg = NULL; + struct sieve_extprogram *sprog; + int ret; + + /* + * Read operands + */ + + /* The is_test flag */ + + if (!sieve_binary_read_byte(renv->sblock, address, &is_test)) { + sieve_runtime_trace_error(renv, "invalid is_test flag"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + /* Optional operands */ + + if (sieve_action_opr_optional_read(renv, address, NULL, + &ret, NULL) != 0) + return ret; + + /* Fixed operands */ + + if ((ret = sieve_extprogram_command_read_operands(renv, address, &pname, + &args_list)) <= 0) + return ret; + + program_name = str_c(pname); + if (args_list != NULL && + sieve_stringlist_read_all(args_list, pool_datastack_create(), + &args) < 0) { + sieve_runtime_trace_error(renv, "failed to read args operand"); + return args_list->exec_status; + } + + /* + * Perform operation + */ + + /* Trace */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "filter action"); + sieve_runtime_trace_descend(renv); + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, + "execute program `%s'", str_sanitize(program_name, 128)); + + sprog = sieve_extprogram_create(this_ext, eenv->scriptenv, + eenv->msgdata, "filter", + program_name, args, &error); + if (sprog != NULL) { + struct mail *mail = sieve_message_get_mail(renv->msgctx); + + if (sieve_extprogram_set_input_mail(sprog, mail) < 0) { + sieve_extprogram_destroy(&sprog); + return sieve_runtime_mail_error( + renv, mail, + "filter action: failed to read input message"); + } + sieve_extprogram_set_output_seekable(sprog); + ret = sieve_extprogram_run(sprog); + } else { + ret = -1; + } + + if (ret > 0) + newmsg = sieve_extprogram_get_output_seekable(sprog); + if (sprog != NULL) + sieve_extprogram_destroy(&sprog); + + if (ret > 0 && newmsg != NULL) { + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, + "executed program successfully"); + + i_stream_set_name(newmsg, t_strdup_printf("filter %s output", + program_name)); + newmsg->blocking = TRUE; + if ((ret = sieve_message_substitute(renv->msgctx, + newmsg)) >= 0) { + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, + "changed message"); + } else { + sieve_runtime_critical(renv, NULL, "filter action", + "filter action: " + "failed to substitute message"); + } + + i_stream_unref(&newmsg); + } else if (ret < 0) { + if (error == SIEVE_ERROR_NOT_FOUND) { + sieve_runtime_error(renv, NULL, "filter action: " + "program `%s' not found", + str_sanitize(program_name, 80)); + } else { + sieve_extprogram_exec_error( + renv->ehandler, + sieve_runtime_get_full_command_location(renv), + "filter action: " + "failed to execute to program `%s'", + str_sanitize(program_name, 80)); + } + + } else { + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "filter action: " + "program indicated false result"); + } + + if (is_test > 0) { + sieve_interpreter_set_test_result(renv->interp, (ret > 0)); + + return SIEVE_EXEC_OK; + } + + return (ret >= 0 ? SIEVE_EXEC_OK : SIEVE_EXEC_FAILURE); +} diff --git a/pigeonhole/src/plugins/sieve-extprograms/cmd-pipe.c b/pigeonhole/src/plugins/sieve-extprograms/cmd-pipe.c new file mode 100644 index 0000000..f5a5c34 --- /dev/null +++ b/pigeonhole/src/plugins/sieve-extprograms/cmd-pipe.c @@ -0,0 +1,457 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" +#include "str.h" +#include "str-sanitize.h" + +#include "sieve-common.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-message.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-actions.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-result.h" + +#include "sieve-extprograms-common.h" + +/* Pipe command + * + * Syntax: + * pipe [":copy"] [":try"] <program-name: string> [<arguments: string-list>] + * + */ + +static bool +cmd_pipe_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool +cmd_pipe_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +const struct sieve_command_def sieve_cmd_pipe = { + .identifier = "pipe", + .type = SCT_COMMAND, + .positional_args = -1, /* We check positional arguments ourselves */ + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = cmd_pipe_registered, + .validate = sieve_extprogram_command_validate, + .generate = cmd_pipe_generate, +}; + +/* + * Tagged arguments + */ + +static const struct sieve_argument_def pipe_try_tag = { + .identifier = "try", +}; + +/* + * Pipe operation + */ + +static bool +cmd_pipe_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +cmd_pipe_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def sieve_opr_pipe = { + .mnemonic = "PIPE", + .ext_def = &sieve_ext_vnd_pipe, + .dump = cmd_pipe_operation_dump, + .execute = cmd_pipe_operation_execute, +}; + +/* Codes for optional operands */ + +enum cmd_pipe_optional { + OPT_END, + OPT_TRY, +}; + +/* + * Pipe action + */ + +/* Forward declarations */ + +static int +act_pipe_check_duplicate(const struct sieve_runtime_env *renv, + const struct sieve_action *act, + const struct sieve_action *act_other); +static void +act_pipe_print(const struct sieve_action *action, + const struct sieve_result_print_env *rpenv, + bool *keep); +static int +act_pipe_start(const struct sieve_action_exec_env *aenv, void **tr_context); +static int +act_pipe_execute(const struct sieve_action_exec_env *aenv, + void *tr_context, bool *keep); +static int +act_pipe_commit(const struct sieve_action_exec_env *aenv, + void *tr_context); +static void +act_pipe_rollback(const struct sieve_action_exec_env *aenv, + void *tr_context, bool success); + +/* Action object */ + +const struct sieve_action_def act_pipe = { + .name = "pipe", + .flags = SIEVE_ACTFLAG_TRIES_DELIVER, + .check_duplicate = act_pipe_check_duplicate, + .print = act_pipe_print, + .start = act_pipe_start, + .execute = act_pipe_execute, + .commit = act_pipe_commit, + .rollback = act_pipe_rollback, +}; + +/* Action context information */ + +struct ext_pipe_action { + const char *program_name; + const char * const *args; + bool try; +}; + +/* + * Command registration + */ + +static bool +cmd_pipe_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &pipe_try_tag, OPT_TRY); + return TRUE; +} + +/* + * Code generation + */ + +static bool +cmd_pipe_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd) +{ + sieve_operation_emit(cgenv->sblock, cmd->ext, &sieve_opr_pipe); + + /* Generate arguments */ + if (!sieve_generate_arguments(cgenv, cmd, NULL)) + return FALSE; + + /* Emit a placeholder when the <arguments> argument is missing */ + if (sieve_ast_argument_next(cmd->first_positional) == NULL) + sieve_opr_omitted_emit(cgenv->sblock); + return TRUE; +} + +/* + * Code dump + */ + +static bool +cmd_pipe_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + int opt_code = 0; + + sieve_code_dumpf(denv, "PIPE"); + sieve_code_descend(denv); + + /* Dump optional operands */ + for (;;) { + int opt; + + if ((opt = sieve_action_opr_optional_dump(denv, address, + &opt_code)) < 0) + return FALSE; + + if (opt == 0) + break; + + switch (opt_code) { + case OPT_TRY: + sieve_code_dumpf(denv, "try"); + break; + default: + return FALSE; + } + } + + if (!sieve_opr_string_dump(denv, address, "program-name")) + return FALSE; + + return sieve_opr_stringlist_dump_ex(denv, address, "arguments", ""); +} + +/* + * Code execution + */ + +static int +cmd_pipe_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + const struct sieve_extension *this_ext = renv->oprtn->ext; + struct sieve_side_effects_list *slist = NULL; + struct ext_pipe_action *act; + pool_t pool; + int opt_code = 0; + struct sieve_stringlist *args_list = NULL; + string_t *pname = NULL; + bool try = FALSE; + int ret; + + /* + * Read operands + */ + + /* Optional operands */ + + for (;;) { + int opt; + + if ((opt = sieve_action_opr_optional_read(renv, address, + &opt_code, &ret, + &slist)) < 0) + return ret; + + if (opt == 0) + break; + + switch (opt_code) { + case OPT_TRY: + try = TRUE; + break; + default: + sieve_runtime_trace_error( + renv, "unknown optional operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + } + + /* Fixed operands */ + + if ((ret = sieve_extprogram_command_read_operands(renv, address, &pname, + &args_list)) <= 0) + return ret; + + /* + * Perform operation + */ + + /* Trace */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "pipe action"); + + /* Compose action */ + + pool = sieve_result_pool(renv->result); + act = p_new(pool, struct ext_pipe_action, 1); + + if (args_list != NULL && + sieve_stringlist_read_all(args_list, pool, &act->args) < 0) { + sieve_runtime_trace_error(renv, "failed to read args operand"); + return args_list->exec_status; + } + + act->program_name = p_strdup(pool, str_c(pname)); + act->try = try; + + if (sieve_result_add_action(renv, this_ext, "pipe", &act_pipe, slist, + (void *)act, 0, TRUE) < 0) + return SIEVE_EXEC_FAILURE; + return SIEVE_EXEC_OK; +} + +/* + * Action + */ + +/* Runtime verification */ + +static int +act_pipe_check_duplicate(const struct sieve_runtime_env *renv ATTR_UNUSED, + const struct sieve_action *act, + const struct sieve_action *act_other) +{ + struct ext_pipe_action *new_act, *old_act; + + if (act->context == NULL || act_other->context == NULL) + return 0; + + new_act = (struct ext_pipe_action *) act->context; + old_act = (struct ext_pipe_action *) act_other->context; + + if (strcmp(new_act->program_name, old_act->program_name) == 0) { + sieve_runtime_error(renv, act->location, + "duplicate pipe \"%s\" action not allowed " + "(previously triggered one was here: %s)", + new_act->program_name, act_other->location); + return -1; + } + + return 0; +} + +/* Result printing */ + +static void +act_pipe_print(const struct sieve_action *action, + const struct sieve_result_print_env *rpenv, + bool *keep ATTR_UNUSED) +{ + const struct ext_pipe_action *act = + (const struct ext_pipe_action *)action->context; + + sieve_result_action_printf( + rpenv, "pipe message to external program '%s':", + act->program_name); + + /* Print main method parameters */ + + sieve_result_printf( + rpenv, " => try : %s\n", + (act->try ? "yes" : "no")); + + /* FIXME: print args */ + + /* Finish output with an empty line */ + sieve_result_printf(rpenv, "\n"); +} + +/* Result execution */ + +struct act_pipe_transaction { + struct sieve_extprogram *sprog; +}; + +static int +act_pipe_start(const struct sieve_action_exec_env *aenv, void **tr_context) +{ + struct act_pipe_transaction *trans; + pool_t pool = sieve_result_pool(aenv->result); + + /* Create transaction context */ + trans = p_new(pool, struct act_pipe_transaction, 1); + *tr_context = (void *)trans; + + return SIEVE_EXEC_OK; +} + +static int +act_pipe_execute(const struct sieve_action_exec_env *aenv, + void *tr_context, bool *keep) +{ + const struct sieve_action *action = aenv->action; + const struct sieve_execute_env *eenv = aenv->exec_env; + const struct ext_pipe_action *act = + (const struct ext_pipe_action *)action->context; + struct act_pipe_transaction *trans = tr_context; + struct mail *mail = (action->mail != NULL ? + action->mail : + sieve_message_get_mail(aenv->msgctx)); + enum sieve_error error = SIEVE_ERROR_NONE; + + trans->sprog = sieve_extprogram_create(action->ext, eenv->scriptenv, + eenv->msgdata, "pipe", + act->program_name, act->args, + &error); + if (trans->sprog != NULL) { + if (sieve_extprogram_set_input_mail(trans->sprog, mail) < 0) { + sieve_extprogram_destroy(&trans->sprog); + return sieve_result_mail_error( + aenv, mail, "failed to read input message"); + } + } + + *keep = FALSE; + return SIEVE_EXEC_OK; +} + +static int +act_pipe_commit(const struct sieve_action_exec_env *aenv, + void *tr_context ATTR_UNUSED) +{ + const struct sieve_action *action = aenv->action; + const struct sieve_execute_env *eenv = aenv->exec_env; + const struct ext_pipe_action *act = + (const struct ext_pipe_action *)action->context; + struct act_pipe_transaction *trans = tr_context; + enum sieve_error error = SIEVE_ERROR_NONE; + int ret; + + if (trans->sprog != NULL) { + ret = sieve_extprogram_run(trans->sprog); + sieve_extprogram_destroy(&trans->sprog); + } else { + ret = -1; + } + + if (ret > 0) { + struct event_passthrough *e = + sieve_action_create_finish_event(aenv)-> + add_str("pipe_program", + str_sanitize(act->program_name, 256)); + + sieve_result_event_log(aenv, e->event(), + "piped message to program `%s'", + str_sanitize(act->program_name, 128)); + + /* Indicate that message was successfully 'forwarded' */ + eenv->exec_status->message_forwarded = TRUE; + } else { + if (ret < 0) { + if (error == SIEVE_ERROR_NOT_FOUND) { + sieve_result_error( + aenv, + "failed to pipe message to program: " + "program `%s' not found", + str_sanitize(act->program_name, 80)); + } else { + sieve_extprogram_exec_error( + aenv->ehandler, NULL, + "failed to pipe message to program `%s'", + str_sanitize(act->program_name, 80)); + } + } else { + sieve_extprogram_exec_error( + aenv->ehandler, NULL, + "failed to execute to program `%s'", + str_sanitize(act->program_name, 80)); + } + + if (act->try) + return SIEVE_EXEC_OK; + return SIEVE_EXEC_FAILURE; + } + + return SIEVE_EXEC_OK; +} + +static void +act_pipe_rollback(const struct sieve_action_exec_env *aenv ATTR_UNUSED, + void *tr_context, bool success ATTR_UNUSED) +{ + struct act_pipe_transaction *trans = tr_context; + + if (trans->sprog != NULL) + sieve_extprogram_destroy(&trans->sprog); +} diff --git a/pigeonhole/src/plugins/sieve-extprograms/ext-execute.c b/pigeonhole/src/plugins/sieve-extprograms/ext-execute.c new file mode 100644 index 0000000..055ee88 --- /dev/null +++ b/pigeonhole/src/plugins/sieve-extprograms/ext-execute.c @@ -0,0 +1,80 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension vnd.dovecot.execute + * ----------------------------- + * + * Authors: Stephan Bosch + * Specification: vendor-defined; spec-bosch-sieve-extprograms + * Implementation: full + * Status: experimental + * + */ + +#include "lib.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-binary.h" + +#include "sieve-validator.h" +#include "sieve-interpreter.h" + +#include "sieve-ext-copy.h" + +#include "sieve-extprograms-common.h" + +/* + * Extension + */ + +static bool ext_execute_load(const struct sieve_extension *ext, void **context); +static void ext_execute_unload(const struct sieve_extension *ext); +static bool ext_execute_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); + +const struct sieve_extension_def sieve_ext_vnd_execute = { + .name = "vnd.dovecot.execute", + .load = ext_execute_load, + .unload = ext_execute_unload, + .validator_load = ext_execute_validator_load, + SIEVE_EXT_DEFINE_OPERATION(sieve_opr_execute) +}; + +/* + * Context + */ + +static bool ext_execute_load(const struct sieve_extension *ext, void **context) +{ + if ( *context != NULL ) { + ext_execute_unload(ext); + *context = NULL; + } + + *context = (void *)sieve_extprograms_config_init(ext); + return TRUE; +} + +static void ext_execute_unload(const struct sieve_extension *ext) +{ + struct sieve_extprograms_config *ext_config = + (struct sieve_extprograms_config *)ext->context; + + if ( ext_config == NULL ) return; + + sieve_extprograms_config_deinit(&ext_config); +} + +/* + * Validation + */ + +static bool ext_execute_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + /* Register commands */ + sieve_validator_register_command(valdtr, ext, &sieve_cmd_execute); + + return TRUE; +} diff --git a/pigeonhole/src/plugins/sieve-extprograms/ext-filter.c b/pigeonhole/src/plugins/sieve-extprograms/ext-filter.c new file mode 100644 index 0000000..af1f8f1 --- /dev/null +++ b/pigeonhole/src/plugins/sieve-extprograms/ext-filter.c @@ -0,0 +1,80 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension vnd.dovecot.filter + * ----------------------------- + * + * Authors: Stephan Bosch + * Specification: vendor-defined; spec-bosch-sieve-extprograms + * Implementation: full + * Status: experimental + * + */ + +#include "lib.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-binary.h" + +#include "sieve-validator.h" +#include "sieve-interpreter.h" + +#include "sieve-ext-copy.h" + +#include "sieve-extprograms-common.h" + +/* + * Extension + */ + +static bool ext_filter_load(const struct sieve_extension *ext, void **context); +static void ext_filter_unload(const struct sieve_extension *ext); +static bool ext_filter_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); + +const struct sieve_extension_def sieve_ext_vnd_filter = { + .name = "vnd.dovecot.filter", + .load = ext_filter_load, + .unload = ext_filter_unload, + .validator_load = ext_filter_validator_load, + SIEVE_EXT_DEFINE_OPERATION(sieve_opr_filter), +}; + +/* + * Context + */ + +static bool ext_filter_load(const struct sieve_extension *ext, void **context) +{ + if ( *context != NULL ) { + ext_filter_unload(ext); + *context = NULL; + } + + *context = (void *)sieve_extprograms_config_init(ext); + return TRUE; +} + +static void ext_filter_unload(const struct sieve_extension *ext) +{ + struct sieve_extprograms_config *ext_config = + (struct sieve_extprograms_config *)ext->context; + + if ( ext_config == NULL ) return; + + sieve_extprograms_config_deinit(&ext_config); +} + +/* + * Validation + */ + +static bool ext_filter_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + /* Register commands */ + sieve_validator_register_command(valdtr, ext, &sieve_cmd_filter); + + return TRUE; +} diff --git a/pigeonhole/src/plugins/sieve-extprograms/ext-pipe.c b/pigeonhole/src/plugins/sieve-extprograms/ext-pipe.c new file mode 100644 index 0000000..5da7d36 --- /dev/null +++ b/pigeonhole/src/plugins/sieve-extprograms/ext-pipe.c @@ -0,0 +1,114 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension vnd.dovecot.pipe + * ----------------------------- + * + * Authors: Stephan Bosch + * Specification: vendor-define; spec-bosch-sieve-extprograms + * Implementation: full + * Status: experimental + * + */ + +#include "lib.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-binary.h" + +#include "sieve-validator.h" +#include "sieve-interpreter.h" + +#include "sieve-ext-copy.h" + +#include "sieve-extprograms-common.h" + +/* + * Extension + */ + +static bool ext_pipe_load(const struct sieve_extension *ext, void **context); +static void ext_pipe_unload(const struct sieve_extension *ext); +static bool ext_pipe_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); + +const struct sieve_extension_def sieve_ext_vnd_pipe = { + .name = "vnd.dovecot.pipe", + .load = ext_pipe_load, + .unload = ext_pipe_unload, + .validator_load = ext_pipe_validator_load, + SIEVE_EXT_DEFINE_OPERATION(sieve_opr_pipe), +}; + +/* + * Context + */ + +static bool ext_pipe_load(const struct sieve_extension *ext, void **context) +{ + if ( *context != NULL ) { + ext_pipe_unload(ext); + *context = NULL; + } + + *context = (void *)sieve_extprograms_config_init(ext); + return TRUE; +} + +static void ext_pipe_unload(const struct sieve_extension *ext) +{ + struct sieve_extprograms_config *ext_config = + (struct sieve_extprograms_config *)ext->context; + + if ( ext_config == NULL ) return; + + sieve_extprograms_config_deinit(&ext_config); +} + +/* + * Validation + */ + +static bool ext_pipe_validator_validate + (const struct sieve_extension *ext, + struct sieve_validator *valdtr, void *context, + struct sieve_ast_argument *require_arg, + bool required); + +static const struct sieve_validator_extension pipe_validator_extension = { + .ext = &sieve_ext_vnd_pipe, + .validate = ext_pipe_validator_validate +}; + +static bool ext_pipe_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + /* Register commands */ + sieve_validator_register_command(valdtr, ext, &sieve_cmd_pipe); + + /* Register extension to validator */ + sieve_validator_extension_register + (valdtr, ext, &pipe_validator_extension, NULL); + + return TRUE; +} + +static bool ext_pipe_validator_validate +(const struct sieve_extension *ext, + struct sieve_validator *valdtr, void *context ATTR_UNUSED, + struct sieve_ast_argument *require_arg ATTR_UNUSED, + bool required ATTR_UNUSED) +{ + struct sieve_extprograms_config *ext_config = + (struct sieve_extprograms_config *) ext->context; + + if ( ext_config != NULL && ext_config->copy_ext != NULL ) { + /* Register :copy command tag */ + sieve_ext_copy_register_tag(valdtr, + ext_config->copy_ext, sieve_cmd_pipe.identifier); + } + return TRUE; +} + + diff --git a/pigeonhole/src/plugins/sieve-extprograms/sieve-extprograms-common.c b/pigeonhole/src/plugins/sieve-extprograms/sieve-extprograms-common.c new file mode 100644 index 0000000..3c9ff23 --- /dev/null +++ b/pigeonhole/src/plugins/sieve-extprograms/sieve-extprograms-common.c @@ -0,0 +1,648 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "lib-signals.h" +#include "str.h" +#include "strfuncs.h" +#include "str-sanitize.h" +#include "unichar.h" +#include "array.h" +#include "eacces-error.h" +#include "smtp-params.h" +#include "istream.h" +#include "istream-crlf.h" +#include "istream-header-filter.h" +#include "ostream.h" +#include "mail-user.h" +#include "mail-storage.h" + +#include "program-client.h" + +#include "sieve-common.h" +#include "sieve-settings.h" +#include "sieve-error.h" +#include "sieve-extensions.h" +#include "sieve-ast.h" +#include "sieve-commands.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-actions.h" +#include "sieve-validator.h" +#include "sieve-runtime.h" +#include "sieve-interpreter.h" + +#include "sieve-ext-copy.h" +#include "sieve-ext-variables.h" + +#include "sieve-extprograms-common.h" + +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/socket.h> + +/* + * Limits + */ + +#define SIEVE_EXTPROGRAMS_MAX_PROGRAM_NAME_LEN 128 +#define SIEVE_EXTPROGRAMS_MAX_PROGRAM_ARG_LEN 1024 + +#define SIEVE_EXTPROGRAMS_DEFAULT_EXEC_TIMEOUT_SECS 10 +#define SIEVE_EXTPROGRAMS_CONNECT_TIMEOUT_MSECS 5 + +/* + * Pipe Extension Context + */ + +struct sieve_extprograms_config *sieve_extprograms_config_init +(const struct sieve_extension *ext) +{ + struct sieve_instance *svinst = ext->svinst; + struct sieve_extprograms_config *ext_config; + const char *extname = sieve_extension_name(ext); + const char *bin_dir, *socket_dir, *input_eol; + sieve_number_t execute_timeout; + + extname = strrchr(extname, '.'); + i_assert(extname != NULL); + extname++; + + bin_dir = sieve_setting_get + (svinst, t_strdup_printf("sieve_%s_bin_dir", extname)); + socket_dir = sieve_setting_get + (svinst, t_strdup_printf("sieve_%s_socket_dir", extname)); + input_eol = sieve_setting_get + (svinst, t_strdup_printf("sieve_%s_input_eol", extname)); + + ext_config = i_new(struct sieve_extprograms_config, 1); + ext_config->execute_timeout = + SIEVE_EXTPROGRAMS_DEFAULT_EXEC_TIMEOUT_SECS; + + if ( bin_dir == NULL && socket_dir == NULL ) { + e_debug(svinst->event, "%s extension: " + "no bin or socket directory specified; extension is unconfigured " + "(both sieve_%s_bin_dir and sieve_%s_socket_dir are not set)", + sieve_extension_name(ext), extname, extname); + } else { + ext_config->bin_dir = i_strdup(bin_dir); + ext_config->socket_dir = i_strdup(socket_dir); + + if (sieve_setting_get_duration_value + (svinst, t_strdup_printf("sieve_%s_exec_timeout", extname), + &execute_timeout)) { + ext_config->execute_timeout = execute_timeout; + } + + ext_config->default_input_eol = SIEVE_EXTPROGRAMS_EOL_CRLF; + if (input_eol != NULL && strcasecmp(input_eol, "lf") == 0) + ext_config->default_input_eol = SIEVE_EXTPROGRAMS_EOL_LF; + } + + if ( sieve_extension_is(ext, sieve_ext_vnd_pipe) ) + ext_config->copy_ext = sieve_ext_copy_get_extension(ext->svinst); + if ( sieve_extension_is(ext, sieve_ext_vnd_execute) ) + ext_config->var_ext = sieve_ext_variables_get_extension(ext->svinst); + return ext_config; +} + +void sieve_extprograms_config_deinit +(struct sieve_extprograms_config **ext_config) +{ + if ( *ext_config == NULL ) + return; + + i_free((*ext_config)->bin_dir); + i_free((*ext_config)->socket_dir); + i_free((*ext_config)); + + *ext_config = NULL; +} + +/* + * Program name and arguments + */ + +bool sieve_extprogram_name_is_valid(string_t *name) +{ + ARRAY_TYPE(unichars) uni_name; + unsigned int count, i; + const unichar_t *name_chars; + size_t namelen = str_len(name); + + /* Check minimum length */ + if ( namelen == 0 ) + return FALSE; + + /* Check worst-case maximum length */ + if ( namelen > SIEVE_EXTPROGRAMS_MAX_PROGRAM_NAME_LEN * 4 ) + return FALSE; + + /* Intialize array for unicode characters */ + t_array_init(&uni_name, namelen * 4); + + /* Convert UTF-8 to UCS4/UTF-32 */ + if ( uni_utf8_to_ucs4_n(str_data(name), namelen, &uni_name) < 0 ) + return FALSE; + name_chars = array_get(&uni_name, &count); + + /* Check true maximum length */ + if ( count > SIEVE_EXTPROGRAMS_MAX_PROGRAM_NAME_LEN ) + return FALSE; + + /* Scan name for invalid characters + * FIXME: compliance with Net-Unicode Definition (Section 2 of + * RFC 5198) is not checked fully and no normalization + * is performed. + */ + for ( i = 0; i < count; i++ ) { + + /* 0000-001F; [CONTROL CHARACTERS] */ + if ( name_chars[i] <= 0x001f ) + return FALSE; + + /* 002F; SLASH */ + if ( name_chars[i] == 0x002f ) + return FALSE; + + /* 007F; DELETE */ + if ( name_chars[i] == 0x007f ) + return FALSE; + + /* 0080-009F; [CONTROL CHARACTERS] */ + if ( name_chars[i] >= 0x0080 && name_chars[i] <= 0x009f ) + return FALSE; + + /* 00FF */ + if ( name_chars[i] == 0x00ff ) + return FALSE; + + /* 2028; LINE SEPARATOR */ + /* 2029; PARAGRAPH SEPARATOR */ + if ( name_chars[i] == 0x2028 || name_chars[i] == 0x2029 ) + return FALSE; + } + + return TRUE; +} + +bool sieve_extprogram_arg_is_valid(string_t *arg) +{ + const unsigned char *chars; + unsigned int i; + + /* Check maximum length */ + if ( str_len(arg) > SIEVE_EXTPROGRAMS_MAX_PROGRAM_ARG_LEN ) + return FALSE; + + /* Check invalid characters */ + chars = str_data(arg); + for ( i = 0; i < str_len(arg); i++ ) { + /* 0010; CR */ + if ( chars[i] == 0x0D ) + return FALSE; + + /* 0010; LF */ + if ( chars[i] == 0x0A ) + return FALSE; + } + + return TRUE; +} + +/* + * Command validation + */ + +struct _arg_validate_context { + struct sieve_validator *valdtr; + struct sieve_command *cmd; +}; + +static int _arg_validate +(void *context, struct sieve_ast_argument *item) +{ + struct _arg_validate_context *actx = (struct _arg_validate_context *) context; + + if ( sieve_argument_is_string_literal(item) ) { + string_t *arg = sieve_ast_argument_str(item); + + if ( !sieve_extprogram_arg_is_valid(arg) ) { + sieve_argument_validate_error(actx->valdtr, item, + "%s %s: specified external program argument `%s' is invalid", + sieve_command_identifier(actx->cmd), sieve_command_type_name(actx->cmd), + str_sanitize(str_c(arg), 128)); + + return -1; + } + } + + return 1; +} + +bool sieve_extprogram_command_validate +(struct sieve_validator *valdtr, struct sieve_command *cmd) +{ + struct sieve_ast_argument *arg = cmd->first_positional; + struct sieve_ast_argument *stritem; + struct _arg_validate_context actx; + string_t *program_name; + + if ( arg == NULL ) { + sieve_command_validate_error(valdtr, cmd, + "the %s %s expects at least one positional argument, but none was found", + sieve_command_identifier(cmd), sieve_command_type_name(cmd)); + return FALSE; + } + + /* <program-name: string> argument */ + + if ( !sieve_validate_positional_argument + (valdtr, cmd, arg, "program-name", 1, SAAT_STRING) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, cmd, arg, FALSE) ) + return FALSE; + + /* Variables are not allowed */ + if ( !sieve_argument_is_string_literal(arg) ) { + sieve_argument_validate_error(valdtr, arg, + "the %s %s requires a constant string " + "for its program-name argument", + sieve_command_identifier(cmd), sieve_command_type_name(cmd)); + return FALSE; + } + + /* Check program name */ + program_name = sieve_ast_argument_str(arg); + if ( !sieve_extprogram_name_is_valid(program_name) ) { + sieve_argument_validate_error(valdtr, arg, + "%s %s: invalid program name '%s'", + sieve_command_identifier(cmd), sieve_command_type_name(cmd), + str_sanitize(str_c(program_name), 80)); + return FALSE; + } + + /* Optional <arguments: string-list> argument */ + + arg = sieve_ast_argument_next(arg); + if ( arg == NULL ) + return TRUE; + + if ( !sieve_validate_positional_argument + (valdtr, cmd, arg, "arguments", 2, SAAT_STRING_LIST) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, cmd, arg, FALSE) ) + return FALSE; + + /* Check arguments */ + actx.valdtr = valdtr; + actx.cmd = cmd; + stritem = arg; + if ( sieve_ast_stringlist_map + (&stritem, (void *)&actx, _arg_validate) <= 0 ) { + return FALSE; + } + + if ( sieve_ast_argument_next(arg) != NULL ) { + sieve_command_validate_error(valdtr, cmd, + "the %s %s expects at most two positional arguments, but more were found", + sieve_command_identifier(cmd), sieve_command_type_name(cmd)); + return FALSE; + } + + return TRUE; +} + +/* + * Common command operands + */ + +int sieve_extprogram_command_read_operands +(const struct sieve_runtime_env *renv, sieve_size_t *address, + string_t **pname_r, struct sieve_stringlist **args_list_r) +{ + string_t *arg; + int ret; + + /* + * Read fixed operands + */ + + if ( (ret=sieve_opr_string_read + (renv, address, "program-name", pname_r)) <= 0 ) + return ret; + + if ( (ret=sieve_opr_stringlist_read_ex + (renv, address, "arguments", TRUE, args_list_r)) <= 0 ) + return ret; + + /* + * Check operands + */ + + arg = NULL; + while ( *args_list_r != NULL && + (ret=sieve_stringlist_next_item(*args_list_r, &arg)) > 0 ) { + if ( !sieve_extprogram_arg_is_valid(arg) ) { + sieve_runtime_error(renv, NULL, + "specified :args item `%s' is invalid", + str_sanitize(str_c(arg), 128)); + return SIEVE_EXEC_FAILURE; + } + } + + if ( ret < 0 ) { + sieve_runtime_trace_error(renv, "invalid args-list item"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + return SIEVE_EXEC_OK; +} + +/* + * Running external programs + */ + +struct sieve_extprogram { + struct sieve_instance *svinst; + const struct sieve_extprograms_config *ext_config; + + const struct sieve_script_env *scriptenv; + struct program_client_settings set; + struct program_client *program_client; +}; + +void sieve_extprogram_exec_error +(struct sieve_error_handler *ehandler, const char *location, + const char *fmt, ...) +{ + char str[256]; + struct tm *tm; + const char *timestamp; + + tm = localtime(&ioloop_time); + + timestamp = + ( strftime(str, sizeof(str), " [%Y-%m-%d %H:%M:%S]", tm) > 0 ? str : "" ); + + va_list args; + va_start(args, fmt); + + T_BEGIN { + sieve_error(ehandler, location, + "%s: refer to server log for more information.%s", + t_strdup_vprintf(fmt, args), timestamp); + } T_END; + + va_end(args); +} + +/* API */ + +struct sieve_extprogram *sieve_extprogram_create +(const struct sieve_extension *ext, const struct sieve_script_env *senv, + const struct sieve_message_data *msgdata, const char *action, + const char *program_name, const char * const *args, + enum sieve_error *error_r) +{ + struct sieve_instance *svinst = ext->svinst; + struct sieve_extprograms_config *ext_config = + (struct sieve_extprograms_config *) ext->context; + const struct smtp_address *sender, *recipient, *orig_recipient; + struct sieve_extprogram *sprog; + const char *path = NULL; + struct stat st; + bool fork = FALSE; + + e_debug(svinst->event, "action %s: " + "running program: %s", action, program_name); + + if ( ext_config == NULL || + (ext_config->bin_dir == NULL && ext_config->socket_dir == NULL) ) { + e_error(svinst->event, "action %s: " + "failed to execute program `%s': " + "vnd.dovecot.%s extension is unconfigured", + action, program_name, action); + *error_r = SIEVE_ERROR_NOT_FOUND; + return NULL; + } + + /* Try socket first */ + if ( ext_config->socket_dir != NULL ) { + path = t_strconcat(senv->user->set->base_dir, "/", + ext_config->socket_dir, "/", program_name, NULL); + if ( stat(path, &st) < 0 ) { + switch ( errno ) { + case ENOENT: + e_debug(svinst->event, "action %s: " + "socket path `%s' for program `%s' not found", + action, path, program_name); + break; + case EACCES: + e_error(svinst->event, "action %s: " + "failed to stat socket: %s", + action, eacces_error_get("stat", path)); + *error_r = SIEVE_ERROR_NO_PERMISSION; + return NULL; + default: + e_error(svinst->event, "action %s: " + "failed to stat socket `%s': %m", + action, path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return NULL; + } + path = NULL; + } else if ( !S_ISSOCK(st.st_mode) ) { + e_error(svinst->event, "action %s: " + "socket path `%s' for program `%s' is not a socket", + action, path, program_name); + *error_r = SIEVE_ERROR_NOT_POSSIBLE; + return NULL; + } + } + + /* Try executable next */ + if ( path == NULL && ext_config->bin_dir != NULL ) { + fork = TRUE; + path = t_strconcat(ext_config->bin_dir, "/", program_name, NULL); + if ( stat(path, &st) < 0 ) { + switch ( errno ) { + case ENOENT: + e_debug(svinst->event, "action %s: " + "executable path `%s' for program `%s' not found", + action, path, program_name); + *error_r = SIEVE_ERROR_NOT_FOUND; + break; + case EACCES: + e_error(svinst->event, "action %s: " + "failed to stat program: %s", + action, eacces_error_get("stat", path)); + *error_r = SIEVE_ERROR_NO_PERMISSION; + break; + default: + e_error(svinst->event, "action %s: " + "failed to stat program `%s': %m", + action, path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + break; + } + + return NULL; + } else if ( !S_ISREG(st.st_mode) ) { + e_error(svinst->event, "action %s: " + "executable `%s' for program `%s' is not a regular file", + action, path, program_name); + *error_r = SIEVE_ERROR_NOT_POSSIBLE; + return NULL; + } else if ( (st.st_mode & S_IWOTH) != 0 ) { + e_error(svinst->event, "action %s: " + "executable `%s' for program `%s' is world-writable", + action, path, program_name); + *error_r = SIEVE_ERROR_NO_PERMISSION; + return NULL; + } + } + + /* None found ? */ + if ( path == NULL ) { + e_error(svinst->event, "action %s: " + "program `%s' not found", action, program_name); + *error_r = SIEVE_ERROR_NOT_FOUND; + return NULL; + } + + sprog = i_new(struct sieve_extprogram, 1); + sprog->svinst = ext->svinst; + sprog->ext_config = ext_config; + sprog->scriptenv = senv; + + sprog->set.client_connect_timeout_msecs = + SIEVE_EXTPROGRAMS_CONNECT_TIMEOUT_MSECS; + sprog->set.input_idle_timeout_msecs = + ext_config->execute_timeout * 1000; + restrict_access_init(&sprog->set.restrict_set); + if (senv->user->uid != 0) + sprog->set.restrict_set.uid = senv->user->uid; + if (senv->user->gid != 0) + sprog->set.restrict_set.gid = senv->user->gid; + sprog->set.debug = svinst->debug; + + if ( fork ) { + sprog->program_client = + program_client_local_create(path, args, &sprog->set); + } else { + sprog->program_client = + program_client_unix_create(path, args, &sprog->set, FALSE); + } + + if ( svinst->username != NULL ) + program_client_set_env(sprog->program_client, "USER", svinst->username); + if ( svinst->home_dir != NULL ) + program_client_set_env(sprog->program_client, "HOME", svinst->home_dir); + if ( svinst->hostname != NULL ) + program_client_set_env(sprog->program_client, "HOST", svinst->hostname); + + sender = msgdata->envelope.mail_from; + recipient = msgdata->envelope.rcpt_to; + orig_recipient = NULL; + if ( msgdata->envelope.rcpt_params != NULL ) + orig_recipient = msgdata->envelope.rcpt_params->orcpt.addr; + + if ( !smtp_address_isnull(sender) ) { + program_client_set_env(sprog->program_client, "SENDER", + smtp_address_encode(sender)); + } + if ( !smtp_address_isnull(recipient) ) { + program_client_set_env(sprog->program_client, "RECIPIENT", + smtp_address_encode(recipient)); + } + if ( !smtp_address_isnull(orig_recipient) ) { + program_client_set_env(sprog->program_client, "ORIG_RECIPIENT", + smtp_address_encode(orig_recipient)); + } + + return sprog; +} + +void sieve_extprogram_destroy(struct sieve_extprogram **_sprog) +{ + struct sieve_extprogram *sprog = *_sprog; + + program_client_destroy(&sprog->program_client); + i_free(sprog); + *_sprog = NULL; +} + +/* I/0 */ + +void sieve_extprogram_set_output +(struct sieve_extprogram *sprog, struct ostream *output) +{ + program_client_set_output(sprog->program_client, output); +} + +void sieve_extprogram_set_input +(struct sieve_extprogram *sprog, struct istream *input) +{ + switch (sprog->ext_config->default_input_eol) { + case SIEVE_EXTPROGRAMS_EOL_LF: + input = i_stream_create_lf(input); + break; + case SIEVE_EXTPROGRAMS_EOL_CRLF: + input = i_stream_create_crlf(input); + break; + default: + i_unreached(); + } + + program_client_set_input(sprog->program_client, input); + + i_stream_unref(&input); +} + +void sieve_extprogram_set_output_seekable +(struct sieve_extprogram *sprog) +{ + string_t *prefix; + prefix = t_str_new(128); + mail_user_set_get_temp_prefix(prefix, sprog->scriptenv->user->set); + + program_client_set_output_seekable(sprog->program_client, str_c(prefix)); +} + +struct istream *sieve_extprogram_get_output_seekable +(struct sieve_extprogram *sprog) +{ + return program_client_get_output_seekable(sprog->program_client); +} + +int sieve_extprogram_set_input_mail +(struct sieve_extprogram *sprog, struct mail *mail) +{ + struct istream *input; + + if (mail_get_stream(mail, NULL, NULL, &input) < 0) + return -1; + + sieve_extprogram_set_input(sprog, input); + return 1; +} + +int sieve_extprogram_run(struct sieve_extprogram *sprog) +{ + switch (program_client_run(sprog->program_client)) { + case PROGRAM_CLIENT_EXIT_STATUS_INTERNAL_FAILURE: + return -1; + case PROGRAM_CLIENT_EXIT_STATUS_FAILURE: + return 0; + case PROGRAM_CLIENT_EXIT_STATUS_SUCCESS: + return 1; + } + i_unreached(); +} + diff --git a/pigeonhole/src/plugins/sieve-extprograms/sieve-extprograms-common.h b/pigeonhole/src/plugins/sieve-extprograms/sieve-extprograms-common.h new file mode 100644 index 0000000..063c02b --- /dev/null +++ b/pigeonhole/src/plugins/sieve-extprograms/sieve-extprograms-common.h @@ -0,0 +1,107 @@ +#ifndef SIEVE_EXTPROGRAMS_COMMON_H +#define SIEVE_EXTPROGRAMS_COMMON_H + +#include "sieve-common.h" + +/* + * Extension configuration + */ + +enum sieve_extprograms_eol { + SIEVE_EXTPROGRAMS_EOL_CRLF = 0, + SIEVE_EXTPROGRAMS_EOL_LF +}; + +struct sieve_extprograms_config { + const struct sieve_extension *copy_ext; + const struct sieve_extension *var_ext; + + char *socket_dir; + char *bin_dir; + + enum sieve_extprograms_eol default_input_eol; + + unsigned int execute_timeout; +}; + +struct sieve_extprograms_config *sieve_extprograms_config_init + (const struct sieve_extension *ext); +void sieve_extprograms_config_deinit + (struct sieve_extprograms_config **ext_config); + +/* + * Extensions + */ + +extern const struct sieve_extension_def sieve_ext_vnd_pipe; +extern const struct sieve_extension_def sieve_ext_vnd_filter; +extern const struct sieve_extension_def sieve_ext_vnd_execute; + +/* + * Commands + */ + +extern const struct sieve_command_def sieve_cmd_pipe; +extern const struct sieve_command_def sieve_cmd_filter; +extern const struct sieve_command_def sieve_cmd_execute; + +/* + * Operations + */ + +extern const struct sieve_operation_def sieve_opr_pipe; +extern const struct sieve_operation_def sieve_opr_filter; +extern const struct sieve_operation_def sieve_opr_execute; + +/* + * Program name and arguments + */ + +bool sieve_extprogram_arg_is_valid(string_t *arg); +bool sieve_extprogram_name_is_valid(string_t *name); + +/* + * Command validation + */ + +bool sieve_extprogram_command_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); + +/* + * Common command operands + */ + +int sieve_extprogram_command_read_operands + (const struct sieve_runtime_env *renv, sieve_size_t *address, + string_t **pname_r, struct sieve_stringlist **args_list_r); + +/* + * Running external programs + */ + +void sieve_extprogram_exec_error + (struct sieve_error_handler *ehandler, const char *location, + const char *fmt, ...) ATTR_FORMAT(3, 4); + +struct sieve_extprogram *sieve_extprogram_create + (const struct sieve_extension *ext, const struct sieve_script_env *senv, + const struct sieve_message_data *msgdata, const char *action, + const char *program_name, const char * const *args, + enum sieve_error *error_r); +void sieve_extprogram_destroy(struct sieve_extprogram **_sprog); + +void sieve_extprogram_set_output + (struct sieve_extprogram *sprog, struct ostream *output); +void sieve_extprogram_set_output_seekable + (struct sieve_extprogram *sprog); +struct istream *sieve_extprogram_get_output_seekable + (struct sieve_extprogram *sprog); + +void sieve_extprogram_set_input + (struct sieve_extprogram *sprog, struct istream *input); +int sieve_extprogram_set_input_mail + (struct sieve_extprogram *sprog, struct mail *mail); + +int sieve_extprogram_run(struct sieve_extprogram *sprog); + +#endif diff --git a/pigeonhole/src/plugins/sieve-extprograms/sieve-extprograms-plugin.c b/pigeonhole/src/plugins/sieve-extprograms/sieve-extprograms-plugin.c new file mode 100644 index 0000000..bf17a80 --- /dev/null +++ b/pigeonhole/src/plugins/sieve-extprograms/sieve-extprograms-plugin.c @@ -0,0 +1,68 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-extensions.h" + +#include "sieve-extprograms-common.h" +#include "sieve-extprograms-plugin.h" + +/* + * Sieve plugin interface + */ + +struct _plugin_context { + const struct sieve_extension *ext_pipe; + const struct sieve_extension *ext_filter; + const struct sieve_extension *ext_execute; +}; + +const char *sieve_extprograms_plugin_version = PIGEONHOLE_ABI_VERSION; + +void sieve_extprograms_plugin_load +(struct sieve_instance *svinst, void **context) +{ + struct _plugin_context *pctx = i_new(struct _plugin_context, 1); + + pctx->ext_pipe = sieve_extension_register + (svinst, &sieve_ext_vnd_pipe, FALSE); + pctx->ext_filter = sieve_extension_register + (svinst, &sieve_ext_vnd_filter, FALSE); + pctx->ext_execute = sieve_extension_register + (svinst, &sieve_ext_vnd_execute, FALSE); + + if ( svinst->debug ) { + e_debug(svinst->event, + "Sieve Extprograms plugin for %s version %s loaded", + PIGEONHOLE_NAME, PIGEONHOLE_VERSION_FULL); + } + + *context = (void *)pctx; +} + +void sieve_extprograms_plugin_unload +(struct sieve_instance *svinst ATTR_UNUSED, void *context) +{ + struct _plugin_context *pctx = (struct _plugin_context *)context; + + sieve_extension_unregister(pctx->ext_pipe); + sieve_extension_unregister(pctx->ext_filter); + sieve_extension_unregister(pctx->ext_execute); + + i_free(pctx); +} + +/* + * Module interface + */ + +void sieve_extprograms_plugin_init(void) +{ + /* Nothing */ +} + +void sieve_extprograms_plugin_deinit(void) +{ + /* Nothing */ +} diff --git a/pigeonhole/src/plugins/sieve-extprograms/sieve-extprograms-plugin.h b/pigeonhole/src/plugins/sieve-extprograms/sieve-extprograms-plugin.h new file mode 100644 index 0000000..20fa55a --- /dev/null +++ b/pigeonhole/src/plugins/sieve-extprograms/sieve-extprograms-plugin.h @@ -0,0 +1,20 @@ +#ifndef SIEVE_EXTPROGRAMS_PLUGIN_H +#define SIEVE_EXTPROGRAMS_PLUGIN_H + +/* + * Plugin interface + */ + +void sieve_extprograms_plugin_load + (struct sieve_instance *svinst, void **context); +void sieve_extprograms_plugin_unload + (struct sieve_instance *svinst, void *context); + +/* + * Module interface + */ + +void sieve_extprograms_plugin_init(void); +void sieve_extprograms_plugin_deinit(void); + +#endif diff --git a/pigeonhole/src/sieve-tools/Makefile.am b/pigeonhole/src/sieve-tools/Makefile.am new file mode 100644 index 0000000..f2ceffd --- /dev/null +++ b/pigeonhole/src/sieve-tools/Makefile.am @@ -0,0 +1,59 @@ +bin_PROGRAMS = sievec sieve-dump sieve-test sieve-filter + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib-sieve \ + -I$(top_srcdir)/src/lib-sieve-tool \ + -I$(srcdir)/debug \ + $(LIBDOVECOT_INCLUDE) \ + $(LIBDOVECOT_SERVICE_INCLUDE) + +libs = \ + $(top_builddir)/src/lib-sieve/libdovecot-sieve.la \ + $(top_builddir)/src/lib-sieve-tool/libsieve-tool.la + +libs_ldadd = $(libs) $(LIBDOVECOT_STORAGE) $(LIBDOVECOT_LDA) $(LIBDOVECOT) +libs_deps = $(libs) $(LIBDOVECOT_STORAGE_DEPS) $(LIBDOVECOT_LDA_DEPS) $(LIBDOVECOT_DEPS) + +# Sieve Compile Tool + +sievec_CPPFLAGS = $(AM_CPPFLAGS) $(BINARY_CFLAGS) +sievec_LDFLAGS = -export-dynamic $(BINARY_LDFLAGS) +sievec_LDADD = $(libs_ldadd) +sievec_DEPENDENCIES = $(libs_deps) + +sievec_SOURCES = \ + sievec.c + +# Sieve Dump Tool + +sieve_dump_CPPFLAGS = $(AM_CPPFLAGS) $(BINARY_CFLAGS) +sieve_dump_LDFLAGS = -export-dynamic $(BINARY_LDFLAGS) +sieve_dump_LDADD = $(libs_ldadd) +sieve_dump_DEPENDENCIES = $(libs_deps) + +sieve_dump_SOURCES = \ + sieve-dump.c + +# Sieve Test Tool + +sieve_test_CPPFLAGS = $(AM_CPPFLAGS) $(BINARY_CFLAGS) +sieve_test_LDFLAGS = -export-dynamic $(BINARY_LDFLAGS) +sieve_test_LDADD = $(libs_ldadd) +sieve_test_DEPENDENCIES = $(libs_deps) + +sieve_test_SOURCES = \ + sieve-test.c + +## Unfinished tools + +# Sieve Filter Tool + +sieve_filter_CPPFLAGS = $(AM_CPPFLAGS) $(BINARY_CFLAGS) +sieve_filter_LDFLAGS = -export-dynamic $(BINARY_LDFLAGS) +sieve_filter_LDADD = $(libs_ldadd) +sieve_filter_DEPENDENCIES = $(libs_deps) + +sieve_filter_SOURCES = \ + sieve-filter.c + +noinst_HEADERS = diff --git a/pigeonhole/src/sieve-tools/Makefile.in b/pigeonhole/src/sieve-tools/Makefile.in new file mode 100644 index 0000000..6b5b0d0 --- /dev/null +++ b/pigeonhole/src/sieve-tools/Makefile.in @@ -0,0 +1,863 @@ +# 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@ +bin_PROGRAMS = sievec$(EXEEXT) sieve-dump$(EXEEXT) sieve-test$(EXEEXT) \ + sieve-filter$(EXEEXT) +subdir = src/sieve-tools +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 = +am__installdirs = "$(DESTDIR)$(bindir)" +PROGRAMS = $(bin_PROGRAMS) +am_sieve_dump_OBJECTS = sieve_dump-sieve-dump.$(OBJEXT) +sieve_dump_OBJECTS = $(am_sieve_dump_OBJECTS) +am__DEPENDENCIES_1 = +am__DEPENDENCIES_2 = $(libs) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +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 = +sieve_dump_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(sieve_dump_LDFLAGS) $(LDFLAGS) -o $@ +am_sieve_filter_OBJECTS = sieve_filter-sieve-filter.$(OBJEXT) +sieve_filter_OBJECTS = $(am_sieve_filter_OBJECTS) +sieve_filter_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(sieve_filter_LDFLAGS) $(LDFLAGS) -o $@ +am_sieve_test_OBJECTS = sieve_test-sieve-test.$(OBJEXT) +sieve_test_OBJECTS = $(am_sieve_test_OBJECTS) +sieve_test_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(sieve_test_LDFLAGS) $(LDFLAGS) -o $@ +am_sievec_OBJECTS = sievec-sievec.$(OBJEXT) +sievec_OBJECTS = $(am_sievec_OBJECTS) +sievec_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(sievec_LDFLAGS) $(LDFLAGS) -o $@ +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_dump-sieve-dump.Po \ + ./$(DEPDIR)/sieve_filter-sieve-filter.Po \ + ./$(DEPDIR)/sieve_test-sieve-test.Po \ + ./$(DEPDIR)/sievec-sievec.Po +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 = $(sieve_dump_SOURCES) $(sieve_filter_SOURCES) \ + $(sieve_test_SOURCES) $(sievec_SOURCES) +DIST_SOURCES = $(sieve_dump_SOURCES) $(sieve_filter_SOURCES) \ + $(sieve_test_SOURCES) $(sievec_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@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib-sieve \ + -I$(top_srcdir)/src/lib-sieve-tool \ + -I$(srcdir)/debug \ + $(LIBDOVECOT_INCLUDE) \ + $(LIBDOVECOT_SERVICE_INCLUDE) + +libs = \ + $(top_builddir)/src/lib-sieve/libdovecot-sieve.la \ + $(top_builddir)/src/lib-sieve-tool/libsieve-tool.la + +libs_ldadd = $(libs) $(LIBDOVECOT_STORAGE) $(LIBDOVECOT_LDA) $(LIBDOVECOT) +libs_deps = $(libs) $(LIBDOVECOT_STORAGE_DEPS) $(LIBDOVECOT_LDA_DEPS) $(LIBDOVECOT_DEPS) + +# Sieve Compile Tool +sievec_CPPFLAGS = $(AM_CPPFLAGS) $(BINARY_CFLAGS) +sievec_LDFLAGS = -export-dynamic $(BINARY_LDFLAGS) +sievec_LDADD = $(libs_ldadd) +sievec_DEPENDENCIES = $(libs_deps) +sievec_SOURCES = \ + sievec.c + + +# Sieve Dump Tool +sieve_dump_CPPFLAGS = $(AM_CPPFLAGS) $(BINARY_CFLAGS) +sieve_dump_LDFLAGS = -export-dynamic $(BINARY_LDFLAGS) +sieve_dump_LDADD = $(libs_ldadd) +sieve_dump_DEPENDENCIES = $(libs_deps) +sieve_dump_SOURCES = \ + sieve-dump.c + + +# Sieve Test Tool +sieve_test_CPPFLAGS = $(AM_CPPFLAGS) $(BINARY_CFLAGS) +sieve_test_LDFLAGS = -export-dynamic $(BINARY_LDFLAGS) +sieve_test_LDADD = $(libs_ldadd) +sieve_test_DEPENDENCIES = $(libs_deps) +sieve_test_SOURCES = \ + sieve-test.c + + +# Sieve Filter Tool +sieve_filter_CPPFLAGS = $(AM_CPPFLAGS) $(BINARY_CFLAGS) +sieve_filter_LDFLAGS = -export-dynamic $(BINARY_LDFLAGS) +sieve_filter_LDADD = $(libs_ldadd) +sieve_filter_DEPENDENCIES = $(libs_deps) +sieve_filter_SOURCES = \ + sieve-filter.c + +noinst_HEADERS = +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/sieve-tools/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/sieve-tools/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): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +sieve-dump$(EXEEXT): $(sieve_dump_OBJECTS) $(sieve_dump_DEPENDENCIES) $(EXTRA_sieve_dump_DEPENDENCIES) + @rm -f sieve-dump$(EXEEXT) + $(AM_V_CCLD)$(sieve_dump_LINK) $(sieve_dump_OBJECTS) $(sieve_dump_LDADD) $(LIBS) + +sieve-filter$(EXEEXT): $(sieve_filter_OBJECTS) $(sieve_filter_DEPENDENCIES) $(EXTRA_sieve_filter_DEPENDENCIES) + @rm -f sieve-filter$(EXEEXT) + $(AM_V_CCLD)$(sieve_filter_LINK) $(sieve_filter_OBJECTS) $(sieve_filter_LDADD) $(LIBS) + +sieve-test$(EXEEXT): $(sieve_test_OBJECTS) $(sieve_test_DEPENDENCIES) $(EXTRA_sieve_test_DEPENDENCIES) + @rm -f sieve-test$(EXEEXT) + $(AM_V_CCLD)$(sieve_test_LINK) $(sieve_test_OBJECTS) $(sieve_test_LDADD) $(LIBS) + +sievec$(EXEEXT): $(sievec_OBJECTS) $(sievec_DEPENDENCIES) $(EXTRA_sievec_DEPENDENCIES) + @rm -f sievec$(EXEEXT) + $(AM_V_CCLD)$(sievec_LINK) $(sievec_OBJECTS) $(sievec_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve_dump-sieve-dump.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve_filter-sieve-filter.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve_test-sieve-test.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sievec-sievec.Po@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 $@ $< + +sieve_dump-sieve-dump.o: sieve-dump.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sieve_dump_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sieve_dump-sieve-dump.o -MD -MP -MF $(DEPDIR)/sieve_dump-sieve-dump.Tpo -c -o sieve_dump-sieve-dump.o `test -f 'sieve-dump.c' || echo '$(srcdir)/'`sieve-dump.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/sieve_dump-sieve-dump.Tpo $(DEPDIR)/sieve_dump-sieve-dump.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sieve-dump.c' object='sieve_dump-sieve-dump.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sieve_dump_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sieve_dump-sieve-dump.o `test -f 'sieve-dump.c' || echo '$(srcdir)/'`sieve-dump.c + +sieve_dump-sieve-dump.obj: sieve-dump.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sieve_dump_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sieve_dump-sieve-dump.obj -MD -MP -MF $(DEPDIR)/sieve_dump-sieve-dump.Tpo -c -o sieve_dump-sieve-dump.obj `if test -f 'sieve-dump.c'; then $(CYGPATH_W) 'sieve-dump.c'; else $(CYGPATH_W) '$(srcdir)/sieve-dump.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/sieve_dump-sieve-dump.Tpo $(DEPDIR)/sieve_dump-sieve-dump.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sieve-dump.c' object='sieve_dump-sieve-dump.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sieve_dump_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sieve_dump-sieve-dump.obj `if test -f 'sieve-dump.c'; then $(CYGPATH_W) 'sieve-dump.c'; else $(CYGPATH_W) '$(srcdir)/sieve-dump.c'; fi` + +sieve_filter-sieve-filter.o: sieve-filter.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sieve_filter_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sieve_filter-sieve-filter.o -MD -MP -MF $(DEPDIR)/sieve_filter-sieve-filter.Tpo -c -o sieve_filter-sieve-filter.o `test -f 'sieve-filter.c' || echo '$(srcdir)/'`sieve-filter.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/sieve_filter-sieve-filter.Tpo $(DEPDIR)/sieve_filter-sieve-filter.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sieve-filter.c' object='sieve_filter-sieve-filter.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sieve_filter_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sieve_filter-sieve-filter.o `test -f 'sieve-filter.c' || echo '$(srcdir)/'`sieve-filter.c + +sieve_filter-sieve-filter.obj: sieve-filter.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sieve_filter_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sieve_filter-sieve-filter.obj -MD -MP -MF $(DEPDIR)/sieve_filter-sieve-filter.Tpo -c -o sieve_filter-sieve-filter.obj `if test -f 'sieve-filter.c'; then $(CYGPATH_W) 'sieve-filter.c'; else $(CYGPATH_W) '$(srcdir)/sieve-filter.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/sieve_filter-sieve-filter.Tpo $(DEPDIR)/sieve_filter-sieve-filter.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sieve-filter.c' object='sieve_filter-sieve-filter.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sieve_filter_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sieve_filter-sieve-filter.obj `if test -f 'sieve-filter.c'; then $(CYGPATH_W) 'sieve-filter.c'; else $(CYGPATH_W) '$(srcdir)/sieve-filter.c'; fi` + +sieve_test-sieve-test.o: sieve-test.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sieve_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sieve_test-sieve-test.o -MD -MP -MF $(DEPDIR)/sieve_test-sieve-test.Tpo -c -o sieve_test-sieve-test.o `test -f 'sieve-test.c' || echo '$(srcdir)/'`sieve-test.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/sieve_test-sieve-test.Tpo $(DEPDIR)/sieve_test-sieve-test.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sieve-test.c' object='sieve_test-sieve-test.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sieve_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sieve_test-sieve-test.o `test -f 'sieve-test.c' || echo '$(srcdir)/'`sieve-test.c + +sieve_test-sieve-test.obj: sieve-test.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sieve_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sieve_test-sieve-test.obj -MD -MP -MF $(DEPDIR)/sieve_test-sieve-test.Tpo -c -o sieve_test-sieve-test.obj `if test -f 'sieve-test.c'; then $(CYGPATH_W) 'sieve-test.c'; else $(CYGPATH_W) '$(srcdir)/sieve-test.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/sieve_test-sieve-test.Tpo $(DEPDIR)/sieve_test-sieve-test.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sieve-test.c' object='sieve_test-sieve-test.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sieve_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sieve_test-sieve-test.obj `if test -f 'sieve-test.c'; then $(CYGPATH_W) 'sieve-test.c'; else $(CYGPATH_W) '$(srcdir)/sieve-test.c'; fi` + +sievec-sievec.o: sievec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sievec_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sievec-sievec.o -MD -MP -MF $(DEPDIR)/sievec-sievec.Tpo -c -o sievec-sievec.o `test -f 'sievec.c' || echo '$(srcdir)/'`sievec.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/sievec-sievec.Tpo $(DEPDIR)/sievec-sievec.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sievec.c' object='sievec-sievec.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sievec_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sievec-sievec.o `test -f 'sievec.c' || echo '$(srcdir)/'`sievec.c + +sievec-sievec.obj: sievec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sievec_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sievec-sievec.obj -MD -MP -MF $(DEPDIR)/sievec-sievec.Tpo -c -o sievec-sievec.obj `if test -f 'sievec.c'; then $(CYGPATH_W) 'sievec.c'; else $(CYGPATH_W) '$(srcdir)/sievec.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/sievec-sievec.Tpo $(DEPDIR)/sievec-sievec.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sievec.c' object='sievec-sievec.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sievec_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sievec-sievec.obj `if test -f 'sievec.c'; then $(CYGPATH_W) 'sievec.c'; else $(CYGPATH_W) '$(srcdir)/sievec.c'; fi` + +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 $(PROGRAMS) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/sieve_dump-sieve-dump.Po + -rm -f ./$(DEPDIR)/sieve_filter-sieve-filter.Po + -rm -f ./$(DEPDIR)/sieve_test-sieve-test.Po + -rm -f ./$(DEPDIR)/sievec-sievec.Po + -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-binPROGRAMS + +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_dump-sieve-dump.Po + -rm -f ./$(DEPDIR)/sieve_filter-sieve-filter.Po + -rm -f ./$(DEPDIR)/sieve_test-sieve-test.Po + -rm -f ./$(DEPDIR)/sievec-sievec.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-binPROGRAMS clean-generic clean-libtool 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-binPROGRAMS \ + 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 uninstall-binPROGRAMS + +.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/sieve-tools/sieve-dump.c b/pigeonhole/src/sieve-tools/sieve-dump.c new file mode 100644 index 0000000..23b2be4 --- /dev/null +++ b/pigeonhole/src/sieve-tools/sieve-dump.c @@ -0,0 +1,97 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" +#include "master-service.h" +#include "master-service-settings.h" +#include "mail-storage-service.h" +#include "mail-user.h" + +#include "sieve.h" +#include "sieve-extensions.h" +#include "sieve-tool.h" + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <sysexits.h> + +/* + * Print help + */ + +static void print_help(void) +{ + printf( +"Usage: sieve-dump [-c <config-file>] [-D] [-h] [-P <plugin>] [-x <extensions>]\n" +" <sieve-binary> [<out-file>]\n" + ); +} + +/* + * Tool implementation + */ + +int main(int argc, char **argv) +{ + struct sieve_instance *svinst; + struct sieve_binary *sbin; + const char *binfile, *outfile; + bool hexdump = FALSE; + int exit_status = EXIT_SUCCESS; + int c; + + sieve_tool = sieve_tool_init("sieve-dump", &argc, &argv, "DhP:x:", FALSE); + + outfile = NULL; + + while ((c = sieve_tool_getopt(sieve_tool)) > 0) { + switch (c) { + case 'h': + /* produce hexdump */ + hexdump = TRUE; + break; + default: + print_help(); + i_fatal_status(EX_USAGE, "Unknown argument: %c", c); + break; + } + } + + if ( optind < argc ) { + binfile = argv[optind++]; + } else { + print_help(); + i_fatal_status(EX_USAGE, "Missing <script-file> argument"); + } + + if ( optind < argc ) { + outfile = argv[optind++]; + } + + /* Finish tool initialization */ + svinst = sieve_tool_init_finish(sieve_tool, FALSE, TRUE); + + /* Enable debug extension */ + sieve_enable_debug_extension(svinst); + + /* Dump binary */ + sbin = sieve_load(svinst, binfile, NULL); + if ( sbin != NULL ) { + sieve_tool_dump_binary_to(sbin, outfile == NULL ? "-" : outfile, hexdump); + + sieve_close(&sbin); + } else { + i_error("failed to load binary: %s", binfile); + exit_status = EXIT_FAILURE; + } + + sieve_tool_deinit(&sieve_tool); + + return exit_status; +} + diff --git a/pigeonhole/src/sieve-tools/sieve-filter.c b/pigeonhole/src/sieve-tools/sieve-filter.c new file mode 100644 index 0000000..2b98fe6 --- /dev/null +++ b/pigeonhole/src/sieve-tools/sieve-filter.c @@ -0,0 +1,614 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "lib-signals.h" +#include "ioloop.h" +#include "env-util.h" +#include "str.h" +#include "str-sanitize.h" +#include "ostream.h" +#include "array.h" +#include "mail-namespace.h" +#include "mail-storage.h" +#include "mail-search-build.h" + +#include "sieve.h" +#include "sieve-extensions.h" +#include "sieve-binary.h" + +#include "sieve-tool.h" + +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <pwd.h> +#include <sysexits.h> + +/* + * Print help + */ + +static void print_help(void) +{ + printf( +"Usage: sieve-filter [-c <config-file>] [-C] [-D] [-e] [-m <default-mailbox>]\n" +" [-P <plugin>] [-q <output-mailbox>] [-Q <mail-command>]\n" +" [-s <script-file>] [-u <user>] [-v] [-W] [-x <extensions>]\n" +" <script-file> <source-mailbox> [<discard-action>]\n" + ); +} + +enum sieve_filter_discard_action { + SIEVE_FILTER_DACT_KEEP, /* Keep discarded messages in source folder */ + SIEVE_FILTER_DACT_MOVE, /* Move discarded messages to Trash folder */ + SIEVE_FILTER_DACT_DELETE, /* Flag discarded messages as \DELETED */ + SIEVE_FILTER_DACT_EXPUNGE /* Expunge discarded messages */ +}; + +struct sieve_filter_data { + enum sieve_filter_discard_action discard_action; + struct mailbox *move_mailbox; + + struct sieve_script_env *senv; + struct sieve_binary *main_sbin; + struct sieve_error_handler *ehandler; + + bool execute:1; + bool source_write:1; + bool default_move:1; +}; + +struct sieve_filter_context { + const struct sieve_filter_data *data; + + struct mailbox_transaction_context *move_trans; + + struct ostream *teststream; +}; + +static const char * +result_amend_log_message(const struct sieve_script_env *senv, + enum log_type log_type, const char *message) +{ + const struct sieve_message_data *msgdata = senv->script_context; + string_t *str; + + if (log_type == LOG_TYPE_DEBUG) + return message; + + str = t_str_new(256); + str_printfa(str, "msgid=%s", (msgdata->id == NULL ? + "unspecified" : msgdata->id)); + str_append(str, ": "); + str_append(str, message); + + return str_c(str); +} + +static int filter_message(struct sieve_filter_context *sfctx, struct mail *mail) +{ + struct sieve_error_handler *ehandler = sfctx->data->ehandler; + enum sieve_execute_flags exflags = SIEVE_EXECUTE_FLAG_LOG_RESULT; + struct sieve_script_env *senv = sfctx->data->senv; + struct sieve_exec_status estatus; + struct sieve_binary *sbin; + struct sieve_message_data msgdata; + bool execute = sfctx->data->execute; + bool source_write = sfctx->data->source_write; + const char *subject, *date; + uoff_t size = 0; + int ret; + + /* Initialize execution status */ + i_zero(&estatus); + senv->exec_status = &estatus; + + /* Collect necessary message data */ + i_zero(&msgdata); + msgdata.mail = mail; + msgdata.auth_user = senv->user->username; + (void)mail_get_message_id(mail, &msgdata.id); + senv->script_context = &msgdata; + + sieve_tool_get_envelope_data(&msgdata, mail, NULL, NULL, NULL); + + if (mail_get_virtual_size(mail, &size) < 0) { + if (mail->expunged) + return 1; + + sieve_error(ehandler, NULL, "failed to obtain message size; " + "skipping this message (id=%s)", + (msgdata.id == NULL ? "none" : msgdata.id)); + return 0; + } + + if (mail_get_first_header(mail, "date", &date) <= 0) + date = ""; + if (mail_get_first_header(mail, "subject", &subject) <= 0) + subject = ""; + + /* Single script */ + sbin = sfctx->data->main_sbin; + + /* Execute script */ + if (execute) { + sieve_info(ehandler, NULL, + "filtering: [%s; %"PRIuUOFF_T" bytes] `%s'", + date, size, str_sanitize(subject, 40)); + + ret = sieve_execute(sbin, &msgdata, senv, ehandler, ehandler, + exflags); + } else { + o_stream_nsend_str( + sfctx->teststream, + t_strdup_printf(">> Filtering message:\n\n" + " ID: %s\n" + " Date: %s\n" + " Size: %"PRIuUOFF_T" bytes\n" + " Subject: %s\n", + (msgdata.id == NULL ? + "none" : msgdata.id), date, size, + str_sanitize(subject, 40))); + + ret = sieve_test(sbin, &msgdata, senv, ehandler, + sfctx->teststream, exflags); + } + + /* Handle message in source folder */ + if (ret > 0) { + struct mailbox *move_box = sfctx->data->move_mailbox; + enum sieve_filter_discard_action discard_action = + sfctx->data->discard_action; + + if (!source_write) { + /* READ-ONLY; Do nothing */ + } else if (estatus.keep_original) { + /* Explicitly `stored' in source box; just keep it there */ + sieve_info(ehandler, NULL, + "message kept in source mailbox"); + } else if (estatus.message_saved) { + sieve_info( + ehandler, NULL, + "message expunged from source mailbox upon successful move"); + + if (execute) + mail_expunge(mail); + } else { + switch (discard_action) { + /* Leave it there */ + case SIEVE_FILTER_DACT_KEEP: + sieve_info(ehandler, NULL, + "message left in source mailbox"); + break; + /* Move message to indicated folder */ + case SIEVE_FILTER_DACT_MOVE: + sieve_info( + ehandler, NULL, + "message in source mailbox moved to mailbox '%s'", + mailbox_get_name(move_box)); + + if (execute && move_box != NULL) { + struct mailbox_transaction_context *t = + sfctx->move_trans; + struct mail_save_context *save_ctx; + + save_ctx = mailbox_save_alloc(t); + + if (mailbox_copy(&save_ctx, mail) < 0) { + enum mail_error error; + const char *errstr; + + errstr = mail_storage_get_last_error( + mailbox_get_storage(move_box), &error); + + sieve_error( + ehandler, NULL, + "failed to move message to mailbox %s: %s", + mailbox_get_name(move_box), errstr); + return -1; + } + mail_expunge(mail); + } + break; + /* Flag message as \DELETED */ + case SIEVE_FILTER_DACT_DELETE: + sieve_info( + ehandler, NULL, + "message flagged as deleted in source mailbox"); + if (execute) + mail_update_flags(mail, MODIFY_ADD, MAIL_DELETED); + break; + /* Expunge the message immediately */ + case SIEVE_FILTER_DACT_EXPUNGE: + sieve_info(ehandler, NULL, + "message expunged from source mailbox"); + if (execute) + mail_expunge(mail); + break; + /* Unknown */ + default: + i_unreached(); + break; + } + } + } + + switch (ret) { + case SIEVE_EXEC_OK: + break; + case SIEVE_EXEC_RESOURCE_LIMIT: + sieve_error(ehandler, NULL, "sieve resource limit exceeded"); + return -1; + case SIEVE_EXEC_BIN_CORRUPT: + sieve_error(ehandler, NULL, "sieve script binary is corrupt"); + return -1; + case SIEVE_EXEC_FAILURE: + case SIEVE_EXEC_TEMP_FAILURE: + if (source_write && execute && sfctx->data->default_move && + !estatus.keep_original && estatus.message_saved) { + /* The implicit keep action moved message to default + mailbox, so the source message still needs to be + expunged */ + sieve_error( + ehandler, NULL, + "sieve script execution failed for this message; " + "message moved to default mailbox"); + mail_expunge(mail); + return 0; + } + /* Fall through */ + case SIEVE_EXEC_KEEP_FAILED: + sieve_error( + ehandler, NULL, + "sieve script execution failed for this message; " + "message left in source mailbox"); + return 0; + } + + return 1; +} + +/* FIXME: introduce this into Dovecot */ +static void +mail_search_build_add_flags(struct mail_search_args *args, + enum mail_flags flags, bool not) +{ + struct mail_search_arg *arg; + + arg = p_new(args->pool, struct mail_search_arg, 1); + arg->type = SEARCH_FLAGS; + arg->value.flags = flags; + arg->match_not = not; + + arg->next = args->args; + args->args = arg; +} + +static int +filter_mailbox(const struct sieve_filter_data *sfdata, struct mailbox *src_box) +{ + struct sieve_filter_context sfctx; + struct mailbox *move_box = sfdata->move_mailbox; + struct sieve_error_handler *ehandler = sfdata->ehandler; + struct mail_search_args *search_args; + struct mailbox_transaction_context *t; + struct mail_search_context *search_ctx; + struct mail *mail; + int ret = 1; + + /* Sync source mailbox */ + + if (mailbox_sync(src_box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) { + sieve_error(ehandler, NULL, "failed to sync source mailbox"); + return -1; + } + + /* Initialize */ + + i_zero(&sfctx); + sfctx.data = sfdata; + + /* Create test stream */ + if (!sfdata->execute) { + sfctx.teststream = o_stream_create_fd(1, 0); + o_stream_set_no_error_handling(sfctx.teststream, TRUE); + } + + /* Start move mailbox transaction */ + + if (move_box != NULL) { + sfctx.move_trans = mailbox_transaction_begin( + move_box, MAILBOX_TRANSACTION_FLAG_EXTERNAL, + "sieve_filter_data move_box"); + } + + /* Search non-deleted messages in the source folder */ + + search_args = mail_search_build_init(); + mail_search_build_add_flags(search_args, MAIL_DELETED, TRUE); + + t = mailbox_transaction_begin(src_box, 0, + "sieve_filter_data src_box"); + search_ctx = mailbox_search_init(t, search_args, NULL, 0, NULL); + mail_search_args_unref(&search_args); + + /* Iterate through all requested messages */ + + while (ret >= 0 && mailbox_search_next(search_ctx, &mail)) + ret = filter_message(&sfctx, mail); + + /* Cleanup */ + + if (mailbox_search_deinit(&search_ctx) < 0) + ret = -1; + + if (sfctx.move_trans != NULL) { + if (mailbox_transaction_commit(&sfctx.move_trans) < 0) + ret = -1; + } + + if (mailbox_transaction_commit(&t) < 0) + ret = -1; + + if (sfctx.teststream != NULL) + o_stream_destroy(&sfctx.teststream); + + if (ret < 0) return ret; + + /* Sync mailbox */ + + if (sfdata->execute) { + if (mailbox_sync(src_box, MAILBOX_SYNC_FLAG_FULL_WRITE) < 0) { + sieve_error(ehandler, NULL, + "failed to sync source mailbox"); + return -1; + } + } + + return ret; +} + +/* + * Tool implementation + */ + +int main(int argc, char **argv) +{ + struct sieve_instance *svinst; + ARRAY_TYPE(const_string) scriptfiles; + const char *scriptfile, *src_mailbox, *dst_mailbox, *move_mailbox; + struct sieve_filter_data sfdata; + enum sieve_filter_discard_action discard_action = + SIEVE_FILTER_DACT_KEEP; + struct mail_user *mail_user; + struct sieve_binary *main_sbin; + struct sieve_script_env scriptenv; + struct sieve_error_handler *ehandler; + bool force_compile, execute, source_write, verbose, default_move; + struct mail_namespace *ns; + struct mailbox *src_box = NULL, *move_box = NULL; + enum mailbox_flags open_flags = MAILBOX_FLAG_IGNORE_ACLS; + enum mail_error error; + const char *errstr; + int c; + + sieve_tool = sieve_tool_init("sieve-filter", &argc, &argv, + "m:s:x:P:u:q:Q:DCevW", FALSE); + + t_array_init(&scriptfiles, 16); + + /* Parse arguments */ + dst_mailbox = move_mailbox = NULL; + force_compile = execute = source_write = default_move = FALSE; + verbose = FALSE; + while ((c = sieve_tool_getopt(sieve_tool)) > 0) { + switch (c) { + case 'm': + /* default mailbox (keep box) */ + dst_mailbox = optarg; + break; + case 's': + /* scriptfile executed before main script */ + { + const char *file; + + file = t_strdup(optarg); + array_append(&scriptfiles, &file, 1); + + /* FIXME: */ + i_fatal_status( + EX_USAGE, + "The -s argument is currently NOT IMPLEMENTED"); + } + break; + case 'q': + i_fatal_status( + EX_USAGE, + "The -q argument is currently NOT IMPLEMENTED"); + break; + case 'Q': + i_fatal_status( + EX_USAGE, + "The -Q argument is currently NOT IMPLEMENTED"); + break; + case 'e': + /* execution mode */ + execute = TRUE; + break; + case 'C': + /* force script compile */ + force_compile = TRUE; + break; + case 'W': + /* enable source mailbox write */ + source_write = TRUE; + break; + case 'v': + /* enable verbose output */ + verbose = TRUE; + break; + default: + /* unrecognized option */ + print_help(); + i_fatal_status(EX_USAGE, "Unknown argument: %c", c); + break; + } + } + + /* Script file argument */ + if (optind < argc) + scriptfile = t_strdup(argv[optind++]); + else { + print_help(); + i_fatal_status(EX_USAGE, "Missing <script-file> argument"); + } + + /* Source mailbox argument */ + if (optind < argc) + src_mailbox = t_strdup(argv[optind++]); + else { + print_help(); + i_fatal_status(EX_USAGE, "Missing <source-mailbox> argument"); + } + + /* Source action argument */ + if (optind < argc) { + const char *srcact = argv[optind++]; + + if (strcmp(srcact, "keep") == 0) { + discard_action = SIEVE_FILTER_DACT_KEEP; + } else if (strcmp(srcact, "move") == 0) { + discard_action = SIEVE_FILTER_DACT_MOVE; + if (optind < argc) + move_mailbox = t_strdup(argv[optind++]); + else { + print_help(); + i_fatal_status( + EX_USAGE, + "Invalid <discard-action> argument: " + "the `move' action requires mailbox argument"); + } + } else if (strcmp(srcact, "delete") == 0) { + discard_action = SIEVE_FILTER_DACT_DELETE; + } else if (strcmp(srcact, "expunge") == 0) { + discard_action = SIEVE_FILTER_DACT_EXPUNGE; + } else { + print_help(); + i_fatal_status(EX_USAGE, + "Invalid <discard-action> argument"); + } + } + + if (optind != argc) { + print_help(); + i_fatal_status(EX_USAGE, "Unknown argument: %s", argv[optind]); + } + + if (dst_mailbox == NULL) { + dst_mailbox = src_mailbox; + } else { + /* The (implicit) keep action will move the message */ + default_move = TRUE; + } + + /* Finish tool initialization */ + svinst = sieve_tool_init_finish(sieve_tool, TRUE, FALSE); + + /* Enable debug extension */ + sieve_enable_debug_extension(svinst); + + /* Create error handler */ + ehandler = sieve_stderr_ehandler_create(svinst, 0); + sieve_error_handler_accept_infolog(ehandler, verbose); + sieve_error_handler_accept_debuglog(ehandler, svinst->debug); + + /* Compile main sieve script */ + if (force_compile) { + main_sbin = sieve_tool_script_compile(svinst, scriptfile, NULL); + if (main_sbin != NULL) + (void)sieve_save(main_sbin, TRUE, NULL); + } else { + main_sbin = sieve_tool_script_open(svinst, scriptfile); + } + + /* Initialize mail user */ + mail_user = sieve_tool_get_mail_user(sieve_tool); + + /* Open the source mailbox */ + + ns = mail_namespace_find(mail_user->namespaces, src_mailbox); + if (ns == NULL) { + i_fatal("Unknown namespace for source mailbox '%s'", + src_mailbox); + } + + if (!source_write || !execute) + open_flags |= MAILBOX_FLAG_READONLY; + + src_box = mailbox_alloc(ns->list, src_mailbox, open_flags); + if (mailbox_open(src_box) < 0) { + i_fatal("Couldn't open source mailbox '%s': %s", + src_mailbox, mailbox_get_last_internal_error(src_box, &error)); + } + + /* Open move box if necessary */ + + if (execute && discard_action == SIEVE_FILTER_DACT_MOVE && + move_mailbox != NULL) { + ns = mail_namespace_find(mail_user->namespaces, move_mailbox); + if (ns == NULL) + i_fatal("Unknown namespace for mailbox '%s'", + move_mailbox); + + move_box = mailbox_alloc(ns->list, move_mailbox, open_flags); + if (mailbox_open(move_box) < 0) { + i_fatal("Couldn't open mailbox '%s': %s", + move_mailbox, + mailbox_get_last_internal_error(move_box, &error)); + } + + if (mailbox_backends_equal(src_box, move_box)) + i_fatal("Source mailbox and mailbox for move action are identical."); + } + + /* Compose script environment */ + if (sieve_script_env_init(&scriptenv, mail_user, &errstr) < 0) + i_fatal("Failed to initialize script execution: %s", errstr); + scriptenv.mailbox_autocreate = FALSE; + scriptenv.default_mailbox = dst_mailbox; + scriptenv.result_amend_log_message = result_amend_log_message; + + /* Compose filter context */ + i_zero(&sfdata); + sfdata.senv = &scriptenv; + sfdata.discard_action = discard_action; + sfdata.move_mailbox = move_box; + sfdata.main_sbin = main_sbin; + sfdata.ehandler = ehandler; + sfdata.execute = execute; + sfdata.source_write = source_write; + sfdata.default_move = default_move; + + /* Apply Sieve filter to all messages found */ + (void)filter_mailbox(&sfdata, src_box); + + /* Close the source mailbox */ + if (src_box != NULL) + mailbox_free(&src_box); + + /* Close the move mailbox */ + if (move_box != NULL) + mailbox_free(&move_box); + + /* Close the script binary */ + if (main_sbin != NULL) + sieve_close(&main_sbin); + + /* Cleanup error handler */ + sieve_error_handler_unref(&ehandler); + + sieve_tool_deinit(&sieve_tool); + + return 0; +} diff --git a/pigeonhole/src/sieve-tools/sieve-test.c b/pigeonhole/src/sieve-tools/sieve-test.c new file mode 100644 index 0000000..e7345be --- /dev/null +++ b/pigeonhole/src/sieve-tools/sieve-test.c @@ -0,0 +1,515 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "lib-signals.h" +#include "ioloop.h" +#include "env-util.h" +#include "str.h" +#include "ostream.h" +#include "array.h" +#include "mail-namespace.h" +#include "mail-storage.h" +#include "master-service.h" +#include "master-service-settings.h" +#include "mail-storage-service.h" + +#include "sieve.h" +#include "sieve-binary.h" +#include "sieve-extensions.h" + +#include "sieve-tool.h" + +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <pwd.h> +#include <sysexits.h> + + +/* + * Configuration + */ + +#define DEFAULT_SENDMAIL_PATH "/usr/lib/sendmail" + +/* + * Print help + */ + +static void print_help(void) +{ + printf( +"Usage: sieve-test [-a <orig-recipient-address] [-c <config-file>]\n" +" [-C] [-D] [-d <dump-filename>] [-e]\n" +" [-f <envelope-sender>] [-l <mail-location>]\n" +" [-m <default-mailbox>] [-P <plugin>]\n" +" [-r <recipient-address>] [-s <script-file>]\n" +" [-t <trace-file>] [-T <trace-option>] [-x <extensions>]\n" +" <script-file> <mail-file>\n" + ); +} + +/* + * Dummy SMTP session + */ + +static void * +sieve_smtp_start(const struct sieve_script_env *senv ATTR_UNUSED, + const struct smtp_address *mail_from) +{ + struct ostream *output; + + i_info("sending message from <%s>:", smtp_address_encode(mail_from)); + + output = o_stream_create_fd(STDOUT_FILENO, (size_t)-1); + o_stream_set_no_error_handling(output, TRUE); + return (void*)output; +} + +static void +sieve_smtp_add_rcpt(const struct sieve_script_env *senv ATTR_UNUSED, + void *handle ATTR_UNUSED, + const struct smtp_address *rcpt_to) +{ + printf("\nRECIPIENT: %s\n", smtp_address_encode(rcpt_to)); +} + +static struct ostream * +sieve_smtp_send(const struct sieve_script_env *senv ATTR_UNUSED, void *handle) +{ + printf("START MESSAGE:\n"); + + return (struct ostream *)handle; +} + +static void +sieve_smtp_abort(const struct sieve_script_env *senv ATTR_UNUSED, void *handle) +{ + struct ostream *output = (struct ostream *)handle; + + printf("#### ABORT MESSAGE ####\n\n"); + o_stream_unref(&output); +} + +static int +sieve_smtp_finish(const struct sieve_script_env *senv ATTR_UNUSED, void *handle, + const char **error_r ATTR_UNUSED) +{ + struct ostream *output = (struct ostream *)handle; + + printf("END MESSAGE\n\n"); + o_stream_unref(&output); + return 1; +} + +/* + * Dummy duplicate check implementation + */ + +static void * +duplicate_transaction_begin(const struct sieve_script_env *senv ATTR_UNUSED) +{ + return NULL; +} + +static void duplicate_transaction_commit(void **_dup_trans ATTR_UNUSED) +{ +} + +static void duplicate_transaction_rollback(void **_dup_trans ATTR_UNUSED) +{ +} + +static int +duplicate_check(void *_dup_trans ATTR_UNUSED, + const struct sieve_script_env *senv, + const void *id ATTR_UNUSED, size_t id_size ATTR_UNUSED) +{ + i_info("checked duplicate for user %s.\n", senv->user->username); + return 0; +} + +static void +duplicate_mark(void *_dup_trans ATTR_UNUSED, + const struct sieve_script_env *senv, + const void *id ATTR_UNUSED, size_t id_size ATTR_UNUSED, + time_t time ATTR_UNUSED) +{ + i_info("marked duplicate for user %s.\n", senv->user->username); +} + +/* + * Result logging + */ + +static const char * +result_amend_log_message(const struct sieve_script_env *senv, + enum log_type log_type, const char *message) +{ + const struct sieve_message_data *msgdata = senv->script_context; + string_t *str; + + if (log_type == LOG_TYPE_DEBUG) + return message; + + str = t_str_new(256); + str_printfa(str, "msgid=%s", (msgdata->id == NULL ? + "unspecified" : msgdata->id)); + str_append(str, ": "); + str_append(str, message); + + return str_c(str); +} + +/* + * Tool implementation + */ + +int main(int argc, char **argv) +{ + struct sieve_instance *svinst; + ARRAY_TYPE (const_string) scriptfiles; + const char *scriptfile, *mailbox, *dumpfile, *tracefile, *mailfile, + *mailloc, *errstr; + struct smtp_address *rcpt_to, *final_rcpt_to, *mail_from; + struct sieve_trace_config trace_config; + struct mail *mail; + struct sieve_binary *main_sbin, *sbin = NULL; + struct sieve_message_data msgdata; + struct sieve_script_env scriptenv; + struct sieve_exec_status estatus; + enum sieve_execute_flags exflags = SIEVE_EXECUTE_FLAG_LOG_RESULT; + struct sieve_error_handler *ehandler; + struct ostream *teststream = NULL; + struct sieve_trace_log *trace_log = NULL; + bool force_compile = FALSE, execute = FALSE; + int exit_status = EXIT_SUCCESS; + int ret, c; + + sieve_tool = sieve_tool_init("sieve-test", &argc, &argv, + "r:a:f:m:d:l:s:eCt:T:DP:x:u:", FALSE); + + ehandler = NULL; + t_array_init(&scriptfiles, 16); + + /* Parse arguments */ + mailbox = dumpfile = tracefile = mailloc = NULL; + mail_from = final_rcpt_to = rcpt_to = NULL; + i_zero(&trace_config); + trace_config.level = SIEVE_TRLVL_ACTIONS; + while ((c = sieve_tool_getopt(sieve_tool)) > 0) { + switch (c) { + case 'r': + /* final recipient address */ + if (smtp_address_parse_mailbox( + pool_datastack_create(), optarg, + SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART, + &final_rcpt_to, &errstr) < 0) + i_fatal("Invalid -r parameter: %s", errstr); + break; + case 'a': + /* original recipient address */ + if (smtp_address_parse_mailbox( + pool_datastack_create(), optarg, + SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART, + &rcpt_to, &errstr) < 0) + i_fatal("Invalid -a parameter: %s", errstr); + break; + case 'f': + /* envelope sender address */ + if (smtp_address_parse_mailbox( + pool_datastack_create(), optarg, + 0, &mail_from, &errstr) < 0) + i_fatal("Invalid -f parameter: %s", errstr); + break; + case 'm': + /* default mailbox (keep box) */ + mailbox = optarg; + break; + case 'l': + /* mail location */ + mailloc = optarg; + break; + case 't': + /* trace file */ + tracefile = optarg; + break; + /* trace options */ + case 'T': + sieve_tool_parse_trace_option(&trace_config, optarg); + break; + case 'd': + /* dump file */ + dumpfile = optarg; + break; + case 's': + /* scriptfile executed before main script */ + { + const char *file; + + file = t_strdup(optarg); + array_append(&scriptfiles, &file, 1); + } + break; + /* execution mode */ + case 'e': + execute = TRUE; + break; + /* force script compile */ + case 'C': + force_compile = TRUE; + break; + default: + /* unrecognized option */ + print_help(); + i_fatal_status(EX_USAGE, "Unknown argument: %c", c); + break; + } + } + + if (optind < argc) + scriptfile = argv[optind++]; + else { + print_help(); + i_fatal_status(EX_USAGE, "Missing <script-file> argument"); + } + + if (optind < argc) + mailfile = argv[optind++]; + else { + print_help(); + i_fatal_status(EX_USAGE, "Missing <mail-file> argument"); + } + + if (optind != argc) { + print_help(); + i_fatal_status(EX_USAGE, "Unknown argument: %s", argv[optind]); + } + + /* Finish tool initialization */ + svinst = sieve_tool_init_finish(sieve_tool, mailloc == NULL, FALSE); + + /* Enable debug extension */ + sieve_enable_debug_extension(svinst); + + /* Create error handler */ + ehandler = sieve_stderr_ehandler_create(svinst, 0); + sieve_error_handler_accept_infolog(ehandler, TRUE); + sieve_error_handler_accept_debuglog(ehandler, svinst->debug); + + /* Compile main sieve script */ + if (force_compile) { + main_sbin = sieve_tool_script_compile(svinst, scriptfile, NULL); + if (main_sbin != NULL) + (void)sieve_save(main_sbin, TRUE, NULL); + } else { + main_sbin = sieve_tool_script_open(svinst, scriptfile); + } + + if (main_sbin == NULL) { + exit_status = EXIT_FAILURE; + } else { + /* Dump script */ + sieve_tool_dump_binary_to(main_sbin, dumpfile, FALSE); + + /* Obtain mail namespaces from -l argument */ + if (mailloc != NULL) + sieve_tool_init_mail_user(sieve_tool, mailloc); + + /* Initialize raw mail object */ + mail = sieve_tool_open_file_as_mail(sieve_tool, mailfile); + + if (mailbox == NULL) + mailbox = "INBOX"; + + /* Collect necessary message data */ + i_zero(&msgdata); + msgdata.mail = mail; + msgdata.auth_user = sieve_tool_get_username(sieve_tool); + (void)mail_get_message_id(mail, &msgdata.id); + + sieve_tool_get_envelope_data(&msgdata, mail, + mail_from, rcpt_to, final_rcpt_to); + + /* Create streams for test and trace output */ + + if (!execute) { + teststream = o_stream_create_fd(1, 0); + o_stream_set_no_error_handling(teststream, TRUE); + } + + if (tracefile != NULL) { + (void)sieve_trace_log_create( + svinst, (strcmp(tracefile, "-") == 0 ? + NULL : tracefile), &trace_log); + } + + /* Compose script environment */ + if (sieve_script_env_init( + &scriptenv, sieve_tool_get_mail_user(sieve_tool), + &errstr) < 0) { + i_fatal("Failed to initialize script execution: %s", + errstr); + } + + scriptenv.default_mailbox = mailbox; + scriptenv.smtp_start = sieve_smtp_start; + scriptenv.smtp_add_rcpt = sieve_smtp_add_rcpt; + scriptenv.smtp_send = sieve_smtp_send; + scriptenv.smtp_abort = sieve_smtp_abort; + scriptenv.smtp_finish = sieve_smtp_finish; + scriptenv.duplicate_transaction_begin = + duplicate_transaction_begin; + scriptenv.duplicate_transaction_commit = + duplicate_transaction_commit; + scriptenv.duplicate_transaction_rollback = + duplicate_transaction_rollback; + scriptenv.duplicate_mark = duplicate_mark; + scriptenv.duplicate_check = duplicate_check; + scriptenv.result_amend_log_message = result_amend_log_message; + scriptenv.trace_log = trace_log; + scriptenv.trace_config = trace_config; + scriptenv.script_context = &msgdata; + + i_zero(&estatus); + scriptenv.exec_status = &estatus; + + /* Run the test */ + ret = 1; + if (array_count(&scriptfiles) == 0) { + /* Single script */ + sbin = main_sbin; + main_sbin = NULL; + + /* Execute/Test script */ + if (execute) { + ret = sieve_execute(sbin, &msgdata, &scriptenv, + ehandler, ehandler, + exflags); + } else { + ret = sieve_test(sbin, &msgdata, &scriptenv, + ehandler, teststream, exflags); + } + } else { + /* Multiple scripts */ + const char *const *sfiles; + unsigned int i, count; + struct sieve_multiscript *mscript; + bool more = TRUE; + + if (execute) + mscript = sieve_multiscript_start_execute( + svinst, &msgdata, &scriptenv); + else + mscript = sieve_multiscript_start_test( + svinst, &msgdata, &scriptenv, + teststream); + + /* Execute scripts sequentially */ + sfiles = array_get(&scriptfiles, &count); + for (i = 0; i < count && more; i++) { + if (teststream != NULL) { + o_stream_nsend_str( + teststream, + t_strdup_printf("\n## Executing script: %s\n", + sfiles[i])); + } + + /* Close previous script */ + if (sbin != NULL) + sieve_close(&sbin); + + /* Compile sieve script */ + if (force_compile) { + sbin = sieve_tool_script_compile( + svinst, sfiles[i], sfiles[i]); + if (sbin != NULL) + (void)sieve_save(sbin, FALSE, NULL); + } else { + sbin = sieve_tool_script_open(svinst, sfiles[i]); + } + + if (sbin == NULL) { + ret = SIEVE_EXEC_FAILURE; + break; + } + + /* Execute/Test script */ + more = sieve_multiscript_run( + mscript, sbin, ehandler, ehandler, + exflags); + } + + /* Execute/Test main script */ + if (more && ret > 0) { + if (teststream != NULL) { + o_stream_nsend_str( + teststream, + t_strdup_printf("## Executing script: %s\n", + scriptfile)); + } + + /* Close previous script */ + if (sbin != NULL) + sieve_close(&sbin); + + sbin = main_sbin; + main_sbin = NULL; + + (void)sieve_multiscript_run( + mscript, sbin, ehandler, ehandler, + exflags); + } + + ret = sieve_multiscript_finish(&mscript, ehandler, + exflags, ret); + } + + /* Run */ + switch (ret) { + case SIEVE_EXEC_OK: + i_info("final result: success"); + break; + case SIEVE_EXEC_RESOURCE_LIMIT: + i_info("resource limit exceeded"); + exit_status = EXIT_FAILURE; + break; + case SIEVE_EXEC_BIN_CORRUPT: + i_info("corrupt binary deleted."); + i_unlink_if_exists(sieve_binary_path(sbin)); + /* fall through */ + case SIEVE_EXEC_FAILURE: + i_info("final result: failed; " + "resolved with successful implicit keep"); + exit_status = EXIT_FAILURE; + break; + case SIEVE_EXEC_TEMP_FAILURE: + i_info("final result: temporary failure"); + exit_status = EXIT_FAILURE; + break; + case SIEVE_EXEC_KEEP_FAILED: + i_info("final result: utter failure"); + exit_status = EXIT_FAILURE; + break; + } + + if (teststream != NULL) + o_stream_destroy(&teststream); + if (trace_log != NULL) + sieve_trace_log_free(&trace_log); + + /* Cleanup remaining binaries */ + if (sbin != NULL) + sieve_close(&sbin); + if (main_sbin != NULL) + sieve_close(&main_sbin); + } + + /* Cleanup error handler */ + sieve_error_handler_unref(&ehandler); + + sieve_tool_deinit(&sieve_tool); + + return exit_status; +} diff --git a/pigeonhole/src/sieve-tools/sievec.c b/pigeonhole/src/sieve-tools/sievec.c new file mode 100644 index 0000000..d70435d --- /dev/null +++ b/pigeonhole/src/sieve-tools/sievec.c @@ -0,0 +1,157 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" +#include "master-service.h" +#include "master-service-settings.h" +#include "mail-storage-service.h" +#include "mail-user.h" + +#include "sieve.h" +#include "sieve-extensions.h" +#include "sieve-script.h" +#include "sieve-tool.h" + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <dirent.h> +#include <sysexits.h> + +/* + * Print help + */ + +static void print_help(void) +{ + printf( +"Usage: sievec [-c <config-file>] [-d] [-D] [-P <plugin>] [-x <extensions>] \n" +" <script-file> [<out-file>]\n" + ); +} + +/* + * Tool implementation + */ + +int main(int argc, char **argv) +{ + struct sieve_instance *svinst; + struct stat st; + struct sieve_binary *sbin; + bool dump = FALSE; + const char *scriptfile, *outfile; + int exit_status = EXIT_SUCCESS; + int c; + + sieve_tool = sieve_tool_init("sievec", &argc, &argv, "DdP:x:u:", FALSE); + + outfile = NULL; + while ((c = sieve_tool_getopt(sieve_tool)) > 0) { + switch (c) { + case 'd': + /* dump file */ + dump = TRUE; + break; + default: + print_help(); + i_fatal_status(EX_USAGE, "Unknown argument: %c", c); + break; + } + } + + if ( optind < argc ) { + scriptfile = argv[optind++]; + } else { + print_help(); + i_fatal_status(EX_USAGE, "Missing <script-file> argument"); + } + + if ( optind < argc ) { + outfile = argv[optind++]; + } else if ( dump ) { + outfile = "-"; + } + + svinst = sieve_tool_init_finish(sieve_tool, FALSE, TRUE); + + /* Enable debug extension */ + sieve_enable_debug_extension(svinst); + + if ( stat(scriptfile, &st) == 0 && S_ISDIR(st.st_mode) ) { + /* Script directory */ + DIR *dirp; + struct dirent *dp; + + /* Sanity checks on some of the arguments */ + + if ( dump ) + i_fatal_status(EX_USAGE, + "the -d option is not allowed when scriptfile is a directory."); + + if ( outfile != NULL ) + i_fatal_status(EX_USAGE, + "the outfile argument is not allowed when scriptfile is a directory."); + + /* Open the directory */ + if ( (dirp = opendir(scriptfile)) == NULL ) + i_fatal("opendir(%s) failed: %m", scriptfile); + + /* Compile each sieve file */ + for (;;) { + + errno = 0; + if ( (dp = readdir(dirp)) == NULL ) { + if ( errno != 0 ) + i_fatal("readdir(%s) failed: %m", scriptfile); + break; + } + + if ( sieve_script_file_has_extension(dp->d_name) ) { + const char *file; + + if ( scriptfile[strlen(scriptfile)-1] == '/' ) + file = t_strconcat(scriptfile, dp->d_name, NULL); + else + file = t_strconcat(scriptfile, "/", dp->d_name, NULL); + + sbin = sieve_tool_script_compile(svinst, file, NULL); + + if ( sbin != NULL ) { + sieve_save(sbin, TRUE, NULL); + sieve_close(&sbin); + } + } + } + + /* Close the directory */ + if ( closedir(dirp) < 0 ) + i_fatal("closedir(%s) failed: %m", scriptfile); + } else { + /* Script file (i.e. not a directory) + * + * NOTE: For consistency, stat errors are handled here as well + */ + sbin = sieve_tool_script_compile(svinst, scriptfile, NULL); + + if ( sbin != NULL ) { + if ( dump ) + sieve_tool_dump_binary_to(sbin, outfile, FALSE); + else { + sieve_save_as(sbin, outfile, TRUE, 0600, NULL); + } + + sieve_close(&sbin); + } else { + exit_status = EXIT_FAILURE; + } + } + + sieve_tool_deinit(&sieve_tool); + + return exit_status; +} diff --git a/pigeonhole/src/testsuite/Makefile.am b/pigeonhole/src/testsuite/Makefile.am new file mode 100644 index 0000000..f7b85dc --- /dev/null +++ b/pigeonhole/src/testsuite/Makefile.am @@ -0,0 +1,74 @@ +noinst_PROGRAMS = testsuite + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib-sieve \ + -I$(top_srcdir)/src/lib-sieve/util \ + -I$(top_srcdir)/src/lib-sieve/plugins/variables \ + -I$(top_srcdir)/src/lib-sieve-tool \ + $(LIBDOVECOT_INCLUDE) \ + $(LIBDOVECOT_SERVICE_INCLUDE) + +testsuite_LDFLAGS = -export-dynamic + +libs = \ + $(top_builddir)/src/lib-sieve/libdovecot-sieve.la \ + $(top_builddir)/src/lib-sieve-tool/libsieve-tool.la + +testsuite_LDADD = $(libs) $(LIBDOVECOT_STORAGE) $(LIBDOVECOT_LDA) $(LIBDOVECOT) +testsuite_DEPENDENCIES = $(libs) $(LIBDOVECOT_STORAGE_DEPS) $(LIBDOVECOT_LDA_DEPS) $(LIBDOVECOT_DEPS) + +commands = \ + cmd-test.c \ + cmd-test-fail.c \ + cmd-test-config.c \ + cmd-test-set.c \ + cmd-test-result.c \ + cmd-test-message.c \ + cmd-test-mailbox.c \ + cmd-test-binary.c \ + cmd-test-imap-metadata.c + +tests = \ + tst-test-script-compile.c \ + tst-test-script-run.c \ + tst-test-multiscript.c \ + tst-test-error.c \ + tst-test-result-action.c \ + tst-test-result-execute.c + +testsuite_SOURCES = \ + testsuite-common.c \ + testsuite-settings.c \ + testsuite-objects.c \ + testsuite-substitutions.c \ + testsuite-variables.c \ + testsuite-arguments.c \ + testsuite-message.c \ + testsuite-log.c \ + testsuite-script.c \ + testsuite-result.c \ + testsuite-smtp.c \ + testsuite-mailstore.c \ + testsuite-binary.c \ + $(commands) \ + $(tests) \ + ext-testsuite.c \ + testsuite.c + +noinst_HEADERS = \ + testsuite-common.h \ + testsuite-settings.h \ + testsuite-objects.h \ + testsuite-substitutions.h \ + testsuite-variables.h \ + testsuite-arguments.h \ + testsuite-message.h \ + testsuite-log.h \ + testsuite-script.h \ + testsuite-result.h \ + testsuite-smtp.h \ + testsuite-mailstore.h \ + testsuite-binary.h + +clean-local: + -rm -rf test.out.* diff --git a/pigeonhole/src/testsuite/Makefile.in b/pigeonhole/src/testsuite/Makefile.in new file mode 100644 index 0000000..1f9043f --- /dev/null +++ b/pigeonhole/src/testsuite/Makefile.in @@ -0,0 +1,869 @@ +# 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@ +noinst_PROGRAMS = testsuite$(EXEEXT) +subdir = src/testsuite +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 = +PROGRAMS = $(noinst_PROGRAMS) +am__objects_1 = cmd-test.$(OBJEXT) cmd-test-fail.$(OBJEXT) \ + cmd-test-config.$(OBJEXT) cmd-test-set.$(OBJEXT) \ + cmd-test-result.$(OBJEXT) cmd-test-message.$(OBJEXT) \ + cmd-test-mailbox.$(OBJEXT) cmd-test-binary.$(OBJEXT) \ + cmd-test-imap-metadata.$(OBJEXT) +am__objects_2 = tst-test-script-compile.$(OBJEXT) \ + tst-test-script-run.$(OBJEXT) tst-test-multiscript.$(OBJEXT) \ + tst-test-error.$(OBJEXT) tst-test-result-action.$(OBJEXT) \ + tst-test-result-execute.$(OBJEXT) +am_testsuite_OBJECTS = testsuite-common.$(OBJEXT) \ + testsuite-settings.$(OBJEXT) testsuite-objects.$(OBJEXT) \ + testsuite-substitutions.$(OBJEXT) \ + testsuite-variables.$(OBJEXT) testsuite-arguments.$(OBJEXT) \ + testsuite-message.$(OBJEXT) testsuite-log.$(OBJEXT) \ + testsuite-script.$(OBJEXT) testsuite-result.$(OBJEXT) \ + testsuite-smtp.$(OBJEXT) testsuite-mailstore.$(OBJEXT) \ + testsuite-binary.$(OBJEXT) $(am__objects_1) $(am__objects_2) \ + ext-testsuite.$(OBJEXT) testsuite.$(OBJEXT) +testsuite_OBJECTS = $(am_testsuite_OBJECTS) +am__DEPENDENCIES_1 = +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 = +testsuite_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(testsuite_LDFLAGS) $(LDFLAGS) -o $@ +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)/cmd-test-binary.Po \ + ./$(DEPDIR)/cmd-test-config.Po ./$(DEPDIR)/cmd-test-fail.Po \ + ./$(DEPDIR)/cmd-test-imap-metadata.Po \ + ./$(DEPDIR)/cmd-test-mailbox.Po \ + ./$(DEPDIR)/cmd-test-message.Po ./$(DEPDIR)/cmd-test-result.Po \ + ./$(DEPDIR)/cmd-test-set.Po ./$(DEPDIR)/cmd-test.Po \ + ./$(DEPDIR)/ext-testsuite.Po \ + ./$(DEPDIR)/testsuite-arguments.Po \ + ./$(DEPDIR)/testsuite-binary.Po \ + ./$(DEPDIR)/testsuite-common.Po ./$(DEPDIR)/testsuite-log.Po \ + ./$(DEPDIR)/testsuite-mailstore.Po \ + ./$(DEPDIR)/testsuite-message.Po \ + ./$(DEPDIR)/testsuite-objects.Po \ + ./$(DEPDIR)/testsuite-result.Po \ + ./$(DEPDIR)/testsuite-script.Po \ + ./$(DEPDIR)/testsuite-settings.Po \ + ./$(DEPDIR)/testsuite-smtp.Po \ + ./$(DEPDIR)/testsuite-substitutions.Po \ + ./$(DEPDIR)/testsuite-variables.Po ./$(DEPDIR)/testsuite.Po \ + ./$(DEPDIR)/tst-test-error.Po \ + ./$(DEPDIR)/tst-test-multiscript.Po \ + ./$(DEPDIR)/tst-test-result-action.Po \ + ./$(DEPDIR)/tst-test-result-execute.Po \ + ./$(DEPDIR)/tst-test-script-compile.Po \ + ./$(DEPDIR)/tst-test-script-run.Po +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 = $(testsuite_SOURCES) +DIST_SOURCES = $(testsuite_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@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib-sieve \ + -I$(top_srcdir)/src/lib-sieve/util \ + -I$(top_srcdir)/src/lib-sieve/plugins/variables \ + -I$(top_srcdir)/src/lib-sieve-tool \ + $(LIBDOVECOT_INCLUDE) \ + $(LIBDOVECOT_SERVICE_INCLUDE) + +testsuite_LDFLAGS = -export-dynamic +libs = \ + $(top_builddir)/src/lib-sieve/libdovecot-sieve.la \ + $(top_builddir)/src/lib-sieve-tool/libsieve-tool.la + +testsuite_LDADD = $(libs) $(LIBDOVECOT_STORAGE) $(LIBDOVECOT_LDA) $(LIBDOVECOT) +testsuite_DEPENDENCIES = $(libs) $(LIBDOVECOT_STORAGE_DEPS) $(LIBDOVECOT_LDA_DEPS) $(LIBDOVECOT_DEPS) +commands = \ + cmd-test.c \ + cmd-test-fail.c \ + cmd-test-config.c \ + cmd-test-set.c \ + cmd-test-result.c \ + cmd-test-message.c \ + cmd-test-mailbox.c \ + cmd-test-binary.c \ + cmd-test-imap-metadata.c + +tests = \ + tst-test-script-compile.c \ + tst-test-script-run.c \ + tst-test-multiscript.c \ + tst-test-error.c \ + tst-test-result-action.c \ + tst-test-result-execute.c + +testsuite_SOURCES = \ + testsuite-common.c \ + testsuite-settings.c \ + testsuite-objects.c \ + testsuite-substitutions.c \ + testsuite-variables.c \ + testsuite-arguments.c \ + testsuite-message.c \ + testsuite-log.c \ + testsuite-script.c \ + testsuite-result.c \ + testsuite-smtp.c \ + testsuite-mailstore.c \ + testsuite-binary.c \ + $(commands) \ + $(tests) \ + ext-testsuite.c \ + testsuite.c + +noinst_HEADERS = \ + testsuite-common.h \ + testsuite-settings.h \ + testsuite-objects.h \ + testsuite-substitutions.h \ + testsuite-variables.h \ + testsuite-arguments.h \ + testsuite-message.h \ + testsuite-log.h \ + testsuite-script.h \ + testsuite-result.h \ + testsuite-smtp.h \ + testsuite-mailstore.h \ + testsuite-binary.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/testsuite/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/testsuite/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-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +testsuite$(EXEEXT): $(testsuite_OBJECTS) $(testsuite_DEPENDENCIES) $(EXTRA_testsuite_DEPENDENCIES) + @rm -f testsuite$(EXEEXT) + $(AM_V_CCLD)$(testsuite_LINK) $(testsuite_OBJECTS) $(testsuite_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-test-binary.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-test-config.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-test-fail.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-test-imap-metadata.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-test-mailbox.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-test-message.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-test-result.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-test-set.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-test.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-testsuite.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testsuite-arguments.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testsuite-binary.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testsuite-common.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testsuite-log.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testsuite-mailstore.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testsuite-message.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testsuite-objects.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testsuite-result.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testsuite-script.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testsuite-settings.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testsuite-smtp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testsuite-substitutions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testsuite-variables.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testsuite.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-test-error.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-test-multiscript.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-test-result-action.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-test-result-execute.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-test-script-compile.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-test-script-run.Po@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 $(PROGRAMS) $(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-local clean-noinstPROGRAMS \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/cmd-test-binary.Po + -rm -f ./$(DEPDIR)/cmd-test-config.Po + -rm -f ./$(DEPDIR)/cmd-test-fail.Po + -rm -f ./$(DEPDIR)/cmd-test-imap-metadata.Po + -rm -f ./$(DEPDIR)/cmd-test-mailbox.Po + -rm -f ./$(DEPDIR)/cmd-test-message.Po + -rm -f ./$(DEPDIR)/cmd-test-result.Po + -rm -f ./$(DEPDIR)/cmd-test-set.Po + -rm -f ./$(DEPDIR)/cmd-test.Po + -rm -f ./$(DEPDIR)/ext-testsuite.Po + -rm -f ./$(DEPDIR)/testsuite-arguments.Po + -rm -f ./$(DEPDIR)/testsuite-binary.Po + -rm -f ./$(DEPDIR)/testsuite-common.Po + -rm -f ./$(DEPDIR)/testsuite-log.Po + -rm -f ./$(DEPDIR)/testsuite-mailstore.Po + -rm -f ./$(DEPDIR)/testsuite-message.Po + -rm -f ./$(DEPDIR)/testsuite-objects.Po + -rm -f ./$(DEPDIR)/testsuite-result.Po + -rm -f ./$(DEPDIR)/testsuite-script.Po + -rm -f ./$(DEPDIR)/testsuite-settings.Po + -rm -f ./$(DEPDIR)/testsuite-smtp.Po + -rm -f ./$(DEPDIR)/testsuite-substitutions.Po + -rm -f ./$(DEPDIR)/testsuite-variables.Po + -rm -f ./$(DEPDIR)/testsuite.Po + -rm -f ./$(DEPDIR)/tst-test-error.Po + -rm -f ./$(DEPDIR)/tst-test-multiscript.Po + -rm -f ./$(DEPDIR)/tst-test-result-action.Po + -rm -f ./$(DEPDIR)/tst-test-result-execute.Po + -rm -f ./$(DEPDIR)/tst-test-script-compile.Po + -rm -f ./$(DEPDIR)/tst-test-script-run.Po + -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)/cmd-test-binary.Po + -rm -f ./$(DEPDIR)/cmd-test-config.Po + -rm -f ./$(DEPDIR)/cmd-test-fail.Po + -rm -f ./$(DEPDIR)/cmd-test-imap-metadata.Po + -rm -f ./$(DEPDIR)/cmd-test-mailbox.Po + -rm -f ./$(DEPDIR)/cmd-test-message.Po + -rm -f ./$(DEPDIR)/cmd-test-result.Po + -rm -f ./$(DEPDIR)/cmd-test-set.Po + -rm -f ./$(DEPDIR)/cmd-test.Po + -rm -f ./$(DEPDIR)/ext-testsuite.Po + -rm -f ./$(DEPDIR)/testsuite-arguments.Po + -rm -f ./$(DEPDIR)/testsuite-binary.Po + -rm -f ./$(DEPDIR)/testsuite-common.Po + -rm -f ./$(DEPDIR)/testsuite-log.Po + -rm -f ./$(DEPDIR)/testsuite-mailstore.Po + -rm -f ./$(DEPDIR)/testsuite-message.Po + -rm -f ./$(DEPDIR)/testsuite-objects.Po + -rm -f ./$(DEPDIR)/testsuite-result.Po + -rm -f ./$(DEPDIR)/testsuite-script.Po + -rm -f ./$(DEPDIR)/testsuite-settings.Po + -rm -f ./$(DEPDIR)/testsuite-smtp.Po + -rm -f ./$(DEPDIR)/testsuite-substitutions.Po + -rm -f ./$(DEPDIR)/testsuite-variables.Po + -rm -f ./$(DEPDIR)/testsuite.Po + -rm -f ./$(DEPDIR)/tst-test-error.Po + -rm -f ./$(DEPDIR)/tst-test-multiscript.Po + -rm -f ./$(DEPDIR)/tst-test-result-action.Po + -rm -f ./$(DEPDIR)/tst-test-result-execute.Po + -rm -f ./$(DEPDIR)/tst-test-script-compile.Po + -rm -f ./$(DEPDIR)/tst-test-script-run.Po + -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-local clean-noinstPROGRAMS \ + 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 + + +clean-local: + -rm -rf test.out.* + +# 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/testsuite/cmd-test-binary.c b/pigeonhole/src/testsuite/cmd-test-binary.c new file mode 100644 index 0000000..6f810bd --- /dev/null +++ b/pigeonhole/src/testsuite/cmd-test-binary.c @@ -0,0 +1,208 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-code.h" +#include "sieve-binary.h" +#include "sieve-dump.h" + +#include "testsuite-common.h" +#include "testsuite-binary.h" +#include "testsuite-script.h" + +/* + * Commands + */ + +static bool cmd_test_binary_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); +static bool cmd_test_binary_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx); + +/* Test_binary_load command + * + * Syntax: + * test_binary_load <binary-name: string> + */ + +const struct sieve_command_def cmd_test_binary_load = { + .identifier = "test_binary_load", + .type = SCT_COMMAND, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = cmd_test_binary_validate, + .generate = cmd_test_binary_generate +}; + +/* Test_binary_save command + * + * Syntax: + * test_binary_save <binary-name: string> + */ + +const struct sieve_command_def cmd_test_binary_save = { + .identifier = "test_binary_save", + .type = SCT_COMMAND, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = cmd_test_binary_validate, + .generate = cmd_test_binary_generate, +}; + +/* + * Operations + */ + +static bool cmd_test_binary_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int cmd_test_binary_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +/* test_binary_create operation */ + +const struct sieve_operation_def test_binary_load_operation = { + .mnemonic = "TEST_BINARY_LOAD", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERATION_TEST_BINARY_LOAD, + .dump = cmd_test_binary_operation_dump, + .execute = cmd_test_binary_operation_execute +}; + +/* test_binary_delete operation */ + +const struct sieve_operation_def test_binary_save_operation = { + .mnemonic = "TEST_BINARY_SAVE", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERATION_TEST_BINARY_SAVE, + .dump = cmd_test_binary_operation_dump, + .execute = cmd_test_binary_operation_execute +}; + +/* + * Validation + */ + +static bool cmd_test_binary_validate +(struct sieve_validator *valdtr, struct sieve_command *cmd) +{ + struct sieve_ast_argument *arg = cmd->first_positional; + + if ( !sieve_validate_positional_argument + (valdtr, cmd, arg, "binary-name", 1, SAAT_STRING) ) { + return FALSE; + } + + return sieve_validator_argument_activate(valdtr, cmd, arg, FALSE); +} + +/* + * Code generation + */ + +static bool cmd_test_binary_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + /* Emit operation */ + if ( sieve_command_is(cmd, cmd_test_binary_load) ) + sieve_operation_emit(cgenv->sblock, cmd->ext, &test_binary_load_operation); + else if ( sieve_command_is(cmd, cmd_test_binary_save) ) + sieve_operation_emit(cgenv->sblock, cmd->ext, &test_binary_save_operation); + else + i_unreached(); + + /* Generate arguments */ + if ( !sieve_generate_arguments(cgenv, cmd, NULL) ) + return FALSE; + + return TRUE; +} + +/* + * Code dump + */ + +static bool cmd_test_binary_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + sieve_code_dumpf(denv, "%s:", sieve_operation_mnemonic(denv->oprtn)); + + sieve_code_descend(denv); + + return sieve_opr_string_dump(denv, address, "binary-name"); +} + +/* + * Intepretation + */ + +static int cmd_test_binary_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + const struct sieve_operation *oprtn = renv->oprtn; + string_t *binary_name = NULL; + int ret; + + /* + * Read operands + */ + + /* Binary Name */ + + if ( (ret=sieve_opr_string_read(renv, address, "binary-name", &binary_name)) + <= 0 ) + return ret; + + /* + * Perform operation + */ + + if ( sieve_operation_is(oprtn, test_binary_load_operation) ) { + struct sieve_binary *sbin = testsuite_binary_load(str_c(binary_name)); + + if ( sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS) ) { + sieve_runtime_trace(renv, 0, "testsuite: test_binary_load command"); + sieve_runtime_trace_descend(renv); + sieve_runtime_trace(renv, 0, "load binary `%s'", str_c(binary_name)); + } + + if ( sbin != NULL ) { + testsuite_script_set_binary(renv, sbin); + + sieve_binary_unref(&sbin); + } else { + e_error(testsuite_sieve_instance->event, + "failed to load binary %s", str_c(binary_name)); + return SIEVE_EXEC_FAILURE; + } + + } else if ( sieve_operation_is(oprtn, test_binary_save_operation) ) { + struct sieve_binary *sbin = testsuite_script_get_binary(renv); + + if ( sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS) ) { + sieve_runtime_trace(renv, 0, "testsuite: test_binary_save command"); + sieve_runtime_trace_descend(renv); + sieve_runtime_trace(renv, 0, "save binary `%s'", str_c(binary_name)); + } + + if ( sbin != NULL ) + testsuite_binary_save(sbin, str_c(binary_name)); + else { + e_error(testsuite_sieve_instance->event, + "no compiled binary to save as %s", + str_c(binary_name)); + return SIEVE_EXEC_FAILURE; + } + } else { + i_unreached(); + } + + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/testsuite/cmd-test-config.c b/pigeonhole/src/testsuite/cmd-test-config.c new file mode 100644 index 0000000..c1399b0 --- /dev/null +++ b/pigeonhole/src/testsuite/cmd-test-config.c @@ -0,0 +1,504 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-settings.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-code.h" +#include "sieve-binary.h" +#include "sieve-dump.h" + +#include "testsuite-common.h" +#include "testsuite-settings.h" + +/* + * Commands + */ + +static bool +cmd_test_config_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +/* Test_config_set command + * + * Syntax: + * test_config_set <setting: string> <value: string> + */ + +static bool +cmd_test_config_set_validate(struct sieve_validator *valdtr, + struct sieve_command *cmd); + +const struct sieve_command_def cmd_test_config_set = { + .identifier = "test_config_set", + .type = SCT_COMMAND, + .positional_args = 2, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = cmd_test_config_set_validate, + .generate = cmd_test_config_generate +}; + +/* Test_config_unset command + * + * Syntax: + * test_config_unset <setting: string> + */ + +static bool +cmd_test_config_unset_validate(struct sieve_validator *valdtr, + struct sieve_command *cmd); + +const struct sieve_command_def cmd_test_config_unset = { + .identifier = "test_config_unset", + .type = SCT_COMMAND, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = cmd_test_config_unset_validate, + .generate = cmd_test_config_generate, +}; + +/* Test_config_reload command + * + * Syntax: + * test_config_reload [:extension <extension: string>] + */ + +static bool +cmd_test_config_reload_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); + +const struct sieve_command_def cmd_test_config_reload = { + .identifier = "test_config_reload", + .type = SCT_COMMAND, + .positional_args = 0, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = cmd_test_config_reload_registered, + .generate = cmd_test_config_generate +}; + +/* + * Command tags + */ + +/* Forward declarations */ + +static bool +cmd_test_config_reload_validate_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd); + +/* Argument objects */ + +static const struct sieve_argument_def test_config_reload_extension_tag = { + .identifier = "extension", + .validate = cmd_test_config_reload_validate_tag, +}; + +/* Codes for optional arguments */ + +enum cmd_test_config_optional { + OPT_END, + OPT_EXTENSION +}; + +/* + * Operations + */ + +/* Test_config_set operation */ + +static bool +cmd_test_config_set_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +cmd_test_config_set_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def test_config_set_operation = { + .mnemonic = "TEST_CONFIG_SET", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERATION_TEST_CONFIG_SET, + .dump = cmd_test_config_set_operation_dump, + .execute = cmd_test_config_set_operation_execute +}; + +/* Test_config_unset operation */ + +static bool +cmd_test_config_unset_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +cmd_test_config_unset_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def test_config_unset_operation = { + .mnemonic = "TEST_CONFIG_UNSET", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERATION_TEST_CONFIG_UNSET, + .dump = cmd_test_config_unset_operation_dump, + .execute = cmd_test_config_unset_operation_execute +}; + +/* Test_config_read operation */ + +static bool +cmd_test_config_reload_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +cmd_test_config_reload_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def test_config_reload_operation = { + .mnemonic = "TEST_CONFIG_RELOAD", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERATION_TEST_CONFIG_RELOAD, + .dump = cmd_test_config_reload_operation_dump, + .execute = cmd_test_config_reload_operation_execute +}; + +/* + * Tag validation + */ + +static bool +cmd_test_config_reload_validate_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + + /* Detach the tag itself */ + *arg = sieve_ast_arguments_detach(*arg,1); + + /* Check syntax: + * :extension <extension: string> + */ + if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0, + SAAT_STRING, TRUE)) + return FALSE; + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + + return TRUE; +} + +/* + * Command registration + */ + +static bool +cmd_test_config_reload_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &test_config_reload_extension_tag, + OPT_EXTENSION); + return TRUE; +} + +/* + * Command validation + */ + +static bool +cmd_test_config_set_validate(struct sieve_validator *valdtr, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *arg = cmd->first_positional; + + /* Check syntax: + * <setting: string> <value: string> + */ + + if (!sieve_validate_positional_argument(valdtr, cmd, arg, "setting", + 1, SAAT_STRING)) + return FALSE; + + if (!sieve_validator_argument_activate(valdtr, cmd, arg, FALSE)) + return FALSE; + + arg = sieve_ast_argument_next(arg); + + if (!sieve_validate_positional_argument(valdtr, cmd, arg, "value", 2, + SAAT_STRING)) + return FALSE; + + return sieve_validator_argument_activate(valdtr, cmd, arg, FALSE); +} + +static bool +cmd_test_config_unset_validate(struct sieve_validator *valdtr, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *arg = cmd->first_positional; + + /* Check syntax: + * <setting: string> + */ + if (!sieve_validate_positional_argument(valdtr, cmd, arg, "setting", 1, + SAAT_STRING)) + return FALSE; + + return sieve_validator_argument_activate(valdtr, cmd, arg, FALSE); +} + +/* + * Code generation + */ + +static bool +cmd_test_config_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd) +{ + if (sieve_command_is(cmd, cmd_test_config_set)) { + sieve_operation_emit(cgenv->sblock, cmd->ext, + &test_config_set_operation); + } else if (sieve_command_is(cmd, cmd_test_config_unset)) { + sieve_operation_emit(cgenv->sblock, cmd->ext, + &test_config_unset_operation); + } else if (sieve_command_is(cmd, cmd_test_config_reload)) { + sieve_operation_emit(cgenv->sblock, cmd->ext, + &test_config_reload_operation); + } else { + i_unreached(); + } + + /* Generate arguments */ + if (!sieve_generate_arguments(cgenv, cmd, NULL)) + return FALSE; + return TRUE; +} + +/* + * Code dump + */ + +static bool +cmd_test_config_set_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + sieve_code_dumpf(denv, "TEST_CONFIG_SET:"); + + sieve_code_descend(denv); + + return (sieve_opr_string_dump(denv, address, "setting") && + sieve_opr_string_dump(denv, address, "value")); +} + +static bool +cmd_test_config_unset_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + sieve_code_dumpf(denv, "TEST_CONFIG_UNSET:"); + + sieve_code_descend(denv); + + return sieve_opr_string_dump(denv, address, "setting"); +} + +static bool +cmd_test_config_reload_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + int opt_code = 0; + + sieve_code_dumpf(denv, "TEST_CONFIG_RELOAD:"); + sieve_code_descend(denv); + + /* Dump optional operands */ + + for (;;) { + int opt; + bool opok = TRUE; + + if ((opt = sieve_opr_optional_dump(denv, address, + &opt_code)) < 0) + return FALSE; + + if (opt == 0) + break; + + switch (opt_code) { + case OPT_EXTENSION: + opok = sieve_opr_string_dump(denv, address, + "extensions"); + break; + default: + opok = FALSE; + break; + } + + if (!opok) + return FALSE; + } + + return TRUE; +} + +/* + * Interpretation + */ + +static int +cmd_test_config_set_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + string_t *setting; + string_t *value; + int ret; + + /* + * Read operands + */ + + /* Setting */ + if ((ret = sieve_opr_string_read(renv, address, "setting", + &setting)) <= 0) + return ret; + + /* Value */ + if ((ret = sieve_opr_string_read(renv, address, "value", + &value)) <= 0) + return ret; + + /* + * Perform operation + */ + + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS)) { + sieve_runtime_trace( + renv, 0, "testsuite: test_config_set command"); + sieve_runtime_trace_descend(renv); + sieve_runtime_trace( + renv, 0, "set config `%s' = `%s'", + str_c(setting), str_c(value)); + } + + testsuite_setting_set(str_c(setting), str_c(value)); + + return SIEVE_EXEC_OK; +} + +static int +cmd_test_config_unset_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + string_t *setting; + int ret; + + /* + * Read operands + */ + + /* Setting */ + if ((ret = sieve_opr_string_read(renv, address, "setting", + &setting)) <= 0) + return ret; + + /* + * Perform operation + */ + + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS)) { + sieve_runtime_trace( + renv, 0, "testsuite: test_config_unset command"); + sieve_runtime_trace_descend(renv); + sieve_runtime_trace( + renv, 0, "unset config `%s'", str_c(setting)); + } + + testsuite_setting_unset(str_c(setting)); + + return SIEVE_EXEC_OK; +} + +static int +cmd_test_config_reload_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + const struct sieve_extension *ext; + int opt_code = 0; + string_t *extension = NULL; + int ret; + + /* + * Read operands + */ + + /* Optional operands */ + for (;;) { + int opt; + + if ((opt = sieve_opr_optional_read(renv, address, + &opt_code)) < 0) + return SIEVE_EXEC_BIN_CORRUPT; + + if (opt == 0) + break; + + switch (opt_code) { + case OPT_EXTENSION: + ret = sieve_opr_string_read(renv, address, "extension", + &extension); + break; + default: + sieve_runtime_trace_error( + renv, "unknown optional operand"); + ret = SIEVE_EXEC_BIN_CORRUPT; + } + + if (ret <= 0) + return ret; + } + + /* + * Perform operation + */ + + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS)) { + sieve_runtime_trace( + renv, 0, "testsuite: test_config_reload command"); + sieve_runtime_trace_descend(renv); + } + + if (extension == NULL) { + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS)) { + sieve_runtime_trace( + renv, 0, + "reload configuration for sieve engine"); + } + + sieve_settings_load(eenv->svinst); + } else { + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS)) { + sieve_runtime_trace( + renv, 0, + "reload configuration for extension `%s'", + str_c(extension)); + } + + ext = sieve_extension_get_by_name(eenv->svinst, + str_c(extension)); + if (ext == NULL) { + return testsuite_test_failf( + renv, "test_config_reload: " + "unknown extension '%s'", str_c(extension)); + } + sieve_extension_reload(ext); + } + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/testsuite/cmd-test-fail.c b/pigeonhole/src/testsuite/cmd-test-fail.c new file mode 100644 index 0000000..346c187 --- /dev/null +++ b/pigeonhole/src/testsuite/cmd-test-fail.c @@ -0,0 +1,129 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-code.h" +#include "sieve-binary.h" +#include "sieve-dump.h" + +#include "testsuite-common.h" + +/* + * Test_fail command + * + * Syntax: + * test_fail <reason: string> + */ + +static bool +cmd_test_fail_validate(struct sieve_validator *valdtr, + struct sieve_command *cmd); +static bool +cmd_test_fail_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +const struct sieve_command_def cmd_test_fail = { + .identifier = "test_fail", + .type = SCT_COMMAND, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = cmd_test_fail_validate, + .generate = cmd_test_fail_generate, +}; + +/* + * Test operation + */ + +static bool +cmd_test_fail_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +cmd_test_fail_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def test_fail_operation = { + .mnemonic = "TEST_FAIL", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERATION_TEST_FAIL, + .dump = cmd_test_fail_operation_dump, + .execute = cmd_test_fail_operation_execute, +}; + +/* + * Validation + */ + +static bool +cmd_test_fail_validate(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *arg = cmd->first_positional; + + if (!sieve_validate_positional_argument(valdtr, cmd, arg, "reason", 1, + SAAT_STRING)) + return FALSE; + + return sieve_validator_argument_activate(valdtr, cmd, arg, FALSE); +} + +/* + * Code generation + */ + +static bool +cmd_test_fail_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd) +{ + sieve_operation_emit(cgenv->sblock, cmd->ext, &test_fail_operation); + + /* Generate arguments */ + if (!sieve_generate_arguments(cgenv, cmd, NULL)) + return FALSE; + + return TRUE; +} + +/* + * Code dump + */ + +static bool +cmd_test_fail_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + sieve_code_dumpf(denv, "TEST_FAIL:"); + sieve_code_descend(denv); + + if (!sieve_opr_string_dump(denv, address, "reason")) + return FALSE; + + return TRUE; +} + +/* + * Intepretation + */ + +static int +cmd_test_fail_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + string_t *reason; + int ret; + + ret = sieve_opr_string_read(renv, address, "reason", &reason); + if (ret <= 0) + return ret; + + sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "testsuite: " + "test_fail command; FAIL current test"); + + return testsuite_test_fail(renv, reason); +} diff --git a/pigeonhole/src/testsuite/cmd-test-imap-metadata.c b/pigeonhole/src/testsuite/cmd-test-imap-metadata.c new file mode 100644 index 0000000..1c5e2a9 --- /dev/null +++ b/pigeonhole/src/testsuite/cmd-test-imap-metadata.c @@ -0,0 +1,284 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-code.h" +#include "sieve-binary.h" +#include "sieve-dump.h" + +#include "testsuite-common.h" +#include "testsuite-mailstore.h" + +/* + * Commands + */ + +static bool cmd_test_imap_metadata_registered + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool cmd_test_imap_metadata_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); +static bool cmd_test_imap_metadata_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx); + +/* Test_mailbox_create command + * + * Syntax: + * test_imap_metadata_set + * <mailbox: string> <annotation: string> <value:string> + */ + +const struct sieve_command_def cmd_test_imap_metadata_set = { + .identifier = "test_imap_metadata_set", + .type = SCT_COMMAND, + .positional_args = 2, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = cmd_test_imap_metadata_registered, + .validate = cmd_test_imap_metadata_validate, + .generate = cmd_test_imap_metadata_generate, +}; + +/* + * Command tags + */ + +static bool cmd_test_imap_metadata_validate_mailbox_tag + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd); + +static const struct sieve_argument_def test_imap_metadata_mailbox_tag = { + .identifier = "mailbox", + .validate = cmd_test_imap_metadata_validate_mailbox_tag +}; + +/* + * Operations + */ + +static bool cmd_test_imap_metadata_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int cmd_test_imap_metadata_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +/* Test_mailbox_create operation */ + +const struct sieve_operation_def test_imap_metadata_set_operation = { + .mnemonic = "TEST_IMAP_METADATA_SET", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERATION_TEST_IMAP_METADATA_SET, + .dump = cmd_test_imap_metadata_operation_dump, + .execute = cmd_test_imap_metadata_operation_execute +}; + +/* Codes for optional arguments */ + +enum cmd_vacation_optional { + OPT_END, + OPT_MAILBOX +}; + +/* + * Tag validation + */ + +static bool cmd_test_imap_metadata_validate_mailbox_tag +(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + + /* Delete this tag */ + *arg = sieve_ast_arguments_detach(*arg, 1); + + /* Check syntax: + * :mailbox string + */ + if ( !sieve_validate_tag_parameter + (valdtr, cmd, tag, *arg, NULL, 0, SAAT_STRING, FALSE) ) { + return FALSE; + } + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + return TRUE; +} + +/* + * Command registration + */ + +static bool cmd_test_imap_metadata_registered +(struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + sieve_validator_register_tag + (valdtr, cmd_reg, ext, &test_imap_metadata_mailbox_tag, OPT_MAILBOX); + return TRUE; +} + + +/* + * Validation + */ + +static bool cmd_test_imap_metadata_validate +(struct sieve_validator *valdtr, struct sieve_command *cmd) +{ + struct sieve_ast_argument *arg = cmd->first_positional; + + if ( !sieve_validate_positional_argument + (valdtr, cmd, arg, "annotation", 2, SAAT_STRING) ) + return FALSE; + + if (!sieve_validator_argument_activate(valdtr, cmd, arg, FALSE)) + return FALSE; + + arg = sieve_ast_argument_next(arg); + + if ( !sieve_validate_positional_argument + (valdtr, cmd, arg, "value", 3, SAAT_STRING) ) + return FALSE; + + if (!sieve_validator_argument_activate(valdtr, cmd, arg, FALSE)) + return FALSE; + return TRUE; +} + +/* + * Code generation + */ + +static bool cmd_test_imap_metadata_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + /* Emit operation */ + if ( sieve_command_is(cmd, cmd_test_imap_metadata_set) ) + sieve_operation_emit + (cgenv->sblock, cmd->ext, &test_imap_metadata_set_operation); + else + i_unreached(); + + /* Generate arguments */ + if ( !sieve_generate_arguments(cgenv, cmd, NULL) ) + return FALSE; + return TRUE; +} + +/* + * Code dump + */ + +static bool cmd_test_imap_metadata_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + int opt_code = 0; + + sieve_code_dumpf(denv, "%s:", sieve_operation_mnemonic(denv->oprtn)); + sieve_code_descend(denv); + + /* Dump optional operands */ + + for (;;) { + int opt; + bool opok = TRUE; + + if ( (opt=sieve_opr_optional_dump(denv, address, &opt_code)) < 0 ) + return FALSE; + + if ( opt == 0 ) break; + + switch ( opt_code ) { + case OPT_MAILBOX: + opok = sieve_opr_string_dump(denv, address, "mailbox"); + break; + default: + return FALSE; + } + + if ( !opok ) return FALSE; + } + + return + ( sieve_opr_string_dump(denv, address, "annotation") && + sieve_opr_string_dump(denv, address, "value") ); +} + +/* + * Intepretation + */ + +static int cmd_test_imap_metadata_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + const struct sieve_operation *oprtn = renv->oprtn; + int opt_code = 0; + string_t *mailbox = NULL, *annotation = NULL, *value = NULL; + int ret; + + /* + * Read operands + */ + + /* Optional operands */ + + for (;;) { + int opt; + + if ( (opt=sieve_opr_optional_read(renv, address, &opt_code)) < 0 ) + return SIEVE_EXEC_BIN_CORRUPT; + + if ( opt == 0 ) break; + + switch ( opt_code ) { + case OPT_MAILBOX: + ret = sieve_opr_string_read(renv, address, "mailbox", &mailbox); + break; + default: + sieve_runtime_trace_error(renv, "unknown optional operand"); + ret = SIEVE_EXEC_BIN_CORRUPT; + } + + if ( ret <= 0 ) return ret; + } + + /* Fixed operands */ + + if ( (ret=sieve_opr_string_read + (renv, address, "annotation", &annotation)) <= 0 ) + return ret; + if ( (ret=sieve_opr_string_read + (renv, address, "value", &value)) <= 0 ) + return ret; + + /* + * Perform operation + */ + + if ( sieve_operation_is(oprtn, test_imap_metadata_set_operation) ) { + if ( sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS) ) { + sieve_runtime_trace(renv, 0, "testsuite/test_imap_metadata_set command"); + sieve_runtime_trace_descend(renv); + if (mailbox == NULL) { + sieve_runtime_trace(renv, 0, + "set server annotation `%s'", str_c(annotation)); + } else { + sieve_runtime_trace(renv, 0, + "set annotation `%s' for mailbox `%s'", + str_c(annotation), str_c(mailbox)); + } + } + + if (testsuite_mailstore_set_imap_metadata + (( mailbox == NULL ? NULL : str_c(mailbox) ), + str_c(annotation), str_c(value)) < 0) + return SIEVE_EXEC_FAILURE; + } + + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/testsuite/cmd-test-mailbox.c b/pigeonhole/src/testsuite/cmd-test-mailbox.c new file mode 100644 index 0000000..b5d946c --- /dev/null +++ b/pigeonhole/src/testsuite/cmd-test-mailbox.c @@ -0,0 +1,261 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str-sanitize.h" + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-code.h" +#include "sieve-binary.h" +#include "sieve-dump.h" +#include "sieve-actions.h" + +#include "testsuite-common.h" +#include "testsuite-mailstore.h" + +/* + * Commands + */ + +static bool +cmd_test_mailbox_validate(struct sieve_validator *valdtr, + struct sieve_command *cmd); +static bool +cmd_test_mailbox_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +/* Test_mailbox_create command + * + * Syntax: + * test_mailbox_create <mailbox: string> + */ + +const struct sieve_command_def cmd_test_mailbox_create = { + .identifier = "test_mailbox_create", + .type = SCT_COMMAND, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = cmd_test_mailbox_validate, + .generate = cmd_test_mailbox_generate, +}; + +/* Test_mailbox_delete command + * + * Syntax: + * test_mailbox_create <mailbox: string> + */ + +const struct sieve_command_def cmd_test_mailbox_delete = { + .identifier = "test_mailbox_delete", + .type = SCT_COMMAND, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = cmd_test_mailbox_validate, + .generate = cmd_test_mailbox_generate, +}; + +/* + * Operations + */ + +static bool +cmd_test_mailbox_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +cmd_test_mailbox_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +/* Test_mailbox_create operation */ + +const struct sieve_operation_def test_mailbox_create_operation = { + .mnemonic = "TEST_MAILBOX_CREATE", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERATION_TEST_MAILBOX_CREATE, + .dump = cmd_test_mailbox_operation_dump, + .execute = cmd_test_mailbox_operation_execute, +}; + +/* Test_mailbox_delete operation */ + +const struct sieve_operation_def test_mailbox_delete_operation = { + .mnemonic = "TEST_MAILBOX_DELETE", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERATION_TEST_MAILBOX_DELETE, + .dump = cmd_test_mailbox_operation_dump, + .execute = cmd_test_mailbox_operation_execute, +}; + +/* + * Validation + */ + +static bool +cmd_test_mailbox_validate(struct sieve_validator *valdtr, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *arg = cmd->first_positional; + + if (!sieve_validate_positional_argument(valdtr, cmd, arg, "mailbox", 1, + SAAT_STRING)) + return FALSE; + + if ( !sieve_validator_argument_activate(valdtr, cmd, arg, FALSE) ) + return FALSE; + + /* Check name validity when folder argument is not a variable */ + if ( sieve_argument_is_string_literal(arg) ) { + const char *folder = sieve_ast_argument_strc(arg), *error; + + if ( !sieve_mailbox_check_name(folder, &error) ) { + sieve_command_validate_error( + valdtr, cmd, "%s command: " + "invalid mailbox `%s' specified: %s", + sieve_command_identifier(cmd), + str_sanitize(folder, 256), error); + return FALSE; + } + } + + return TRUE; +} + +/* + * Code generation + */ + +static bool +cmd_test_mailbox_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd) +{ + /* Emit operation */ + if (sieve_command_is(cmd, cmd_test_mailbox_create)) { + sieve_operation_emit(cgenv->sblock, cmd->ext, + &test_mailbox_create_operation); + } else if (sieve_command_is(cmd, cmd_test_mailbox_delete)) { + sieve_operation_emit(cgenv->sblock, cmd->ext, + &test_mailbox_delete_operation); + } else { + i_unreached(); + } + + /* Generate arguments */ + if (!sieve_generate_arguments(cgenv, cmd, NULL)) + return FALSE; + + return TRUE; +} + +/* + * Code dump + */ + +static bool +cmd_test_mailbox_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + sieve_code_dumpf(denv, "%s:", sieve_operation_mnemonic(denv->oprtn)); + + sieve_code_descend(denv); + + return sieve_opr_string_dump(denv, address, "mailbox"); +} + +/* + * Intepretation + */ + +static const char * +cmd_test_mailbox_get_command_name(const struct sieve_operation *oprtn) +{ + if (sieve_operation_is(oprtn, test_mailbox_create_operation)) + return "test_mailbox_create"; + if (sieve_operation_is(oprtn, test_mailbox_delete_operation)) + return "test_mailbox_delete"; + + i_unreached(); +} + +static int +cmd_test_mailbox_create_execute(const struct sieve_runtime_env *renv, + const char *mailbox) +{ + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS)) { + sieve_runtime_trace( + renv, 0, + "testsuite/test_mailbox_create command"); + sieve_runtime_trace_descend(renv); + sieve_runtime_trace( + renv, 0, "create mailbox `%s'", mailbox); + } + + testsuite_mailstore_mailbox_create(renv, mailbox); + return SIEVE_EXEC_OK; +} + +static int +cmd_test_mailbox_delete_execute(const struct sieve_runtime_env *renv, + const char *mailbox) +{ + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS)) { + sieve_runtime_trace( + renv, 0, + "testsuite/test_mailbox_delete command"); + sieve_runtime_trace_descend(renv); + sieve_runtime_trace( + renv, 0, "delete mailbox `%s'", mailbox); + } + + /* FIXME: implement */ + return testsuite_test_failf( + renv, "test_mailbox_delete: NOT IMPLEMENTED"); +} + +static int +cmd_test_mailbox_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + const struct sieve_operation *oprtn = renv->oprtn; + string_t *mailbox = NULL; + const char *error; + int ret; + + /* + * Read operands + */ + + /* Mailbox */ + + ret = sieve_opr_string_read(renv, address, "mailbox", &mailbox); + if (ret <= 0) + return ret; + + if (!sieve_mailbox_check_name(str_c(mailbox), &error)) { + sieve_runtime_error( + renv, NULL, "%s command: " + "invalid mailbox `%s' specified: %s", + cmd_test_mailbox_get_command_name(oprtn), + str_c(mailbox), error); + return SIEVE_EXEC_FAILURE; + } + + /* + * Perform operation + */ + + if (sieve_operation_is(oprtn, test_mailbox_create_operation)) + ret = cmd_test_mailbox_create_execute(renv, str_c(mailbox)); + else if (sieve_operation_is(oprtn, test_mailbox_delete_operation)) + ret = cmd_test_mailbox_delete_execute(renv, str_c(mailbox)); + else + i_unreached(); + + return ret; +} diff --git a/pigeonhole/src/testsuite/cmd-test-message.c b/pigeonhole/src/testsuite/cmd-test-message.c new file mode 100644 index 0000000..ff36704 --- /dev/null +++ b/pigeonhole/src/testsuite/cmd-test-message.c @@ -0,0 +1,569 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str-sanitize.h" +#include "istream.h" +#include "mail-storage.h" + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-message.h" +#include "sieve-actions.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-code.h" +#include "sieve-binary.h" +#include "sieve-dump.h" + +#include "testsuite-common.h" +#include "testsuite-smtp.h" +#include "testsuite-mailstore.h" + +/* + * Commands + */ + +/* Test_message command + * + * Syntax: + * test_message ( :smtp / :mailbox <mailbox: string> ) <index: number> + */ + +static bool +cmd_test_message_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool +cmd_test_message_validate(struct sieve_validator *valdtr, + struct sieve_command *cmd); +static bool +cmd_test_message_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +const struct sieve_command_def cmd_test_message = { + .identifier = "test_message", + .type = SCT_HYBRID, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = cmd_test_message_registered, + .validate = cmd_test_message_validate, + .generate = cmd_test_message_generate, +}; + +/* Test_message_print command + * + * Syntax: + * test_message_print + */ + +static bool +cmd_test_message_print_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd); + +const struct sieve_command_def cmd_test_message_print = { + .identifier = "test_message_print", + .type = SCT_COMMAND, + .positional_args = 0, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .generate = cmd_test_message_print_generate, +}; + +/* + * Operations + */ + +/* Test_message_smtp operation */ + +static bool +cmd_test_message_smtp_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +cmd_test_message_smtp_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def test_message_smtp_operation = { + .mnemonic = "TEST_MESSAGE_SMTP", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERATION_TEST_MESSAGE_SMTP, + .dump = cmd_test_message_smtp_operation_dump, + .execute = cmd_test_message_smtp_operation_execute, +}; + +/* Test_message_mailbox operation */ + +static bool +cmd_test_message_mailbox_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +cmd_test_message_mailbox_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def test_message_mailbox_operation = { + .mnemonic = "TEST_MESSAGE_MAILBOX", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERATION_TEST_MESSAGE_MAILBOX, + .dump = cmd_test_message_mailbox_operation_dump, + .execute = cmd_test_message_mailbox_operation_execute, +}; + +/* Test_message_print operation */ + +static bool +cmd_test_message_print_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +cmd_test_message_print_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def test_message_print_operation = { + .mnemonic = "TEST_MESSAGE_PRINT", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERATION_TEST_MESSAGE_PRINT, + .dump = cmd_test_message_print_operation_dump, + .execute = cmd_test_message_print_operation_execute, +}; + +/* + * Compiler context data + */ + +enum test_message_source { + MSG_SOURCE_SMTP, + MSG_SOURCE_MAILBOX, + MSG_SOURCE_LAST, +}; + +const struct sieve_operation_def *test_message_operations[] = { + &test_message_smtp_operation, + &test_message_mailbox_operation, +}; + +struct cmd_test_message_context_data { + enum test_message_source msg_source; + const char *folder; +}; + +#define CMD_TEST_MESSAGE_ERROR_DUP_TAG \ + "exactly one of the ':smtp' or ':folder' tags must be specified " \ + "for the test_message command, but more were found" + +/* + * Command tags + */ + +static bool +cmd_test_message_validate_smtp_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd); +static bool +cmd_test_message_validate_folder_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd); + +static const struct sieve_argument_def test_message_smtp_tag = { + .identifier = "smtp", + .validate = cmd_test_message_validate_smtp_tag, +}; + +static const struct sieve_argument_def test_message_folder_tag = { + .identifier = "folder", + .validate = cmd_test_message_validate_folder_tag, +}; + +static bool +cmd_test_message_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + /* Register our tags */ + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &test_message_folder_tag, 0); + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &test_message_smtp_tag, 0); + return TRUE; +} + +static struct cmd_test_message_context_data * +cmd_test_message_validate_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct cmd_test_message_context_data *ctx_data = + (struct cmd_test_message_context_data *)cmd->data; + + if (ctx_data != NULL) { + sieve_argument_validate_error(valdtr, *arg, + CMD_TEST_MESSAGE_ERROR_DUP_TAG); + return NULL; + } + + ctx_data = p_new(sieve_command_pool(cmd), + struct cmd_test_message_context_data, 1); + cmd->data = ctx_data; + + /* Delete this tag */ + *arg = sieve_ast_arguments_detach(*arg, 1); + + return ctx_data; +} + +static bool +cmd_test_message_validate_smtp_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct cmd_test_message_context_data *ctx_data = + cmd_test_message_validate_tag(valdtr, arg, cmd); + + /* Return value is NULL on error */ + if (ctx_data == NULL) + return FALSE; + + /* Assign chosen message source */ + ctx_data->msg_source = MSG_SOURCE_SMTP; + + return TRUE; +} + +static bool +cmd_test_message_validate_folder_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + struct cmd_test_message_context_data *ctx_data = + cmd_test_message_validate_tag(valdtr, arg, cmd); + + /* Return value is NULL on error */ + if (ctx_data == NULL) + return FALSE; + + /* Assign chose message source */ + ctx_data->msg_source = MSG_SOURCE_MAILBOX; + + /* Check syntax: + * :folder string + */ + if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0, + SAAT_STRING, FALSE)) { + return FALSE; + } + + /* Check name validity when folder argument is not a variable */ + if ( sieve_argument_is_string_literal(*arg) ) { + const char *folder = sieve_ast_argument_strc(*arg), *error; + + if ( !sieve_mailbox_check_name(folder, &error) ) { + sieve_command_validate_error( + valdtr, cmd, "%s command: " + "invalid mailbox `%s' specified: %s", + sieve_command_identifier(cmd), + str_sanitize(folder, 256), error); + return FALSE; + } + } + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + + return TRUE; +} + +/* + * Validation + */ + +static bool +cmd_test_message_validate(struct sieve_validator *valdtr, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *arg = cmd->first_positional; + + if (cmd->data == NULL) { + sieve_command_validate_error( + valdtr, cmd, + "the test_message command requires either " + "the :smtp or the :mailbox tag to be specified"); + return FALSE; + } + + if (!sieve_validate_positional_argument(valdtr, cmd, arg, "index", 1, + SAAT_NUMBER)) + return FALSE; + + return sieve_validator_argument_activate(valdtr, cmd, arg, FALSE); +} + +/* + * Code generation + */ + +static bool +cmd_test_message_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd) +{ + struct cmd_test_message_context_data *ctx_data = + (struct cmd_test_message_context_data *)cmd->data; + + i_assert(ctx_data->msg_source < MSG_SOURCE_LAST); + + /* Emit operation */ + sieve_operation_emit(cgenv->sblock, cmd->ext, + test_message_operations[ctx_data->msg_source]); + + /* Emit is_test flag */ + sieve_binary_emit_byte(cgenv->sblock, + (cmd->ast_node->type == SAT_TEST ? 1 : 0)); + + /* Generate arguments */ + if (!sieve_generate_arguments(cgenv, cmd, NULL)) + return FALSE; + + return TRUE; +} + +static bool +cmd_test_message_print_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd) +{ + /* Emit operation */ + sieve_operation_emit(cgenv->sblock, cmd->ext, + &test_message_print_operation); + return TRUE; +} + +/* + * Code dump + */ + +static bool +cmd_test_message_smtp_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + unsigned int is_test; + + if (!sieve_binary_read_byte(denv->sblock, address, &is_test)) + return FALSE; + + sieve_code_dumpf(denv, "TEST_MESSAGE_SMTP (%s):", + (is_test > 0 ? "TEST" : "COMMAND")); + + sieve_code_descend(denv); + + return sieve_opr_number_dump(denv, address, "index"); +} + +static bool +cmd_test_message_mailbox_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + unsigned int is_test; + + if (!sieve_binary_read_byte(denv->sblock, address, &is_test)) + return FALSE; + + sieve_code_dumpf(denv, "TEST_MESSAGE_MAILBOX (%s):", + (is_test > 0 ? "TEST" : "COMMAND")); + + sieve_code_descend(denv); + + return (sieve_opr_string_dump(denv, address, "folder") && + sieve_opr_number_dump(denv, address, "index")); +} + +static bool +cmd_test_message_print_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address ATTR_UNUSED) +{ + sieve_code_dumpf(denv, "TEST_MESSAGE_PRINT"); + + return TRUE; +} + +/* + * Intepretation + */ + +static int +cmd_test_message_smtp_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + sieve_number_t msg_index; + unsigned int is_test = 0; + bool result; + int ret; + + /* + * Read operands + */ + + /* Is test */ + + if (!sieve_binary_read_byte(renv->sblock, address, &is_test)) { + sieve_runtime_trace_error(renv, "invalid is_test flag"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + /* Index */ + + ret = sieve_opr_number_read(renv, address, "index", &msg_index); + if (ret <= 0) + return ret; + + /* + * Perform operation + */ + + if (is_test > 0) { + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_TESTS)) { + sieve_runtime_trace( + renv, 0, "testsuite: test_message test"); + sieve_runtime_trace_descend(renv); + sieve_runtime_trace( + renv, 0, + "check and retrieve smtp message [index=%llu]", + (unsigned long long)msg_index); + } + } else { + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS)) { + sieve_runtime_trace( + renv, 0, "testsuite: test_message command"); + sieve_runtime_trace_descend(renv); + sieve_runtime_trace( + renv, 0, + "retrieve smtp message [index=%llu]", + (unsigned long long)msg_index); + } + } + + result = testsuite_smtp_get(renv, msg_index); + + if (is_test > 0) { + sieve_interpreter_set_test_result(renv->interp, result); + return SIEVE_EXEC_OK; + } + + if (!result) { + return testsuite_test_failf( + renv, "no outgoing SMTP message with index %llu", + (unsigned long long)msg_index); + } + + return SIEVE_EXEC_OK; +} + +static int +cmd_test_message_mailbox_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + string_t *folder; + sieve_number_t msg_index; + unsigned int is_test = 0; + bool result; + const char *error; + int ret; + + /* + * Read operands + */ + + /* Is test */ + if (!sieve_binary_read_byte(renv->sblock, address, &is_test)) { + sieve_runtime_trace_error(renv, "invalid is_test flag"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + /* Folder */ + ret = sieve_opr_string_read(renv, address, "folder", &folder); + if (ret <= 0) + return ret; + + /* Index */ + ret = sieve_opr_number_read(renv, address, "index", &msg_index); + if (ret <= 0) + return ret; + + if (!sieve_mailbox_check_name(str_c(folder), &error)) { + return testsuite_test_failf( + renv, "invalid mailbox `%s' specified: %s", + str_c(folder), error); + } + + /* + * Perform operation + */ + + if (is_test > 0) { + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_TESTS)) { + sieve_runtime_trace( + renv, 0, "testsuite: test_message test"); + sieve_runtime_trace_descend(renv); + sieve_runtime_trace( + renv, 0, "check and retrieve mailbox message " + "[mailbox=`%s' index=%llu]", + str_c(folder), (unsigned long long)msg_index); + } + } else { + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS)) { + sieve_runtime_trace( + renv, 0, "testsuite: test_message command"); + sieve_runtime_trace_descend(renv); + sieve_runtime_trace( + renv, 0, "retrieve mailbox message " + "[mailbox=`%s' index=%llu]", + str_c(folder), (unsigned long long)msg_index); + } + } + + result = testsuite_mailstore_mail_index(renv, str_c(folder), msg_index); + + if (is_test > 0) { + sieve_interpreter_set_test_result(renv->interp, result); + return SIEVE_EXEC_OK; + } + + if (!result) { + return testsuite_test_failf( + renv, "no message in folder '%s' with index %llu", + str_c(folder), (unsigned long long)msg_index); + } + + return SIEVE_EXEC_OK; +} + +static int +cmd_test_message_print_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address ATTR_UNUSED) +{ + struct mail *mail = sieve_message_get_mail(renv->msgctx); + struct istream *input; + const unsigned char *data; + size_t size; + + if (mail_get_stream(mail, NULL, NULL, &input) < 0) { + sieve_runtime_error(renv, NULL, "test_message_print: " + "failed to read current message"); + return SIEVE_EXEC_OK; + } + + printf("\n--MESSAGE: \n"); + + /* Pipe the message to the outgoing SMTP transport */ + while (i_stream_read_more(input, &data, &size) > 0) { + ssize_t wret; + + wret = write(1, data, size); + if (wret <= 0) + break; + i_stream_skip(input, wret); + } + printf("\n--MESSAGE--\n"); + + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/testsuite/cmd-test-result.c b/pigeonhole/src/testsuite/cmd-test-result.c new file mode 100644 index 0000000..230aa62 --- /dev/null +++ b/pigeonhole/src/testsuite/cmd-test-result.c @@ -0,0 +1,138 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-script.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-code.h" +#include "sieve-binary.h" +#include "sieve-dump.h" +#include "sieve.h" + +#include "testsuite-common.h" +#include "testsuite-result.h" +#include "testsuite-message.h" +#include "testsuite-smtp.h" + +/* + * Commands + */ + +static bool +cmd_test_result_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd); + +/* Test_result_reset command + * + * Syntax: + * test_result_reset + */ + +const struct sieve_command_def cmd_test_result_reset = { + .identifier = "test_result_reset", + .type = SCT_COMMAND, + .positional_args = 0, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .generate = cmd_test_result_generate, +}; + +/* Test_result_print command + * + * Syntax: + * test_result_print + */ + +const struct sieve_command_def cmd_test_result_print = { + .identifier = "test_result_print", + .type = SCT_COMMAND, + .positional_args = 0, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .generate = cmd_test_result_generate, +}; + +/* + * Operations + */ + +/* test_result_reset */ + +static int +cmd_test_result_reset_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def test_result_reset_operation = { + .mnemonic = "TEST_RESULT_RESET", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERATION_TEST_RESULT_RESET, + .execute = cmd_test_result_reset_operation_execute, +}; + +/* test_result_print */ + +static int +cmd_test_result_print_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def test_result_print_operation = { + .mnemonic = "TEST_RESULT_PRINT", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERATION_TEST_RESULT_PRINT, + .execute = cmd_test_result_print_operation_execute, +}; + +/* + * Code generation + */ + +static bool +cmd_test_result_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd) +{ + if (sieve_command_is(cmd, cmd_test_result_reset)) { + sieve_operation_emit(cgenv->sblock, cmd->ext, + &test_result_reset_operation); + } else if (sieve_command_is(cmd, cmd_test_result_print)) { + sieve_operation_emit(cgenv->sblock, cmd->ext, + &test_result_print_operation); + } else { + i_unreached(); + } + + return TRUE; +} + +/* + * Intepretation + */ + +static int +cmd_test_result_reset_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address ATTR_UNUSED) +{ + sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "testsuite: " + "test_result_reset command; reset script result"); + + testsuite_result_reset(renv); + testsuite_smtp_reset(); + + return SIEVE_EXEC_OK; +} + +static int +cmd_test_result_print_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address ATTR_UNUSED) +{ + sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "testsuite: " + "test_result_print command; print script result "); + + testsuite_result_print(renv); + + return SIEVE_EXEC_OK; +} diff --git a/pigeonhole/src/testsuite/cmd-test-set.c b/pigeonhole/src/testsuite/cmd-test-set.c new file mode 100644 index 0000000..22b51d2 --- /dev/null +++ b/pigeonhole/src/testsuite/cmd-test-set.c @@ -0,0 +1,161 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "ioloop.h" +#include "str-sanitize.h" +#include "istream.h" +#include "istream-header-filter.h" + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-code.h" +#include "sieve-actions.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-code-dumper.h" +#include "sieve-result.h" + +#include "testsuite-common.h" +#include "testsuite-objects.h" + +#include <stdio.h> + +/* + * Test_set command + * + * Syntax + * test_set <testsuite object (member): string> <value: string> + */ + +static bool cmd_test_set_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); +static bool cmd_test_set_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd); + +const struct sieve_command_def cmd_test_set = { + .identifier = "test_set", + .type = SCT_COMMAND, + .positional_args = 2, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = cmd_test_set_validate, + .generate = cmd_test_set_generate +}; + +/* + * Test_set operation + */ + +static bool cmd_test_set_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int cmd_test_set_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def test_set_operation = { + .mnemonic = "TEST_SET", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERATION_TEST_SET, + .dump = cmd_test_set_operation_dump, + .execute = cmd_test_set_operation_execute +}; + +/* + * Validation + */ + +static bool cmd_test_set_validate +(struct sieve_validator *valdtr, struct sieve_command *cmd) +{ + struct sieve_ast_argument *arg = cmd->first_positional; + + /* Check arguments */ + + if ( !sieve_validate_positional_argument + (valdtr, cmd, arg, "object", 1, SAAT_STRING) ) { + return FALSE; + } + + if ( !testsuite_object_argument_activate(valdtr, arg, cmd) ) + return FALSE; + + arg = sieve_ast_argument_next(arg); + + if ( !sieve_validate_positional_argument + (valdtr, cmd, arg, "value", 2, SAAT_STRING) ) { + return FALSE; + } + + return sieve_validator_argument_activate(valdtr, cmd, arg, FALSE); +} + +/* + * Code generation + */ + +static bool cmd_test_set_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + sieve_operation_emit(cgenv->sblock, cmd->ext, &test_set_operation); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, cmd, NULL); +} + +/* + * Code dump + */ + +static bool cmd_test_set_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + sieve_code_dumpf(denv, "TEST SET:"); + sieve_code_descend(denv); + + return + testsuite_object_dump(denv, address) && + sieve_opr_string_dump(denv, address, "value"); +} + +/* + * Intepretation + */ + +static int cmd_test_set_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + struct testsuite_object tobj; + string_t *value; + int member_id; + int ret; + + if ( !testsuite_object_read_member + (renv->sblock, address, &tobj, &member_id) ) { + sieve_runtime_trace_error(renv, "invalid testsuite object member"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if ( (ret=sieve_opr_string_read(renv, address, "string", &value)) <= 0 ) + return ret; + + if ( sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS) ) { + sieve_runtime_trace(renv, 0, "testsuite: test_set command"); + sieve_runtime_trace_descend(renv); + sieve_runtime_trace(renv, 0, + "set test parameter '%s' = \"%s\"", + testsuite_object_member_name(&tobj, member_id), str_c(value)); + } + + if ( tobj.def == NULL || tobj.def->set_member == NULL ) { + sieve_runtime_trace_error(renv, "unimplemented testsuite object"); + return SIEVE_EXEC_FAILURE; + } + + tobj.def->set_member(renv, member_id, value); + return SIEVE_EXEC_OK; +} + + + diff --git a/pigeonhole/src/testsuite/cmd-test.c b/pigeonhole/src/testsuite/cmd-test.c new file mode 100644 index 0000000..c817aed --- /dev/null +++ b/pigeonhole/src/testsuite/cmd-test.c @@ -0,0 +1,208 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-code.h" +#include "sieve-binary.h" +#include "sieve-dump.h" + +#include "testsuite-common.h" + +/* + * Test command + * + * Syntax: + * test <test-name: string> <block> + */ + +static bool +cmd_test_validate(struct sieve_validator *valdtr, struct sieve_command *cmd); +static bool +cmd_test_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *md); + +const struct sieve_command_def cmd_test = { + .identifier = "test", + .type = SCT_COMMAND, + .positional_args = 1, + .subtests = 0, + .block_allowed = TRUE, + .block_required = TRUE, + .validate = cmd_test_validate, + .generate = cmd_test_generate, +}; + +/* + * Test operations + */ + +/* Test operation */ + +static bool +cmd_test_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +cmd_test_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def test_operation = { + .mnemonic = "TEST", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERATION_TEST, + .dump = cmd_test_operation_dump, + .execute = cmd_test_operation_execute, +}; + +/* Test_finish operation */ + +static int +cmd_test_finish_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def test_finish_operation = { + .mnemonic = "TEST-FINISH", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERATION_TEST_FINISH, + .execute = cmd_test_finish_operation_execute, +}; + +/* + * Validation + */ + +static bool +cmd_test_validate(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *arg = cmd->first_positional; + + /* Check valid command placement */ + if (!sieve_command_is_toplevel(cmd)) { + sieve_command_validate_error( + valdtr, cmd, "tests cannot be nested: test " + "command must be issued at top-level"); + return FALSE; + } + + if (!sieve_validate_positional_argument(valdtr, cmd, arg, "test-name", + 1, SAAT_STRING)) + return FALSE; + + return sieve_validator_argument_activate(valdtr, cmd, arg, FALSE); +} + +/* + * Code generation + */ + +static inline struct testsuite_generator_context * +_get_generator_context(struct sieve_generator *gentr) +{ + return (struct testsuite_generator_context *) + sieve_generator_extension_get_context(gentr, testsuite_ext); +} + +static bool +cmd_test_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd) +{ + struct testsuite_generator_context *genctx = + _get_generator_context(cgenv->gentr); + + sieve_operation_emit(cgenv->sblock, cmd->ext, &test_operation); + + /* Generate arguments */ + if (!sieve_generate_arguments(cgenv, cmd, NULL)) + return FALSE; + + /* Prepare jumplist */ + sieve_jumplist_reset(genctx->exit_jumps); + sieve_jumplist_add(genctx->exit_jumps, + sieve_binary_emit_offset(cgenv->sblock, 0)); + + /* Test body */ + if (!sieve_generate_block(cgenv, cmd->ast_node)) + return FALSE; + + sieve_operation_emit(cgenv->sblock, cmd->ext, &test_finish_operation); + + /* Resolve exit jumps to this point */ + sieve_jumplist_resolve(genctx->exit_jumps); + + return TRUE; +} + +/* + * Code dump + */ + +static bool +cmd_test_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + sieve_size_t tst_begin; + sieve_offset_t tst_end_offset; + + sieve_code_dumpf(denv, "TEST:"); + sieve_code_descend(denv); + + if (!sieve_opr_string_dump(denv, address, "test name")) + return FALSE; + + sieve_code_mark(denv); + tst_begin = *address; + if (!sieve_binary_read_offset(denv->sblock, address, &tst_end_offset)) + return FALSE; + sieve_code_dumpf(denv, "end: %d [%08llx]", + tst_end_offset, + (unsigned long long)tst_begin + tst_end_offset); + + return TRUE; +} + +/* + * Interpretation + */ + +static int +cmd_test_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + sieve_size_t tst_begin, tst_end; + sieve_offset_t tst_end_offset; + string_t *test_name; + int ret; + + ret = sieve_opr_string_read(renv, address, "test name", &test_name); + if (ret <= 0) + return ret; + + tst_begin = *address; + if (!sieve_binary_read_offset(renv->sblock, address, &tst_end_offset)) { + sieve_runtime_trace_error(renv, "invalid end offset"); + return SIEVE_EXEC_BIN_CORRUPT; + } + tst_end = tst_begin + tst_end_offset; + + sieve_runtime_trace_sep(renv); + sieve_runtime_trace(renv, SIEVE_TRLVL_NONE, + "** Testsuite test start: \"%s\" (end: %08llx)", + str_c(test_name), + (unsigned long long)tst_end); + + return testsuite_test_start(renv, test_name, tst_end); +} + +static int +cmd_test_finish_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + sieve_runtime_trace(renv, SIEVE_TRLVL_NONE, "** Testsuite test end"); + sieve_runtime_trace_sep(renv); + + return testsuite_test_succeed(renv, address, NULL); +} diff --git a/pigeonhole/src/testsuite/ext-testsuite.c b/pigeonhole/src/testsuite/ext-testsuite.c new file mode 100644 index 0000000..cb9c92f --- /dev/null +++ b/pigeonhole/src/testsuite/ext-testsuite.c @@ -0,0 +1,175 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension testsuite + * ------------------- + * + * Authors: Stephan Bosch + * Specification: vendor-specific + * (FIXME: provide specification for test authors) + * + */ + +/* + * Purpose: This custom extension is used to add sieve commands and tests that + * act the Sieve engine and on the test suite itself. This practically + * provides the means to completely control and thereby test the Sieve + * compiler and interpreter. This extension transforms the basic Sieve + * language into something much more powerful and suitable to perform + * complex self-test operations. Of course, this extension is only + * available (as vnd.dovecot.testsuite) when the sieve engine is used + * from within the testsuite commandline tool. Test scripts have the + * extension .svtest by convention to distinguish them from any normal + * sieve scripts that may reside in the same directory. + * + * WARNING: Although this code can serve as an example on how to write + * extensions to the Sieve interpreter, it is generally _NOT_ to be + * used as a source for ideas on new Sieve extensions. Many of the + * commands and tests that this extension introduces conflict with the + * goal and the implied restrictions of the Sieve language. These + * restrictions were put in place with good reason. Therefore, do + * _NOT_ export functionality provided by this testsuite extension to + * your custom extensions that are to be put to general use. + */ + +#include <stdio.h> + +#include "sieve-common.h" + +#include "sieve-code.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-result.h" + +#include "testsuite-common.h" +#include "testsuite-variables.h" +#include "testsuite-arguments.h" + +/* + * Operations + */ + +const struct sieve_operation_def *testsuite_operations[] = { + &test_operation, + &test_finish_operation, + &test_fail_operation, + &test_config_set_operation, + &test_config_unset_operation, + &test_config_reload_operation, + &test_set_operation, + &test_script_compile_operation, + &test_script_run_operation, + &test_multiscript_operation, + &test_error_operation, + &test_result_action_operation, + &test_result_execute_operation, + &test_result_reset_operation, + &test_result_print_operation, + &test_message_smtp_operation, + &test_message_mailbox_operation, + &test_message_print_operation, + &test_mailbox_create_operation, + &test_mailbox_delete_operation, + &test_binary_load_operation, + &test_binary_save_operation, + &test_imap_metadata_set_operation +}; + +/* + * Operands + */ + +const struct sieve_operand_def *testsuite_operands[] = { + &testsuite_object_operand, + &testsuite_substitution_operand, + &testsuite_namespace_operand +}; + +/* + * Extension + */ + +/* Forward declarations */ + +static bool ext_testsuite_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); +static bool ext_testsuite_generator_load + (const struct sieve_extension *ext, const struct sieve_codegen_env *cgenv); +static bool ext_testsuite_interpreter_load + (const struct sieve_extension *ext, const struct sieve_runtime_env *renv, + sieve_size_t *address); +static bool ext_testsuite_binary_load + (const struct sieve_extension *ext, struct sieve_binary *sbin); + +/* Extension object */ + +const struct sieve_extension_def testsuite_extension = { + .name = "vnd.dovecot.testsuite", + .validator_load = ext_testsuite_validator_load, + .generator_load = ext_testsuite_generator_load, + .interpreter_load = ext_testsuite_interpreter_load, + .binary_load = ext_testsuite_binary_load, + SIEVE_EXT_DEFINE_OPERATIONS(testsuite_operations), + SIEVE_EXT_DEFINE_OPERANDS(testsuite_operands) +}; + +/* Extension implementation */ + +static bool ext_testsuite_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + sieve_validator_register_command(valdtr, ext, &cmd_test); + sieve_validator_register_command(valdtr, ext, &cmd_test_fail); + sieve_validator_register_command(valdtr, ext, &cmd_test_config_set); + sieve_validator_register_command(valdtr, ext, &cmd_test_config_unset); + sieve_validator_register_command(valdtr, ext, &cmd_test_config_reload); + sieve_validator_register_command(valdtr, ext, &cmd_test_set); + sieve_validator_register_command(valdtr, ext, &cmd_test_result_print); + sieve_validator_register_command(valdtr, ext, &cmd_test_result_reset); + sieve_validator_register_command(valdtr, ext, &cmd_test_message); + sieve_validator_register_command(valdtr, ext, &cmd_test_message_print); + sieve_validator_register_command(valdtr, ext, &cmd_test_mailbox_create); + sieve_validator_register_command(valdtr, ext, &cmd_test_mailbox_delete); + sieve_validator_register_command(valdtr, ext, &cmd_test_binary_load); + sieve_validator_register_command(valdtr, ext, &cmd_test_binary_save); + sieve_validator_register_command(valdtr, ext, &cmd_test_imap_metadata_set); + + sieve_validator_register_command(valdtr, ext, &tst_test_script_compile); + sieve_validator_register_command(valdtr, ext, &tst_test_script_run); + sieve_validator_register_command(valdtr, ext, &tst_test_multiscript); + sieve_validator_register_command(valdtr, ext, &tst_test_error); + sieve_validator_register_command(valdtr, ext, &tst_test_result_action); + sieve_validator_register_command(valdtr, ext, &tst_test_result_execute); + +/* sieve_validator_argument_override(valdtr, SAT_VAR_STRING, ext, + &testsuite_string_argument);*/ + + testsuite_variables_init(ext, valdtr); + + return testsuite_validator_context_initialize(valdtr); +} + +static bool ext_testsuite_generator_load +(const struct sieve_extension *ext, const struct sieve_codegen_env *cgenv) +{ + return testsuite_generator_context_initialize(cgenv->gentr, ext); +} + +static bool ext_testsuite_interpreter_load +(const struct sieve_extension *ext, const struct sieve_runtime_env *renv, + sieve_size_t *address ATTR_UNUSED) +{ + return testsuite_interpreter_context_initialize(renv->interp, ext); +} + +static bool ext_testsuite_binary_load +(const struct sieve_extension *ext ATTR_UNUSED, struct sieve_binary *sbin ATTR_UNUSED) +{ + return TRUE; +} + + + diff --git a/pigeonhole/src/testsuite/testsuite-arguments.c b/pigeonhole/src/testsuite/testsuite-arguments.c new file mode 100644 index 0000000..f1e17f4 --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-arguments.c @@ -0,0 +1,190 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "str-sanitize.h" +#include "array.h" + +#include "sieve-common.h" +#include "sieve-ast.h" +#include "sieve-commands.h" +#include "sieve-code.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-dump.h" + +#include "testsuite-common.h" +#include "testsuite-substitutions.h" +#include "testsuite-arguments.h" + +#include <ctype.h> + +/* + * Testsuite string argument + */ + +static bool arg_testsuite_string_validate + (struct sieve_validator *validator, struct sieve_ast_argument **arg, + struct sieve_command *context); + +const struct sieve_argument_def testsuite_string_argument = { + .identifier = "@testsuite-string", + .validate = arg_testsuite_string_validate, + .generate = sieve_arg_catenated_string_generate, +}; + +static bool arg_testsuite_string_validate +(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + enum { ST_NONE, ST_OPEN, ST_SUBSTITUTION, ST_PARAM, ST_CLOSE } state = + ST_NONE; + pool_t pool = sieve_ast_pool((*arg)->ast); + struct sieve_arg_catenated_string *catstr = NULL; + string_t *str = sieve_ast_argument_str(*arg); + const char *p, *strstart, *substart = NULL; + const char *strval = (const char *) str_data(str); + const char *strend = strval + str_len(str); + bool result = TRUE; + string_t *subs_name = t_str_new(256); + string_t *subs_param = t_str_new(256); + + T_BEGIN { + /* Initialize substitution structure */ + + p = strval; + strstart = p; + while ( result && p < strend ) { + switch ( state ) { + + /* Nothing found yet */ + case ST_NONE: + if ( *p == '%' ) { + substart = p; + state = ST_OPEN; + str_truncate(subs_name, 0); + str_truncate(subs_param, 0); + } + p++; + break; + + /* Got '%' */ + case ST_OPEN: + if ( *p == '{' ) { + state = ST_SUBSTITUTION; + p++; + } else + state = ST_NONE; + break; + + /* Got '%{' */ + case ST_SUBSTITUTION: + state = ST_PARAM; + + while ( *p != '}' && *p != ':' ) { + if ( !i_isalnum(*p) ) { + state = ST_NONE; + break; + } + str_append_c(subs_name, *p); + p++; + } + break; + + /* Got '%{name' */ + case ST_PARAM: + if ( *p == ':' ) { + p++; + while ( *p != '}' ) { + str_append_c(subs_param, *p); + p++; + } + } + state = ST_CLOSE; + break; + + /* Finished parsing param, expecting '}' */ + case ST_CLOSE: + if ( *p == '}' ) { + struct sieve_ast_argument *strarg; + + /* We now know that the substitution is valid */ + + if ( catstr == NULL ) { + catstr = sieve_arg_catenated_string_create(*arg); + } + + /* Add the substring that is before the substitution to the + * variable-string AST. + */ + if ( substart > strstart ) { + string_t *newstr = str_new(pool, substart - strstart); + str_append_data(newstr, strstart, substart - strstart); + + strarg = sieve_ast_argument_string_create_raw + ((*arg)->ast, newstr, (*arg)->source_line); + sieve_arg_catenated_string_add_element(catstr, strarg); + + /* Give other substitution extensions a chance to do their work */ + if ( !sieve_validator_argument_activate_super + (valdtr, cmd, strarg, FALSE) ) { + result = FALSE; + break; + } + } + + strarg = testsuite_substitution_argument_create + (valdtr, (*arg)->ast, (*arg)->source_line, str_c(subs_name), + str_c(subs_param)); + + if ( strarg != NULL ) + sieve_arg_catenated_string_add_element(catstr, strarg); + else { + sieve_argument_validate_error(valdtr, *arg, + "unknown testsuite substitution type '%s'", str_c(subs_name)); + } + + strstart = p + 1; + substart = strstart; + + p++; + } + + /* Finished, reset for the next substitution */ + state = ST_NONE; + } + } + } T_END; + + /* Bail out early if substitution is invalid */ + if ( !result ) return FALSE; + + /* Check whether any substitutions were found */ + if ( catstr == NULL ) { + /* No substitutions in this string, pass it on to any other substution + * extension. + */ + return sieve_validator_argument_activate_super(valdtr, cmd, *arg, TRUE); + } + + /* Add the final substring that comes after the last substitution to the + * variable-string AST. + */ + if ( strend > strstart ) { + struct sieve_ast_argument *strarg; + string_t *newstr = str_new(pool, strend - strstart); + str_append_data(newstr, strstart, strend - strstart); + + strarg = sieve_ast_argument_string_create_raw + ((*arg)->ast, newstr, (*arg)->source_line); + sieve_arg_catenated_string_add_element(catstr, strarg); + + /* Give other substitution extensions a chance to do their work */ + if ( !sieve_validator_argument_activate_super + (valdtr, cmd, strarg, FALSE) ) + return FALSE; + } + + return TRUE; +} diff --git a/pigeonhole/src/testsuite/testsuite-arguments.h b/pigeonhole/src/testsuite/testsuite-arguments.h new file mode 100644 index 0000000..22ab82b --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-arguments.h @@ -0,0 +1,6 @@ +#ifndef TESTSUITE_ARGUMENTS_H +#define TESTSUITE_ARGUMENTS_H + +extern const struct sieve_argument_def testsuite_string_argument; + +#endif diff --git a/pigeonhole/src/testsuite/testsuite-binary.c b/pigeonhole/src/testsuite/testsuite-binary.c new file mode 100644 index 0000000..6c875a7 --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-binary.c @@ -0,0 +1,82 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "mempool.h" +#include "imem.h" +#include "array.h" +#include "strfuncs.h" +#include "unlink-directory.h" + +#include "sieve.h" +#include "sieve-common.h" +#include "sieve-script.h" +#include "sieve-binary.h" +#include "sieve-error.h" + +#include "testsuite-common.h" +#include "testsuite-binary.h" + +#include <sys/stat.h> +#include <sys/types.h> + +/* + * State + */ + +static char *testsuite_binary_tmp = NULL; + +/* + * Initialization + */ + +void testsuite_binary_init(void) +{ + testsuite_binary_tmp = i_strconcat + (testsuite_tmp_dir_get(), "/binaries", NULL); + + if ( mkdir(testsuite_binary_tmp, 0700) < 0 ) { + i_fatal("failed to create temporary directory '%s': %m.", + testsuite_binary_tmp); + } +} + +void testsuite_binary_deinit(void) +{ + const char *error; + + if ( unlink_directory(testsuite_binary_tmp, UNLINK_DIRECTORY_FLAG_RMDIR, &error) < 0 ) { + i_warning("failed to remove temporary directory '%s': %s.", + testsuite_binary_tmp, error); + } + + i_free(testsuite_binary_tmp); +} + +void testsuite_binary_reset(void) +{ + testsuite_binary_init(); + testsuite_binary_deinit(); +} + +/* + * Binary Access + */ + +bool testsuite_binary_save(struct sieve_binary *sbin, const char *name) +{ + return ( sieve_save_as(sbin, t_strdup_printf + ("%s/%s", testsuite_binary_tmp, sieve_binfile_from_name(name)), TRUE, + 0600, NULL) > 0 ); +} + +struct sieve_binary *testsuite_binary_load(const char *name) +{ + struct sieve_instance *svinst = testsuite_sieve_instance; + + return sieve_load(svinst, t_strdup_printf + ("%s/%s", testsuite_binary_tmp, sieve_binfile_from_name(name)), NULL); +} + + + diff --git a/pigeonhole/src/testsuite/testsuite-binary.h b/pigeonhole/src/testsuite/testsuite-binary.h new file mode 100644 index 0000000..0a09d2b --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-binary.h @@ -0,0 +1,17 @@ +#ifndef TESTSUITE_BINARY_H +#define TESTSUITE_BINARY_H + +#include "sieve-common.h" + +void testsuite_binary_init(void); +void testsuite_binary_deinit(void); +void testsuite_binary_reset(void); + +/* + * Binary Access + */ + +bool testsuite_binary_save(struct sieve_binary *sbin, const char *name); +struct sieve_binary *testsuite_binary_load(const char *name); + +#endif diff --git a/pigeonhole/src/testsuite/testsuite-common.c b/pigeonhole/src/testsuite/testsuite-common.c new file mode 100644 index 0000000..269deee --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-common.c @@ -0,0 +1,374 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "string.h" +#include "ostream.h" +#include "hash.h" +#include "mail-storage.h" +#include "env-util.h" +#include "unlink-directory.h" + +#include "mail-raw.h" + +#include "sieve-common.h" +#include "sieve-code.h" +#include "sieve-message.h" +#include "sieve-commands.h" +#include "sieve-extensions.h" +#include "sieve-binary.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-result.h" +#include "sieve-dump.h" + +#include "testsuite-common.h" +#include "testsuite-settings.h" +#include "testsuite-objects.h" +#include "testsuite-log.h" +#include "testsuite-script.h" +#include "testsuite-binary.h" +#include "testsuite-result.h" +#include "testsuite-smtp.h" + +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <time.h> +#include <sys/stat.h> +#include <sys/types.h> + +/* + * Global data + */ + +struct sieve_instance *testsuite_sieve_instance = NULL; +char *testsuite_test_path = NULL; + +/* Test context */ + +static string_t *test_name; +static sieve_size_t test_block_end; +static unsigned int test_index; +static unsigned int test_failures; + +/* Extension */ + +const struct sieve_extension *testsuite_ext; + +/* + * Validator context + */ + +bool testsuite_validator_context_initialize(struct sieve_validator *valdtr) +{ + pool_t pool = sieve_validator_pool(valdtr); + struct testsuite_validator_context *ctx = + p_new(pool, struct testsuite_validator_context, 1); + + /* Setup object registry */ + ctx->object_registrations = + sieve_validator_object_registry_create(valdtr); + testsuite_register_core_objects(ctx); + + sieve_validator_extension_set_context(valdtr, testsuite_ext, ctx); + + return TRUE; +} + +struct testsuite_validator_context * +testsuite_validator_context_get(struct sieve_validator *valdtr) +{ + return (struct testsuite_validator_context *) + sieve_validator_extension_get_context(valdtr, testsuite_ext); +} + +/* + * Generator context + */ + +bool testsuite_generator_context_initialize( + struct sieve_generator *gentr, const struct sieve_extension *this_ext) +{ + pool_t pool = sieve_generator_pool(gentr); + struct sieve_binary_block *sblock = sieve_generator_get_block(gentr); + struct testsuite_generator_context *ctx = + p_new(pool, struct testsuite_generator_context, 1); + + /* Setup exit jumplist */ + ctx->exit_jumps = sieve_jumplist_create(pool, sblock); + + sieve_generator_extension_set_context(gentr, this_ext, ctx); + + return TRUE; +} + +/* + * Interpreter context + */ + +static void +testsuite_interpreter_free(const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_interpreter *interp ATTR_UNUSED, + void *context) +{ + struct testsuite_interpreter_context *ctx = + (struct testsuite_interpreter_context *)context; + + sieve_binary_unref(&ctx->compiled_script); +} + +const struct sieve_interpreter_extension testsuite_interpreter_ext = { + .ext_def = &testsuite_extension, + .free = testsuite_interpreter_free, +}; + +bool testsuite_interpreter_context_initialize( + struct sieve_interpreter *interp, const struct sieve_extension *this_ext) +{ + pool_t pool = sieve_interpreter_pool(interp); + struct testsuite_interpreter_context *ctx = + p_new(pool, struct testsuite_interpreter_context, 1); + + sieve_interpreter_extension_register(interp, this_ext, + &testsuite_interpreter_ext, ctx); + return TRUE; +} + +struct testsuite_interpreter_context * +testsuite_interpreter_context_get(struct sieve_interpreter *interp, + const struct sieve_extension *this_ext) +{ + struct testsuite_interpreter_context *ctx = + sieve_interpreter_extension_get_context(interp, this_ext); + + return ctx; +} + +/* + * Test context + */ + +static void testsuite_test_context_init(void) +{ + test_name = str_new(default_pool, 128); + test_block_end = 0; + test_index = 0; + test_failures = 0; +} + +int testsuite_test_start(const struct sieve_runtime_env *renv, + string_t *name, sieve_size_t block_end) +{ + if (test_block_end != 0) { + sieve_runtime_trace_error(renv, "already inside test block"); + return SIEVE_EXEC_BIN_CORRUPT; + } + str_truncate(test_name, 0); + str_append_str(test_name, name); + + test_block_end = block_end; + test_index++; + + return SIEVE_EXEC_OK; +} + +int testsuite_test_fail(const struct sieve_runtime_env *renv, + string_t *reason) +{ + return testsuite_test_fail_cstr(renv, str_c(reason)); +} + +int testsuite_test_failf(const struct sieve_runtime_env *renv, + const char *fmt, ...) +{ + va_list args; + int ret; + + va_start(args, fmt); + ret = testsuite_test_fail_cstr(renv, t_strdup_vprintf(fmt, args)); + va_end(args); + + return ret; +} + +int testsuite_test_fail_cstr(const struct sieve_runtime_env *renv, + const char *reason) +{ + sieve_size_t end = test_block_end; + + if (str_len(test_name) == 0) { + if (reason == NULL || *reason == '\0') + printf("%2d: Test FAILED\n", test_index); + else + printf("%2d: Test FAILED: %s\n", test_index, reason); + } else { + if (reason == NULL || *reason == '\0') { + printf("%2d: Test '%s' FAILED\n", + test_index, str_c(test_name)); + } else { + printf("%2d: Test '%s' FAILED: %s\n", + test_index, str_c(test_name), reason); + } + } + + str_truncate(test_name, 0); + test_block_end = 0; + + test_failures++; + + return sieve_interpreter_program_jump_to(renv->interp, end, FALSE); +} + +void testsuite_testcase_fail(const char *reason) +{ + if (reason == NULL || *reason == '\0') + printf("XX: Test CASE FAILED\n"); + else + printf("XX: Test CASE FAILED: %s\n", reason); + + test_failures++; +} + +int testsuite_test_succeed(const struct sieve_runtime_env *renv, + sieve_size_t *address, string_t *reason) +{ + sieve_size_t end = test_block_end; + int ret; + + if (str_len(test_name) == 0) { + if (reason == NULL || str_len(reason) == 0) + printf("%2d: Test SUCCEEDED\n", test_index); + else { + printf("%2d: Test SUCCEEDED: %s\n", + test_index, str_c(reason)); + } + } else { + if (reason == NULL || str_len(reason) == 0) { + printf("%2d: Test '%s' SUCCEEDED\n", + test_index, str_c(test_name)); + } else { + printf("%2d: Test '%s' SUCCEEDED: %s\n", test_index, + str_c(test_name), str_c(reason)); + } + } + + str_truncate(test_name, 0); + test_block_end = 0; + + if (*address > end) { + sieve_runtime_trace_error( + renv, "invalid test block end offset"); + return SIEVE_EXEC_BIN_CORRUPT; + } else if (*address < end) { + ret = sieve_interpreter_program_jump_to( + renv->interp, end, FALSE); + if (ret <= 0) + return ret; + } + + return SIEVE_EXEC_OK; +} + +static void testsuite_test_context_deinit(void) +{ + str_free(&test_name); +} + +bool testsuite_testcase_result(bool expect_failure) +{ + if (expect_failure) { + if (test_failures < test_index) { + printf("\nFAIL: Only %d of %d tests failed " + "(all expected to fail).\n\n", + test_failures, test_index); + return FALSE; + } + + printf("\nPASS: %d tests failed (expected to fail).\n\n", + (test_index == 0 ? 1 : test_index)); + return TRUE; + } + + if (test_failures > 0) { + printf("\nFAIL: %d of %d tests failed.\n\n", + test_failures, test_index); + return FALSE; + } + + printf("\nPASS: %d tests succeeded.\n\n", test_index); + return TRUE; +} + +/* + * Testsuite temporary directory + */ + +static char *testsuite_tmp_dir; + +static void testsuite_tmp_dir_init(void) +{ + testsuite_tmp_dir = i_strdup_printf("/tmp/dsieve-testsuite.%s.%s", + dec2str(time(NULL)), + dec2str(getpid())); + + if (mkdir(testsuite_tmp_dir, 0700) < 0) { + i_fatal("failed to create temporary directory '%s': %m.", + testsuite_tmp_dir); + } +} + +static void testsuite_tmp_dir_deinit(void) +{ + const char *error; + + if (unlink_directory(testsuite_tmp_dir, + UNLINK_DIRECTORY_FLAG_RMDIR, &error) < 0) + i_warning("failed to remove temporary directory '%s': %s.", + testsuite_tmp_dir, error); + + i_free(testsuite_tmp_dir); +} + +const char *testsuite_tmp_dir_get(void) +{ + return testsuite_tmp_dir; +} + +/* + * Main testsuite init/deinit + */ + +void testsuite_init(struct sieve_instance *svinst, const char *test_path, + bool log_stdout) +{ + testsuite_sieve_instance = svinst; + + testsuite_test_context_init(); + testsuite_log_init(log_stdout); + testsuite_tmp_dir_init(); + + testsuite_script_init(); + testsuite_binary_init(); + testsuite_smtp_init(); + + testsuite_ext = + sieve_extension_register(svinst, &testsuite_extension, TRUE); + + testsuite_test_path = i_strdup(test_path); +} + +void testsuite_deinit(void) +{ + i_free(testsuite_test_path); + + testsuite_smtp_deinit(); + testsuite_binary_deinit(); + testsuite_script_deinit(); + + testsuite_tmp_dir_deinit(); + testsuite_log_deinit(); + testsuite_test_context_deinit(); +} diff --git a/pigeonhole/src/testsuite/testsuite-common.h b/pigeonhole/src/testsuite/testsuite-common.h new file mode 100644 index 0000000..9a40cdd --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-common.h @@ -0,0 +1,193 @@ +#ifndef TESTSUITE_COMMON_H +#define TESTSUITE_COMMON_H + +#include "sieve-common.h" + +#include "sieve-tool.h" + +/* + * Global data + */ + +extern struct sieve_instance *testsuite_sieve_instance; + +extern const struct sieve_extension_def testsuite_extension; + +extern const struct sieve_extension *testsuite_ext; + +extern const struct sieve_script_env *testsuite_scriptenv; + +extern char *testsuite_test_path; + + +/* + * Validator context + */ + +struct testsuite_validator_context { + struct sieve_validator_object_registry *object_registrations; +}; + +bool testsuite_validator_context_initialize(struct sieve_validator *valdtr); +struct testsuite_validator_context * +testsuite_validator_context_get(struct sieve_validator *valdtr); + +/* + * Generator context + */ + +struct testsuite_generator_context { + struct sieve_jumplist *exit_jumps; +}; + +bool testsuite_generator_context_initialize( + struct sieve_generator *gentr, const struct sieve_extension *this_ext); + +/* + * Interpreter context + */ + +struct testsuite_interpreter_context { + struct sieve_binary *compiled_script; +}; + +bool testsuite_interpreter_context_initialize( + struct sieve_interpreter *interp, + const struct sieve_extension *this_ext); +struct testsuite_interpreter_context * +testsuite_interpreter_context_get(struct sieve_interpreter *interp, + const struct sieve_extension *this_ext); + +/* + * Commands + */ + +extern const struct sieve_command_def cmd_test; +extern const struct sieve_command_def cmd_test_fail; +extern const struct sieve_command_def cmd_test_config_set; +extern const struct sieve_command_def cmd_test_config_unset; +extern const struct sieve_command_def cmd_test_config_reload; +extern const struct sieve_command_def cmd_test_set; +extern const struct sieve_command_def cmd_test_result_reset; +extern const struct sieve_command_def cmd_test_result_print; +extern const struct sieve_command_def cmd_test_message; +extern const struct sieve_command_def cmd_test_message_print; +extern const struct sieve_command_def cmd_test_mailbox; +extern const struct sieve_command_def cmd_test_mailbox_create; +extern const struct sieve_command_def cmd_test_mailbox_delete; +extern const struct sieve_command_def cmd_test_binary_load; +extern const struct sieve_command_def cmd_test_binary_save; +extern const struct sieve_command_def cmd_test_imap_metadata_set; + +/* + * Tests + */ + +extern const struct sieve_command_def tst_test_script_compile; +extern const struct sieve_command_def tst_test_script_run; +extern const struct sieve_command_def tst_test_multiscript; +extern const struct sieve_command_def tst_test_error; +extern const struct sieve_command_def tst_test_result_action; +extern const struct sieve_command_def tst_test_result_execute; + +/* + * Operations + */ + +enum testsuite_operation_code { + TESTSUITE_OPERATION_TEST, + TESTSUITE_OPERATION_TEST_FINISH, + TESTSUITE_OPERATION_TEST_FAIL, + TESTSUITE_OPERATION_TEST_CONFIG_SET, + TESTSUITE_OPERATION_TEST_CONFIG_UNSET, + TESTSUITE_OPERATION_TEST_CONFIG_RELOAD, + TESTSUITE_OPERATION_TEST_SET, + TESTSUITE_OPERATION_TEST_SCRIPT_COMPILE, + TESTSUITE_OPERATION_TEST_SCRIPT_RUN, + TESTSUITE_OPERATION_TEST_MULTISCRIPT, + TESTSUITE_OPERATION_TEST_ERROR, + TESTSUITE_OPERATION_TEST_RESULT_ACTION, + TESTSUITE_OPERATION_TEST_RESULT_EXECUTE, + TESTSUITE_OPERATION_TEST_RESULT_RESET, + TESTSUITE_OPERATION_TEST_RESULT_PRINT, + TESTSUITE_OPERATION_TEST_MESSAGE_SMTP, + TESTSUITE_OPERATION_TEST_MESSAGE_MAILBOX, + TESTSUITE_OPERATION_TEST_MESSAGE_PRINT, + TESTSUITE_OPERATION_TEST_MAILBOX_CREATE, + TESTSUITE_OPERATION_TEST_MAILBOX_DELETE, + TESTSUITE_OPERATION_TEST_BINARY_LOAD, + TESTSUITE_OPERATION_TEST_BINARY_SAVE, + TESTSUITE_OPERATION_TEST_IMAP_METADATA_SET +}; + +extern const struct sieve_operation_def test_operation; +extern const struct sieve_operation_def test_finish_operation; +extern const struct sieve_operation_def test_fail_operation; +extern const struct sieve_operation_def test_config_set_operation; +extern const struct sieve_operation_def test_config_unset_operation; +extern const struct sieve_operation_def test_config_reload_operation; +extern const struct sieve_operation_def test_set_operation; +extern const struct sieve_operation_def test_script_compile_operation; +extern const struct sieve_operation_def test_script_run_operation; +extern const struct sieve_operation_def test_multiscript_operation; +extern const struct sieve_operation_def test_error_operation; +extern const struct sieve_operation_def test_result_action_operation; +extern const struct sieve_operation_def test_result_execute_operation; +extern const struct sieve_operation_def test_result_reset_operation; +extern const struct sieve_operation_def test_result_print_operation; +extern const struct sieve_operation_def test_message_smtp_operation; +extern const struct sieve_operation_def test_message_mailbox_operation; +extern const struct sieve_operation_def test_message_print_operation; +extern const struct sieve_operation_def test_mailbox_create_operation; +extern const struct sieve_operation_def test_mailbox_delete_operation; +extern const struct sieve_operation_def test_binary_load_operation; +extern const struct sieve_operation_def test_binary_save_operation; +extern const struct sieve_operation_def test_imap_metadata_set_operation; + +/* + * Operands + */ + +extern const struct sieve_operand_def testsuite_object_operand; +extern const struct sieve_operand_def testsuite_substitution_operand; + +enum testsuite_operand_code { + TESTSUITE_OPERAND_OBJECT, + TESTSUITE_OPERAND_SUBSTITUTION, + TESTSUITE_OPERAND_NAMESPACE +}; + +/* + * Test context + */ + +int testsuite_test_start(const struct sieve_runtime_env *renv, + string_t *name, sieve_size_t block_end); +int testsuite_test_fail(const struct sieve_runtime_env *renv, + string_t *reason); +int testsuite_test_failf(const struct sieve_runtime_env *renv, + const char *fmt, ...) ATTR_FORMAT(2, 3); +int testsuite_test_fail_cstr(const struct sieve_runtime_env *renv, + const char *reason); + +int testsuite_test_succeed(const struct sieve_runtime_env *renv, + sieve_size_t *address, string_t *reason); + +void testsuite_testcase_fail(const char *reason); +bool testsuite_testcase_result(bool expect_failure); + +/* + * Testsuite temporary directory + */ + +const char *testsuite_tmp_dir_get(void); + +/* + * Testsuite init/deinit + */ + +void testsuite_init(struct sieve_instance *svinst, const char *test_path, + bool log_stdout); +void testsuite_deinit(void); + +#endif diff --git a/pigeonhole/src/testsuite/testsuite-log.c b/pigeonhole/src/testsuite/testsuite-log.c new file mode 100644 index 0000000..d06b744 --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-log.c @@ -0,0 +1,319 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "array.h" + +#include "sieve-common.h" +#include "sieve-stringlist.h" +#include "sieve-error-private.h" + +#include "testsuite-common.h" +#include "testsuite-log.h" + +/* + * Configuration + */ + +bool _testsuite_log_stdout = FALSE; + +/* + * Testsuite log error handlers + */ + +struct sieve_error_handler *testsuite_log_ehandler = NULL; +struct sieve_error_handler *testsuite_log_main_ehandler = NULL; + +struct _testsuite_log_message { + const char *location; + const char *message; +}; + +static pool_t _testsuite_logmsg_pool = NULL; +ARRAY(struct _testsuite_log_message) _testsuite_log_errors; +ARRAY(struct _testsuite_log_message) _testsuite_log_warnings; +ARRAY(struct _testsuite_log_message) _testsuite_log_messages; + +static inline void +_testsuite_stdout_log(const struct sieve_error_params *params, + const char *prefix, const char *message) +{ + if (_testsuite_log_stdout) { + if (params->location == NULL || *params->location == '\0') { + fprintf(stdout, "LOG: %s: %s\n", + prefix, message); + } else { + fprintf(stdout, "LOG: %s: %s: %s\n", + params->location, prefix, message); + } + } +} + +static void +_testsuite_log(struct sieve_error_handler *ehandler ATTR_UNUSED, + const struct sieve_error_params *params, + enum sieve_error_flags flags ATTR_UNUSED, const char *message) +{ + pool_t pool = _testsuite_logmsg_pool; + struct _testsuite_log_message msg; + const char *prefix; + + switch (params->log_type) { + case LOG_TYPE_ERROR: + prefix = "error"; + break; + case LOG_TYPE_WARNING: + prefix = "warning"; + break; + case LOG_TYPE_INFO: + prefix = "info"; + break; + case LOG_TYPE_DEBUG: + prefix = "debug"; + break; + default: + i_unreached(); + } + + _testsuite_stdout_log(params, prefix, message); + + msg.location = p_strdup(pool, params->location); + msg.message = p_strdup(pool, message); + + switch (params->log_type) { + case LOG_TYPE_ERROR: + array_append(&_testsuite_log_errors, &msg, 1); + break; + case LOG_TYPE_WARNING: + array_append(&_testsuite_log_warnings, &msg, 1); + break; + case LOG_TYPE_INFO: + array_append(&_testsuite_log_messages, &msg, 1); + break; + case LOG_TYPE_DEBUG: + break; + default: + i_unreached(); + } +} + +static void +_testsuite_main_log(struct sieve_error_handler *ehandler, + const struct sieve_error_params *params, + enum sieve_error_flags flags, const char *message) +{ + if (params->log_type != LOG_TYPE_ERROR) + return _testsuite_log(ehandler, params, flags, message); + + if (params->location == NULL || *params->location == '\0') + fprintf(stderr, "error: %s\n", message); + else + fprintf(stderr, "%s: error: %s\n", params->location, message); +} + +static struct sieve_error_handler *_testsuite_log_ehandler_create(void) +{ + pool_t pool; + struct sieve_error_handler *ehandler; + + pool = pool_alloconly_create("testsuite_log_ehandler", + sizeof(struct sieve_error_handler)); + ehandler = p_new(pool, struct sieve_error_handler, 1); + sieve_error_handler_init(ehandler, testsuite_sieve_instance, pool, 0); + + ehandler->log = _testsuite_log; + + return ehandler; +} + +static struct sieve_error_handler *_testsuite_log_main_ehandler_create(void) +{ + pool_t pool; + struct sieve_error_handler *ehandler; + + pool = pool_alloconly_create("testsuite_log_main_ehandler", + sizeof(struct sieve_error_handler)); + ehandler = p_new(pool, struct sieve_error_handler, 1); + sieve_error_handler_init(ehandler, testsuite_sieve_instance, pool, 0); + + ehandler->log = _testsuite_main_log; + + return ehandler; +} + +static void ATTR_FORMAT(2, 0) +testsuite_error_handler(const struct failure_context *ctx, const char *fmt, + va_list args) +{ + struct sieve_error_params params = { + .location = NULL, + }; + pool_t pool = _testsuite_logmsg_pool; + struct _testsuite_log_message msg; + + i_zero(&msg); + switch (ctx->type) { + case LOG_TYPE_DEBUG: + T_BEGIN { + _testsuite_stdout_log(¶ms, "debug", + t_strdup_vprintf(fmt, args)); + } T_END; + break; + case LOG_TYPE_INFO: + msg.message = p_strdup_vprintf(pool, fmt, args); + array_append(&_testsuite_log_messages, &msg, 1); + + _testsuite_stdout_log(¶ms, "info", msg.message); + break; + case LOG_TYPE_WARNING: + msg.message = p_strdup_vprintf(pool, fmt, args); + array_append(&_testsuite_log_warnings, &msg, 1); + + _testsuite_stdout_log(¶ms, "warning", msg.message); + break; + case LOG_TYPE_ERROR: + msg.message = p_strdup_vprintf(pool, fmt, args); + array_append(&_testsuite_log_errors, &msg, 1); + + _testsuite_stdout_log(¶ms, "error", msg.message); + break; + default: + default_error_handler(ctx, fmt, args); + break; + } +} + +/* + * + */ + +void testsuite_log_clear_messages(void) +{ + if (_testsuite_logmsg_pool != NULL) { + if (array_count(&_testsuite_log_errors) == 0) + return; + pool_unref(&_testsuite_logmsg_pool); + } + + _testsuite_logmsg_pool = + pool_alloconly_create("testsuite_log_messages", 8192); + + p_array_init(&_testsuite_log_errors, _testsuite_logmsg_pool, 128); + p_array_init(&_testsuite_log_warnings, _testsuite_logmsg_pool, 128); + p_array_init(&_testsuite_log_messages, _testsuite_logmsg_pool, 128); + + sieve_error_handler_reset(testsuite_log_ehandler); +} + +/* + * + */ + +void testsuite_log_init(bool log_stdout) +{ + _testsuite_log_stdout = log_stdout; + + testsuite_log_ehandler = _testsuite_log_ehandler_create(); + sieve_error_handler_accept_infolog(testsuite_log_ehandler, TRUE); + sieve_error_handler_accept_debuglog(testsuite_log_ehandler, TRUE); + + testsuite_log_main_ehandler = _testsuite_log_main_ehandler_create(); + sieve_error_handler_accept_infolog(testsuite_log_main_ehandler, TRUE); + sieve_error_handler_accept_debuglog(testsuite_log_main_ehandler, TRUE); + + i_set_error_handler(testsuite_error_handler); + i_set_info_handler(testsuite_error_handler); + i_set_debug_handler(testsuite_error_handler); + + testsuite_log_clear_messages(); +} + +void testsuite_log_deinit(void) +{ + sieve_error_handler_unref(&testsuite_log_ehandler); + sieve_error_handler_unref(&testsuite_log_main_ehandler); + + i_set_error_handler(default_error_handler); + i_set_info_handler(default_error_handler); + i_set_debug_handler(default_error_handler); + + pool_unref(&_testsuite_logmsg_pool); +} + +/* + * Log stringlist + */ + +/* Forward declarations */ + +static int +testsuite_log_stringlist_next_item(struct sieve_stringlist *_strlist, + string_t **str_r); +static void testsuite_log_stringlist_reset(struct sieve_stringlist *_strlist); + +/* Stringlist object */ + +struct testsuite_log_stringlist { + struct sieve_stringlist strlist; + + int pos, index; +}; + +struct sieve_stringlist * +testsuite_log_stringlist_create(const struct sieve_runtime_env *renv, + int index) +{ + struct testsuite_log_stringlist *strlist; + + strlist = t_new(struct testsuite_log_stringlist, 1); + strlist->strlist.runenv = renv; + strlist->strlist.exec_status = SIEVE_EXEC_OK; + strlist->strlist.next_item = testsuite_log_stringlist_next_item; + strlist->strlist.reset = testsuite_log_stringlist_reset; + + strlist->index = index; + strlist->pos = 0; + + return &strlist->strlist; +} + +static int +testsuite_log_stringlist_next_item(struct sieve_stringlist *_strlist, + string_t **str_r) +{ + struct testsuite_log_stringlist *strlist = + (struct testsuite_log_stringlist *) _strlist; + const struct _testsuite_log_message *msg; + int pos; + + *str_r = NULL; + + if (strlist->pos < 0) + return 0; + + if (strlist->index > 0) { + pos = strlist->index - 1; + strlist->pos = -1; + } else { + pos = strlist->pos++; + } + + if (pos >= (int) array_count(&_testsuite_log_errors)) { + strlist->pos = -1; + return 0; + } + + msg = array_idx(&_testsuite_log_errors, (unsigned int) pos); + + *str_r = t_str_new_const(msg->message, strlen(msg->message)); + return 1; +} + +static void testsuite_log_stringlist_reset(struct sieve_stringlist *_strlist) +{ + struct testsuite_log_stringlist *strlist = + (struct testsuite_log_stringlist *) _strlist; + + strlist->pos = 0; +} diff --git a/pigeonhole/src/testsuite/testsuite-log.h b/pigeonhole/src/testsuite/testsuite-log.h new file mode 100644 index 0000000..335d044 --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-log.h @@ -0,0 +1,26 @@ +#ifndef TESTSUITE_LOG_H +#define TESTSUITE_LOG_H + +#include "sieve-common.h" + +extern struct sieve_error_handler *testsuite_log_ehandler; +extern struct sieve_error_handler *testsuite_log_main_ehandler; + +/* + * Initialization + */ + +void testsuite_log_init(bool log_stdout); +void testsuite_log_deinit(void); + +/* + * Access + */ + +void testsuite_log_clear_messages(void); + +struct sieve_stringlist * +testsuite_log_stringlist_create(const struct sieve_runtime_env *renv, + int index); + +#endif diff --git a/pigeonhole/src/testsuite/testsuite-mailstore.c b/pigeonhole/src/testsuite/testsuite-mailstore.c new file mode 100644 index 0000000..7762d50 --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-mailstore.c @@ -0,0 +1,349 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "mempool.h" +#include "imem.h" +#include "array.h" +#include "strfuncs.h" +#include "str-sanitize.h" +#include "path-util.h" +#include "unlink-directory.h" +#include "env-util.h" +#include "mail-namespace.h" +#include "mail-storage.h" +#include "imap-metadata.h" + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-actions.h" +#include "sieve-interpreter.h" + +#include "testsuite-message.h" +#include "testsuite-common.h" +#include "testsuite-smtp.h" + +#include "testsuite-mailstore.h" + +#include <sys/stat.h> +#include <sys/types.h> + +struct testsuite_mailstore_mail { + struct testsuite_mailstore_mail *next; + + char *folder; + struct mailbox *box; + struct mailbox_transaction_context *trans; + struct mail *mail; +}; + +/* + * Forward declarations + */ + +static void testsuite_mailstore_free(bool all); + +/* + * State + */ + +static struct mail_user *testsuite_mailstore_user = NULL; + +static struct testsuite_mailstore_mail *testsuite_mailstore_mail = NULL; + +static char *testsuite_mailstore_location = NULL; +static char *testsuite_mailstore_attrs = NULL; + +/* + * Initialization + */ + +void testsuite_mailstore_init(void) +{ + struct mail_user *mail_user_dovecot, *mail_user; + struct mail_namespace *ns; + struct mail_namespace_settings *ns_set; + struct mail_storage_settings *mail_set; + const char *tmpdir, *error, *cwd; + + tmpdir = testsuite_tmp_dir_get(); + testsuite_mailstore_location = + i_strconcat(tmpdir, "/mailstore", NULL); + testsuite_mailstore_attrs = + i_strconcat(tmpdir, "/mail-attrs.dict", NULL); + + if (mkdir(testsuite_mailstore_location, 0700) < 0) { + i_fatal("failed to create temporary directory '%s': %m.", + testsuite_mailstore_location); + } + + mail_user_dovecot = sieve_tool_get_mail_user(sieve_tool); + mail_user = mail_user_alloc(NULL, "testsuite-mail-user@example.org", + mail_user_dovecot->set_info, + mail_user_dovecot->unexpanded_set); + mail_user->autocreated = TRUE; + if (t_get_working_dir(&cwd, &error) < 0) + i_fatal("Failed to get working directory: %s", error); + mail_user_set_home(mail_user, cwd); + if (mail_user_init(mail_user, &error) < 0) + i_fatal("Testsuite user initialization failed: %s", error); + + ns_set = p_new(mail_user->pool, struct mail_namespace_settings, 1); + ns_set->location = testsuite_mailstore_location; + ns_set->separator = "."; + + ns = mail_namespaces_init_empty(mail_user); + ns->flags |= NAMESPACE_FLAG_INBOX_USER; + ns->set = ns_set; + /* absolute paths are ok with raw storage */ + mail_set = p_new(mail_user->pool, struct mail_storage_settings, 1); + *mail_set = *ns->mail_set; + mail_set->mail_location = p_strconcat( + mail_user->pool, "maildir:", + testsuite_mailstore_location, NULL); + mail_set->mail_attribute_dict = p_strconcat( + mail_user->pool, "file:", + testsuite_mailstore_attrs, NULL); + ns->mail_set = mail_set; + + if (mail_storage_create(ns, "maildir", 0, &error) < 0) + i_fatal("Couldn't create testsuite storage: %s", error); + if (mail_namespaces_init_finish(ns, &error) < 0) + i_fatal("Couldn't create testsuite namespace: %s", error); + + testsuite_mailstore_user = mail_user; +} + +void testsuite_mailstore_deinit(void) +{ + const char *error; + + testsuite_mailstore_free(TRUE); + + if (unlink_directory(testsuite_mailstore_location, + UNLINK_DIRECTORY_FLAG_RMDIR, &error) < 0) { + i_warning("failed to remove temporary directory '%s': %s.", + testsuite_mailstore_location, error); + } + + i_free(testsuite_mailstore_location); + i_free(testsuite_mailstore_attrs); + mail_user_unref(&testsuite_mailstore_user); +} + +/* + * Mail user + */ + +struct mail_user *testsuite_mailstore_get_user(void) +{ + if (testsuite_mailstore_user == NULL) + return sieve_tool_get_mail_user(sieve_tool); + return testsuite_mailstore_user; +} + +/* + * Mailbox Access + */ + +bool testsuite_mailstore_mailbox_create( + const struct sieve_runtime_env *renv ATTR_UNUSED, const char *folder) +{ + struct mail_user *mail_user = testsuite_mailstore_user; + struct mail_namespace *ns = mail_user->namespaces; + struct mailbox *box; + + box = mailbox_alloc(ns->list, folder, 0); + + if (mailbox_create(box, NULL, FALSE) < 0) { + mailbox_free(&box); + return FALSE; + } + + mailbox_free(&box); + + return TRUE; +} + +static struct testsuite_mailstore_mail * +testsuite_mailstore_open(const char *folder) +{ + enum mailbox_flags flags = + MAILBOX_FLAG_SAVEONLY | MAILBOX_FLAG_POST_SESSION; + struct mail_user *mail_user = testsuite_mailstore_user; + struct mail_namespace *ns = mail_user->namespaces; + struct mailbox *box; + struct mailbox_transaction_context *t; + struct testsuite_mailstore_mail *tmail, *tmail_prev; + const char *error; + + if (!sieve_mailbox_check_name(folder, &error)) { + e_error(testsuite_sieve_instance->event, + "testsuite: invalid mailbox name `%s' specified: %s", + folder, error); + return NULL; + } + + tmail = testsuite_mailstore_mail; + tmail_prev = NULL; + while (tmail != NULL) { + if (strcmp(tmail->folder, folder) == 0) { + if (tmail_prev != NULL) { + /* Remove it from list if it is not first. */ + tmail_prev->next = tmail->next; + } + break; + } + tmail_prev = tmail; + tmail = tmail->next; + } + if (tmail != NULL) { + if (tmail != testsuite_mailstore_mail) { + /* Bring it to front */ + tmail->next = testsuite_mailstore_mail; + testsuite_mailstore_mail = tmail; + } + return tmail; + } + + box = mailbox_alloc(ns->list, folder, flags); + if (mailbox_open(box) < 0) { + e_error(testsuite_sieve_instance->event, + "testsuite: failed to open mailbox '%s'", folder); + mailbox_free(&box); + return NULL; + } + + /* Sync mailbox */ + + if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) { + e_error(testsuite_sieve_instance->event, + "testsuite: failed to sync mailbox '%s'", folder); + mailbox_free(&box); + return NULL; + } + + /* Start transaction */ + + t = mailbox_transaction_begin(box, 0, __func__); + + tmail = i_new(struct testsuite_mailstore_mail, 1); + tmail->next = testsuite_mailstore_mail; + testsuite_mailstore_mail = tmail; + + tmail->folder = i_strdup(folder); + tmail->box = box; + tmail->trans = t; + tmail->mail = mail_alloc(t, 0, NULL); + + return tmail; +} + +static void testsuite_mailstore_free(bool all) +{ + struct testsuite_mailstore_mail *tmail; + + if (testsuite_mailstore_mail == NULL) + return; + + tmail = (all ? + testsuite_mailstore_mail : testsuite_mailstore_mail->next); + while (tmail != NULL) { + struct testsuite_mailstore_mail *tmail_next = tmail->next; + + mail_free(&tmail->mail); + mailbox_transaction_rollback(&tmail->trans); + mailbox_free(&tmail->box); + i_free(tmail->folder); + i_free(tmail); + + tmail = tmail_next; + } + if (all) + testsuite_mailstore_mail = NULL; + else + testsuite_mailstore_mail->next = NULL; +} + +void testsuite_mailstore_flush(void) +{ + testsuite_mailstore_free(FALSE); +} + +bool testsuite_mailstore_mail_index(const struct sieve_runtime_env *renv, + const char *folder, unsigned int index) +{ + struct testsuite_mailstore_mail *tmail; + struct mailbox_status status; + + tmail = testsuite_mailstore_open(folder); + if (tmail == NULL) + return FALSE; + + mailbox_get_open_status(tmail->box, STATUS_MESSAGES, &status); + if (index >= status.messages) + return FALSE; + + mail_set_seq(tmail->mail, index+1); + testsuite_message_set_mail(renv, tmail->mail); + + return TRUE; +} + +/* + * IMAP metadata + */ + +int testsuite_mailstore_set_imap_metadata(const char *mailbox, + const char *annotation, + const char *value) +{ + struct imap_metadata_transaction *imtrans; + struct mail_attribute_value avalue; + struct mailbox *box; + enum mail_error error_code; + const char *error; + int ret; + + if (!imap_metadata_verify_entry_name(annotation, &error)) { + e_error(testsuite_sieve_instance->event, + "testsuite: imap metadata: " + "specified annotation name `%s' is invalid: %s", + str_sanitize(annotation, 256), error); + return -1; + } + + if (mailbox != NULL) { + struct mail_namespace *ns; + ns = mail_namespace_find(testsuite_mailstore_user->namespaces, + mailbox); + box = mailbox_alloc(ns->list, mailbox, 0); + imtrans = imap_metadata_transaction_begin(box); + } else { + box = NULL; + imtrans = imap_metadata_transaction_begin_server( + testsuite_mailstore_user); + } + + i_zero(&avalue); + avalue.value = value; + if ((ret = imap_metadata_set(imtrans, annotation, &avalue)) < 0) { + error = imap_metadata_transaction_get_last_error( + imtrans, &error_code); + imap_metadata_transaction_rollback(&imtrans); + } else { + ret = imap_metadata_transaction_commit(&imtrans, + &error_code, &error); + } + if (box != NULL) + mailbox_free(&box); + + if (ret < 0) { + e_error(testsuite_sieve_instance->event, + "testsuite: imap metadata: " + "failed to assign annotation `%s': %s", + str_sanitize(annotation, 256), error); + return -1; + } + return 0; +} diff --git a/pigeonhole/src/testsuite/testsuite-mailstore.h b/pigeonhole/src/testsuite/testsuite-mailstore.h new file mode 100644 index 0000000..07b0ab0 --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-mailstore.h @@ -0,0 +1,40 @@ +#ifndef TESTSUITE_MAILSTORE_H +#define TESTSUITE_MAILSTORE_H + +#include "lib.h" + +#include "sieve-common.h" + +/* + * Initialization + */ + +void testsuite_mailstore_init(void); +void testsuite_mailstore_deinit(void); +void testsuite_mailstore_flush(void); + +/* + * Mail user + */ + +struct mail_user *testsuite_mailstore_get_user(void); + +/* + * Mailbox Access + */ + +bool testsuite_mailstore_mailbox_create( + const struct sieve_runtime_env *renv ATTR_UNUSED, const char *folder); + +bool testsuite_mailstore_mail_index(const struct sieve_runtime_env *renv, + const char *folder, unsigned int index); + +/* + * IMAP metadata + */ + +int testsuite_mailstore_set_imap_metadata(const char *mailbox, + const char *annotation, + const char *value); + +#endif diff --git a/pigeonhole/src/testsuite/testsuite-message.c b/pigeonhole/src/testsuite/testsuite-message.c new file mode 100644 index 0000000..68e20eb --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-message.c @@ -0,0 +1,339 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "istream.h" +#include "smtp-params.h" +#include "message-address.h" +#include "mail-storage.h" +#include "master-service.h" +#include "mail-raw.h" + +#include "sieve-common.h" +#include "sieve-address.h" +#include "sieve-error.h" +#include "sieve-message.h" +#include "sieve-interpreter.h" + +#include "sieve-tool.h" + +#include "testsuite-common.h" +#include "testsuite-message.h" + +/* + * Testsuite message environment + */ + +struct testsuite_message { + struct testsuite_message *next; + + struct mail_raw *mail_raw; +}; + +struct sieve_message_data testsuite_msgdata; +static struct smtp_params_rcpt testsuite_rcpt_params; + +static struct testsuite_message *testsuite_msg; + +static const char *_default_message_data = +"From: sender@example.com\n" +"To: recipient@example.org\n" +"Subject: Frop!\n" +"\n" +"Friep!\n"; + +static struct smtp_address *testsuite_env_mail_from = NULL; +static struct smtp_address *testsuite_env_rcpt_to = NULL; +static struct smtp_address *testsuite_env_orig_rcpt_to = NULL; +static char *testsuite_env_auth = NULL; + +static pool_t testsuite_msg_pool; +static char *testsuite_msg_id = NULL; + +static const struct smtp_address * +testsuite_message_get_address(struct mail *mail, const char *header) +{ + struct message_address *addr; + struct smtp_address *smtp_addr; + const char *str; + + if (mail_get_first_header(mail, header, &str) <= 0) + return NULL; + addr = message_address_parse(pool_datastack_create(), + (const unsigned char *)str, + strlen(str), 1, 0); + if (addr == NULL || addr->mailbox == NULL || *addr->mailbox == '\0') + return NULL; + if (smtp_address_create_from_msg_temp(addr, &smtp_addr) < 0) + return NULL; + return smtp_addr; +} + +static void testsuite_message_set_data(struct mail *mail) +{ + const struct smtp_address *recipient = NULL, *sender = NULL; + const char *msg_id; + + static const struct smtp_address default_recipient = { + .localpart = "recipient", + .domain = "example.com", + }; + static const struct smtp_address default_sender = { + .localpart = "sender", + .domain = "example.com", + }; + + i_free(testsuite_env_mail_from); + i_free(testsuite_env_rcpt_to); + i_free(testsuite_env_orig_rcpt_to); + i_free(testsuite_env_auth); + i_free(testsuite_msg_id); + + /* + * Collect necessary message data + */ + + /* Get recipient address */ + recipient = testsuite_message_get_address(mail, "Envelope-To"); + if (recipient == NULL) + recipient = testsuite_message_get_address(mail, "To"); + if (recipient == NULL) + recipient = &default_recipient; + + /* Get sender address */ + sender = testsuite_message_get_address(mail, "Return-path"); + if (sender == NULL) + sender = testsuite_message_get_address(mail, "Sender"); + if (sender == NULL) + sender = testsuite_message_get_address(mail, "From"); + if (sender == NULL) + sender = &default_sender; + + testsuite_env_mail_from = smtp_address_clone(default_pool, sender); + testsuite_env_rcpt_to = smtp_address_clone(default_pool, recipient); + testsuite_env_orig_rcpt_to = smtp_address_clone(default_pool, recipient); + + (void)mail_get_message_id(mail, &msg_id); + testsuite_msg_id = i_strdup(msg_id); + + i_zero(&testsuite_msgdata); + testsuite_msgdata.mail = mail; + testsuite_msgdata.auth_user = sieve_tool_get_username(sieve_tool); + testsuite_msgdata.envelope.mail_from = testsuite_env_mail_from; + testsuite_msgdata.envelope.rcpt_to = testsuite_env_rcpt_to; + testsuite_msgdata.id = testsuite_msg_id; + + i_zero(&testsuite_rcpt_params); + testsuite_rcpt_params.orcpt.addr = testsuite_env_orig_rcpt_to; + + testsuite_msgdata.envelope.rcpt_params = &testsuite_rcpt_params; +} + +static struct testsuite_message *testsuite_message_new(void) +{ + struct testsuite_message *msg; + + msg = i_new(struct testsuite_message, 1); + msg->next = testsuite_msg; + testsuite_msg = msg; + + return msg; +} + +static void testsuite_message_new_string(string_t *mail_str) +{ + struct mail_user *mail_raw_user = + sieve_tool_get_mail_raw_user(sieve_tool); + struct testsuite_message *msg; + + msg = testsuite_message_new(); + msg->mail_raw = mail_raw_open_data(mail_raw_user, mail_str); + + testsuite_message_set_data(msg->mail_raw->mail); +} + +static void testsuite_message_new_file(const char *mail_path) +{ + struct mail_user *mail_raw_user = + sieve_tool_get_mail_raw_user(sieve_tool); + struct testsuite_message *msg; + + msg = testsuite_message_new(); + msg->mail_raw = mail_raw_open_file(mail_raw_user, mail_path); + + testsuite_message_set_data(msg->mail_raw->mail); +} + +static void testsuite_message_free(bool all) +{ + struct testsuite_message *msg; + + if (testsuite_msg == NULL) + return; + + msg = (all ? testsuite_msg : testsuite_msg->next); + while (msg != NULL) { + struct testsuite_message *msg_next = msg->next; + + mail_raw_close(&msg->mail_raw); + i_free(msg); + + msg = msg_next; + } + if (all) + testsuite_msg = NULL; + else + testsuite_msg->next = NULL; +} + +void testsuite_message_flush(void) +{ + testsuite_message_free(FALSE); +} + +void testsuite_message_init(void) +{ + testsuite_msg_pool = pool_alloconly_create("testsuite_message", 6096); + + string_t *default_message = str_new(testsuite_msg_pool, 1024); + str_append(default_message, _default_message_data); + + testsuite_message_new_string(default_message); +} + +void testsuite_message_set_string(const struct sieve_runtime_env *renv, + string_t *message) +{ + sieve_message_context_reset(renv->msgctx); + + testsuite_message_new_string(message); +} + +void testsuite_message_set_file(const struct sieve_runtime_env *renv, + const char *file_path) +{ + sieve_message_context_reset(renv->msgctx); + + testsuite_message_new_file(file_path); +} + +void testsuite_message_set_mail(const struct sieve_runtime_env *renv, + struct mail *mail) +{ + sieve_message_context_reset(renv->msgctx); + + testsuite_message_set_data(mail); +} + +void testsuite_message_deinit(void) +{ + testsuite_message_free(TRUE); + + i_free(testsuite_env_mail_from); + i_free(testsuite_env_rcpt_to); + i_free(testsuite_env_orig_rcpt_to); + i_free(testsuite_env_auth); + pool_unref(&testsuite_msg_pool); + i_free(testsuite_msg_id); +} + +void testsuite_envelope_set_sender_address(const struct sieve_runtime_env *renv, + const struct smtp_address *address) +{ + sieve_message_context_reset(renv->msgctx); + + i_free(testsuite_env_mail_from); + + testsuite_env_mail_from = smtp_address_clone(default_pool, address); + testsuite_msgdata.envelope.mail_from = testsuite_env_mail_from; +} + +void testsuite_envelope_set_sender(const struct sieve_runtime_env *renv, + const char *value) +{ + struct smtp_address *address = NULL; + const char *error; + + if (smtp_address_parse_path(pool_datastack_create(), value, + (SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY | + SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL), + &address, &error) < 0) { + e_error(testsuite_sieve_instance->event, + "testsuite: envelope sender address " + "`%s' is invalid: %s", value, error); + } + testsuite_envelope_set_sender_address(renv, address); +} + +void testsuite_envelope_set_recipient_address( + const struct sieve_runtime_env *renv, + const struct smtp_address *address) +{ + sieve_message_context_reset(renv->msgctx); + + i_free(testsuite_env_rcpt_to); + i_free(testsuite_env_orig_rcpt_to); + + testsuite_env_rcpt_to = smtp_address_clone(default_pool, address); + testsuite_env_orig_rcpt_to = smtp_address_clone(default_pool, address); + testsuite_msgdata.envelope.rcpt_to = testsuite_env_rcpt_to; + testsuite_rcpt_params.orcpt.addr = testsuite_env_orig_rcpt_to; +} + +void testsuite_envelope_set_recipient(const struct sieve_runtime_env *renv, + const char *value) +{ + struct smtp_address *address = NULL; + const char *error; + + if (smtp_address_parse_path(pool_datastack_create(), value, + (SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART | + SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL), + &address, &error) < 0) { + e_error(testsuite_sieve_instance->event, + "testsuite: envelope recipient address " + "`%s' is invalid: %s", value, error); + } + testsuite_envelope_set_recipient_address(renv, address); +} + +void testsuite_envelope_set_orig_recipient_address( + const struct sieve_runtime_env *renv, + const struct smtp_address *address) +{ + sieve_message_context_reset(renv->msgctx); + + i_free(testsuite_env_orig_rcpt_to); + + testsuite_env_orig_rcpt_to = smtp_address_clone(default_pool, address); + testsuite_rcpt_params.orcpt.addr = testsuite_env_orig_rcpt_to; +} + +void testsuite_envelope_set_orig_recipient(const struct sieve_runtime_env *renv, + const char *value) +{ + struct smtp_address *address = NULL; + const char *error; + + if (smtp_address_parse_path(pool_datastack_create(), value, + (SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART | + SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL), + &address, &error) < 0) { + e_error(testsuite_sieve_instance->event, + "testsuite: envelope recipient address " + "`%s' is invalid: %s", value, error); + } + testsuite_envelope_set_orig_recipient_address(renv, address); +} + +void testsuite_envelope_set_auth_user(const struct sieve_runtime_env *renv, + const char *value) +{ + sieve_message_context_reset(renv->msgctx); + + i_free(testsuite_env_auth); + + testsuite_env_auth = i_strdup(value); + testsuite_msgdata.auth_user = testsuite_env_auth; +} diff --git a/pigeonhole/src/testsuite/testsuite-message.h b/pigeonhole/src/testsuite/testsuite-message.h new file mode 100644 index 0000000..2a4d3b4 --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-message.h @@ -0,0 +1,44 @@ +#ifndef TESTSUITE_MESSAGE_H +#define TESTSUITE_MESSAGE_H + +#include "lib.h" +#include "master-service.h" + +#include "sieve-common.h" +#include "sieve-tool.h" + +extern struct sieve_message_data testsuite_msgdata; + +void testsuite_message_init(void); +void testsuite_message_deinit(void); + +void testsuite_message_flush(void); + +void testsuite_message_set_string(const struct sieve_runtime_env *renv, + string_t *message); +void testsuite_message_set_file(const struct sieve_runtime_env *renv, + const char *file_path); +void testsuite_message_set_mail(const struct sieve_runtime_env *renv, + struct mail *mail); + +void testsuite_envelope_set_sender_address(const struct sieve_runtime_env *renv, + const struct smtp_address *address); +void testsuite_envelope_set_sender(const struct sieve_runtime_env *renv, + const char *value); + +void testsuite_envelope_set_recipient_address( + const struct sieve_runtime_env *renv, + const struct smtp_address *address); +void testsuite_envelope_set_recipient(const struct sieve_runtime_env *renv, + const char *value); + +void testsuite_envelope_set_orig_recipient_address( + const struct sieve_runtime_env *renv, + const struct smtp_address *address); +void testsuite_envelope_set_orig_recipient(const struct sieve_runtime_env *renv, + const char *value); + +void testsuite_envelope_set_auth_user(const struct sieve_runtime_env *renv, + const char *value); + +#endif diff --git a/pigeonhole/src/testsuite/testsuite-objects.c b/pigeonhole/src/testsuite/testsuite-objects.c new file mode 100644 index 0000000..4c09c85 --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-objects.c @@ -0,0 +1,369 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "string.h" +#include "ostream.h" +#include "hash.h" +#include "mail-storage.h" + +#include "sieve.h" +#include "sieve-code.h" +#include "sieve-commands.h" +#include "sieve-extensions.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-binary.h" +#include "sieve-dump.h" + +#include "testsuite-common.h" +#include "testsuite-objects.h" +#include "testsuite-message.h" + +/* + * Testsuite core objects + */ + +enum testsuite_object_code { + TESTSUITE_OBJECT_MESSAGE, + TESTSUITE_OBJECT_ENVELOPE +}; + +const struct testsuite_object_def *testsuite_core_objects[] = { + &message_testsuite_object, &envelope_testsuite_object +}; + +const unsigned int testsuite_core_objects_count = + N_ELEMENTS(testsuite_core_objects); + +/* + * Testsuite object registry + */ + +static inline struct sieve_validator_object_registry *_get_object_registry +(struct sieve_validator *valdtr) +{ + struct testsuite_validator_context *ctx = + testsuite_validator_context_get(valdtr); + + return ctx->object_registrations; +} + +void testsuite_object_register +(struct sieve_validator *valdtr, const struct sieve_extension *ext, + const struct testsuite_object_def *tobj_def) +{ + struct sieve_validator_object_registry *regs = _get_object_registry(valdtr); + + sieve_validator_object_registry_add(regs, ext, &tobj_def->obj_def); +} + +static const struct testsuite_object *testsuite_object_create +(struct sieve_validator *valdtr, struct sieve_command *cmd, + const char *identifier) +{ + struct sieve_validator_object_registry *regs = _get_object_registry(valdtr); + struct sieve_object object; + struct testsuite_object *tobj; + + if ( !sieve_validator_object_registry_find(regs, identifier, &object) ) + return NULL; + + tobj = p_new(sieve_command_pool(cmd), struct testsuite_object, 1); + tobj->object = object; + tobj->def = (const struct testsuite_object_def *) object.def; + + return tobj; +} + +void testsuite_register_core_objects +(struct testsuite_validator_context *ctx) +{ + struct sieve_validator_object_registry *regs = ctx->object_registrations; + unsigned int i; + + /* Register core testsuite objects */ + for ( i = 0; i < testsuite_core_objects_count; i++ ) { + const struct testsuite_object_def *tobj_def = testsuite_core_objects[i]; + + sieve_validator_object_registry_add + (regs, testsuite_ext, &tobj_def->obj_def); + } +} + +/* + * Testsuite object code + */ + +const struct sieve_operand_class sieve_testsuite_object_operand_class = + { "testsuite object" }; + +static const struct sieve_extension_objects core_testsuite_objects = + SIEVE_EXT_DEFINE_OBJECTS(testsuite_core_objects); + +const struct sieve_operand_def testsuite_object_operand = { + .name = "testsuite-object", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERAND_OBJECT, + .class = &sieve_testsuite_object_operand_class, + .interface = &core_testsuite_objects +}; + +static void testsuite_object_emit +(struct sieve_binary_block *sblock, const struct testsuite_object *tobj, + int member_id) +{ + sieve_opr_object_emit(sblock, tobj->object.ext, tobj->object.def); + + if ( tobj->def != NULL && tobj->def->get_member_id != NULL ) { + (void) sieve_binary_emit_byte(sblock, (unsigned char) member_id); + } +} + +bool testsuite_object_read +(struct sieve_binary_block *sblock, sieve_size_t *address, + struct testsuite_object *tobj) +{ + struct sieve_operand oprnd; + + if ( !sieve_operand_read(sblock, address, NULL, &oprnd) ) + return FALSE; + + if ( !sieve_opr_object_read_data + (sblock, &oprnd, &sieve_testsuite_object_operand_class, address, + &tobj->object) ) + return FALSE; + + tobj->def = (const struct testsuite_object_def *) tobj->object.def; + i_assert(tobj->def != NULL); + return TRUE; +} + +bool testsuite_object_read_member +(struct sieve_binary_block *sblock, sieve_size_t *address, + struct testsuite_object *tobj, int *member_id_r) +{ + if ( !testsuite_object_read(sblock, address, tobj) ) + return FALSE; + + *member_id_r = -1; + if ( tobj->def->get_member_id != NULL ) { + if ( !sieve_binary_read_code(sblock, address, member_id_r) ) + return FALSE; + } + + return TRUE; +} + +const char *testsuite_object_member_name +(const struct testsuite_object *object, int member_id) +{ + const struct testsuite_object_def *obj_def = object->def; + const char *member = NULL; + + if ( obj_def->get_member_id != NULL ) { + if ( obj_def->get_member_name != NULL ) + member = obj_def->get_member_name(member_id); + } else + return obj_def->obj_def.identifier; + + if ( member == NULL ) + return t_strdup_printf("%s.%d", obj_def->obj_def.identifier, member_id); + + return t_strdup_printf("%s.%s", obj_def->obj_def.identifier, member); +} + +bool testsuite_object_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + struct testsuite_object object; + int member_id; + + sieve_code_mark(denv); + + if ( !testsuite_object_read_member + (denv->sblock, address, &object, &member_id) ) + return FALSE; + + sieve_code_dumpf(denv, "%s: %s", + sieve_testsuite_object_operand_class.name, + testsuite_object_member_name(&object, member_id)); + + return TRUE; +} + +/* + * Testsuite object argument + */ + +static bool arg_testsuite_object_generate + (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *cmd); + +const struct sieve_argument_def testsuite_object_argument = { + .identifier = "testsuite-object", + .generate = arg_testsuite_object_generate +}; + +struct testsuite_object_argctx { + const struct testsuite_object *object; + int member; +}; + +bool testsuite_object_argument_activate +(struct sieve_validator *valdtr, struct sieve_ast_argument *arg, + struct sieve_command *cmd) +{ + const char *objname = sieve_ast_argument_strc(arg); + const struct testsuite_object *tobj; + int member_id; + const char *member; + struct testsuite_object_argctx *ctx; + + /* Parse the object specifier */ + + member = strchr(objname, '.'); + if ( member != NULL ) { + objname = t_strdup_until(objname, member); + member++; + } + + /* Find the object */ + + tobj = testsuite_object_create(valdtr, cmd, objname); + if ( tobj == NULL ) { + sieve_argument_validate_error(valdtr, arg, + "unknown testsuite object '%s'", objname); + return FALSE; + } + + /* Find the object member */ + + member_id = -1; + if ( member != NULL ) { + if ( tobj->def == NULL || tobj->def->get_member_id == NULL || + (member_id=tobj->def->get_member_id(member)) == -1 ) { + sieve_argument_validate_error(valdtr, arg, + "member '%s' does not exist for testsuite object '%s'", member, objname); + return FALSE; + } + } + + /* Assign argument context */ + + ctx = p_new(sieve_command_pool(cmd), struct testsuite_object_argctx, 1); + ctx->object = tobj; + ctx->member = member_id; + + arg->argument = sieve_argument_create + (arg->ast, &testsuite_object_argument, testsuite_ext, 0); + arg->argument->data = (void *) ctx; + + return TRUE; +} + +static bool arg_testsuite_object_generate + (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *cmd ATTR_UNUSED) +{ + struct testsuite_object_argctx *ctx = + (struct testsuite_object_argctx *) arg->argument->data; + + testsuite_object_emit(cgenv->sblock, ctx->object, ctx->member); + + return TRUE; +} + +/* + * Testsuite core object implementation + */ + +static bool tsto_message_set_member + (const struct sieve_runtime_env *renv, int id, string_t *value); + +static int tsto_envelope_get_member_id(const char *identifier); +static const char *tsto_envelope_get_member_name(int id); +static bool tsto_envelope_set_member + (const struct sieve_runtime_env *renv, int id, string_t *value); + +const struct testsuite_object_def message_testsuite_object = { + SIEVE_OBJECT("message", + &testsuite_object_operand, TESTSUITE_OBJECT_MESSAGE), + .set_member = tsto_message_set_member +}; + +const struct testsuite_object_def envelope_testsuite_object = { + SIEVE_OBJECT("envelope", + &testsuite_object_operand, TESTSUITE_OBJECT_ENVELOPE), + .get_member_id = tsto_envelope_get_member_id, + .get_member_name = tsto_envelope_get_member_name, + .set_member = tsto_envelope_set_member +}; + +enum testsuite_object_envelope_field { + TESTSUITE_OBJECT_ENVELOPE_FROM, + TESTSUITE_OBJECT_ENVELOPE_TO, + TESTSUITE_OBJECT_ENVELOPE_ORIG_TO, + TESTSUITE_OBJECT_ENVELOPE_AUTH_USER +}; + +static bool tsto_message_set_member +(const struct sieve_runtime_env *renv, int id, string_t *value) +{ + if ( id != -1 ) return FALSE; + + testsuite_message_set_string(renv, value); + + return TRUE; +} + +static int tsto_envelope_get_member_id(const char *identifier) +{ + if ( strcasecmp(identifier, "from") == 0 ) + return TESTSUITE_OBJECT_ENVELOPE_FROM; + if ( strcasecmp(identifier, "to") == 0 ) + return TESTSUITE_OBJECT_ENVELOPE_TO; + if ( strcasecmp(identifier, "orig_to") == 0 ) + return TESTSUITE_OBJECT_ENVELOPE_ORIG_TO; + if ( strcasecmp(identifier, "auth") == 0 ) + return TESTSUITE_OBJECT_ENVELOPE_AUTH_USER; + + return -1; +} + +static const char *tsto_envelope_get_member_name(int id) +{ + switch ( id ) { + case TESTSUITE_OBJECT_ENVELOPE_FROM: + return "from"; + case TESTSUITE_OBJECT_ENVELOPE_TO: + return "to"; + case TESTSUITE_OBJECT_ENVELOPE_ORIG_TO: + return "orig_to"; + case TESTSUITE_OBJECT_ENVELOPE_AUTH_USER: + return "auth"; + } + + return NULL; +} + +static bool tsto_envelope_set_member +(const struct sieve_runtime_env *renv, int id, string_t *value) +{ + switch ( id ) { + case TESTSUITE_OBJECT_ENVELOPE_FROM: + testsuite_envelope_set_sender(renv, str_c(value)); + return TRUE; + case TESTSUITE_OBJECT_ENVELOPE_TO: + testsuite_envelope_set_recipient(renv, str_c(value)); + return TRUE; + case TESTSUITE_OBJECT_ENVELOPE_ORIG_TO: + testsuite_envelope_set_orig_recipient(renv, str_c(value)); + return TRUE; + case TESTSUITE_OBJECT_ENVELOPE_AUTH_USER: + testsuite_envelope_set_auth_user(renv, str_c(value)); + return TRUE; + } + + return FALSE; +} diff --git a/pigeonhole/src/testsuite/testsuite-objects.h b/pigeonhole/src/testsuite/testsuite-objects.h new file mode 100644 index 0000000..0aa6f4f --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-objects.h @@ -0,0 +1,83 @@ +#ifndef TESTSUITE_OBJECTS_H +#define TESTSUITE_OBJECTS_H + +#include "sieve-common.h" +#include "sieve-objects.h" + +#include "testsuite-common.h" + +/* + * Testsuite object operand + */ + +struct testsuite_object_operand_interface { + struct sieve_extension_objects testsuite_objects; +}; + +extern const struct sieve_operand_class testsuite_object_oprclass; + +/* + * Testsuite object access + */ + +struct testsuite_object_def { + struct sieve_object_def obj_def; + + int (*get_member_id)(const char *identifier); + const char *(*get_member_name)(int id); + + bool (*set_member) + (const struct sieve_runtime_env *renv, int id, string_t *value); + string_t *(*get_member) + (const struct sieve_runtime_env *renv, int id); +}; + +struct testsuite_object { + struct sieve_object object; + + const struct testsuite_object_def *def; +}; + +/* + * Testsuite object registration + */ + +void testsuite_register_core_objects + (struct testsuite_validator_context *ctx); +void testsuite_object_register + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + const struct testsuite_object_def *tobj_def); + +/* + * Testsuite object argument + */ + +bool testsuite_object_argument_activate + (struct sieve_validator *valdtr, struct sieve_ast_argument *arg, + struct sieve_command *cmd); + +/* + * Testsuite object code + */ + +bool testsuite_object_read + (struct sieve_binary_block *sblock, sieve_size_t *address, + struct testsuite_object *tobj); +bool testsuite_object_read_member + (struct sieve_binary_block *sblock, sieve_size_t *address, + struct testsuite_object *tobj, int *member_id_r); + +bool testsuite_object_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); + +const char *testsuite_object_member_name + (const struct testsuite_object *object, int member_id); + +/* + * Testsuite core objects + */ + +extern const struct testsuite_object_def message_testsuite_object; +extern const struct testsuite_object_def envelope_testsuite_object; + +#endif diff --git a/pigeonhole/src/testsuite/testsuite-result.c b/pigeonhole/src/testsuite/testsuite-result.c new file mode 100644 index 0000000..51be3ff --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-result.c @@ -0,0 +1,206 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "ostream.h" +#include "str.h" + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-stringlist.h" +#include "sieve-actions.h" +#include "sieve-interpreter.h" +#include "sieve-result.h" + +#include "testsuite-common.h" +#include "testsuite-log.h" +#include "testsuite-message.h" +#include "testsuite-mailstore.h" + +#include "testsuite-result.h" + +struct sieve_execute_env testsuite_execute_env; + +static pool_t testsuite_execute_pool = NULL; +static struct sieve_result *_testsuite_result = NULL; +static struct sieve_result_execution *_testsuite_rexec = NULL; + +void testsuite_result_init(void) +{ + struct sieve_instance *svinst = testsuite_sieve_instance; + + testsuite_execute_pool = pool_alloconly_create("sieve execution", 4096); + + sieve_execute_init(&testsuite_execute_env, testsuite_sieve_instance, + testsuite_execute_pool, &testsuite_msgdata, + testsuite_scriptenv, 0); + + _testsuite_result = sieve_result_create(svinst, testsuite_execute_pool, + &testsuite_execute_env); +} + +void testsuite_result_deinit(void) +{ + sieve_result_execution_destroy(&_testsuite_rexec); + if (_testsuite_result != NULL) + sieve_result_unref(&_testsuite_result); + sieve_execute_deinit(&testsuite_execute_env); + pool_unref(&testsuite_execute_pool); +} + +void testsuite_result_reset(const struct sieve_runtime_env *renv) +{ + struct sieve_instance *svinst = testsuite_sieve_instance; + + if (_testsuite_result != NULL) { + sieve_result_execution_destroy(&_testsuite_rexec); + sieve_result_unref(&_testsuite_result); + pool_unref(&testsuite_execute_pool); + } + + testsuite_message_flush(); + testsuite_mailstore_flush(); + i_zero(testsuite_execute_env.exec_status); + + testsuite_execute_pool = pool_alloconly_create("sieve execution", 4096); + _testsuite_result = sieve_result_create(svinst, testsuite_execute_pool, + &testsuite_execute_env); + sieve_interpreter_set_result(renv->interp, _testsuite_result); +} + +struct sieve_result *testsuite_result_get(void) +{ + return _testsuite_result; +} + +struct sieve_result_iterate_context *testsuite_result_iterate_init(void) +{ + if (_testsuite_result == NULL) + return NULL; + + return sieve_result_iterate_init(_testsuite_result); +} + +bool testsuite_result_execute(const struct sieve_runtime_env *renv) +{ + int ret; + + if (_testsuite_result == NULL) { + sieve_runtime_error(renv, NULL, "testsuite: " + "trying to execute result, " + "but no result evaluated yet"); + return FALSE; + } + + testsuite_log_clear_messages(); + + if (_testsuite_rexec == NULL) { + _testsuite_rexec = sieve_result_execution_create( + _testsuite_result, testsuite_execute_pool); + } + + /* Execute the result */ + ret = sieve_result_execute(_testsuite_rexec, SIEVE_EXEC_OK, TRUE, + testsuite_log_ehandler, NULL); + + return (ret > 0); +} + +void testsuite_result_print(const struct sieve_runtime_env *renv) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + struct ostream *out; + + out = o_stream_create_fd(1, 0); + o_stream_set_no_error_handling(out, TRUE); + + o_stream_nsend_str(out, "\n--"); + sieve_result_print(_testsuite_result, eenv->scriptenv, out, NULL); + o_stream_nsend_str(out, "--\n\n"); + + o_stream_destroy(&out); +} + +/* + * Result stringlist + */ + +/* Forward declarations */ + +static int +testsuite_result_stringlist_next_item(struct sieve_stringlist *_strlist, + string_t **str_r); +static void +testsuite_result_stringlist_reset(struct sieve_stringlist *_strlist); + +/* Stringlist object */ + +struct testsuite_result_stringlist { + struct sieve_stringlist strlist; + + struct sieve_result_iterate_context *result_iter; + int pos, index; +}; + +struct sieve_stringlist * +testsuite_result_stringlist_create(const struct sieve_runtime_env *renv, + int index) +{ + struct testsuite_result_stringlist *strlist; + + strlist = t_new(struct testsuite_result_stringlist, 1); + strlist->strlist.runenv = renv; + strlist->strlist.exec_status = SIEVE_EXEC_OK; + strlist->strlist.next_item = testsuite_result_stringlist_next_item; + strlist->strlist.reset = testsuite_result_stringlist_reset; + + strlist->result_iter = testsuite_result_iterate_init(); + strlist->index = index; + strlist->pos = 0; + + return &strlist->strlist; +} + +static int +testsuite_result_stringlist_next_item(struct sieve_stringlist *_strlist, + string_t **str_r) +{ + struct testsuite_result_stringlist *strlist = + (struct testsuite_result_stringlist *)_strlist; + const struct sieve_action *action; + const char *act_name; + bool keep; + + *str_r = NULL; + + if (strlist->index > 0 && strlist->pos > 0) + return 0; + + do { + if ((action = sieve_result_iterate_next(strlist->result_iter, + &keep)) == NULL) + return 0; + + strlist->pos++; + } while (strlist->pos < strlist->index); + + if (keep) + act_name = "keep"; + else { + act_name = ((action == NULL || action->def == NULL || + action->def->name == NULL) ? + "" : action->def->name); + } + + *str_r = t_str_new_const(act_name, strlen(act_name)); + return 1; +} + +static void testsuite_result_stringlist_reset(struct sieve_stringlist *_strlist) +{ + struct testsuite_result_stringlist *strlist = + (struct testsuite_result_stringlist *)_strlist; + + strlist->result_iter = testsuite_result_iterate_init(); + strlist->pos = 0; +} diff --git a/pigeonhole/src/testsuite/testsuite-result.h b/pigeonhole/src/testsuite/testsuite-result.h new file mode 100644 index 0000000..3e232d2 --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-result.h @@ -0,0 +1,25 @@ +#ifndef TESTSUITE_RESULT_H +#define TESTSUITE_RESULT_H + +#include "sieve-execute.h" + +extern struct sieve_execute_env testsuite_execute_env; + +void testsuite_result_init(void); +void testsuite_result_deinit(void); + +void testsuite_result_reset(const struct sieve_runtime_env *renv); + +struct sieve_result *testsuite_result_get(void); + +struct sieve_result_iterate_context *testsuite_result_iterate_init(void); + +bool testsuite_result_execute(const struct sieve_runtime_env *renv); + +void testsuite_result_print(const struct sieve_runtime_env *renv ATTR_UNUSED); + +struct sieve_stringlist * +testsuite_result_stringlist_create(const struct sieve_runtime_env *renv, + int index); + +#endif diff --git a/pigeonhole/src/testsuite/testsuite-script.c b/pigeonhole/src/testsuite/testsuite-script.c new file mode 100644 index 0000000..b6edd9b --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-script.c @@ -0,0 +1,256 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" + +#include "sieve.h" +#include "sieve-common.h" +#include "sieve-script.h" +#include "sieve-binary.h" +#include "sieve-interpreter.h" +#include "sieve-runtime-trace.h" +#include "sieve-result.h" + +#include "testsuite-common.h" +#include "testsuite-settings.h" +#include "testsuite-log.h" +#include "testsuite-smtp.h" +#include "testsuite-result.h" + +#include "testsuite-script.h" + +/* + * Tested script environment + */ + +void testsuite_script_init(void) +{ +} + +void testsuite_script_deinit(void) +{ +} + +static struct sieve_binary * +_testsuite_script_compile(const struct sieve_runtime_env *renv, + const char *script) +{ + struct sieve_instance *svinst = testsuite_sieve_instance; + struct sieve_binary *sbin; + const char *script_path; + + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "compile script `%s'", script); + + script_path = sieve_file_script_get_dirpath(renv->script); + if (script_path == NULL) + return NULL; + + script_path = t_strconcat(script_path, "/", script, NULL); + if ((sbin = sieve_compile(svinst, script_path, NULL, + testsuite_log_ehandler, 0, NULL)) == NULL) + return NULL; + + return sbin; +} + +bool testsuite_script_compile(const struct sieve_runtime_env *renv, + const char *script) +{ + struct testsuite_interpreter_context *ictx = + testsuite_interpreter_context_get(renv->interp, testsuite_ext); + struct sieve_binary *sbin; + + i_assert(ictx != NULL); + testsuite_log_clear_messages(); + + if ((sbin = _testsuite_script_compile(renv, script)) == NULL) + return FALSE; + + sieve_binary_unref(&ictx->compiled_script); + + ictx->compiled_script = sbin; + return TRUE; +} + +bool testsuite_script_is_subtest(const struct sieve_runtime_env *renv) +{ + struct testsuite_interpreter_context *ictx = + testsuite_interpreter_context_get(renv->interp, testsuite_ext); + + i_assert(ictx != NULL); + if (ictx->compiled_script == NULL) + return FALSE; + + return (sieve_binary_extension_get_index(ictx->compiled_script, + testsuite_ext) >= 0); +} + +bool testsuite_script_run(const struct sieve_runtime_env *renv) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + const struct sieve_script_env *senv = eenv->scriptenv; + struct testsuite_interpreter_context *ictx = + testsuite_interpreter_context_get(renv->interp, testsuite_ext); + struct sieve_script_env scriptenv; + struct sieve_exec_status exec_status; + struct sieve_result *result; + struct sieve_interpreter *interp; + pool_t pool; + struct sieve_execute_env exec_env; + const char *error; + int ret; + + i_assert(ictx != NULL); + + if (ictx->compiled_script == NULL) { + sieve_runtime_error(renv, NULL, "testsuite: " + "trying to run script, but no script compiled yet"); + return FALSE; + } + + testsuite_log_clear_messages(); + + i_zero(&exec_status); + + /* Compose script execution environment */ + if (sieve_script_env_init(&scriptenv, senv->user, &error) < 0) { + sieve_runtime_error(renv, NULL, "testsuite: " + "failed to initialize script execution: %s", error); + return FALSE; + } + scriptenv.default_mailbox = "INBOX"; + scriptenv.smtp_start = testsuite_smtp_start; + scriptenv.smtp_add_rcpt = testsuite_smtp_add_rcpt; + scriptenv.smtp_send = testsuite_smtp_send; + scriptenv.smtp_abort = testsuite_smtp_abort; + scriptenv.smtp_finish = testsuite_smtp_finish; + scriptenv.duplicate_mark = NULL; + scriptenv.duplicate_check = NULL; + scriptenv.trace_log = eenv->scriptenv->trace_log; + scriptenv.trace_config = eenv->scriptenv->trace_config; + + result = testsuite_result_get(); + + pool = pool_alloconly_create("sieve execution", 4096); + sieve_execute_init(&exec_env, eenv->svinst, pool, eenv->msgdata, + &scriptenv, eenv->flags); + pool_unref(&pool); + + /* Execute the script */ + interp = sieve_interpreter_create(ictx->compiled_script, NULL, + &exec_env, testsuite_log_ehandler); + + if (interp == NULL) { + sieve_execute_deinit(&exec_env); + return FALSE; + } + + ret = sieve_interpreter_run(interp, result); + sieve_interpreter_free(&interp); + + sieve_execute_finish(&exec_env, ret); + sieve_execute_deinit(&exec_env); + + return (ret > 0 || + sieve_binary_extension_get_index(ictx->compiled_script, + testsuite_ext) >= 0); +} + +struct sieve_binary * +testsuite_script_get_binary(const struct sieve_runtime_env *renv) +{ + struct testsuite_interpreter_context *ictx = + testsuite_interpreter_context_get(renv->interp, testsuite_ext); + + i_assert(ictx != NULL); + return ictx->compiled_script; +} + +void testsuite_script_set_binary(const struct sieve_runtime_env *renv, + struct sieve_binary *sbin) +{ + struct testsuite_interpreter_context *ictx = + testsuite_interpreter_context_get(renv->interp, testsuite_ext); + + i_assert(ictx != NULL); + + sieve_binary_unref(&ictx->compiled_script); + + ictx->compiled_script = sbin; + sieve_binary_ref(sbin); +} + +/* + * Multiscript + */ + +bool testsuite_script_multiscript(const struct sieve_runtime_env *renv, + ARRAY_TYPE (const_string) *scriptfiles) +{ + struct sieve_instance *svinst = testsuite_sieve_instance; + const struct sieve_execute_env *eenv = renv->exec_env; + const struct sieve_script_env *senv = eenv->scriptenv; + struct sieve_script_env scriptenv; + struct sieve_exec_status exec_status; + struct sieve_multiscript *mscript; + const char *const *scripts; + const char *error; + unsigned int count, i; + bool more = TRUE; + bool result = TRUE; + + testsuite_log_clear_messages(); + + /* Compose script execution environment */ + if (sieve_script_env_init(&scriptenv, senv->user, &error) < 0) { + sieve_runtime_error(renv, NULL, + "testsuite: failed to initialize script execution: %s", + error); + return FALSE; + } + scriptenv.default_mailbox = "INBOX"; + scriptenv.smtp_start = testsuite_smtp_start; + scriptenv.smtp_add_rcpt = testsuite_smtp_add_rcpt; + scriptenv.smtp_send = testsuite_smtp_send; + scriptenv.smtp_abort = testsuite_smtp_abort; + scriptenv.smtp_finish = testsuite_smtp_finish; + scriptenv.duplicate_mark = NULL; + scriptenv.duplicate_check = NULL; + scriptenv.trace_log = eenv->scriptenv->trace_log; + scriptenv.trace_config = eenv->scriptenv->trace_config; + scriptenv.exec_status = &exec_status; + + /* Start execution */ + + mscript = sieve_multiscript_start_execute(svinst, eenv->msgdata, + &scriptenv); + + /* Execute scripts before main script */ + + scripts = array_get(scriptfiles, &count); + for (i = 0; i < count && more; i++) { + struct sieve_binary *sbin = NULL; + const char *script = scripts[i]; + + /* Open */ + if ((sbin = _testsuite_script_compile(renv, script)) == NULL) { + result = FALSE; + break; + } + + /* Execute */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "run script `%s'", script); + + more = sieve_multiscript_run(mscript, sbin, + testsuite_log_ehandler, + testsuite_log_ehandler, 0); + + sieve_close(&sbin); + } + + return (sieve_multiscript_finish(&mscript, testsuite_log_ehandler, + 0, SIEVE_EXEC_OK) > 0 && result); +} diff --git a/pigeonhole/src/testsuite/testsuite-script.h b/pigeonhole/src/testsuite/testsuite-script.h new file mode 100644 index 0000000..547fdfc --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-script.h @@ -0,0 +1,22 @@ +#ifndef TESTSUITE_SCRIPT_H +#define TESTSUITE_SCRIPT_H + +#include "sieve-common.h" + +void testsuite_script_init(void); +void testsuite_script_deinit(void); + +bool testsuite_script_is_subtest(const struct sieve_runtime_env *renv); + +bool testsuite_script_compile(const struct sieve_runtime_env *renv, + const char *script); +bool testsuite_script_run(const struct sieve_runtime_env *renv); +bool testsuite_script_multiscript(const struct sieve_runtime_env *renv, + ARRAY_TYPE (const_string) *scriptfiles); + +struct sieve_binary * +testsuite_script_get_binary(const struct sieve_runtime_env *renv); +void testsuite_script_set_binary(const struct sieve_runtime_env *renv, + struct sieve_binary *sbin); + +#endif diff --git a/pigeonhole/src/testsuite/testsuite-settings.c b/pigeonhole/src/testsuite/testsuite-settings.c new file mode 100644 index 0000000..9d3c872 --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-settings.c @@ -0,0 +1,96 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "hash.h" +#include "imem.h" +#include "strfuncs.h" +#include "mail-user.h" + +#include "sieve-common.h" + +#include "testsuite-common.h" +#include "testsuite-mailstore.h" +#include "testsuite-settings.h" + +struct testsuite_setting { + char *identifier; + char *value; +}; + +static HASH_TABLE(const char *, struct testsuite_setting *) settings; + +static const char *testsuite_setting_get + (void *context, const char *identifier); + +void testsuite_settings_init(void) +{ + hash_table_create(&settings, default_pool, 0, str_hash, strcmp); + + sieve_tool_set_setting_callback(sieve_tool, testsuite_setting_get, NULL); +} + +void testsuite_settings_deinit(void) +{ + struct hash_iterate_context *itx = + hash_table_iterate_init(settings); + const char *key; + struct testsuite_setting *setting; + + while ( hash_table_iterate(itx, settings, &key, &setting) ) { + i_free(setting->identifier); + i_free(setting->value); + i_free(setting); + } + + hash_table_iterate_deinit(&itx); + + hash_table_destroy(&settings); +} + +static const char *testsuite_setting_get +(void *context ATTR_UNUSED, const char *identifier) +{ + struct testsuite_setting *setting; + struct mail_user *user; + + setting = hash_table_lookup(settings, identifier); + if ( setting != NULL ) + return setting->value; + + user = testsuite_mailstore_get_user(); + if ( user == NULL ) + return NULL; + return mail_user_plugin_getenv(user, identifier); +} + +void testsuite_setting_set(const char *identifier, const char *value) +{ + struct testsuite_setting *setting = + hash_table_lookup(settings, identifier); + + if ( setting != NULL ) { + i_free(setting->value); + setting->value = i_strdup(value); + } else { + setting = i_new(struct testsuite_setting, 1); + setting->identifier = i_strdup(identifier); + setting->value = i_strdup(value); + + hash_table_insert(settings, identifier, setting); + } +} + +void testsuite_setting_unset(const char *identifier) +{ + struct testsuite_setting *setting = + hash_table_lookup(settings, identifier); + + if ( setting != NULL ) { + i_free(setting->identifier); + i_free(setting->value); + i_free(setting); + + hash_table_remove(settings, identifier); + } +} diff --git a/pigeonhole/src/testsuite/testsuite-settings.h b/pigeonhole/src/testsuite/testsuite-settings.h new file mode 100644 index 0000000..b84e620 --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-settings.h @@ -0,0 +1,12 @@ +#ifndef TESTSUITE_SETTINGS_H +#define TESTSUITE_SETTINGS_H + +#include "sieve-common.h" + +void testsuite_settings_init(void); +void testsuite_settings_deinit(void); + +void testsuite_setting_set(const char *identifier, const char *value); +void testsuite_setting_unset(const char *identifier); + +#endif diff --git a/pigeonhole/src/testsuite/testsuite-smtp.c b/pigeonhole/src/testsuite/testsuite-smtp.c new file mode 100644 index 0000000..0eb40ac --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-smtp.c @@ -0,0 +1,176 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" +#include "ostream.h" +#include "unlink-directory.h" + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-interpreter.h" + +#include "testsuite-message.h" +#include "testsuite-common.h" +#include "testsuite-smtp.h" + +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> + +struct testsuite_smtp_message { + const struct smtp_address *envelope_from, *envelope_to; + const char *file; +}; + +static pool_t testsuite_smtp_pool; +static const char *testsuite_smtp_tmp; +static ARRAY(struct testsuite_smtp_message) testsuite_smtp_messages; + +/* + * Initialize + */ + +void testsuite_smtp_init(void) +{ + pool_t pool; + + testsuite_smtp_pool = pool = pool_alloconly_create("testsuite_smtp", 8192); + + testsuite_smtp_tmp = p_strconcat + (pool, testsuite_tmp_dir_get(), "/smtp", NULL); + + if ( mkdir(testsuite_smtp_tmp, 0700) < 0 ) { + i_fatal("failed to create temporary directory '%s': %m.", + testsuite_smtp_tmp); + } + + p_array_init(&testsuite_smtp_messages, pool, 16); +} + +void testsuite_smtp_deinit(void) +{ + const char *error; + + if ( unlink_directory(testsuite_smtp_tmp, UNLINK_DIRECTORY_FLAG_RMDIR, &error) < 0 ) + i_warning("failed to remove temporary directory '%s': %s.", + testsuite_smtp_tmp, error); + + pool_unref(&testsuite_smtp_pool); +} + +void testsuite_smtp_reset(void) +{ + testsuite_smtp_deinit(); + testsuite_smtp_init(); +} + +/* + * Simulated SMTP out + */ + +struct testsuite_smtp { + char *msg_file; + struct smtp_address *mail_from; + struct ostream *output; +}; + +void *testsuite_smtp_start +(const struct sieve_script_env *senv ATTR_UNUSED, + const struct smtp_address *mail_from) +{ + struct testsuite_smtp *smtp; + unsigned int smtp_count = array_count(&testsuite_smtp_messages); + int fd; + + smtp = i_new(struct testsuite_smtp, 1); + + smtp->msg_file = i_strdup_printf("%s/%d.eml", testsuite_smtp_tmp, smtp_count); + smtp->mail_from = smtp_address_clone(default_pool, mail_from); + + if ( (fd=open(smtp->msg_file, O_WRONLY | O_CREAT, 0600)) < 0 ) { + i_fatal("failed create tmp file for SMTP simulation: open(%s) failed: %m", + smtp->msg_file); + } + + smtp->output = o_stream_create_fd_autoclose(&fd, (size_t)-1); + + return (void *) smtp; +} + +void testsuite_smtp_add_rcpt +(const struct sieve_script_env *senv ATTR_UNUSED, + void *handle, const struct smtp_address *rcpt_to) +{ + struct testsuite_smtp *smtp = (struct testsuite_smtp *) handle; + struct testsuite_smtp_message *msg; + + msg = array_append_space(&testsuite_smtp_messages); + + msg->file = p_strdup(testsuite_smtp_pool, smtp->msg_file); + msg->envelope_from = smtp_address_clone(testsuite_smtp_pool, smtp->mail_from); + msg->envelope_to = smtp_address_clone(testsuite_smtp_pool, rcpt_to); +} + +struct ostream *testsuite_smtp_send +(const struct sieve_script_env *senv ATTR_UNUSED, void *handle) +{ + struct testsuite_smtp *smtp = (struct testsuite_smtp *) handle; + + return smtp->output; +} + +void testsuite_smtp_abort +(const struct sieve_script_env *senv ATTR_UNUSED, + void *handle) +{ + struct testsuite_smtp *smtp = (struct testsuite_smtp *) handle; + + o_stream_ignore_last_errors(smtp->output); + o_stream_unref(&smtp->output); + i_unlink(smtp->msg_file); + i_free(smtp->msg_file); + i_free(smtp->mail_from); + i_free(smtp); +} + +int testsuite_smtp_finish +(const struct sieve_script_env *senv ATTR_UNUSED, + void *handle, const char **error_r ATTR_UNUSED) +{ + struct testsuite_smtp *smtp = (struct testsuite_smtp *) handle; + int ret = 1; + + if (o_stream_finish(smtp->output) < 0) { + i_error("write(%s) failed: %s", smtp->msg_file, + o_stream_get_error(smtp->output)); + ret = -1; + } + o_stream_unref(&smtp->output); + i_free(smtp->msg_file); + i_free(smtp->mail_from); + i_free(smtp); + return ret; +} + +/* + * Access + */ + +bool testsuite_smtp_get +(const struct sieve_runtime_env *renv, unsigned int index) +{ + const struct testsuite_smtp_message *smtp_msg; + + if ( index >= array_count(&testsuite_smtp_messages) ) + return FALSE; + + smtp_msg = array_idx(&testsuite_smtp_messages, index); + + testsuite_message_set_file(renv, smtp_msg->file); + testsuite_envelope_set_sender_address(renv, smtp_msg->envelope_from); + testsuite_envelope_set_recipient_address(renv, smtp_msg->envelope_to); + + return TRUE; +} diff --git a/pigeonhole/src/testsuite/testsuite-smtp.h b/pigeonhole/src/testsuite/testsuite-smtp.h new file mode 100644 index 0000000..0b120b2 --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-smtp.h @@ -0,0 +1,35 @@ +#ifndef TESTSUITE_SMTP_H +#define TESTSUITE_SMTP_H + +void testsuite_smtp_init(void); +void testsuite_smtp_deinit(void); +void testsuite_smtp_reset(void); + +/* + * Simulated SMTP out + */ + +void *testsuite_smtp_start + (const struct sieve_script_env *senv ATTR_UNUSED, + const struct smtp_address *mail_from); +void testsuite_smtp_add_rcpt + (const struct sieve_script_env *senv ATTR_UNUSED, + void *handle, const struct smtp_address *rcpt_to); +struct ostream *testsuite_smtp_send + (const struct sieve_script_env *senv ATTR_UNUSED, + void *handle); +void testsuite_smtp_abort + (const struct sieve_script_env *senv ATTR_UNUSED, + void *handle); +int testsuite_smtp_finish + (const struct sieve_script_env *senv ATTR_UNUSED, + void *handle, const char **error_r); + +/* + * Access + */ + +bool testsuite_smtp_get + (const struct sieve_runtime_env *renv, unsigned int index); + +#endif diff --git a/pigeonhole/src/testsuite/testsuite-substitutions.c b/pigeonhole/src/testsuite/testsuite-substitutions.c new file mode 100644 index 0000000..b165587 --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-substitutions.c @@ -0,0 +1,253 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" + +#include "sieve.h" +#include "sieve-code.h" +#include "sieve-commands.h" +#include "sieve-binary.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "testsuite-common.h" +#include "testsuite-substitutions.h" + +/* + * Forward declarations + */ + +void testsuite_opr_substitution_emit + (struct sieve_binary_block *sblock, const struct testsuite_substitution *tsub, + const char *param); + +/* + * Testsuite substitutions + */ + +/* FIXME: make this extendible */ + +enum { + TESTSUITE_SUBSTITUTION_FILE, +}; + +static const struct testsuite_substitution_def testsuite_file_substitution; + +static const struct testsuite_substitution_def *substitutions[] = { + &testsuite_file_substitution, +}; + +static const unsigned int substitutions_count = N_ELEMENTS(substitutions); + +static inline const struct testsuite_substitution_def * +testsuite_substitution_get +(unsigned int code) +{ + if ( code >= substitutions_count ) + return NULL; + + return substitutions[code]; +} + +static const struct testsuite_substitution *testsuite_substitution_create +(struct sieve_ast *ast, const char *identifier) +{ + unsigned int i; + + for ( i = 0; i < substitutions_count; i++ ) { + if ( strcasecmp(substitutions[i]->obj_def.identifier, identifier) == 0 ) { + const struct testsuite_substitution_def *tsub_def = substitutions[i]; + struct testsuite_substitution *tsub; + + tsub = p_new(sieve_ast_pool(ast), struct testsuite_substitution, 1); + tsub->object.def = &tsub_def->obj_def; + tsub->object.ext = testsuite_ext; + tsub->def = tsub_def; + + return tsub; + } + } + + return NULL; +} + +/* + * Substitution argument + */ + +static bool arg_testsuite_substitution_generate + (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *context); + +struct _testsuite_substitution_context { + const struct testsuite_substitution *tsub; + const char *param; +}; + +const struct sieve_argument_def testsuite_substitution_argument = { + .identifier = "@testsuite-substitution", + .generate = arg_testsuite_substitution_generate +}; + +struct sieve_ast_argument *testsuite_substitution_argument_create +(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_ast *ast, + unsigned int source_line, const char *substitution, const char *param) +{ + const struct testsuite_substitution *tsub; + struct _testsuite_substitution_context *tsctx; + struct sieve_ast_argument *arg; + pool_t pool; + + tsub = testsuite_substitution_create(ast, substitution); + if ( tsub == NULL ) + return NULL; + + arg = sieve_ast_argument_create(ast, source_line); + arg->type = SAAT_STRING; + + pool = sieve_ast_pool(ast); + tsctx = p_new(pool, struct _testsuite_substitution_context, 1); + tsctx->tsub = tsub; + tsctx->param = p_strdup(pool, param); + + arg->argument = sieve_argument_create + (ast, &testsuite_substitution_argument, testsuite_ext, 0); + arg->argument->data = (void *) tsctx; + + return arg; +} + +static bool arg_testsuite_substitution_generate +(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *context ATTR_UNUSED) +{ + struct _testsuite_substitution_context *tsctx = + (struct _testsuite_substitution_context *) arg->argument->data; + + testsuite_opr_substitution_emit(cgenv->sblock, tsctx->tsub, tsctx->param); + + return TRUE; +} + +/* + * Substitution operand + */ + +static bool opr_substitution_dump + (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, + sieve_size_t *address); +static int opr_substitution_read_value + (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + sieve_size_t *address, string_t **str); + +const struct sieve_opr_string_interface testsuite_substitution_interface = { + opr_substitution_dump, + opr_substitution_read_value +}; + +const struct sieve_operand_def testsuite_substitution_operand = { + .name = "test-substitution", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERAND_SUBSTITUTION, + .class = &string_class, + .interface = &testsuite_substitution_interface +}; + +void testsuite_opr_substitution_emit +(struct sieve_binary_block *sblock, const struct testsuite_substitution *tsub, + const char *param) +{ + /* Default variable storage */ + (void) sieve_operand_emit + (sblock, testsuite_ext, &testsuite_substitution_operand); + (void) sieve_binary_emit_unsigned(sblock, tsub->object.def->code); + (void) sieve_binary_emit_cstring(sblock, param); +} + +static bool opr_substitution_dump +(const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, + sieve_size_t *address) +{ + unsigned int code = 0; + const struct testsuite_substitution_def *tsub; + string_t *param; + + if ( !sieve_binary_read_unsigned(denv->sblock, address, &code) ) + return FALSE; + + tsub = testsuite_substitution_get(code); + if ( tsub == NULL ) + return FALSE; + + if ( !sieve_binary_read_string(denv->sblock, address, ¶m) ) + return FALSE; + + if ( oprnd->field_name != NULL ) + sieve_code_dumpf(denv, "%s: TEST_SUBS %%{%s:%s}", + oprnd->field_name, tsub->obj_def.identifier, str_c(param)); + else + sieve_code_dumpf(denv, "TEST_SUBS %%{%s:%s}", + tsub->obj_def.identifier, str_c(param)); + return TRUE; +} + +static int opr_substitution_read_value +(const struct sieve_runtime_env *renv, + const struct sieve_operand *oprnd ATTR_UNUSED, sieve_size_t *address, + string_t **str_r) +{ + const struct testsuite_substitution_def *tsub; + unsigned int code = 0; + string_t *param; + + if ( !sieve_binary_read_unsigned(renv->sblock, address, &code) ) + return SIEVE_EXEC_BIN_CORRUPT; + + tsub = testsuite_substitution_get(code); + if ( tsub == NULL ) + return SIEVE_EXEC_FAILURE; + + /* Parameter str can be NULL if we are requested to only skip and not + * actually read the argument. + */ + if ( str_r == NULL ) { + if ( !sieve_binary_read_string(renv->sblock, address, NULL) ) + return SIEVE_EXEC_BIN_CORRUPT; + + return SIEVE_EXEC_OK; + } + + if ( !sieve_binary_read_string(renv->sblock, address, ¶m) ) + return SIEVE_EXEC_BIN_CORRUPT; + + if ( !tsub->get_value(str_c(param), str_r) ) + return SIEVE_EXEC_FAILURE; + + return SIEVE_EXEC_OK; +} + +/* + * Testsuite substitution definitions + */ + +static bool testsuite_file_substitution_get_value + (const char *param, string_t **result); + +static const struct testsuite_substitution_def +testsuite_file_substitution = { + SIEVE_OBJECT("file", + &testsuite_substitution_operand, + TESTSUITE_SUBSTITUTION_FILE), + .get_value = testsuite_file_substitution_get_value +}; + +static bool testsuite_file_substitution_get_value +(const char *param, string_t **result) +{ + *result = t_str_new(256); + + str_printfa(*result, "[FILE: %s]", param); + return TRUE; +} + diff --git a/pigeonhole/src/testsuite/testsuite-substitutions.h b/pigeonhole/src/testsuite/testsuite-substitutions.h new file mode 100644 index 0000000..8b256d0 --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-substitutions.h @@ -0,0 +1,23 @@ +#ifndef TESTSUITE_SUBSTITUTIONS_H +#define TESTSUITE_SUBSTITUTIONS_H + +#include "sieve-common.h" +#include "sieve-objects.h" + +struct testsuite_substitution_def { + struct sieve_object_def obj_def; + + bool (*get_value)(const char *param, string_t **result); +}; + +struct testsuite_substitution { + struct sieve_object object; + + const struct testsuite_substitution_def *def; +}; + +struct sieve_ast_argument *testsuite_substitution_argument_create + (struct sieve_validator *valdtr, struct sieve_ast *ast, + unsigned int source_line, const char *substitution, const char *param); + +#endif diff --git a/pigeonhole/src/testsuite/testsuite-variables.c b/pigeonhole/src/testsuite/testsuite-variables.c new file mode 100644 index 0000000..f644393 --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-variables.c @@ -0,0 +1,183 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" + +#include "sieve-common.h" +#include "sieve-ast.h" +#include "sieve-binary.h" +#include "sieve-code.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "sieve-ext-variables.h" + +#include "testsuite-common.h" +#include "testsuite-variables.h" + +/* + * + */ + +static const struct sieve_extension *testsuite_ext_variables = NULL; + +/* + * + */ + +bool testsuite_varnamespace_validate + (struct sieve_validator *valdtr, const struct sieve_variables_namespace *nspc, + struct sieve_ast_argument *arg, struct sieve_command *cmd, + ARRAY_TYPE(sieve_variable_name) *var_name, void **var_data, + bool assignment); +bool testsuite_varnamespace_generate + (const struct sieve_codegen_env *cgenv, + const struct sieve_variables_namespace *nspc, + struct sieve_ast_argument *arg, struct sieve_command *cmd, void *var_data); +bool testsuite_varnamespace_dump_variable + (const struct sieve_dumptime_env *denv, + const struct sieve_variables_namespace *nspc, + const struct sieve_operand *oprnd, sieve_size_t *address); +int testsuite_varnamespace_read_variable + (const struct sieve_runtime_env *renv, + const struct sieve_variables_namespace *nspc, + const struct sieve_operand *oprnd, sieve_size_t *address, string_t **str_r); + +static const struct sieve_variables_namespace_def testsuite_namespace = { + SIEVE_OBJECT("tst", &testsuite_namespace_operand, 0), + testsuite_varnamespace_validate, + testsuite_varnamespace_generate, + testsuite_varnamespace_dump_variable, + testsuite_varnamespace_read_variable +}; + +bool testsuite_varnamespace_validate +(struct sieve_validator *valdtr, + const struct sieve_variables_namespace *nspc ATTR_UNUSED, + struct sieve_ast_argument *arg, struct sieve_command *cmd ATTR_UNUSED, + ARRAY_TYPE(sieve_variable_name) *var_name, void **var_data, + bool assignment) +{ + struct sieve_ast *ast = arg->ast; + const struct sieve_variable_name *name_element; + const char *variable; + + /* Check variable name */ + + if ( array_count(var_name) != 2 ) { + sieve_argument_validate_error(valdtr, arg, + "testsuite: invalid variable name within testsuite namespace: " + "encountered sub-namespace"); + return FALSE; + } + + name_element = array_idx(var_name, 1); + if ( name_element->num_variable >= 0 ) { + sieve_argument_validate_error(valdtr, arg, + "testsuite: invalid variable name within testsuite namespace 'tst.%d': " + "encountered numeric variable name", name_element->num_variable); + return FALSE; + } + + variable = str_c(name_element->identifier); + + if ( assignment ) { + sieve_argument_validate_error(valdtr, arg, + "testsuite: cannot assign to testsuite variable 'tst.%s'", variable); + return FALSE; + } + + *var_data = (void *) p_strdup(sieve_ast_pool(ast), variable); + + return TRUE; +} + +bool testsuite_varnamespace_generate +(const struct sieve_codegen_env *cgenv, + const struct sieve_variables_namespace *nspc, + struct sieve_ast_argument *arg ATTR_UNUSED, + struct sieve_command *cmd ATTR_UNUSED, void *var_data) +{ + const struct sieve_extension *this_ext = SIEVE_OBJECT_EXTENSION(nspc); + const char *variable = (const char *) var_data; + + if ( this_ext == NULL ) + return FALSE; + + sieve_variables_opr_namespace_variable_emit + (cgenv->sblock, testsuite_ext_variables, this_ext, &testsuite_namespace); + sieve_binary_emit_cstring(cgenv->sblock, variable); + + return TRUE; +} + +bool testsuite_varnamespace_dump_variable +(const struct sieve_dumptime_env *denv, + const struct sieve_variables_namespace *nspc ATTR_UNUSED, + const struct sieve_operand *oprnd, sieve_size_t *address) +{ + string_t *var_name; + + if ( !sieve_binary_read_string(denv->sblock, address, &var_name) ) + return FALSE; + + if ( oprnd->field_name != NULL ) + sieve_code_dumpf(denv, "%s: VAR ${tst.%s}", + oprnd->field_name, str_c(var_name)); + else + sieve_code_dumpf(denv, "VAR ${tst.%s}", + str_c(var_name)); + + return TRUE; +} + +int testsuite_varnamespace_read_variable +(const struct sieve_runtime_env *renv, + const struct sieve_variables_namespace *nspc ATTR_UNUSED, + const struct sieve_operand *oprnd, sieve_size_t *address, + string_t **str_r) +{ + string_t *var_name; + + if ( !sieve_binary_read_string(renv->sblock, address, &var_name) ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "testsuite variable operand corrupt: invalid name"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if ( str_r != NULL ) { + if ( strcmp(str_c(var_name), "path") == 0 ) + *str_r = t_str_new_const(testsuite_test_path, strlen(testsuite_test_path)); + else + *str_r = NULL; + } + return SIEVE_EXEC_OK; +} + + +/* + * Namespace registration + */ + +static const struct sieve_extension_objects testsuite_namespaces = + SIEVE_VARIABLES_DEFINE_NAMESPACE(testsuite_namespace); + +const struct sieve_operand_def testsuite_namespace_operand = { + .name = "testsuite-namespace", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERAND_NAMESPACE, + .class = &sieve_variables_namespace_operand_class, + .interface = &testsuite_namespaces +}; + +void testsuite_variables_init +(const struct sieve_extension *this_ext, struct sieve_validator *valdtr) +{ + testsuite_ext_variables = sieve_ext_variables_get_extension(this_ext->svinst); + + sieve_variables_namespace_register + (testsuite_ext_variables, valdtr, this_ext, &testsuite_namespace); +} diff --git a/pigeonhole/src/testsuite/testsuite-variables.h b/pigeonhole/src/testsuite/testsuite-variables.h new file mode 100644 index 0000000..60f6b18 --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite-variables.h @@ -0,0 +1,11 @@ +#ifndef TESTSUITE_VARIABLES_H +#define TESTSUITE_VARIABLES_H + +extern const struct sieve_operand_def testsuite_namespace_operand; + +void testsuite_variables_init + (const struct sieve_extension *this_ext, struct sieve_validator *valdtr); + +#endif + + diff --git a/pigeonhole/src/testsuite/testsuite.c b/pigeonhole/src/testsuite/testsuite.c new file mode 100644 index 0000000..2a6b82e --- /dev/null +++ b/pigeonhole/src/testsuite/testsuite.c @@ -0,0 +1,256 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "lib-signals.h" +#include "ioloop.h" +#include "env-util.h" +#include "ostream.h" +#include "hostpid.h" +#include "path-util.h" + +#include "sieve.h" +#include "sieve-extensions.h" +#include "sieve-script.h" +#include "sieve-binary.h" +#include "sieve-result.h" +#include "sieve-interpreter.h" + +#include "sieve-tool.h" + +#include "testsuite-common.h" +#include "testsuite-log.h" +#include "testsuite-settings.h" +#include "testsuite-result.h" +#include "testsuite-message.h" +#include "testsuite-smtp.h" +#include "testsuite-mailstore.h" + +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <pwd.h> +#include <sysexits.h> + +const struct sieve_script_env *testsuite_scriptenv; + +/* + * Configuration + */ + +#define DEFAULT_SENDMAIL_PATH "/usr/lib/sendmail" + +/* + * Testsuite execution + */ + +static void print_help(void) +{ + printf( +"Usage: testsuite [-D] [-E] [-F] [-d <dump-filename>]\n" +" [-t <trace-filename>] [-T <trace-option>]\n" +" [-P <plugin>] [-x <extensions>]\n" +" <scriptfile>\n" + ); +} + +static int +testsuite_run(struct sieve_binary *sbin, struct sieve_error_handler *ehandler) +{ + struct sieve_interpreter *interp; + struct sieve_result *result; + int ret = 0; + + /* Create the interpreter */ + interp = sieve_interpreter_create(sbin, NULL, &testsuite_execute_env, + ehandler); + if (interp == NULL) + return SIEVE_EXEC_BIN_CORRUPT; + + /* Run the interpreter */ + result = testsuite_result_get(); + ret = sieve_interpreter_run(interp, result); + + /* Free the interpreter */ + sieve_interpreter_free(&interp); + + return ret; +} + +int main(int argc, char **argv) +{ + struct sieve_instance *svinst; + const char *scriptfile, *dumpfile, *tracefile; + struct sieve_trace_config trace_config; + struct sieve_binary *sbin; + const char *sieve_dir, *cwd, *error; + bool log_stdout = FALSE, expect_failure = FALSE; + int ret, c; + + sieve_tool = sieve_tool_init("testsuite", &argc, &argv, + "d:t:T:EFDP:", TRUE); + + /* Parse arguments */ + dumpfile = tracefile = NULL; + i_zero(&trace_config); + trace_config.level = SIEVE_TRLVL_ACTIONS; + while ((c = sieve_tool_getopt(sieve_tool)) > 0) { + switch (c) { + case 'd': + /* destination address */ + dumpfile = optarg; + break; + case 't': + /* trace file */ + tracefile = optarg; + break; + case 'T': + sieve_tool_parse_trace_option(&trace_config, optarg); + break; + case 'E': + log_stdout = TRUE; + break; + case 'F': + expect_failure = TRUE; + break; + default: + print_help(); + i_fatal_status(EX_USAGE, "Unknown argument: %c", c); + break; + } + } + + if (optind < argc) { + scriptfile = t_strdup(argv[optind++]); + } else { + print_help(); + i_fatal_status(EX_USAGE, "Missing <scriptfile> argument"); + } + + if (optind != argc) { + print_help(); + i_fatal_status(EX_USAGE, "Unknown argument: %s", argv[optind]); + } + + // FIXME: very very ugly + master_service_parse_option( + master_service, 'o', + "postmaster_address=postmaster@example.com"); + + /* Initialize mail user */ + if (t_get_working_dir(&cwd, &error) < 0) + i_fatal("Failed to get working directory: %s", error); + sieve_tool_set_homedir(sieve_tool, cwd); + + /* Initialize settings environment */ + testsuite_settings_init(); + + /* Currently needed for include (FIXME) */ + sieve_dir = strrchr(scriptfile, '/'); + if (sieve_dir == NULL) + sieve_dir = "./"; + else + sieve_dir = t_strdup_until(scriptfile, sieve_dir+1); + + testsuite_setting_set("sieve_dir", + t_strconcat(sieve_dir, "included", NULL)); + testsuite_setting_set("sieve_global_dir", + t_strconcat(sieve_dir, "included-global", NULL)); + + /* Finish testsuite initialization */ + svinst = sieve_tool_init_finish(sieve_tool, FALSE, FALSE); + testsuite_init(svinst, sieve_dir, log_stdout); + + printf("Test case: %s:\n\n", scriptfile); + + /* Compile sieve script */ + if ((sbin = sieve_compile(svinst, scriptfile, NULL, + testsuite_log_main_ehandler, + 0, NULL)) != NULL) { + struct sieve_trace_log *trace_log = NULL; + struct sieve_exec_status exec_status; + struct sieve_script_env scriptenv; + + /* Dump script */ + sieve_tool_dump_binary_to(sbin, dumpfile, FALSE); + + if (tracefile != NULL) { + (void)sieve_trace_log_create( + svinst, (strcmp(tracefile, "-") == 0 ? + NULL : tracefile), &trace_log); + } + + testsuite_mailstore_init(); + testsuite_message_init(); + + if (sieve_script_env_init(&scriptenv, + testsuite_mailstore_get_user(), + &error) < 0) { + i_fatal("Failed to initialize script execution: %s", + error); + } + + i_zero(&exec_status); + + scriptenv.default_mailbox = "INBOX"; + scriptenv.smtp_start = testsuite_smtp_start; + scriptenv.smtp_add_rcpt = testsuite_smtp_add_rcpt; + scriptenv.smtp_send = testsuite_smtp_send; + scriptenv.smtp_abort = testsuite_smtp_abort; + scriptenv.smtp_finish = testsuite_smtp_finish; + scriptenv.trace_log = trace_log; + scriptenv.trace_config = trace_config; + scriptenv.exec_status = &exec_status; + + testsuite_scriptenv = &scriptenv; + + testsuite_result_init(); + + /* Run the test */ + ret = testsuite_run(sbin, testsuite_log_main_ehandler); + + switch (ret) { + case SIEVE_EXEC_OK: + break; + case SIEVE_EXEC_FAILURE: + case SIEVE_EXEC_KEEP_FAILED: + case SIEVE_EXEC_TEMP_FAILURE: + testsuite_testcase_fail( + "test script execution aborted due to error"); + break; + case SIEVE_EXEC_BIN_CORRUPT: + testsuite_testcase_fail( + "compiled test script binary is corrupt"); + break; + case SIEVE_EXEC_RESOURCE_LIMIT: + testsuite_testcase_fail( + "resource limit exceeded"); + break; + } + + sieve_close(&sbin); + + /* De-initialize message environment */ + testsuite_result_deinit(); + testsuite_message_deinit(); + testsuite_mailstore_deinit(); + + if (trace_log != NULL) + sieve_trace_log_free(&trace_log); + + testsuite_scriptenv = NULL; + } else { + testsuite_testcase_fail("failed to compile testcase script"); + } + + /* De-initialize testsuite */ + testsuite_deinit(); + testsuite_settings_deinit(); + + sieve_tool_deinit(&sieve_tool); + + if (!testsuite_testcase_result(expect_failure)) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} diff --git a/pigeonhole/src/testsuite/tst-test-error.c b/pigeonhole/src/testsuite/tst-test-error.c new file mode 100644 index 0000000..f2f2eb9 --- /dev/null +++ b/pigeonhole/src/testsuite/tst-test-error.c @@ -0,0 +1,273 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-script.h" +#include "sieve-commands.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-code.h" +#include "sieve-binary.h" +#include "sieve-dump.h" +#include "sieve-match.h" + +#include "testsuite-common.h" +#include "testsuite-log.h" + +/* + * Test_error command + * + * Syntax: + * test [MATCH-TYPE] [COMPARATOR] [:index number] <key-list: string-list> + */ + +static bool tst_test_error_registered + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool tst_test_error_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); +static bool tst_test_error_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx); + +const struct sieve_command_def tst_test_error = { + .identifier = "test_error", + .type = SCT_TEST, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = tst_test_error_registered, + .validate = tst_test_error_validate, + .generate = tst_test_error_generate +}; + +/* + * Operation + */ + +static bool tst_test_error_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int tst_test_error_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def test_error_operation = { + .mnemonic = "TEST_ERROR", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERATION_TEST_ERROR, + .dump = tst_test_error_operation_dump, + .execute = tst_test_error_operation_execute +}; + +/* + * Tagged arguments + */ + +/* NOTE: This will be merged with the date-index extension when it is + * implemented. + */ + +static bool tst_test_error_validate_index_tag + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd); + +static const struct sieve_argument_def test_error_index_tag = { + .identifier = "index", + .validate = tst_test_error_validate_index_tag +}; + +enum tst_test_error_optional { + OPT_INDEX = SIEVE_MATCH_OPT_LAST, +}; + + +/* + * Argument implementation + */ + +static bool tst_test_error_validate_index_tag +(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + + /* Detach the tag itself */ + *arg = sieve_ast_arguments_detach(*arg,1); + + /* Check syntax: + * :index number + */ + if ( !sieve_validate_tag_parameter + (valdtr, cmd, tag, *arg, NULL, 0, SAAT_NUMBER, FALSE) ) { + return FALSE; + } + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + return TRUE; +} + + +/* + * Command registration + */ + +static bool tst_test_error_registered +(struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + /* The order of these is not significant */ + sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR); + sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE); + + sieve_validator_register_tag + (valdtr, cmd_reg, ext, &test_error_index_tag, OPT_INDEX); + + return TRUE; +} + +/* + * Validation + */ + +static bool tst_test_error_validate +(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + struct sieve_comparator cmp_default = + SIEVE_COMPARATOR_DEFAULT(i_octet_comparator); + struct sieve_match_type mcht_default = + SIEVE_COMPARATOR_DEFAULT(is_match_type); + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "key list", 2, SAAT_STRING_LIST) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) + return FALSE; + + /* Validate the key argument to a specified match type */ + return sieve_match_type_validate + (valdtr, tst, arg, &mcht_default, &cmp_default); +} + +/* + * Code generation + */ + +static bool tst_test_error_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *tst) +{ + sieve_operation_emit(cgenv->sblock, tst->ext, &test_error_operation); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, tst, NULL); +} + +/* + * Code dump + */ + +static bool tst_test_error_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + int opt_code = 0; + + sieve_code_dumpf(denv, "TEST_ERROR:"); + sieve_code_descend(denv); + + /* Handle any optional arguments */ + for (;;) { + int opt; + + if ( (opt=sieve_match_opr_optional_dump(denv, address, &opt_code)) + < 0 ) + return FALSE; + + if ( opt == 0 ) break; + + if ( opt_code == OPT_INDEX ) { + if ( !sieve_opr_number_dump(denv, address, "index") ) + return FALSE; + } else { + return FALSE; + } + } + + return sieve_opr_stringlist_dump(denv, address, "key list"); +} + +/* + * Intepretation + */ + +static int tst_test_error_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + int opt_code = 0; + struct sieve_comparator cmp = SIEVE_COMPARATOR_DEFAULT(i_octet_comparator); + struct sieve_match_type mcht = SIEVE_COMPARATOR_DEFAULT(is_match_type); + struct sieve_stringlist *value_list, *key_list; + int index = -1; + int match, ret; + + /* + * Read operands + */ + + /* Read optional operands */ + for (;;) { + sieve_number_t number; + int opt; + + if ( (opt=sieve_match_opr_optional_read + (renv, address, &opt_code, &ret, &cmp, &mcht)) < 0 ) + return ret; + + if ( opt == 0 ) break; + + if ( opt_code == OPT_INDEX ) { + if ( (ret=sieve_opr_number_read(renv, address, "index", &number)) <= 0 ) + return ret; + index = (int) number; + } else { + sieve_runtime_trace_error(renv, "invalid optional operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + } + + /* Read key-list */ + if ( (ret=sieve_opr_stringlist_read(renv, address, "key_list", &key_list)) + <= 0 ) + return ret; + + /* + * Perform operation + */ + + if ( index > 0 ) + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "testsuite: test_error test; match error message [index=%d]", index); + else + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "testsuite: test_error test; match error messages"); + + /* Create value stringlist */ + value_list = testsuite_log_stringlist_create(renv, index); + + /* Perform match */ + if ( (match=sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret)) < 0 ) + return ret; + + /* Set test result for subsequent conditional jump */ + sieve_interpreter_set_test_result(renv->interp, match > 0); + return SIEVE_EXEC_OK; +} + + + + diff --git a/pigeonhole/src/testsuite/tst-test-multiscript.c b/pigeonhole/src/testsuite/tst-test-multiscript.c new file mode 100644 index 0000000..b4b2a0e --- /dev/null +++ b/pigeonhole/src/testsuite/tst-test-multiscript.c @@ -0,0 +1,155 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-script.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-binary.h" +#include "sieve-dump.h" +#include "sieve.h" + +#include "testsuite-common.h" +#include "testsuite-script.h" + +/* + * Test_multiscript command + * + * Syntax: + * test_multiscript <scripts: string-list> + */ + +static bool tst_test_multiscript_validate + (struct sieve_validator *validator, struct sieve_command *cmd); +static bool tst_test_multiscript_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *tst); + +const struct sieve_command_def tst_test_multiscript = { + .identifier = "test_multiscript", + .type = SCT_TEST, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = tst_test_multiscript_validate, + .generate = tst_test_multiscript_generate, +}; + +/* + * Operation + */ + +static bool tst_test_multiscript_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int tst_test_multiscript_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def test_multiscript_operation = { + .mnemonic = "TEST_MULTISCRIPT", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERATION_TEST_MULTISCRIPT, + .dump = tst_test_multiscript_operation_dump, + .execute = tst_test_multiscript_operation_execute +}; + +/* + * Validation + */ + +static bool tst_test_multiscript_validate +(struct sieve_validator *valdtr, struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "scripts", 1, SAAT_STRING_LIST) ) { + return FALSE; + } + + return sieve_validator_argument_activate(valdtr, tst, arg, FALSE); +} + +/* + * Code generation + */ + +static bool tst_test_multiscript_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *tst) +{ + sieve_operation_emit(cgenv->sblock, tst->ext, &test_multiscript_operation); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, tst, NULL); +} + +/* + * Code dump + */ + +static bool tst_test_multiscript_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + sieve_code_dumpf(denv, "TEST_MULTISCRIPT:"); + sieve_code_descend(denv); + + if ( !sieve_opr_stringlist_dump(denv, address, "scripts") ) + return FALSE; + + return TRUE; +} + +/* + * Intepretation + */ + +static int tst_test_multiscript_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + struct sieve_stringlist *scripts_list; + string_t *script_name; + ARRAY_TYPE (const_string) scriptfiles; + bool result = TRUE; + int ret; + + /* + * Read operands + */ + + if ( (ret=sieve_opr_stringlist_read(renv, address, "scripts", &scripts_list)) + <= 0 ) + return ret; + + /* + * Perform operation + */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "testsuite: test_multiscript test"); + sieve_runtime_trace_descend(renv); + + t_array_init(&scriptfiles, 16); + + script_name = NULL; + while ( result && + (ret=sieve_stringlist_next_item(scripts_list, &script_name)) > 0 ) { + const char *script = t_strdup(str_c(script_name)); + + array_append(&scriptfiles, &script, 1); + } + + result = result && (ret >= 0) && + testsuite_script_multiscript(renv, &scriptfiles); + + /* Set result */ + sieve_interpreter_set_test_result(renv->interp, result); + + return SIEVE_EXEC_OK; +} + + + + diff --git a/pigeonhole/src/testsuite/tst-test-result-action.c b/pigeonhole/src/testsuite/tst-test-result-action.c new file mode 100644 index 0000000..75d4a2f --- /dev/null +++ b/pigeonhole/src/testsuite/tst-test-result-action.c @@ -0,0 +1,268 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-script.h" +#include "sieve-commands.h" +#include "sieve-actions.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-code.h" +#include "sieve-binary.h" +#include "sieve-result.h" +#include "sieve-dump.h" +#include "sieve-match.h" + +#include "testsuite-common.h" +#include "testsuite-result.h" + +/* + * test_result_action command + * + * Syntax: + * test_result_action [MATCH-TYPE] [COMPARATOR] [:index number] + * <key-list: string-list> + */ + +static bool tst_test_result_action_registered + (struct sieve_validator *validator, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool tst_test_result_action_validate + (struct sieve_validator *validator, struct sieve_command *cmd); +static bool tst_test_result_action_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx); + +const struct sieve_command_def tst_test_result_action = { + .identifier = "test_result_action", + .type = SCT_TEST, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = tst_test_result_action_registered, + .validate = tst_test_result_action_validate, + .generate = tst_test_result_action_generate +}; + +/* + * Operation + */ + +static bool tst_test_result_action_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int tst_test_result_action_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def test_result_action_operation = { + .mnemonic = "TEST_RESULT_ACTION", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERATION_TEST_RESULT_ACTION, + .dump = tst_test_result_action_operation_dump, + .execute = tst_test_result_action_operation_execute +}; + +/* + * Tagged arguments + */ + +/* FIXME: merge this with the test_error version of this tag */ + +static bool tst_test_result_action_validate_index_tag + (struct sieve_validator *validator, struct sieve_ast_argument **arg, + struct sieve_command *cmd); + +static const struct sieve_argument_def test_result_action_index_tag = { + .identifier = "index", + .validate = tst_test_result_action_validate_index_tag +}; + +enum tst_test_result_action_optional { + OPT_INDEX = SIEVE_MATCH_OPT_LAST, +}; + +/* + * Argument implementation + */ + +static bool tst_test_result_action_validate_index_tag +(struct sieve_validator *validator, struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + + /* Detach the tag itself */ + *arg = sieve_ast_arguments_detach(*arg,1); + + /* Check syntax: + * :index number + */ + if ( !sieve_validate_tag_parameter + (validator, cmd, tag, *arg, NULL, 0, SAAT_NUMBER, FALSE) ) { + return FALSE; + } + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + return TRUE; +} + + +/* + * Command registration + */ + +static bool tst_test_result_action_registered +(struct sieve_validator *validator, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + /* The order of these is not significant */ + sieve_comparators_link_tag(validator, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR); + sieve_match_types_link_tags(validator, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE); + + sieve_validator_register_tag + (validator, cmd_reg, ext, &test_result_action_index_tag, OPT_INDEX); + + return TRUE; +} + +/* + * Validation + */ + +static bool tst_test_result_action_validate +(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + struct sieve_comparator cmp_default = + SIEVE_COMPARATOR_DEFAULT(i_octet_comparator); + struct sieve_match_type mcht_default = + SIEVE_COMPARATOR_DEFAULT(is_match_type); + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "key list", 2, SAAT_STRING_LIST) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) + return FALSE; + + /* Validate the key argument to a specified match type */ + return sieve_match_type_validate + (valdtr, tst, arg, &mcht_default, &cmp_default); +} + +/* + * Code generation + */ + +static bool tst_test_result_action_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *tst) +{ + sieve_operation_emit(cgenv->sblock, tst->ext, &test_result_action_operation); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, tst, NULL); +} + +/* + * Code dump + */ + +static bool tst_test_result_action_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + int opt_code = 0; + + sieve_code_dumpf(denv, "TEST_RESULT_ACTION:"); + sieve_code_descend(denv); + + /* Handle any optional arguments */ + for (;;) { + int opt; + + if ( (opt=sieve_match_opr_optional_dump(denv, address, &opt_code)) + < 0 ) + return FALSE; + + if ( opt == 0 ) break; + + if ( opt_code == OPT_INDEX ) { + if ( !sieve_opr_number_dump(denv, address, "index") ) + return FALSE; + } else { + return FALSE; + } + } + + return sieve_opr_stringlist_dump(denv, address, "key list"); +} + +/* + * Intepretation + */ + +static int tst_test_result_action_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + int opt_code = 0; + struct sieve_comparator cmp = SIEVE_COMPARATOR_DEFAULT(i_octet_comparator); + struct sieve_match_type mcht = SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + struct sieve_stringlist *value_list, *key_list; + int index = 0; + int match, ret; + + /* + * Read operands + */ + + /* Read optional operands */ + for (;;) { + sieve_number_t number; + int opt; + + if ( (opt=sieve_match_opr_optional_read + (renv, address, &opt_code, &ret, &cmp, &mcht)) < 0 ) + return ret; + + if ( opt == 0 ) break; + + if ( opt_code == OPT_INDEX ) { + if ( (ret=sieve_opr_number_read(renv, address, "index", &number)) <= 0 ) + return ret; + index = (int) number; + } else { + sieve_runtime_trace_error(renv, "invalid optional operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + } + + /* Read key-list */ + if ( (ret=sieve_opr_stringlist_read(renv, address, "key-list", &key_list)) + <= 0 ) + return ret; + + /* + * Perform operation + */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "testsuite: test_result_action test; match result name (index: %d)", index); + + /* Create value stringlist */ + value_list = testsuite_result_stringlist_create(renv, index); + + /* Perform match */ + if ( (match=sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret)) < 0 ) + return ret; + + /* Set test result for subsequent conditional jump */ + sieve_interpreter_set_test_result(renv->interp, match > 0); + return SIEVE_EXEC_OK; +} + + + diff --git a/pigeonhole/src/testsuite/tst-test-result-execute.c b/pigeonhole/src/testsuite/tst-test-result-execute.c new file mode 100644 index 0000000..1b9a7ee --- /dev/null +++ b/pigeonhole/src/testsuite/tst-test-result-execute.c @@ -0,0 +1,96 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-script.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-code.h" +#include "sieve-binary.h" +#include "sieve-dump.h" +#include "sieve.h" + +#include "testsuite-common.h" +#include "testsuite-result.h" + +/* + * Test_result_execute command + * + * Syntax: + * test_result_execute + */ + +static bool tst_test_result_execute_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd); + +const struct sieve_command_def tst_test_result_execute = { + .identifier = "test_result_execute", + .type = SCT_TEST, + .positional_args = 0, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .generate = tst_test_result_execute_generate +}; + +/* + * Operation + */ + +static int tst_test_result_execute_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def test_result_execute_operation = { + .mnemonic = "TEST_RESULT_EXECUTE", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERATION_TEST_RESULT_EXECUTE, + .execute = tst_test_result_execute_operation_execute +}; + +/* + * Code generation + */ + +static bool tst_test_result_execute_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *tst) +{ + sieve_operation_emit(cgenv->sblock, tst->ext, &test_result_execute_operation); + + return TRUE; +} + +/* + * Intepretation + */ + +static int tst_test_result_execute_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address ATTR_UNUSED) +{ + bool result = TRUE; + + /* + * Perform operation + */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "testsuite: test_result_execute test"); + + result = testsuite_result_execute(renv); + + if ( sieve_runtime_trace_active(renv, SIEVE_TRLVL_TESTS) ) { + sieve_runtime_trace_descend(renv); + sieve_runtime_trace(renv, 0, "execution of result %s", + ( result ? "succeeded" : "failed" )); + } + + /* Set result */ + sieve_interpreter_set_test_result(renv->interp, result); + + return SIEVE_EXEC_OK; +} + + + + diff --git a/pigeonhole/src/testsuite/tst-test-script-compile.c b/pigeonhole/src/testsuite/tst-test-script-compile.c new file mode 100644 index 0000000..c053486 --- /dev/null +++ b/pigeonhole/src/testsuite/tst-test-script-compile.c @@ -0,0 +1,144 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-script.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-code.h" +#include "sieve-binary.h" +#include "sieve-dump.h" +#include "sieve.h" + +#include "testsuite-common.h" +#include "testsuite-script.h" + +/* + * Test_script_compile command + * + * Syntax: + * test_script_compile <scriptpath: string> + */ + +static bool tst_test_script_compile_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); +static bool tst_test_script_compile_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd); + +const struct sieve_command_def tst_test_script_compile = { + .identifier = "test_script_compile", + .type = SCT_TEST, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = tst_test_script_compile_validate, + .generate = tst_test_script_compile_generate +}; + +/* + * Operation + */ + +static bool tst_test_script_compile_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int tst_test_script_compile_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def test_script_compile_operation = { + .mnemonic = "TEST_SCRIPT_COMPILE", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERATION_TEST_SCRIPT_COMPILE, + .dump = tst_test_script_compile_operation_dump, + .execute = tst_test_script_compile_operation_execute +}; + +/* + * Validation + */ + +static bool tst_test_script_compile_validate +(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "script", 1, SAAT_STRING) ) { + return FALSE; + } + + return sieve_validator_argument_activate(valdtr, tst, arg, FALSE); +} + +/* + * Code generation + */ + +static bool tst_test_script_compile_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *tst) +{ + sieve_operation_emit(cgenv->sblock, tst->ext, &test_script_compile_operation); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, tst, NULL); +} + +/* + * Code dump + */ + +static bool tst_test_script_compile_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + sieve_code_dumpf(denv, "TEST_SCRIPT_COMPILE:"); + sieve_code_descend(denv); + + if ( !sieve_opr_string_dump(denv, address, "script-name") ) + return FALSE; + + return TRUE; +} + +/* + * Intepretation + */ + +static int tst_test_script_compile_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + string_t *script_name; + bool result = TRUE; + int ret; + + /* + * Read operands + */ + + if ( (ret=sieve_opr_string_read(renv, address, "script-name", &script_name)) + <= 0 ) + return ret; + + /* + * Perform operation + */ + + if ( sieve_runtime_trace_active(renv, SIEVE_TRLVL_TESTS) ) { + sieve_runtime_trace(renv, 0, "testsuite: test_script_compile test"); + sieve_runtime_trace_descend(renv); + } + + /* Attempt script compile */ + + result = testsuite_script_compile(renv, str_c(script_name)); + + /* Set result */ + sieve_interpreter_set_test_result(renv->interp, result); + + return SIEVE_EXEC_OK; +} + + + + diff --git a/pigeonhole/src/testsuite/tst-test-script-run.c b/pigeonhole/src/testsuite/tst-test-script-run.c new file mode 100644 index 0000000..214a5c6 --- /dev/null +++ b/pigeonhole/src/testsuite/tst-test-script-run.c @@ -0,0 +1,198 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-script.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-code.h" +#include "sieve-binary.h" +#include "sieve-dump.h" +#include "sieve.h" + +#include "testsuite-common.h" +#include "testsuite-script.h" +#include "testsuite-result.h" + +/* + * Test_script_run command + * + * Syntax: + * test_script_run + */ + +static bool tst_test_script_run_registered +(struct sieve_validator *validator, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool tst_test_script_run_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd); + +const struct sieve_command_def tst_test_script_run = { + .identifier = "test_script_run", + .type = SCT_TEST, + .positional_args = 0, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = tst_test_script_run_registered, + .generate = tst_test_script_run_generate +}; + +/* + * Operation + */ + +static bool tst_test_script_run_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int tst_test_script_run_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def test_script_run_operation = { + .mnemonic = "TEST_SCRIPT_RUN", + .ext_def = &testsuite_extension, + .code = TESTSUITE_OPERATION_TEST_SCRIPT_RUN, + .dump = tst_test_script_run_operation_dump, + .execute = tst_test_script_run_operation_execute +}; + +/* + * Tagged arguments + */ + +/* Codes for optional arguments */ + +enum cmd_vacation_optional { + OPT_END, + OPT_APPEND_RESULT +}; + +/* Tags */ + +static const struct sieve_argument_def append_result_tag = { + .identifier = "append_result" +}; + +static bool tst_test_script_run_registered +(struct sieve_validator *validator, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + sieve_validator_register_tag + (validator, cmd_reg, ext, &append_result_tag, OPT_APPEND_RESULT); + + return TRUE; +} + + +/* + * Code generation + */ + +static bool tst_test_script_run_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *tst) +{ + sieve_operation_emit(cgenv->sblock, tst->ext, &test_script_run_operation); + + return sieve_generate_arguments(cgenv, tst, NULL); +} + +/* + * Code dump + */ + +static bool tst_test_script_run_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + int opt_code = 0; + + sieve_code_dumpf(denv, "TEST_SCRIPT_RUN"); + sieve_code_descend(denv); + + /* Dump optional operands */ + for (;;) { + int opt; + + if ( (opt=sieve_opr_optional_dump(denv, address, &opt_code)) < 0 ) + return FALSE; + + if ( opt == 0 ) break; + + switch ( opt_code ) { + case OPT_APPEND_RESULT: + sieve_code_dumpf(denv, "append_result"); + break; + default: + return FALSE; + } + } + + return TRUE; +} + + +/* + * Intepretation + */ + +static int tst_test_script_run_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + bool append_result = FALSE; + int opt_code = 0; + bool result = TRUE; + + /* + * Read operands + */ + + /* Optional operands */ + for (;;) { + int opt; + + if ( (opt=sieve_opr_optional_read(renv, address, &opt_code)) < 0 ) + return SIEVE_EXEC_BIN_CORRUPT; + + if ( opt == 0 ) break; + + switch ( opt_code ) { + case OPT_APPEND_RESULT: + append_result = TRUE; + break; + default: + sieve_runtime_trace_error(renv, + "unknown optional operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + } + + /* + * Perform operation + */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "testsuite: run compiled script [append_result=%s]", + ( append_result ? "yes" : "no" )); + + /* Reset result object */ + if ( !append_result ) + testsuite_result_reset(renv); + + /* Run script */ + result = testsuite_script_run(renv); + + if ( sieve_runtime_trace_active(renv, SIEVE_TRLVL_TESTS) ) { + sieve_runtime_trace_descend(renv); + sieve_runtime_trace(renv, 0, "execution of script %s", + ( result ? "succeeded" : "failed" )); + } + + /* Indicate test status */ + sieve_interpreter_set_test_result(renv->interp, result); + + return SIEVE_EXEC_OK; +} + + + + |