diff options
Diffstat (limited to 'pigeonhole/src/testsuite')
45 files changed, 8699 insertions, 0 deletions
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; +} + + + + |