diff options
Diffstat (limited to 'pigeonhole/src/plugins/sieve-extprograms')
12 files changed, 3140 insertions, 0 deletions
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..6cd11bf --- /dev/null +++ b/pigeonhole/src/plugins/sieve-extprograms/Makefile.in @@ -0,0 +1,790 @@ +# Makefile.in generated by automake 1.16.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2018 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/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 |